From 142361f5b4b2a3891b624e5f0dda00f562ca6a51 Mon Sep 17 00:00:00 2001
From: Peter Ludikovsky
Date: Tue, 23 Jul 2019 14:33:29 +0200
Subject: [PATCH] Initial import
---
_data/.htaccess | 2 +
_data/calendar.json | 3 +
_include/.htaccess | 2 +
_include/calendar.inc.php | 56 +
_include/calendar/autoload.php | 7 +
_include/calendar/composer/ClassLoader.php | 445 +
_include/calendar/composer/LICENSE | 21 +
.../calendar/composer/autoload_classmap.php | 9 +
_include/calendar/composer/autoload_files.php | 10 +
.../calendar/composer/autoload_namespaces.php | 10 +
_include/calendar/composer/autoload_psr4.php | 12 +
_include/calendar/composer/autoload_real.php | 70 +
.../calendar/composer/autoload_static.php | 56 +
_include/calendar/composer/installed.json | 253 +
.../johngrogg/ics-parser/.editorconfig | 11 +
.../ics-parser/.github/CONTRIBUTING.md | 17 +
.../ics-parser/.github/ISSUE_TEMPLATE.md | 15 +
.../.github/PULL_REQUEST_TEMPLATE.md | 9 +
.../ics-parser/.github/RELEASE_CHECKLIST.md | 8 +
.../calendar/johngrogg/ics-parser/.gitignore | 51 +
.../calendar/johngrogg/ics-parser/LICENSE | 15 +
.../calendar/johngrogg/ics-parser/README.md | 178 +
.../johngrogg/ics-parser/composer.json | 49 +
.../johngrogg/ics-parser/composer.lock | 274 +
.../johngrogg/ics-parser/examples/ICal.ics | 309 +
.../johngrogg/ics-parser/examples/index.php | 175 +
.../johngrogg/ics-parser/src/ICal/Event.php | 204 +
.../johngrogg/ics-parser/src/ICal/ICal.php | 2430 +++
_include/calendar/nesbot/carbon/LICENSE | 19 +
_include/calendar/nesbot/carbon/composer.json | 60 +
_include/calendar/nesbot/carbon/readme.md | 94 +
.../nesbot/carbon/src/Carbon/Carbon.php | 4841 +++++
.../carbon/src/Carbon/CarbonInterval.php | 1130 ++
.../nesbot/carbon/src/Carbon/CarbonPeriod.php | 1445 ++
.../Exceptions/InvalidDateException.php | 67 +
.../nesbot/carbon/src/Carbon/Lang/af.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/ar.php | 31 +
.../carbon/src/Carbon/Lang/ar_Shakl.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/az.php | 40 +
.../nesbot/carbon/src/Carbon/Lang/bg.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/bn.php | 38 +
.../nesbot/carbon/src/Carbon/Lang/bs_BA.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/ca.php | 40 +
.../nesbot/carbon/src/Carbon/Lang/cs.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/cy.php | 29 +
.../nesbot/carbon/src/Carbon/Lang/da.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/de.php | 46 +
.../nesbot/carbon/src/Carbon/Lang/dv_MV.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/el.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/en.php | 40 +
.../nesbot/carbon/src/Carbon/Lang/eo.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/es.php | 36 +
.../nesbot/carbon/src/Carbon/Lang/et.php | 38 +
.../nesbot/carbon/src/Carbon/Lang/eu.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/fa.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/fi.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/fo.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/fr.php | 40 +
.../nesbot/carbon/src/Carbon/Lang/gl.php | 24 +
.../nesbot/carbon/src/Carbon/Lang/gu.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/he.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/hi.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/hr.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/hu.php | 52 +
.../nesbot/carbon/src/Carbon/Lang/hy.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/id.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/is.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/it.php | 36 +
.../nesbot/carbon/src/Carbon/Lang/ja.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/ka.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/kk.php | 29 +
.../nesbot/carbon/src/Carbon/Lang/km.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/ko.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/lt.php | 38 +
.../nesbot/carbon/src/Carbon/Lang/lv.php | 47 +
.../nesbot/carbon/src/Carbon/Lang/mk.php | 24 +
.../nesbot/carbon/src/Carbon/Lang/mn.php | 62 +
.../nesbot/carbon/src/Carbon/Lang/ms.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/my.php | 37 +
.../nesbot/carbon/src/Carbon/Lang/ne.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/nl.php | 36 +
.../nesbot/carbon/src/Carbon/Lang/no.php | 36 +
.../nesbot/carbon/src/Carbon/Lang/oc.php | 44 +
.../nesbot/carbon/src/Carbon/Lang/pl.php | 36 +
.../nesbot/carbon/src/Carbon/Lang/ps.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/pt.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/pt_BR.php | 40 +
.../nesbot/carbon/src/Carbon/Lang/ro.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/ru.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/sh.php | 35 +
.../nesbot/carbon/src/Carbon/Lang/sk.php | 38 +
.../nesbot/carbon/src/Carbon/Lang/sl.php | 43 +
.../nesbot/carbon/src/Carbon/Lang/sq.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/sr.php | 37 +
.../nesbot/carbon/src/Carbon/Lang/sr_Cyrl.php | 43 +
.../carbon/src/Carbon/Lang/sr_Cyrl_ME.php | 43 +
.../carbon/src/Carbon/Lang/sr_Latn_ME.php | 43 +
.../nesbot/carbon/src/Carbon/Lang/sr_ME.php | 12 +
.../nesbot/carbon/src/Carbon/Lang/sv.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/sw.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/th.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/tr.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/uk.php | 40 +
.../nesbot/carbon/src/Carbon/Lang/ur.php | 24 +
.../nesbot/carbon/src/Carbon/Lang/uz.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/vi.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/zh.php | 31 +
.../nesbot/carbon/src/Carbon/Lang/zh_TW.php | 31 +
.../src/Carbon/Laravel/ServiceProvider.php | 37 +
.../nesbot/carbon/src/Carbon/Translator.php | 143 +
.../nesbot/carbon/src/JsonSerializable.php | 18 +
.../symfony/polyfill-mbstring/LICENSE | 19 +
.../symfony/polyfill-mbstring/Mbstring.php | 789 +
.../symfony/polyfill-mbstring/README.md | 13 +
.../Resources/unidata/lowerCase.php | 1101 ++
.../Resources/unidata/titleCaseRegexp.php | 5 +
.../Resources/unidata/upperCase.php | 1109 ++
.../symfony/polyfill-mbstring/bootstrap.php | 58 +
.../symfony/polyfill-mbstring/composer.json | 34 +
.../calendar/symfony/translation/.gitignore | 3 +
.../calendar/symfony/translation/CHANGELOG.md | 108 +
.../Catalogue/AbstractOperation.php | 157 +
.../translation/Catalogue/MergeOperation.php | 55 +
.../Catalogue/OperationInterface.php | 77 +
.../translation/Catalogue/TargetOperation.php | 69 +
.../translation/Command/XliffLintCommand.php | 270 +
.../TranslationDataCollector.php | 167 +
.../translation/DataCollectorTranslator.php | 165 +
.../TranslationDumperPass.php | 44 +
.../TranslationExtractorPass.php | 49 +
.../DependencyInjection/TranslatorPass.php | 79 +
.../translation/Dumper/CsvFileDumper.php | 63 +
.../translation/Dumper/DumperInterface.php | 31 +
.../symfony/translation/Dumper/FileDumper.php | 113 +
.../translation/Dumper/IcuResFileDumper.php | 106 +
.../translation/Dumper/IniFileDumper.php | 45 +
.../translation/Dumper/JsonFileDumper.php | 44 +
.../translation/Dumper/MoFileDumper.php | 82 +
.../translation/Dumper/PhpFileDumper.php | 38 +
.../translation/Dumper/PoFileDumper.php | 61 +
.../translation/Dumper/QtFileDumper.php | 50 +
.../translation/Dumper/XliffFileDumper.php | 205 +
.../translation/Dumper/YamlFileDumper.php | 62 +
.../Exception/ExceptionInterface.php | 21 +
.../Exception/InvalidArgumentException.php | 21 +
.../Exception/InvalidResourceException.php | 21 +
.../translation/Exception/LogicException.php | 21 +
.../Exception/NotFoundResourceException.php | 21 +
.../Exception/RuntimeException.php | 21 +
.../Extractor/AbstractFileExtractor.php | 80 +
.../translation/Extractor/ChainExtractor.php | 60 +
.../Extractor/ExtractorInterface.php | 38 +
.../translation/Extractor/PhpExtractor.php | 256 +
.../Extractor/PhpStringTokenParser.php | 142 +
.../ChoiceMessageFormatterInterface.php | 30 +
.../Formatter/MessageFormatter.php | 48 +
.../Formatter/MessageFormatterInterface.php | 30 +
.../translation/IdentityTranslator.php | 63 +
.../calendar/symfony/translation/Interval.php | 109 +
_include/calendar/symfony/translation/LICENSE | 19 +
.../translation/Loader/ArrayLoader.php | 66 +
.../translation/Loader/CsvFileLoader.php | 65 +
.../symfony/translation/Loader/FileLoader.php | 65 +
.../translation/Loader/IcuDatFileLoader.php | 61 +
.../translation/Loader/IcuResFileLoader.php | 91 +
.../translation/Loader/IniFileLoader.php | 28 +
.../translation/Loader/JsonFileLoader.php | 64 +
.../translation/Loader/LoaderInterface.php | 38 +
.../translation/Loader/MoFileLoader.php | 145 +
.../translation/Loader/PhpFileLoader.php | 28 +
.../translation/Loader/PoFileLoader.php | 148 +
.../translation/Loader/QtFileLoader.php | 77 +
.../translation/Loader/XliffFileLoader.php | 314 +
.../translation/Loader/YamlFileLoader.php | 50 +
.../dic/xliff-core/xliff-core-1.2-strict.xsd | 2223 +++
.../schema/dic/xliff-core/xliff-core-2.0.xsd | 411 +
.../Loader/schema/dic/xliff-core/xml.xsd | 309 +
.../symfony/translation/LoggingTranslator.php | 136 +
.../symfony/translation/MessageCatalogue.php | 271 +
.../translation/MessageCatalogueInterface.php | 136 +
.../symfony/translation/MessageSelector.php | 94 +
.../translation/MetadataAwareInterface.php | 54 +
.../translation/PluralizationRules.php | 210 +
.../calendar/symfony/translation/README.md | 13 +
.../translation/Reader/TranslationReader.php | 63 +
.../Reader/TranslationReaderInterface.php | 30 +
.../schemas/xliff-core-1.2-strict.xsd | 2223 +++
.../Tests/Catalogue/AbstractOperationTest.php | 74 +
.../Tests/Catalogue/MergeOperationTest.php | 83 +
.../Tests/Catalogue/TargetOperationTest.php | 82 +
.../Tests/Command/XliffLintCommandTest.php | 163 +
.../TranslationDataCollectorTest.php | 150 +
.../Tests/DataCollectorTranslatorTest.php | 92 +
.../TranslationDumperPassTest.php | 48 +
.../TranslationExtractorPassTest.php | 66 +
.../TranslationPassTest.php | 57 +
.../Tests/Dumper/CsvFileDumperTest.php | 30 +
.../Tests/Dumper/FileDumperTest.php | 66 +
.../Tests/Dumper/IcuResFileDumperTest.php | 29 +
.../Tests/Dumper/IniFileDumperTest.php | 29 +
.../Tests/Dumper/JsonFileDumperTest.php | 39 +
.../Tests/Dumper/MoFileDumperTest.php | 29 +
.../Tests/Dumper/PhpFileDumperTest.php | 29 +
.../Tests/Dumper/PoFileDumperTest.php | 29 +
.../Tests/Dumper/QtFileDumperTest.php | 29 +
.../Tests/Dumper/XliffFileDumperTest.php | 115 +
.../Tests/Dumper/YamlFileDumperTest.php | 47 +
.../Tests/Extractor/PhpExtractorTest.php | 95 +
.../Tests/Formatter/MessageFormatterTest.php | 82 +
.../Tests/IdentityTranslatorTest.php | 96 +
.../translation/Tests/IntervalTest.php | 49 +
.../Tests/Loader/CsvFileLoaderTest.php | 61 +
.../Tests/Loader/IcuDatFileLoaderTest.php | 64 +
.../Tests/Loader/IcuResFileLoaderTest.php | 51 +
.../Tests/Loader/IniFileLoaderTest.php | 51 +
.../Tests/Loader/JsonFileLoaderTest.php | 62 +
.../Tests/Loader/LocalizedTestCase.php | 24 +
.../Tests/Loader/MoFileLoaderTest.php | 72 +
.../Tests/Loader/PhpFileLoaderTest.php | 50 +
.../Tests/Loader/PoFileLoaderTest.php | 109 +
.../Tests/Loader/QtFileLoaderTest.php | 75 +
.../Tests/Loader/XliffFileLoaderTest.php | 260 +
.../Tests/Loader/YamlFileLoaderTest.php | 71 +
.../Tests/LoggingTranslatorTest.php | 50 +
.../Tests/MessageCatalogueTest.php | 222 +
.../translation/Tests/MessageSelectorTest.php | 137 +
.../Tests/PluralizationRulesTest.php | 122 +
.../translation/Tests/TranslatorCacheTest.php | 311 +
.../translation/Tests/TranslatorTest.php | 549 +
.../Tests/Util/ArrayConverterTest.php | 74 +
.../Tests/Writer/TranslationWriterTest.php | 69 +
.../Tests/fixtures/empty-translation.mo | Bin 0 -> 49 bytes
.../Tests/fixtures/empty-translation.po | 3 +
.../translation/Tests/fixtures/empty.csv | 0
.../translation/Tests/fixtures/empty.ini | 0
.../translation/Tests/fixtures/empty.json | 0
.../translation/Tests/fixtures/empty.mo | 0
.../translation/Tests/fixtures/empty.po | 0
.../translation/Tests/fixtures/empty.xlf | 0
.../translation/Tests/fixtures/empty.yml | 0
.../translation/Tests/fixtures/encoding.xlf | 16 +
.../Tests/fixtures/escaped-id-plurals.po | 10 +
.../translation/Tests/fixtures/escaped-id.po | 8 +
.../fixtures/extractor/resource.format.engine | 0
.../this.is.a.template.format.engine | 0
.../fixtures/extractor/translation.html.php | 49 +
.../Tests/fixtures/fuzzy-translations.po | 10 +
.../Tests/fixtures/invalid-xml-resources.xlf | 23 +
.../translation/Tests/fixtures/malformed.json | 3 +
.../translation/Tests/fixtures/messages.yml | 3 +
.../Tests/fixtures/messages_linear.yml | 2 +
.../translation/Tests/fixtures/non-valid.xlf | 11 +
.../translation/Tests/fixtures/non-valid.yml | 1 +
.../translation/Tests/fixtures/plurals.mo | Bin 0 -> 74 bytes
.../translation/Tests/fixtures/plurals.po | 5 +
.../translation/Tests/fixtures/resname.xlf | 19 +
.../resourcebundle/corrupted/resources.dat | 1 +
.../Tests/fixtures/resourcebundle/dat/en.res | Bin 0 -> 120 bytes
.../Tests/fixtures/resourcebundle/dat/en.txt | 3 +
.../Tests/fixtures/resourcebundle/dat/fr.res | Bin 0 -> 124 bytes
.../Tests/fixtures/resourcebundle/dat/fr.txt | 3 +
.../resourcebundle/dat/packagelist.txt | 2 +
.../fixtures/resourcebundle/dat/resources.dat | Bin 0 -> 352 bytes
.../Tests/fixtures/resourcebundle/res/en.res | Bin 0 -> 84 bytes
.../Tests/fixtures/resources-2.0-clean.xlf | 23 +
.../resources-2.0-multi-segment-unit.xlf | 17 +
.../Tests/fixtures/resources-2.0.xlf | 25 +
.../Tests/fixtures/resources-clean.xlf | 25 +
.../Tests/fixtures/resources-notes-meta.xlf | 26 +
.../fixtures/resources-target-attributes.xlf | 14 +
.../Tests/fixtures/resources-tool-info.xlf | 14 +
.../translation/Tests/fixtures/resources.csv | 4 +
.../Tests/fixtures/resources.dump.json | 1 +
.../translation/Tests/fixtures/resources.ini | 1 +
.../translation/Tests/fixtures/resources.json | 3 +
.../translation/Tests/fixtures/resources.mo | Bin 0 -> 52 bytes
.../translation/Tests/fixtures/resources.php | 5 +
.../translation/Tests/fixtures/resources.po | 8 +
.../translation/Tests/fixtures/resources.ts | 10 +
.../translation/Tests/fixtures/resources.xlf | 23 +
.../translation/Tests/fixtures/resources.yml | 1 +
.../translation/Tests/fixtures/valid.csv | 4 +
.../Tests/fixtures/with-attributes.xlf | 21 +
.../Tests/fixtures/withdoctype.xlf | 12 +
.../translation/Tests/fixtures/withnote.xlf | 22 +
.../symfony/translation/Translator.php | 437 +
.../translation/TranslatorBagInterface.php | 33 +
.../translation/TranslatorInterface.php | 67 +
.../translation/Util/ArrayConverter.php | 99 +
.../translation/Writer/TranslationWriter.php | 90 +
.../Writer/TranslationWriterInterface.php | 34 +
.../symfony/translation/composer.json | 53 +
.../symfony/translation/phpunit.xml.dist | 30 +
_include/database.inc.php | 41 +
_include/functions.inc.php | 120 +
_include/session.inc.php | 18 +
_include/settings.inc.php | 12 +
_include/templates.inc.php | 16 +
_templates/.htaccess | 2 +
_templates/about.htm | 39 +
_templates/calendar.htm | 14 +
_templates/calendar.js | 20 +
_templates/datenschutz.htm | 24 +
_templates/impressum.htm | 20 +
_templates/index.htm | 79 +
_templates/index_old.htm | 67 +
_templates/index_v2.htm | 72 +
_templates/kontakt.htm | 26 +
_templates/main.htm | 87 +
_templates/main_old.htm | 81 +
_templates/main_v2.htm | 81 +
_templates/mitgliedschaft.htm | 40 +
_templates/sidebar.htm | 66 +
_templates/sidebar_old.htm | 70 +
_templates/sidebar_v2.htm | 63 +
_templates/stammtisch.htm | 16 +
_templates/stammtisch_map.js | 28 +
about.php | 16 +
assets/css/font-awesome.min.css | 4 +
assets/css/fullcalendar.css | 1293 ++
assets/css/fullcalendar.min.css | 5 +
assets/css/fullcalendar.print.css | 176 +
assets/css/fullcalendar.print.min.css | 9 +
assets/css/ie8.css | 21 +
assets/css/ie9.css | 73 +
assets/css/images/layers-2x.png | Bin 0 -> 1259 bytes
assets/css/images/layers.png | Bin 0 -> 696 bytes
assets/css/images/marker-icon-2x.png | Bin 0 -> 2586 bytes
assets/css/images/marker-icon.png | Bin 0 -> 1466 bytes
assets/css/images/marker-shadow.png | Bin 0 -> 618 bytes
assets/css/leaflet.css | 624 +
assets/css/main.css | 4112 +++++
assets/fonts/FontAwesome.otf | Bin 0 -> 134808 bytes
assets/fonts/fontawesome-webfont.eot | Bin 0 -> 165742 bytes
assets/fonts/fontawesome-webfont.svg | 2671 +++
assets/fonts/fontawesome-webfont.ttf | Bin 0 -> 165548 bytes
assets/fonts/fontawesome-webfont.woff | Bin 0 -> 98024 bytes
assets/fonts/fontawesome-webfont.woff2 | Bin 0 -> 77160 bytes
assets/js/fullcalendar/fullcalendar.js | 15010 ++++++++++++++++
assets/js/fullcalendar/fullcalendar.min.js | 12 +
assets/js/fullcalendar/gcal.js | 324 +
assets/js/fullcalendar/gcal.min.js | 6 +
assets/js/fullcalendar/lib/ical.js | 9325 ++++++++++
assets/js/fullcalendar/lib/ical_events.js | 40 +
.../js/fullcalendar/lib/ical_fullcalendar.js | 78 +
assets/js/fullcalendar/lib/jquery-ui.min.js | 13 +
assets/js/fullcalendar/lib/jquery.min.js | 2 +
assets/js/fullcalendar/lib/moment.min.js | 1 +
assets/js/fullcalendar/locale-all.js | 6 +
assets/js/fullcalendar/locale/af.js | 1 +
assets/js/fullcalendar/locale/ar-dz.js | 1 +
assets/js/fullcalendar/locale/ar-kw.js | 1 +
assets/js/fullcalendar/locale/ar-ly.js | 1 +
assets/js/fullcalendar/locale/ar-ma.js | 1 +
assets/js/fullcalendar/locale/ar-sa.js | 1 +
assets/js/fullcalendar/locale/ar-tn.js | 1 +
assets/js/fullcalendar/locale/ar.js | 1 +
assets/js/fullcalendar/locale/bg.js | 1 +
assets/js/fullcalendar/locale/bs.js | 1 +
assets/js/fullcalendar/locale/ca.js | 1 +
assets/js/fullcalendar/locale/cs.js | 1 +
assets/js/fullcalendar/locale/da.js | 1 +
assets/js/fullcalendar/locale/de-at.js | 1 +
assets/js/fullcalendar/locale/de-ch.js | 1 +
assets/js/fullcalendar/locale/de.js | 1 +
assets/js/fullcalendar/locale/el.js | 1 +
assets/js/fullcalendar/locale/en-au.js | 1 +
assets/js/fullcalendar/locale/en-ca.js | 1 +
assets/js/fullcalendar/locale/en-gb.js | 1 +
assets/js/fullcalendar/locale/en-ie.js | 1 +
assets/js/fullcalendar/locale/en-nz.js | 1 +
assets/js/fullcalendar/locale/es-do.js | 1 +
assets/js/fullcalendar/locale/es-us.js | 1 +
assets/js/fullcalendar/locale/es.js | 1 +
assets/js/fullcalendar/locale/et.js | 1 +
assets/js/fullcalendar/locale/eu.js | 1 +
assets/js/fullcalendar/locale/fa.js | 1 +
assets/js/fullcalendar/locale/fi.js | 1 +
assets/js/fullcalendar/locale/fr-ca.js | 1 +
assets/js/fullcalendar/locale/fr-ch.js | 1 +
assets/js/fullcalendar/locale/fr.js | 1 +
assets/js/fullcalendar/locale/gl.js | 1 +
assets/js/fullcalendar/locale/he.js | 1 +
assets/js/fullcalendar/locale/hi.js | 1 +
assets/js/fullcalendar/locale/hr.js | 1 +
assets/js/fullcalendar/locale/hu.js | 1 +
assets/js/fullcalendar/locale/id.js | 1 +
assets/js/fullcalendar/locale/is.js | 1 +
assets/js/fullcalendar/locale/it.js | 1 +
assets/js/fullcalendar/locale/ja.js | 1 +
assets/js/fullcalendar/locale/ka.js | 1 +
assets/js/fullcalendar/locale/kk.js | 1 +
assets/js/fullcalendar/locale/ko.js | 1 +
assets/js/fullcalendar/locale/lb.js | 1 +
assets/js/fullcalendar/locale/lt.js | 1 +
assets/js/fullcalendar/locale/lv.js | 1 +
assets/js/fullcalendar/locale/mk.js | 1 +
assets/js/fullcalendar/locale/ms-my.js | 1 +
assets/js/fullcalendar/locale/ms.js | 1 +
assets/js/fullcalendar/locale/nb.js | 1 +
assets/js/fullcalendar/locale/nl-be.js | 1 +
assets/js/fullcalendar/locale/nl.js | 1 +
assets/js/fullcalendar/locale/nn.js | 1 +
assets/js/fullcalendar/locale/pl.js | 1 +
assets/js/fullcalendar/locale/pt-br.js | 1 +
assets/js/fullcalendar/locale/pt.js | 1 +
assets/js/fullcalendar/locale/ro.js | 1 +
assets/js/fullcalendar/locale/ru.js | 1 +
assets/js/fullcalendar/locale/sk.js | 1 +
assets/js/fullcalendar/locale/sl.js | 1 +
assets/js/fullcalendar/locale/sq.js | 1 +
assets/js/fullcalendar/locale/sr-cyrl.js | 1 +
assets/js/fullcalendar/locale/sr.js | 1 +
assets/js/fullcalendar/locale/sv.js | 1 +
assets/js/fullcalendar/locale/th.js | 1 +
assets/js/fullcalendar/locale/tr.js | 1 +
assets/js/fullcalendar/locale/uk.js | 1 +
assets/js/fullcalendar/locale/vi.js | 1 +
assets/js/fullcalendar/locale/zh-cn.js | 1 +
assets/js/fullcalendar/locale/zh-tw.js | 1 +
assets/js/gcal.min.js | 6 +
assets/js/ie/html5shiv.js | 8 +
assets/js/ie/respond.min.js | 6 +
assets/js/jquery-ui.min.js | 13 +
assets/js/jquery.min.1.11.3.js | 5 +
assets/js/jquery.min.js | 4 +
assets/js/leaflet.js | 9 +
assets/js/main.js | 285 +
assets/js/skel.min.js | 2 +
assets/js/underscore-min.js | 6 +
assets/js/util.js | 587 +
calendar.html | 75 +
calendar.php | 212 +
common.php | 31 +
datenschutz.php | 16 +
datenschutzerklaerung.md | 54 +
datenschutzerklaerung.md.asc | 73 +
datenschutzerklaerung_mitglieder.md.asc | 43 +
images/3d-printer.jpg | Bin 0 -> 90222 bytes
images/3d-printer_small.jpg | Bin 0 -> 51691 bytes
images/Logo_SW.svg | 129 +
images/arduino.jpg | Bin 0 -> 110379 bytes
images/arduino_small.jpg | Bin 0 -> 54750 bytes
images/coding.jpg | Bin 0 -> 72928 bytes
images/coding_small.jpg | Bin 0 -> 42243 bytes
images/header.png | Bin 0 -> 243609 bytes
images/icon/android-icon-144x144.png | Bin 0 -> 22152 bytes
images/icon/android-icon-192x192.png | Bin 0 -> 27230 bytes
images/icon/android-icon-36x36.png | Bin 0 -> 3244 bytes
images/icon/android-icon-48x48.png | Bin 0 -> 4912 bytes
images/icon/android-icon-72x72.png | Bin 0 -> 8183 bytes
images/icon/android-icon-96x96.png | Bin 0 -> 11855 bytes
images/icon/apple-icon-114x114.png | Bin 0 -> 16325 bytes
images/icon/apple-icon-120x120.png | Bin 0 -> 17421 bytes
images/icon/apple-icon-144x144.png | Bin 0 -> 22152 bytes
images/icon/apple-icon-152x152.png | Bin 0 -> 24194 bytes
images/icon/apple-icon-180x180.png | Bin 0 -> 30807 bytes
images/icon/apple-icon-57x57.png | Bin 0 -> 5982 bytes
images/icon/apple-icon-60x60.png | Bin 0 -> 6299 bytes
images/icon/apple-icon-72x72.png | Bin 0 -> 8183 bytes
images/icon/apple-icon-76x76.png | Bin 0 -> 8611 bytes
images/icon/apple-icon-precomposed.png | Bin 0 -> 27804 bytes
images/icon/apple-icon.png | Bin 0 -> 27804 bytes
images/icon/favicon-16x16.png | Bin 0 -> 1617 bytes
images/icon/favicon-32x32.png | Bin 0 -> 2847 bytes
images/icon/favicon-96x96.png | Bin 0 -> 11855 bytes
images/icon/favicon.ico | Bin 0 -> 1150 bytes
images/icon/manifest.json | 41 +
images/icon/ms-icon-144x144.png | Bin 0 -> 22152 bytes
images/icon/ms-icon-150x150.png | Bin 0 -> 23686 bytes
images/icon/ms-icon-310x310.png | Bin 0 -> 69481 bytes
images/icon/ms-icon-70x70.png | Bin 0 -> 7937 bytes
images/laptop.jpg | Bin 0 -> 62152 bytes
images/laptop_small.jpg | Bin 0 -> 34483 bytes
images/logo.png | Bin 0 -> 44985 bytes
images/logo_klein.png | Bin 0 -> 12138 bytes
images/logo_raumformat_web.jpg | Bin 0 -> 47695 bytes
images/muehlgasse8-final.gif | Bin 0 -> 13768 bytes
images/openmind-logo_web.jpg | Bin 0 -> 44099 bytes
images/pic01.jpg | Bin 0 -> 20660 bytes
images/raumformat_logo_groß.jpg | Bin 0 -> 149167 bytes
images/raumformat_logo_web.jpg | Bin 0 -> 44298 bytes
images/solder-station.jpg | Bin 0 -> 83208 bytes
images/solder-station_banner.jpg | Bin 0 -> 235353 bytes
images/solder-station_small.jpg | Bin 0 -> 43735 bytes
impressum.php | 16 +
index.php | 22 +
kontakt.php | 19 +
logo_sw.png | Bin 0 -> 51792 bytes
mitgliedschaft.php | 16 +
paths.php | 18 +
robots.txt | 281 +
sitemap.xml | 29 +
stammtisch.php | 44 +
494 files changed, 71136 insertions(+)
create mode 100644 _data/.htaccess
create mode 100644 _data/calendar.json
create mode 100644 _include/.htaccess
create mode 100644 _include/calendar.inc.php
create mode 100644 _include/calendar/autoload.php
create mode 100644 _include/calendar/composer/ClassLoader.php
create mode 100644 _include/calendar/composer/LICENSE
create mode 100644 _include/calendar/composer/autoload_classmap.php
create mode 100644 _include/calendar/composer/autoload_files.php
create mode 100644 _include/calendar/composer/autoload_namespaces.php
create mode 100644 _include/calendar/composer/autoload_psr4.php
create mode 100644 _include/calendar/composer/autoload_real.php
create mode 100644 _include/calendar/composer/autoload_static.php
create mode 100644 _include/calendar/composer/installed.json
create mode 100644 _include/calendar/johngrogg/ics-parser/.editorconfig
create mode 100644 _include/calendar/johngrogg/ics-parser/.github/CONTRIBUTING.md
create mode 100644 _include/calendar/johngrogg/ics-parser/.github/ISSUE_TEMPLATE.md
create mode 100644 _include/calendar/johngrogg/ics-parser/.github/PULL_REQUEST_TEMPLATE.md
create mode 100644 _include/calendar/johngrogg/ics-parser/.github/RELEASE_CHECKLIST.md
create mode 100644 _include/calendar/johngrogg/ics-parser/.gitignore
create mode 100644 _include/calendar/johngrogg/ics-parser/LICENSE
create mode 100644 _include/calendar/johngrogg/ics-parser/README.md
create mode 100644 _include/calendar/johngrogg/ics-parser/composer.json
create mode 100644 _include/calendar/johngrogg/ics-parser/composer.lock
create mode 100644 _include/calendar/johngrogg/ics-parser/examples/ICal.ics
create mode 100644 _include/calendar/johngrogg/ics-parser/examples/index.php
create mode 100644 _include/calendar/johngrogg/ics-parser/src/ICal/Event.php
create mode 100644 _include/calendar/johngrogg/ics-parser/src/ICal/ICal.php
create mode 100644 _include/calendar/nesbot/carbon/LICENSE
create mode 100644 _include/calendar/nesbot/carbon/composer.json
create mode 100644 _include/calendar/nesbot/carbon/readme.md
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Carbon.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/CarbonInterval.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/CarbonPeriod.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Exceptions/InvalidDateException.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/af.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/ar.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/ar_Shakl.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/az.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/bg.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/bn.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/bs_BA.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/ca.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/cs.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/cy.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/da.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/de.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/dv_MV.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/el.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/en.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/eo.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/es.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/et.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/eu.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/fa.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/fi.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/fo.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/fr.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/gl.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/gu.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/he.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/hi.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/hr.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/hu.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/hy.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/id.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/is.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/it.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/ja.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/ka.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/kk.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/km.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/ko.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/lt.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/lv.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/mk.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/mn.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/ms.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/my.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/ne.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/nl.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/no.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/oc.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/pl.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/ps.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/pt.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/pt_BR.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/ro.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/ru.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/sh.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/sk.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/sl.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/sq.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/sr.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/sr_Cyrl.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_ME.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/sr_Latn_ME.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/sr_ME.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/sv.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/sw.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/th.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/tr.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/uk.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/ur.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/uz.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/vi.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/zh.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Lang/zh_TW.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Laravel/ServiceProvider.php
create mode 100644 _include/calendar/nesbot/carbon/src/Carbon/Translator.php
create mode 100644 _include/calendar/nesbot/carbon/src/JsonSerializable.php
create mode 100644 _include/calendar/symfony/polyfill-mbstring/LICENSE
create mode 100644 _include/calendar/symfony/polyfill-mbstring/Mbstring.php
create mode 100644 _include/calendar/symfony/polyfill-mbstring/README.md
create mode 100644 _include/calendar/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php
create mode 100644 _include/calendar/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php
create mode 100644 _include/calendar/symfony/polyfill-mbstring/Resources/unidata/upperCase.php
create mode 100644 _include/calendar/symfony/polyfill-mbstring/bootstrap.php
create mode 100644 _include/calendar/symfony/polyfill-mbstring/composer.json
create mode 100644 _include/calendar/symfony/translation/.gitignore
create mode 100644 _include/calendar/symfony/translation/CHANGELOG.md
create mode 100644 _include/calendar/symfony/translation/Catalogue/AbstractOperation.php
create mode 100644 _include/calendar/symfony/translation/Catalogue/MergeOperation.php
create mode 100644 _include/calendar/symfony/translation/Catalogue/OperationInterface.php
create mode 100644 _include/calendar/symfony/translation/Catalogue/TargetOperation.php
create mode 100644 _include/calendar/symfony/translation/Command/XliffLintCommand.php
create mode 100644 _include/calendar/symfony/translation/DataCollector/TranslationDataCollector.php
create mode 100644 _include/calendar/symfony/translation/DataCollectorTranslator.php
create mode 100644 _include/calendar/symfony/translation/DependencyInjection/TranslationDumperPass.php
create mode 100644 _include/calendar/symfony/translation/DependencyInjection/TranslationExtractorPass.php
create mode 100644 _include/calendar/symfony/translation/DependencyInjection/TranslatorPass.php
create mode 100644 _include/calendar/symfony/translation/Dumper/CsvFileDumper.php
create mode 100644 _include/calendar/symfony/translation/Dumper/DumperInterface.php
create mode 100644 _include/calendar/symfony/translation/Dumper/FileDumper.php
create mode 100644 _include/calendar/symfony/translation/Dumper/IcuResFileDumper.php
create mode 100644 _include/calendar/symfony/translation/Dumper/IniFileDumper.php
create mode 100644 _include/calendar/symfony/translation/Dumper/JsonFileDumper.php
create mode 100644 _include/calendar/symfony/translation/Dumper/MoFileDumper.php
create mode 100644 _include/calendar/symfony/translation/Dumper/PhpFileDumper.php
create mode 100644 _include/calendar/symfony/translation/Dumper/PoFileDumper.php
create mode 100644 _include/calendar/symfony/translation/Dumper/QtFileDumper.php
create mode 100644 _include/calendar/symfony/translation/Dumper/XliffFileDumper.php
create mode 100644 _include/calendar/symfony/translation/Dumper/YamlFileDumper.php
create mode 100644 _include/calendar/symfony/translation/Exception/ExceptionInterface.php
create mode 100644 _include/calendar/symfony/translation/Exception/InvalidArgumentException.php
create mode 100644 _include/calendar/symfony/translation/Exception/InvalidResourceException.php
create mode 100644 _include/calendar/symfony/translation/Exception/LogicException.php
create mode 100644 _include/calendar/symfony/translation/Exception/NotFoundResourceException.php
create mode 100644 _include/calendar/symfony/translation/Exception/RuntimeException.php
create mode 100644 _include/calendar/symfony/translation/Extractor/AbstractFileExtractor.php
create mode 100644 _include/calendar/symfony/translation/Extractor/ChainExtractor.php
create mode 100644 _include/calendar/symfony/translation/Extractor/ExtractorInterface.php
create mode 100644 _include/calendar/symfony/translation/Extractor/PhpExtractor.php
create mode 100644 _include/calendar/symfony/translation/Extractor/PhpStringTokenParser.php
create mode 100644 _include/calendar/symfony/translation/Formatter/ChoiceMessageFormatterInterface.php
create mode 100644 _include/calendar/symfony/translation/Formatter/MessageFormatter.php
create mode 100644 _include/calendar/symfony/translation/Formatter/MessageFormatterInterface.php
create mode 100644 _include/calendar/symfony/translation/IdentityTranslator.php
create mode 100644 _include/calendar/symfony/translation/Interval.php
create mode 100644 _include/calendar/symfony/translation/LICENSE
create mode 100644 _include/calendar/symfony/translation/Loader/ArrayLoader.php
create mode 100644 _include/calendar/symfony/translation/Loader/CsvFileLoader.php
create mode 100644 _include/calendar/symfony/translation/Loader/FileLoader.php
create mode 100644 _include/calendar/symfony/translation/Loader/IcuDatFileLoader.php
create mode 100644 _include/calendar/symfony/translation/Loader/IcuResFileLoader.php
create mode 100644 _include/calendar/symfony/translation/Loader/IniFileLoader.php
create mode 100644 _include/calendar/symfony/translation/Loader/JsonFileLoader.php
create mode 100644 _include/calendar/symfony/translation/Loader/LoaderInterface.php
create mode 100644 _include/calendar/symfony/translation/Loader/MoFileLoader.php
create mode 100644 _include/calendar/symfony/translation/Loader/PhpFileLoader.php
create mode 100644 _include/calendar/symfony/translation/Loader/PoFileLoader.php
create mode 100644 _include/calendar/symfony/translation/Loader/QtFileLoader.php
create mode 100644 _include/calendar/symfony/translation/Loader/XliffFileLoader.php
create mode 100644 _include/calendar/symfony/translation/Loader/YamlFileLoader.php
create mode 100644 _include/calendar/symfony/translation/Loader/schema/dic/xliff-core/xliff-core-1.2-strict.xsd
create mode 100644 _include/calendar/symfony/translation/Loader/schema/dic/xliff-core/xliff-core-2.0.xsd
create mode 100644 _include/calendar/symfony/translation/Loader/schema/dic/xliff-core/xml.xsd
create mode 100644 _include/calendar/symfony/translation/LoggingTranslator.php
create mode 100644 _include/calendar/symfony/translation/MessageCatalogue.php
create mode 100644 _include/calendar/symfony/translation/MessageCatalogueInterface.php
create mode 100644 _include/calendar/symfony/translation/MessageSelector.php
create mode 100644 _include/calendar/symfony/translation/MetadataAwareInterface.php
create mode 100644 _include/calendar/symfony/translation/PluralizationRules.php
create mode 100644 _include/calendar/symfony/translation/README.md
create mode 100644 _include/calendar/symfony/translation/Reader/TranslationReader.php
create mode 100644 _include/calendar/symfony/translation/Reader/TranslationReaderInterface.php
create mode 100644 _include/calendar/symfony/translation/Resources/schemas/xliff-core-1.2-strict.xsd
create mode 100644 _include/calendar/symfony/translation/Tests/Catalogue/AbstractOperationTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/Catalogue/MergeOperationTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/Catalogue/TargetOperationTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/Command/XliffLintCommandTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/DataCollector/TranslationDataCollectorTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/DataCollectorTranslatorTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/DependencyInjection/TranslationDumperPassTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/DependencyInjection/TranslationExtractorPassTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/DependencyInjection/TranslationPassTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/Dumper/CsvFileDumperTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/Dumper/FileDumperTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/Dumper/IcuResFileDumperTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/Dumper/IniFileDumperTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/Dumper/JsonFileDumperTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/Dumper/MoFileDumperTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/Dumper/PhpFileDumperTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/Dumper/PoFileDumperTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/Dumper/QtFileDumperTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/Dumper/XliffFileDumperTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/Dumper/YamlFileDumperTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/Extractor/PhpExtractorTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/Formatter/MessageFormatterTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/IdentityTranslatorTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/IntervalTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/Loader/CsvFileLoaderTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/Loader/IcuDatFileLoaderTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/Loader/IcuResFileLoaderTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/Loader/IniFileLoaderTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/Loader/JsonFileLoaderTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/Loader/LocalizedTestCase.php
create mode 100644 _include/calendar/symfony/translation/Tests/Loader/MoFileLoaderTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/Loader/PhpFileLoaderTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/Loader/PoFileLoaderTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/Loader/QtFileLoaderTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/Loader/XliffFileLoaderTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/Loader/YamlFileLoaderTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/LoggingTranslatorTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/MessageCatalogueTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/MessageSelectorTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/PluralizationRulesTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/TranslatorCacheTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/TranslatorTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/Util/ArrayConverterTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/Writer/TranslationWriterTest.php
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/empty-translation.mo
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/empty-translation.po
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/empty.csv
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/empty.ini
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/empty.json
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/empty.mo
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/empty.po
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/empty.xlf
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/empty.yml
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/encoding.xlf
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/escaped-id-plurals.po
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/escaped-id.po
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/extractor/resource.format.engine
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/extractor/this.is.a.template.format.engine
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/extractor/translation.html.php
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/fuzzy-translations.po
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/invalid-xml-resources.xlf
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/malformed.json
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/messages.yml
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/messages_linear.yml
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/non-valid.xlf
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/non-valid.yml
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/plurals.mo
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/plurals.po
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/resname.xlf
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/resourcebundle/corrupted/resources.dat
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/resourcebundle/dat/en.res
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/resourcebundle/dat/en.txt
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/resourcebundle/dat/fr.res
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/resourcebundle/dat/fr.txt
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/resourcebundle/dat/packagelist.txt
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/resourcebundle/dat/resources.dat
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/resourcebundle/res/en.res
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/resources-2.0-clean.xlf
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/resources-2.0-multi-segment-unit.xlf
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/resources-2.0.xlf
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/resources-clean.xlf
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/resources-notes-meta.xlf
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/resources-target-attributes.xlf
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/resources-tool-info.xlf
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/resources.csv
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/resources.dump.json
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/resources.ini
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/resources.json
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/resources.mo
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/resources.php
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/resources.po
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/resources.ts
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/resources.xlf
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/resources.yml
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/valid.csv
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/with-attributes.xlf
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/withdoctype.xlf
create mode 100644 _include/calendar/symfony/translation/Tests/fixtures/withnote.xlf
create mode 100644 _include/calendar/symfony/translation/Translator.php
create mode 100644 _include/calendar/symfony/translation/TranslatorBagInterface.php
create mode 100644 _include/calendar/symfony/translation/TranslatorInterface.php
create mode 100644 _include/calendar/symfony/translation/Util/ArrayConverter.php
create mode 100644 _include/calendar/symfony/translation/Writer/TranslationWriter.php
create mode 100644 _include/calendar/symfony/translation/Writer/TranslationWriterInterface.php
create mode 100644 _include/calendar/symfony/translation/composer.json
create mode 100644 _include/calendar/symfony/translation/phpunit.xml.dist
create mode 100644 _include/database.inc.php
create mode 100644 _include/functions.inc.php
create mode 100644 _include/session.inc.php
create mode 100644 _include/settings.inc.php
create mode 100644 _include/templates.inc.php
create mode 100644 _templates/.htaccess
create mode 100644 _templates/about.htm
create mode 100644 _templates/calendar.htm
create mode 100644 _templates/calendar.js
create mode 100644 _templates/datenschutz.htm
create mode 100644 _templates/impressum.htm
create mode 100644 _templates/index.htm
create mode 100644 _templates/index_old.htm
create mode 100644 _templates/index_v2.htm
create mode 100644 _templates/kontakt.htm
create mode 100644 _templates/main.htm
create mode 100644 _templates/main_old.htm
create mode 100644 _templates/main_v2.htm
create mode 100644 _templates/mitgliedschaft.htm
create mode 100644 _templates/sidebar.htm
create mode 100644 _templates/sidebar_old.htm
create mode 100644 _templates/sidebar_v2.htm
create mode 100644 _templates/stammtisch.htm
create mode 100644 _templates/stammtisch_map.js
create mode 100644 about.php
create mode 100644 assets/css/font-awesome.min.css
create mode 100644 assets/css/fullcalendar.css
create mode 100644 assets/css/fullcalendar.min.css
create mode 100644 assets/css/fullcalendar.print.css
create mode 100644 assets/css/fullcalendar.print.min.css
create mode 100644 assets/css/ie8.css
create mode 100644 assets/css/ie9.css
create mode 100644 assets/css/images/layers-2x.png
create mode 100644 assets/css/images/layers.png
create mode 100644 assets/css/images/marker-icon-2x.png
create mode 100644 assets/css/images/marker-icon.png
create mode 100644 assets/css/images/marker-shadow.png
create mode 100644 assets/css/leaflet.css
create mode 100644 assets/css/main.css
create mode 100644 assets/fonts/FontAwesome.otf
create mode 100644 assets/fonts/fontawesome-webfont.eot
create mode 100644 assets/fonts/fontawesome-webfont.svg
create mode 100644 assets/fonts/fontawesome-webfont.ttf
create mode 100644 assets/fonts/fontawesome-webfont.woff
create mode 100644 assets/fonts/fontawesome-webfont.woff2
create mode 100644 assets/js/fullcalendar/fullcalendar.js
create mode 100644 assets/js/fullcalendar/fullcalendar.min.js
create mode 100644 assets/js/fullcalendar/gcal.js
create mode 100644 assets/js/fullcalendar/gcal.min.js
create mode 100644 assets/js/fullcalendar/lib/ical.js
create mode 100644 assets/js/fullcalendar/lib/ical_events.js
create mode 100644 assets/js/fullcalendar/lib/ical_fullcalendar.js
create mode 100644 assets/js/fullcalendar/lib/jquery-ui.min.js
create mode 100644 assets/js/fullcalendar/lib/jquery.min.js
create mode 100644 assets/js/fullcalendar/lib/moment.min.js
create mode 100644 assets/js/fullcalendar/locale-all.js
create mode 100644 assets/js/fullcalendar/locale/af.js
create mode 100644 assets/js/fullcalendar/locale/ar-dz.js
create mode 100644 assets/js/fullcalendar/locale/ar-kw.js
create mode 100644 assets/js/fullcalendar/locale/ar-ly.js
create mode 100644 assets/js/fullcalendar/locale/ar-ma.js
create mode 100644 assets/js/fullcalendar/locale/ar-sa.js
create mode 100644 assets/js/fullcalendar/locale/ar-tn.js
create mode 100644 assets/js/fullcalendar/locale/ar.js
create mode 100644 assets/js/fullcalendar/locale/bg.js
create mode 100644 assets/js/fullcalendar/locale/bs.js
create mode 100644 assets/js/fullcalendar/locale/ca.js
create mode 100644 assets/js/fullcalendar/locale/cs.js
create mode 100644 assets/js/fullcalendar/locale/da.js
create mode 100644 assets/js/fullcalendar/locale/de-at.js
create mode 100644 assets/js/fullcalendar/locale/de-ch.js
create mode 100644 assets/js/fullcalendar/locale/de.js
create mode 100644 assets/js/fullcalendar/locale/el.js
create mode 100644 assets/js/fullcalendar/locale/en-au.js
create mode 100644 assets/js/fullcalendar/locale/en-ca.js
create mode 100644 assets/js/fullcalendar/locale/en-gb.js
create mode 100644 assets/js/fullcalendar/locale/en-ie.js
create mode 100644 assets/js/fullcalendar/locale/en-nz.js
create mode 100644 assets/js/fullcalendar/locale/es-do.js
create mode 100644 assets/js/fullcalendar/locale/es-us.js
create mode 100644 assets/js/fullcalendar/locale/es.js
create mode 100644 assets/js/fullcalendar/locale/et.js
create mode 100644 assets/js/fullcalendar/locale/eu.js
create mode 100644 assets/js/fullcalendar/locale/fa.js
create mode 100644 assets/js/fullcalendar/locale/fi.js
create mode 100644 assets/js/fullcalendar/locale/fr-ca.js
create mode 100644 assets/js/fullcalendar/locale/fr-ch.js
create mode 100644 assets/js/fullcalendar/locale/fr.js
create mode 100644 assets/js/fullcalendar/locale/gl.js
create mode 100644 assets/js/fullcalendar/locale/he.js
create mode 100644 assets/js/fullcalendar/locale/hi.js
create mode 100644 assets/js/fullcalendar/locale/hr.js
create mode 100644 assets/js/fullcalendar/locale/hu.js
create mode 100644 assets/js/fullcalendar/locale/id.js
create mode 100644 assets/js/fullcalendar/locale/is.js
create mode 100644 assets/js/fullcalendar/locale/it.js
create mode 100644 assets/js/fullcalendar/locale/ja.js
create mode 100644 assets/js/fullcalendar/locale/ka.js
create mode 100644 assets/js/fullcalendar/locale/kk.js
create mode 100644 assets/js/fullcalendar/locale/ko.js
create mode 100644 assets/js/fullcalendar/locale/lb.js
create mode 100644 assets/js/fullcalendar/locale/lt.js
create mode 100644 assets/js/fullcalendar/locale/lv.js
create mode 100644 assets/js/fullcalendar/locale/mk.js
create mode 100644 assets/js/fullcalendar/locale/ms-my.js
create mode 100644 assets/js/fullcalendar/locale/ms.js
create mode 100644 assets/js/fullcalendar/locale/nb.js
create mode 100644 assets/js/fullcalendar/locale/nl-be.js
create mode 100644 assets/js/fullcalendar/locale/nl.js
create mode 100644 assets/js/fullcalendar/locale/nn.js
create mode 100644 assets/js/fullcalendar/locale/pl.js
create mode 100644 assets/js/fullcalendar/locale/pt-br.js
create mode 100644 assets/js/fullcalendar/locale/pt.js
create mode 100644 assets/js/fullcalendar/locale/ro.js
create mode 100644 assets/js/fullcalendar/locale/ru.js
create mode 100644 assets/js/fullcalendar/locale/sk.js
create mode 100644 assets/js/fullcalendar/locale/sl.js
create mode 100644 assets/js/fullcalendar/locale/sq.js
create mode 100644 assets/js/fullcalendar/locale/sr-cyrl.js
create mode 100644 assets/js/fullcalendar/locale/sr.js
create mode 100644 assets/js/fullcalendar/locale/sv.js
create mode 100644 assets/js/fullcalendar/locale/th.js
create mode 100644 assets/js/fullcalendar/locale/tr.js
create mode 100644 assets/js/fullcalendar/locale/uk.js
create mode 100644 assets/js/fullcalendar/locale/vi.js
create mode 100644 assets/js/fullcalendar/locale/zh-cn.js
create mode 100644 assets/js/fullcalendar/locale/zh-tw.js
create mode 100644 assets/js/gcal.min.js
create mode 100644 assets/js/ie/html5shiv.js
create mode 100644 assets/js/ie/respond.min.js
create mode 100644 assets/js/jquery-ui.min.js
create mode 100644 assets/js/jquery.min.1.11.3.js
create mode 100644 assets/js/jquery.min.js
create mode 100644 assets/js/leaflet.js
create mode 100644 assets/js/main.js
create mode 100644 assets/js/skel.min.js
create mode 100644 assets/js/underscore-min.js
create mode 100644 assets/js/util.js
create mode 100644 calendar.html
create mode 100644 calendar.php
create mode 100644 common.php
create mode 100644 datenschutz.php
create mode 100644 datenschutzerklaerung.md
create mode 100644 datenschutzerklaerung.md.asc
create mode 100644 datenschutzerklaerung_mitglieder.md.asc
create mode 100644 images/3d-printer.jpg
create mode 100644 images/3d-printer_small.jpg
create mode 100644 images/Logo_SW.svg
create mode 100644 images/arduino.jpg
create mode 100644 images/arduino_small.jpg
create mode 100644 images/coding.jpg
create mode 100644 images/coding_small.jpg
create mode 100644 images/header.png
create mode 100644 images/icon/android-icon-144x144.png
create mode 100644 images/icon/android-icon-192x192.png
create mode 100644 images/icon/android-icon-36x36.png
create mode 100644 images/icon/android-icon-48x48.png
create mode 100644 images/icon/android-icon-72x72.png
create mode 100644 images/icon/android-icon-96x96.png
create mode 100644 images/icon/apple-icon-114x114.png
create mode 100644 images/icon/apple-icon-120x120.png
create mode 100644 images/icon/apple-icon-144x144.png
create mode 100644 images/icon/apple-icon-152x152.png
create mode 100644 images/icon/apple-icon-180x180.png
create mode 100644 images/icon/apple-icon-57x57.png
create mode 100644 images/icon/apple-icon-60x60.png
create mode 100644 images/icon/apple-icon-72x72.png
create mode 100644 images/icon/apple-icon-76x76.png
create mode 100644 images/icon/apple-icon-precomposed.png
create mode 100644 images/icon/apple-icon.png
create mode 100644 images/icon/favicon-16x16.png
create mode 100644 images/icon/favicon-32x32.png
create mode 100644 images/icon/favicon-96x96.png
create mode 100644 images/icon/favicon.ico
create mode 100644 images/icon/manifest.json
create mode 100644 images/icon/ms-icon-144x144.png
create mode 100644 images/icon/ms-icon-150x150.png
create mode 100644 images/icon/ms-icon-310x310.png
create mode 100644 images/icon/ms-icon-70x70.png
create mode 100644 images/laptop.jpg
create mode 100644 images/laptop_small.jpg
create mode 100644 images/logo.png
create mode 100644 images/logo_klein.png
create mode 100644 images/logo_raumformat_web.jpg
create mode 100644 images/muehlgasse8-final.gif
create mode 100644 images/openmind-logo_web.jpg
create mode 100644 images/pic01.jpg
create mode 100644 images/raumformat_logo_groß.jpg
create mode 100644 images/raumformat_logo_web.jpg
create mode 100644 images/solder-station.jpg
create mode 100644 images/solder-station_banner.jpg
create mode 100644 images/solder-station_small.jpg
create mode 100644 impressum.php
create mode 100644 index.php
create mode 100644 kontakt.php
create mode 100644 logo_sw.png
create mode 100644 mitgliedschaft.php
create mode 100644 paths.php
create mode 100644 robots.txt
create mode 100644 sitemap.xml
create mode 100644 stammtisch.php
diff --git a/_data/.htaccess b/_data/.htaccess
new file mode 100644
index 0000000..d57c506
--- /dev/null
+++ b/_data/.htaccess
@@ -0,0 +1,2 @@
+Satisfy Any
+Deny from all
diff --git a/_data/calendar.json b/_data/calendar.json
new file mode 100644
index 0000000..5d533c3
--- /dev/null
+++ b/_data/calendar.json
@@ -0,0 +1,3 @@
+{ "calendar":[
+ { "date":"2018-05-17", "link":"https://usrspace.at/w/index.php?title=Stammtisch_2018-05-17", "name":"Stammtisch" }
+]}
\ No newline at end of file
diff --git a/_include/.htaccess b/_include/.htaccess
new file mode 100644
index 0000000..d57c506
--- /dev/null
+++ b/_include/.htaccess
@@ -0,0 +1,2 @@
+Satisfy Any
+Deny from all
diff --git a/_include/calendar.inc.php b/_include/calendar.inc.php
new file mode 100644
index 0000000..5f21aef
--- /dev/null
+++ b/_include/calendar.inc.php
@@ -0,0 +1,56 @@
+ "Jänner", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember");
+ $today = date("w") ; // 0 = Sonntag
+ if ($today == 0) $today = 7;
+ // Zeitraum 5 Wochen
+ $first_day = strtotime("-1 week") - ((60*60*24)*($today-1)) ; // Erster Montag
+ $last_day = strtotime("+3 weeks") + ((60*60*24)*(7-$today)) ; // letzter Sonntag
+ if (date("F", $first_day) != date("F", $last_day)) { // 2 Monate
+ return $months[date("n", $first_day)] . " - " . $months[date("n", $last_day)] . date(" Y", $last_day) ;
+ } else {
+ return $months[date("F", $first_day)] . date(" Y", $first_day) ;
+ }
+}
+
+function gen_calendar_content()
+{
+ $today = date("w") ; // 0 = Sonntag
+ if ($today == 0) $today = 7;
+ // Zeitraum 5 Wochen
+ $first_day = strtotime("-1 week") - ((60*60*24)*($today-1)) ; // Erster Montag
+ $last_day = strtotime("+3 weeks") + ((60*60*24)*(7-$today)) ; // letzter Sonntag
+ $output = "";
+ $day = $first_day;
+
+
+ for ($w = 0; $w < 5; $w++) {
+ $output .= "";
+ for ($d = 0; $d < 7; $d++) {
+ if (date("Y-m-d", $day) == date("Y-m-d", time())) {
+ $output .= ''.date("d",$day)." ";
+ } else {
+ $output .= "".date("d",$day)." ";
+ }
+ $day = $day + (60*60*24);
+ }
+ $output .= " \n";
+ }
+ return $output;
+}
+
+function check_for_event($events, $date)
+{
+ foreach ( $events as $event ) {
+ if (date("Y-m-d", $event->dtstart_array[2]) == date("Y-m-d", $date)) {
+ return $event->summary;
+ }
+ }
+ return false;
+}
+
+?>
\ No newline at end of file
diff --git a/_include/calendar/autoload.php b/_include/calendar/autoload.php
new file mode 100644
index 0000000..c987faf
--- /dev/null
+++ b/_include/calendar/autoload.php
@@ -0,0 +1,7 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Autoload;
+
+/**
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
+ *
+ * $loader = new \Composer\Autoload\ClassLoader();
+ *
+ * // register classes with namespaces
+ * $loader->add('Symfony\Component', __DIR__.'/component');
+ * $loader->add('Symfony', __DIR__.'/framework');
+ *
+ * // activate the autoloader
+ * $loader->register();
+ *
+ * // to enable searching the include path (eg. for PEAR packages)
+ * $loader->setUseIncludePath(true);
+ *
+ * In this example, if you try to use a class in the Symfony\Component
+ * namespace or one of its children (Symfony\Component\Console for instance),
+ * the autoloader will first look for the class under the component/
+ * directory, and it will then fallback to the framework/ directory if not
+ * found before giving up.
+ *
+ * This class is loosely based on the Symfony UniversalClassLoader.
+ *
+ * @author Fabien Potencier
+ * @author Jordi Boggiano
+ * @see http://www.php-fig.org/psr/psr-0/
+ * @see http://www.php-fig.org/psr/psr-4/
+ */
+class ClassLoader
+{
+ // PSR-4
+ private $prefixLengthsPsr4 = array();
+ private $prefixDirsPsr4 = array();
+ private $fallbackDirsPsr4 = array();
+
+ // PSR-0
+ private $prefixesPsr0 = array();
+ private $fallbackDirsPsr0 = array();
+
+ private $useIncludePath = false;
+ private $classMap = array();
+ private $classMapAuthoritative = false;
+ private $missingClasses = array();
+ private $apcuPrefix;
+
+ public function getPrefixes()
+ {
+ if (!empty($this->prefixesPsr0)) {
+ return call_user_func_array('array_merge', $this->prefixesPsr0);
+ }
+
+ return array();
+ }
+
+ public function getPrefixesPsr4()
+ {
+ return $this->prefixDirsPsr4;
+ }
+
+ public function getFallbackDirs()
+ {
+ return $this->fallbackDirsPsr0;
+ }
+
+ public function getFallbackDirsPsr4()
+ {
+ return $this->fallbackDirsPsr4;
+ }
+
+ public function getClassMap()
+ {
+ return $this->classMap;
+ }
+
+ /**
+ * @param array $classMap Class to filename map
+ */
+ public function addClassMap(array $classMap)
+ {
+ if ($this->classMap) {
+ $this->classMap = array_merge($this->classMap, $classMap);
+ } else {
+ $this->classMap = $classMap;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix, either
+ * appending or prepending to the ones previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param array|string $paths The PSR-0 root directories
+ * @param bool $prepend Whether to prepend the directories
+ */
+ public function add($prefix, $paths, $prepend = false)
+ {
+ if (!$prefix) {
+ if ($prepend) {
+ $this->fallbackDirsPsr0 = array_merge(
+ (array) $paths,
+ $this->fallbackDirsPsr0
+ );
+ } else {
+ $this->fallbackDirsPsr0 = array_merge(
+ $this->fallbackDirsPsr0,
+ (array) $paths
+ );
+ }
+
+ return;
+ }
+
+ $first = $prefix[0];
+ if (!isset($this->prefixesPsr0[$first][$prefix])) {
+ $this->prefixesPsr0[$first][$prefix] = (array) $paths;
+
+ return;
+ }
+ if ($prepend) {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ (array) $paths,
+ $this->prefixesPsr0[$first][$prefix]
+ );
+ } else {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ $this->prefixesPsr0[$first][$prefix],
+ (array) $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace, either
+ * appending or prepending to the ones previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param array|string $paths The PSR-4 base directories
+ * @param bool $prepend Whether to prepend the directories
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function addPsr4($prefix, $paths, $prepend = false)
+ {
+ if (!$prefix) {
+ // Register directories for the root namespace.
+ if ($prepend) {
+ $this->fallbackDirsPsr4 = array_merge(
+ (array) $paths,
+ $this->fallbackDirsPsr4
+ );
+ } else {
+ $this->fallbackDirsPsr4 = array_merge(
+ $this->fallbackDirsPsr4,
+ (array) $paths
+ );
+ }
+ } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
+ // Register directories for a new namespace.
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
+ } elseif ($prepend) {
+ // Prepend directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ (array) $paths,
+ $this->prefixDirsPsr4[$prefix]
+ );
+ } else {
+ // Append directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ $this->prefixDirsPsr4[$prefix],
+ (array) $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix,
+ * replacing any others previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param array|string $paths The PSR-0 base directories
+ */
+ public function set($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr0 = (array) $paths;
+ } else {
+ $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace,
+ * replacing any others previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param array|string $paths The PSR-4 base directories
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function setPsr4($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr4 = (array) $paths;
+ } else {
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Turns on searching the include path for class files.
+ *
+ * @param bool $useIncludePath
+ */
+ public function setUseIncludePath($useIncludePath)
+ {
+ $this->useIncludePath = $useIncludePath;
+ }
+
+ /**
+ * Can be used to check if the autoloader uses the include path to check
+ * for classes.
+ *
+ * @return bool
+ */
+ public function getUseIncludePath()
+ {
+ return $this->useIncludePath;
+ }
+
+ /**
+ * Turns off searching the prefix and fallback directories for classes
+ * that have not been registered with the class map.
+ *
+ * @param bool $classMapAuthoritative
+ */
+ public function setClassMapAuthoritative($classMapAuthoritative)
+ {
+ $this->classMapAuthoritative = $classMapAuthoritative;
+ }
+
+ /**
+ * Should class lookup fail if not found in the current class map?
+ *
+ * @return bool
+ */
+ public function isClassMapAuthoritative()
+ {
+ return $this->classMapAuthoritative;
+ }
+
+ /**
+ * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
+ *
+ * @param string|null $apcuPrefix
+ */
+ public function setApcuPrefix($apcuPrefix)
+ {
+ $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
+ }
+
+ /**
+ * The APCu prefix in use, or null if APCu caching is not enabled.
+ *
+ * @return string|null
+ */
+ public function getApcuPrefix()
+ {
+ return $this->apcuPrefix;
+ }
+
+ /**
+ * Registers this instance as an autoloader.
+ *
+ * @param bool $prepend Whether to prepend the autoloader or not
+ */
+ public function register($prepend = false)
+ {
+ spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+ }
+
+ /**
+ * Unregisters this instance as an autoloader.
+ */
+ public function unregister()
+ {
+ spl_autoload_unregister(array($this, 'loadClass'));
+ }
+
+ /**
+ * Loads the given class or interface.
+ *
+ * @param string $class The name of the class
+ * @return bool|null True if loaded, null otherwise
+ */
+ public function loadClass($class)
+ {
+ if ($file = $this->findFile($class)) {
+ includeFile($file);
+
+ return true;
+ }
+ }
+
+ /**
+ * Finds the path to the file where the class is defined.
+ *
+ * @param string $class The name of the class
+ *
+ * @return string|false The path if found, false otherwise
+ */
+ public function findFile($class)
+ {
+ // class map lookup
+ if (isset($this->classMap[$class])) {
+ return $this->classMap[$class];
+ }
+ if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
+ return false;
+ }
+ if (null !== $this->apcuPrefix) {
+ $file = apcu_fetch($this->apcuPrefix.$class, $hit);
+ if ($hit) {
+ return $file;
+ }
+ }
+
+ $file = $this->findFileWithExtension($class, '.php');
+
+ // Search for Hack files if we are running on HHVM
+ if (false === $file && defined('HHVM_VERSION')) {
+ $file = $this->findFileWithExtension($class, '.hh');
+ }
+
+ if (null !== $this->apcuPrefix) {
+ apcu_add($this->apcuPrefix.$class, $file);
+ }
+
+ if (false === $file) {
+ // Remember that this class does not exist.
+ $this->missingClasses[$class] = true;
+ }
+
+ return $file;
+ }
+
+ private function findFileWithExtension($class, $ext)
+ {
+ // PSR-4 lookup
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
+
+ $first = $class[0];
+ if (isset($this->prefixLengthsPsr4[$first])) {
+ $subPath = $class;
+ while (false !== $lastPos = strrpos($subPath, '\\')) {
+ $subPath = substr($subPath, 0, $lastPos);
+ $search = $subPath . '\\';
+ if (isset($this->prefixDirsPsr4[$search])) {
+ $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
+ foreach ($this->prefixDirsPsr4[$search] as $dir) {
+ if (file_exists($file = $dir . $pathEnd)) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-4 fallback dirs
+ foreach ($this->fallbackDirsPsr4 as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 lookup
+ if (false !== $pos = strrpos($class, '\\')) {
+ // namespaced class name
+ $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
+ . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
+ } else {
+ // PEAR-like class name
+ $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
+ }
+
+ if (isset($this->prefixesPsr0[$first])) {
+ foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
+ if (0 === strpos($class, $prefix)) {
+ foreach ($dirs as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-0 fallback dirs
+ foreach ($this->fallbackDirsPsr0 as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 include paths.
+ if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
+ return $file;
+ }
+
+ return false;
+ }
+}
+
+/**
+ * Scope isolated include.
+ *
+ * Prevents access to $this/self from included files.
+ */
+function includeFile($file)
+{
+ include $file;
+}
diff --git a/_include/calendar/composer/LICENSE b/_include/calendar/composer/LICENSE
new file mode 100644
index 0000000..f27399a
--- /dev/null
+++ b/_include/calendar/composer/LICENSE
@@ -0,0 +1,21 @@
+
+Copyright (c) Nils Adermann, Jordi Boggiano
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/_include/calendar/composer/autoload_classmap.php b/_include/calendar/composer/autoload_classmap.php
new file mode 100644
index 0000000..7a91153
--- /dev/null
+++ b/_include/calendar/composer/autoload_classmap.php
@@ -0,0 +1,9 @@
+ $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
+);
diff --git a/_include/calendar/composer/autoload_namespaces.php b/_include/calendar/composer/autoload_namespaces.php
new file mode 100644
index 0000000..b945d1d
--- /dev/null
+++ b/_include/calendar/composer/autoload_namespaces.php
@@ -0,0 +1,10 @@
+ array($vendorDir . '/johngrogg/ics-parser/src'),
+);
diff --git a/_include/calendar/composer/autoload_psr4.php b/_include/calendar/composer/autoload_psr4.php
new file mode 100644
index 0000000..7ad00c1
--- /dev/null
+++ b/_include/calendar/composer/autoload_psr4.php
@@ -0,0 +1,12 @@
+ array($vendorDir . '/symfony/polyfill-mbstring'),
+ 'Symfony\\Component\\Translation\\' => array($vendorDir . '/symfony/translation'),
+ '' => array($vendorDir . '/nesbot/carbon/src'),
+);
diff --git a/_include/calendar/composer/autoload_real.php b/_include/calendar/composer/autoload_real.php
new file mode 100644
index 0000000..683f64c
--- /dev/null
+++ b/_include/calendar/composer/autoload_real.php
@@ -0,0 +1,70 @@
+= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
+ if ($useStaticLoader) {
+ require_once __DIR__ . '/autoload_static.php';
+
+ call_user_func(\Composer\Autoload\ComposerStaticInitd8636b9e4362b54727922ecd718fad8c::getInitializer($loader));
+ } else {
+ $map = require __DIR__ . '/autoload_namespaces.php';
+ foreach ($map as $namespace => $path) {
+ $loader->set($namespace, $path);
+ }
+
+ $map = require __DIR__ . '/autoload_psr4.php';
+ foreach ($map as $namespace => $path) {
+ $loader->setPsr4($namespace, $path);
+ }
+
+ $classMap = require __DIR__ . '/autoload_classmap.php';
+ if ($classMap) {
+ $loader->addClassMap($classMap);
+ }
+ }
+
+ $loader->register(true);
+
+ if ($useStaticLoader) {
+ $includeFiles = Composer\Autoload\ComposerStaticInitd8636b9e4362b54727922ecd718fad8c::$files;
+ } else {
+ $includeFiles = require __DIR__ . '/autoload_files.php';
+ }
+ foreach ($includeFiles as $fileIdentifier => $file) {
+ composerRequired8636b9e4362b54727922ecd718fad8c($fileIdentifier, $file);
+ }
+
+ return $loader;
+ }
+}
+
+function composerRequired8636b9e4362b54727922ecd718fad8c($fileIdentifier, $file)
+{
+ if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
+ require $file;
+
+ $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
+ }
+}
diff --git a/_include/calendar/composer/autoload_static.php b/_include/calendar/composer/autoload_static.php
new file mode 100644
index 0000000..e0bbfb2
--- /dev/null
+++ b/_include/calendar/composer/autoload_static.php
@@ -0,0 +1,56 @@
+ __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
+ );
+
+ public static $prefixLengthsPsr4 = array (
+ 'S' =>
+ array (
+ 'Symfony\\Polyfill\\Mbstring\\' => 26,
+ 'Symfony\\Component\\Translation\\' => 30,
+ ),
+ );
+
+ public static $prefixDirsPsr4 = array (
+ 'Symfony\\Polyfill\\Mbstring\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring',
+ ),
+ 'Symfony\\Component\\Translation\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/symfony/translation',
+ ),
+ );
+
+ public static $fallbackDirsPsr4 = array (
+ 0 => __DIR__ . '/..' . '/nesbot/carbon/src',
+ );
+
+ public static $prefixesPsr0 = array (
+ 'I' =>
+ array (
+ 'ICal' =>
+ array (
+ 0 => __DIR__ . '/..' . '/johngrogg/ics-parser/src',
+ ),
+ ),
+ );
+
+ public static function getInitializer(ClassLoader $loader)
+ {
+ return \Closure::bind(function () use ($loader) {
+ $loader->prefixLengthsPsr4 = ComposerStaticInitd8636b9e4362b54727922ecd718fad8c::$prefixLengthsPsr4;
+ $loader->prefixDirsPsr4 = ComposerStaticInitd8636b9e4362b54727922ecd718fad8c::$prefixDirsPsr4;
+ $loader->fallbackDirsPsr4 = ComposerStaticInitd8636b9e4362b54727922ecd718fad8c::$fallbackDirsPsr4;
+ $loader->prefixesPsr0 = ComposerStaticInitd8636b9e4362b54727922ecd718fad8c::$prefixesPsr0;
+
+ }, null, ClassLoader::class);
+ }
+}
diff --git a/_include/calendar/composer/installed.json b/_include/calendar/composer/installed.json
new file mode 100644
index 0000000..d541ad9
--- /dev/null
+++ b/_include/calendar/composer/installed.json
@@ -0,0 +1,253 @@
+[
+ {
+ "name": "johngrogg/ics-parser",
+ "version": "v2.1.5",
+ "version_normalized": "2.1.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/u01jmg3/ics-parser.git",
+ "reference": "ffd1788215b19da9813420935715b2bb7919b88e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/u01jmg3/ics-parser/zipball/ffd1788215b19da9813420935715b2bb7919b88e",
+ "reference": "ffd1788215b19da9813420935715b2bb7919b88e",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "nesbot/carbon": "~1.28",
+ "php": ">=5.3.0"
+ },
+ "require-dev": {
+ "squizlabs/php_codesniffer": "~2.9.1"
+ },
+ "time": "2018-05-24T14:12:00+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-0": {
+ "ICal": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Martin Thoma",
+ "email": "info@martin-thoma.de",
+ "role": "Original Developer"
+ },
+ {
+ "name": "John Grogg",
+ "email": "john.grogg@gmail.com",
+ "role": "Developer/Prior Owner"
+ },
+ {
+ "name": "Jonathan Goode",
+ "role": "Developer/Owner"
+ }
+ ],
+ "description": "ICS Parser",
+ "homepage": "https://github.com/u01jmg3/ics-parser",
+ "keywords": [
+ "iCalendar",
+ "ical",
+ "ical-parser",
+ "ics",
+ "ics-parser",
+ "ifb"
+ ]
+ },
+ {
+ "name": "nesbot/carbon",
+ "version": "1.34.0",
+ "version_normalized": "1.34.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/briannesbitt/Carbon.git",
+ "reference": "1dbd3cb01c5645f3e7deda7aa46ef780d95fcc33"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/1dbd3cb01c5645f3e7deda7aa46ef780d95fcc33",
+ "reference": "1dbd3cb01c5645f3e7deda7aa46ef780d95fcc33",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.9",
+ "symfony/translation": "~2.6 || ~3.0 || ~4.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "~2",
+ "phpunit/phpunit": "^4.8.35 || ^5.7"
+ },
+ "time": "2018-09-20T19:36:25+00:00",
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "Carbon\\Laravel\\ServiceProvider"
+ ]
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Brian Nesbitt",
+ "email": "brian@nesbot.com",
+ "homepage": "http://nesbot.com"
+ }
+ ],
+ "description": "A simple API extension for DateTime.",
+ "homepage": "http://carbon.nesbot.com",
+ "keywords": [
+ "date",
+ "datetime",
+ "time"
+ ]
+ },
+ {
+ "name": "symfony/polyfill-mbstring",
+ "version": "v1.9.0",
+ "version_normalized": "1.9.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-mbstring.git",
+ "reference": "d0cd638f4634c16d8df4508e847f14e9e43168b8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d0cd638f4634c16d8df4508e847f14e9e43168b8",
+ "reference": "d0cd638f4634c16d8df4508e847f14e9e43168b8",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "suggest": {
+ "ext-mbstring": "For best performance"
+ },
+ "time": "2018-08-06T14:22:27+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.9-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Mbstring\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for the Mbstring extension",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "mbstring",
+ "polyfill",
+ "portable",
+ "shim"
+ ]
+ },
+ {
+ "name": "symfony/translation",
+ "version": "v4.1.6",
+ "version_normalized": "4.1.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/translation.git",
+ "reference": "9f0b61e339160a466ebcde167a6c5521c810e304"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/translation/zipball/9f0b61e339160a466ebcde167a6c5521c810e304",
+ "reference": "9f0b61e339160a466ebcde167a6c5521c810e304",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1.3",
+ "symfony/polyfill-mbstring": "~1.0"
+ },
+ "conflict": {
+ "symfony/config": "<3.4",
+ "symfony/dependency-injection": "<3.4",
+ "symfony/yaml": "<3.4"
+ },
+ "require-dev": {
+ "psr/log": "~1.0",
+ "symfony/config": "~3.4|~4.0",
+ "symfony/console": "~3.4|~4.0",
+ "symfony/dependency-injection": "~3.4|~4.0",
+ "symfony/finder": "~2.8|~3.0|~4.0",
+ "symfony/intl": "~3.4|~4.0",
+ "symfony/yaml": "~3.4|~4.0"
+ },
+ "suggest": {
+ "psr/log-implementation": "To use logging capability in translator",
+ "symfony/config": "",
+ "symfony/yaml": ""
+ },
+ "time": "2018-10-02T16:36:10+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.1-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Translation\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony Translation Component",
+ "homepage": "https://symfony.com"
+ }
+]
diff --git a/_include/calendar/johngrogg/ics-parser/.editorconfig b/_include/calendar/johngrogg/ics-parser/.editorconfig
new file mode 100644
index 0000000..9c1f764
--- /dev/null
+++ b/_include/calendar/johngrogg/ics-parser/.editorconfig
@@ -0,0 +1,11 @@
+# https://editorconfig.org/
+
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+indent_size = 4
+indent_style = space
+insert_final_newline = true
+trim_trailing_whitespace = true
diff --git a/_include/calendar/johngrogg/ics-parser/.github/CONTRIBUTING.md b/_include/calendar/johngrogg/ics-parser/.github/CONTRIBUTING.md
new file mode 100644
index 0000000..2d56d6c
--- /dev/null
+++ b/_include/calendar/johngrogg/ics-parser/.github/CONTRIBUTING.md
@@ -0,0 +1,17 @@
+## Contributing
+
+ICS Parser is an open source project. It is licensed under the [MIT license](https://opensource.org/licenses/MIT).
+We appreciate pull requests, here are our guidelines:
+
+1. Firstly, check if your issue is present within the latest version (`dev-master`) as the problem may already have been fixed.
+1. Log a bug in our [issue tracker](https://github.com/u01jmg3/ics-parser/issues) (if there isn't one already).
+ - If your patch is going to be large it might be a good idea to get the discussion started early.
+ - We are happy to discuss it in an issue beforehand.
+ - If you could provide an iCal snippet causing the parser to behave incorrectly it is extremely useful for debugging
+ - Please remove all irrelevant events
+1. Please follow the coding standard already present in the file you are editing _before_ committing
+ - Adhere to the [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) coding standard
+ - Use *4 spaces* instead of tabs for indentation
+ - Trim all trailing whitespace and blank lines
+ - Use single quotes (`'`) where possible instead of double
+ - Abide by the [1TBS](https://en.wikipedia.org/wiki/Indent_style#Variant:_1TBS_.28OTBS.29) indentation style
diff --git a/_include/calendar/johngrogg/ics-parser/.github/ISSUE_TEMPLATE.md b/_include/calendar/johngrogg/ics-parser/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 0000000..dbee5eb
--- /dev/null
+++ b/_include/calendar/johngrogg/ics-parser/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,15 @@
+> :information_source:
+> - Firstly, check you are using the latest version (`dev-master`) as the problem may already have been fixed.
+> - It is **essential** to be provided with the offending iCal causing the parser to behave incorrectly.
+> - Best to upload the iCal file directly to this issue
+
+- PHP Version: `5.#.#`
+- PHP date.timezone: `[Country]/[City]`
+- ICS Parser Version: `2.#.#`
+- Windows/Mac/Linux
+
+### Description of the Issue:
+
+
+### Steps to Reproduce:
+
diff --git a/_include/calendar/johngrogg/ics-parser/.github/PULL_REQUEST_TEMPLATE.md b/_include/calendar/johngrogg/ics-parser/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..fcacb28
--- /dev/null
+++ b/_include/calendar/johngrogg/ics-parser/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,9 @@
+> :information_source:
+> - File a bug on our [issue tracker](https://github.com/u01jmg3/ics-parser/issues) (if there isn't one already).
+> - If your patch is going to be large it might be a good idea to get the discussion started early. We are happy to discuss it in a new issue beforehand.
+> - Please follow the coding standards already adhered to in the file you're editing before committing
+> - This includes the use of *4 spaces* over tabs for indentation
+> - Trimming all trailing whitespace
+> - Using single quotes (`'`) where possible
+> - Using the [1TBS](https://en.wikipedia.org/wiki/Indent_style#Variant:_1TBS_.28OTBS.29) indent style
+> - If a function is added or changed, please remember to update the [API documentation in the README](https://github.com/u01jmg3/ics-parser/blob/master/README.md#api)
diff --git a/_include/calendar/johngrogg/ics-parser/.github/RELEASE_CHECKLIST.md b/_include/calendar/johngrogg/ics-parser/.github/RELEASE_CHECKLIST.md
new file mode 100644
index 0000000..603d89c
--- /dev/null
+++ b/_include/calendar/johngrogg/ics-parser/.github/RELEASE_CHECKLIST.md
@@ -0,0 +1,8 @@
+# Release Checklist
+
+- [ ] Ensure the documentation is up to date
+- [ ] Push the code changes to GitHub (`git push`)
+- [ ] Tag the release (`git tag v1.2.3`)
+- [ ] Push the tag (`git push --tag`)
+- [ ] Check [Packagist](https://packagist.org/packages/johngrogg/ics-parser) is updated
+- [ ] Notify anyone who opened [an issue or PR](https://github.com/u01jmg3/ics-parser/issues?q=is%3Aopen) of the fix
diff --git a/_include/calendar/johngrogg/ics-parser/.gitignore b/_include/calendar/johngrogg/ics-parser/.gitignore
new file mode 100644
index 0000000..7a5c9e2
--- /dev/null
+++ b/_include/calendar/johngrogg/ics-parser/.gitignore
@@ -0,0 +1,51 @@
+###################
+# Compiled Source #
+###################
+*.com
+*.class
+*.dll
+*.exe
+*.o
+*.so
+
+############
+# Packages #
+############
+*.7z
+*.dmg
+*.gz
+*.iso
+*.jar
+*.rar
+*.tar
+*.zip
+
+######################
+# Logs and Databases #
+######################
+*.log
+*.sqlite
+
+######################
+# OS Generated Files #
+######################
+.DS_Store
+.DS_Store?
+._*
+.Spotlight-V100
+.Trashes
+ehthumbs.db
+Thumbs.db
+workbench
+
+####################
+# Package Managers #
+####################
+auth.json
+node_modules
+vendor
+
+##########
+# Custom #
+##########
+*-report.*
diff --git a/_include/calendar/johngrogg/ics-parser/LICENSE b/_include/calendar/johngrogg/ics-parser/LICENSE
new file mode 100644
index 0000000..11b5829
--- /dev/null
+++ b/_include/calendar/johngrogg/ics-parser/LICENSE
@@ -0,0 +1,15 @@
+The MIT License (MIT)
+Copyright (c) 2017
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
+modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/_include/calendar/johngrogg/ics-parser/README.md b/_include/calendar/johngrogg/ics-parser/README.md
new file mode 100644
index 0000000..5876591
--- /dev/null
+++ b/_include/calendar/johngrogg/ics-parser/README.md
@@ -0,0 +1,178 @@
+# PHP ICS Parser
+
+[![Latest Stable Release](https://poser.pugx.org/johngrogg/ics-parser/v/stable.png "Latest Stable Release")](https://packagist.org/packages/johngrogg/ics-parser)
+[![Total Downloads](https://poser.pugx.org/johngrogg/ics-parser/downloads.png "Total Downloads")](https://packagist.org/packages/johngrogg/ics-parser)
+
+---
+
+## Installation
+
+### Requirements
+ - PHP 5 (≥ 5.3.0)
+ - [Valid ICS](https://icalendar.org/validator.html) (`.ics`, `.ical`, `.ifb`) file
+ - [IANA](https://www.iana.org/time-zones) or [Unicode CLDR](http://cldr.unicode.org/translation/timezones) Time Zones
+
+### Setup
+
+ - Install [Composer](https://getcomposer.org/)
+ - Add the following dependency to `composer.json`
+ - :warning: **Note with Composer the owner is `johngrogg` and not `u01jmg3`**
+ - To access the latest stable branch (`v2`) use the following
+ - To access new features you can require [`dev-master`](https://getcomposer.org/doc/articles/aliases.md#branch-alias)
+
+ ```yaml
+ {
+ "require": {
+ "johngrogg/ics-parser": "^2"
+ }
+ }
+ ```
+
+## How to use
+
+### How to instantiate the Parser
+
+ - Using the example script as a guide, [refer to this code](https://github.com/u01jmg3/ics-parser/blob/master/examples/index.php#L1-L22)
+
+#### What will the parser return?
+
+ - Each key/value pair from the iCal file will be parsed creating an associative array for both the calendar and every event it contains.
+ - Also injected will be content under `dtstart_tz` and `dtend_tz` for accessing start and end dates with time zone data applied.
+ - Where possible [`DateTime`](https://secure.php.net/manual/en/class.datetime.php) objects are used and returned.
+
+ ```php
+ // Dump the whole calendar
+ var_dump($ical->cal);
+
+ // Dump every event
+ var_dump($ical->events());
+ ```
+
+ - Also included are special `{property}_array` arrays which further resolve the contents of a key/value pair.
+
+ ```php
+ // Dump a parsed event's start date
+ var_dump($event->dtstart_array);
+
+ // array (size=4)
+ // 0 =>
+ // array (size=1)
+ // 'TZID' => string 'America/Detroit' (length=15)
+ // 1 => string '20160409T090000' (length=15)
+ // 2 => int 1460192400
+ // 3 => string 'TZID=America/Detroit:20160409T090000' (length=36)
+ ```
+
+---
+
+## API
+
+### `ICal` API
+
+#### Variables
+
+| Name | Description | Configurable | Default Value |
+|--------------------------------|---------------------------------------------------------------------|:------------------------:|-------------------------------------------------------------------------------------------|
+| `$defaultSpan` | The value in years to use for indefinite, recurring events | :ballot_box_with_check: | `2` |
+| `$defaultTimeZone` | Enables customisation of the default time zone | :ballot_box_with_check: | [System default](https://secure.php.net/manual/en/function.date-default-timezone-get.php) |
+| `$defaultWeekStart` | The two letter representation of the first day of the week | :ballot_box_with_check: | `MO` |
+| `$disableCharacterReplacement` | Toggles whether to disable all character replacement | :ballot_box_with_check: | `false` |
+| `$skipRecurrence` | Toggles whether to skip the parsing of recurrence rules | :ballot_box_with_check: | `false` |
+| `$useTimeZoneWithRRules` | Toggles whether to use time zone info when parsing recurrence rules | :ballot_box_with_check: | `false` |
+| `$alarmCount` | Tracks the number of alarms in the current iCal feed | :heavy_multiplication_x: | N/A |
+| `$cal` | The parsed calendar | :heavy_multiplication_x: | N/A |
+| `$eventCount` | Tracks the number of events in the current iCal feed | :heavy_multiplication_x: | N/A |
+| `$freeBusyCount` | Tracks the free/busy count in the current iCal feed | :heavy_multiplication_x: | N/A |
+| `$todoCount` | Tracks the number of todos in the current iCal feed | :heavy_multiplication_x: | N/A |
+
+#### Methods
+
+| Method | Parameter(s) | Visibility | Description |
+|---------------------------------------|------------------------------------------------------------|-------------|-------------------------------------------------------------------------------------------------------|
+| `__construct` | `$files = false`, `$options = array()` | `public` | Creates the ICal object |
+| `initFile` | `$file` | `protected` | Initialises lines from a file |
+| `initLines` | `$lines` | `protected` | Initialises the parser using an array containing each line of iCal content |
+| `initString` | `$string` | `protected` | Initialises lines from a string |
+| `initUrl` | `$url` | `protected` | Initialises lines from a URL |
+| `addCalendarComponentWithKeyAndValue` | `$component`, `$keyword`, `$value` | `protected` | Add one key and value pair to the `$this->cal` array |
+| `calendarDescription` | - | `public` | Returns the calendar description |
+| `calendarName` | - | `public` | Returns the calendar name |
+| `calendarTimeZone` | `$ignoreUtc` | `public` | Returns the calendar time zone |
+| `cleanData` | `$data` | `protected` | Replaces curly quotes and other special characters with their standard equivalents |
+| `convertDayOrdinalToPositive` | `$dayNumber`, `$weekday`, `$timestamp` | `protected` | Converts a negative day ordinal to its equivalent positive form |
+| `eventsFromInterval` | `$interval` | `public` | Returns a sorted array of events following a given string, or `false` if no events exist in the range |
+| `eventsFromRange` | `$rangeStart = false`, `$rangeEnd = false` | `public` | Returns a sorted array of events in a given range, or an empty array if no events exist in the range |
+| `events` | - | `public` | Returns an array of Events |
+| `fileOrUrl` | `$filename` | `protected` | Reads an entire file or URL into an array |
+| `freeBusyEvents` | - | `public` | Returns an array of arrays with all free/busy events |
+| `hasEvents` | - | `public` | Returns a boolean value whether the current calendar has events or not |
+| `iCalDateToDateTime` | `$icalDate`, `$forceTimeZone = false`, `$forceUtc = false` | `public` | Returns a `DateTime` object from an iCal date time format |
+| `iCalDateToUnixTimestamp` | `$icalDate`, `$forceTimeZone = false`, `$forceUtc = false` | `public` | Returns a Unix timestamp from an iCal date time format |
+| `iCalDateWithTimeZone` | `$event`, `$key`, `$format = DATE_TIME_FORMAT` | `public` | Returns a date adapted to the calendar time zone depending on the event `TZID` |
+| `isExdateMatch` | `$exdate`, `$anEvent`, `$recurringOffset` | `protected` | Checks if an excluded date matches a given date by reconciling time zones |
+| `isFileOrUrl` | `$filename` | `protected` | Checks if a filename exists as a file or URL |
+| `isValidDate` | `$value` | `public` | Checks if a date string is a valid date |
+| `isValidTimeZoneId` | `$timeZone` | `protected` | Checks if a time zone is valid (IANA or CLDR) |
+| `isValidIanaTimeZoneId` | `$timeZone` | `protected` | Checks if a time zone is a valid IANA time zone |
+| `isValidCldrTimeZoneId` | `$timeZone`, `doConversion = false` | `protected` | Checks if a time zone is a valid CLDR time zone |
+| `keyValueFromString` | `$text` | `protected` | Gets the key value pair from an iCal string |
+| `mb_chr` | `$code` | `protected` | Provides a polyfill for PHP 7.2's `mb_chr()`, which is a multibyte safe version of `chr()` |
+| `mb_str_replace` | `$search`, `$replace`, `$subject`, `$count = 0` | `protected` | Replaces all occurrences of a search string with a given replacement string |
+| `numberOfDays` | `$days`, `$start`, `$end` | `protected` | Gets the number of days between a start and end date |
+| `parseDuration` | `$date`, `$duration`, `$format = 'U'` | `protected` | Parses a duration and applies it to a date |
+| `parseExdates` | `$event` | `public` | Parses a list of excluded dates to be applied to an Event |
+| `processDateConversions` | - | `protected` | Processes date conversions using the time zone |
+| `processEventIcalDateTime` | `$event`, `$index = 3` | `protected` | Extends the `{DTSTART\|DTEND\|RECURRENCE-ID}_array` array to include an iCal date time for each event |
+| `processEvents` | - | `protected` | Performs admin tasks on all events as read from the iCal file |
+| `processRecurrences` | - | `protected` | Processes recurrence rules |
+| `removeUnprintableChars` | `$data` | `protected` | Removes unprintable ASCII and UTF-8 characters |
+| `sortEventsWithOrder` | `$events`, `$sortOrder = SORT_ASC` | `public` | Sorts events based on a given sort order |
+| `trimToRecurrenceCount` | `$rrules`, `$recurrenceEvents` | `protected` | Ensures the recurrence count is enforced against generated recurrence events |
+| `unfold` | `$lines` | `protected` | Unfolds an iCal file in preparation for parsing |
+
+#### Constants
+
+| Name | Description |
+|---------------------------|-----------------------------------------------|
+| `DATE_TIME_FORMAT_PRETTY` | Default pretty date time format to use |
+| `DATE_TIME_FORMAT` | Default date time format to use |
+| `ICAL_DATE_TIME_TEMPLATE` | String template to generate an iCal date time |
+| `RECURRENCE_EVENT` | Used to isolate generated recurrence events |
+| `SECONDS_IN_A_WEEK` | The number of seconds in a week |
+| `TIME_FORMAT` | Default time format to use |
+| `TIME_ZONE_UTC` | UTC time zone string |
+| `UNIX_FORMAT` | Unix timestamp date format |
+| `UNIX_MIN_YEAR` | The year Unix time began |
+
+---
+
+### `Event` API (extends `ICal` API)
+
+#### Methods
+
+| Method | Parameter(s) | Visibility | Description |
+|---------------|---------------------------------------------|-------------|---------------------------------------------------------------------|
+| `__construct` | `$data = array()` | `public` | Creates the Event object |
+| `prepareData` | `$value` | `protected` | Prepares the data for output |
+| `printData` | `$html = HTML_TEMPLATE` | `public` | Returns Event data excluding anything blank within an HTML template |
+| `snakeCase` | `$input`, `$glue = '_'`, `$separator = '-'` | `protected` | Converts the given input to snake_case |
+
+#### Constants
+
+| Name | Description |
+|-----------------|-----------------------------------------------------|
+| `HTML_TEMPLATE` | String template to use when pretty printing content |
+
+---
+
+## Credits
+ - [Jonathan Goode](https://github.com/u01jmg3) (programming, bug fixing, enhancement, coding standard)
+ - [John Grogg](john.grogg@gmail.com) (programming, addition of event recurrence handling)
+
+---
+
+## Tools for Testing
+
+ - [iCal Validator](https://icalendar.org/validator.html)
+ - [Recurrence Rule Tester](https://jakubroztocil.github.io/rrule/)
+ - [Unix Timestamp Converter](https://www.unixtimestamp.com)
diff --git a/_include/calendar/johngrogg/ics-parser/composer.json b/_include/calendar/johngrogg/ics-parser/composer.json
new file mode 100644
index 0000000..55c77cd
--- /dev/null
+++ b/_include/calendar/johngrogg/ics-parser/composer.json
@@ -0,0 +1,49 @@
+{
+ "name": "johngrogg/ics-parser",
+ "description": "ICS Parser",
+ "homepage": "https://github.com/u01jmg3/ics-parser",
+ "keywords": [
+ "icalendar",
+ "ics",
+ "ics-parser",
+ "ical",
+ "ical-parser",
+ "ifb"
+ ],
+ "type": "library",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Jonathan Goode",
+ "role": "Developer/Owner"
+ },
+ {
+ "name": "John Grogg",
+ "email": "john.grogg@gmail.com",
+ "role": "Developer/Prior Owner"
+ },
+ {
+ "name": "Martin Thoma",
+ "email": "info@martin-thoma.de",
+ "role": "Original Developer"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.0",
+ "ext-mbstring": "*",
+ "nesbot/carbon": "~1.28"
+ },
+ "require-dev": {
+ "squizlabs/php_codesniffer": "~2.9.1"
+ },
+ "autoload": {
+ "psr-0": {
+ "ICal": "src/"
+ }
+ },
+ "config": {
+ "platform": {
+ "php": "5.3.29"
+ }
+ }
+}
diff --git a/_include/calendar/johngrogg/ics-parser/composer.lock b/_include/calendar/johngrogg/ics-parser/composer.lock
new file mode 100644
index 0000000..f7f8e13
--- /dev/null
+++ b/_include/calendar/johngrogg/ics-parser/composer.lock
@@ -0,0 +1,274 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+ "This file is @generated automatically"
+ ],
+ "content-hash": "bd0091adbd9e5072ec5c42d44b6643fe",
+ "packages": [
+ {
+ "name": "nesbot/carbon",
+ "version": "1.28.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/briannesbitt/Carbon.git",
+ "reference": "00149d95fc91ef10f19d3a66889bc3bb7500fa7b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/00149d95fc91ef10f19d3a66889bc3bb7500fa7b",
+ "reference": "00149d95fc91ef10f19d3a66889bc3bb7500fa7b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.9",
+ "symfony/translation": "~2.6 || ~3.0 || ~4.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "~2",
+ "phpunit/phpunit": "^4.8.35 || ^5.7"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Brian Nesbitt",
+ "email": "brian@nesbot.com",
+ "homepage": "http://nesbot.com"
+ }
+ ],
+ "description": "A simple API extension for DateTime.",
+ "homepage": "http://carbon.nesbot.com",
+ "keywords": [
+ "date",
+ "datetime",
+ "time"
+ ],
+ "time": "2018-05-18T15:26:18+00:00"
+ },
+ {
+ "name": "symfony/polyfill-mbstring",
+ "version": "v1.8.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-mbstring.git",
+ "reference": "3296adf6a6454a050679cde90f95350ad604b171"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/3296adf6a6454a050679cde90f95350ad604b171",
+ "reference": "3296adf6a6454a050679cde90f95350ad604b171",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "suggest": {
+ "ext-mbstring": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.8-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Mbstring\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for the Mbstring extension",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "mbstring",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "time": "2018-04-26T10:06:28+00:00"
+ },
+ {
+ "name": "symfony/translation",
+ "version": "v2.8.40",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/translation.git",
+ "reference": "c6a27966a92fa361bf2c3a938abc6dee91f7ad67"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/translation/zipball/c6a27966a92fa361bf2c3a938abc6dee91f7ad67",
+ "reference": "c6a27966a92fa361bf2c3a938abc6dee91f7ad67",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.9",
+ "symfony/polyfill-mbstring": "~1.0"
+ },
+ "conflict": {
+ "symfony/config": "<2.7"
+ },
+ "require-dev": {
+ "psr/log": "~1.0",
+ "symfony/config": "~2.8",
+ "symfony/intl": "~2.7.25|^2.8.18|~3.2.5",
+ "symfony/yaml": "~2.2|~3.0.0"
+ },
+ "suggest": {
+ "psr/log-implementation": "To use logging capability in translator",
+ "symfony/config": "",
+ "symfony/yaml": ""
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.8-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Translation\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony Translation Component",
+ "homepage": "https://symfony.com",
+ "time": "2018-05-21T09:59:10+00:00"
+ }
+ ],
+ "packages-dev": [
+ {
+ "name": "squizlabs/php_codesniffer",
+ "version": "2.9.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
+ "reference": "dcbed1074f8244661eecddfc2a675430d8d33f62"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/dcbed1074f8244661eecddfc2a675430d8d33f62",
+ "reference": "dcbed1074f8244661eecddfc2a675430d8d33f62",
+ "shasum": ""
+ },
+ "require": {
+ "ext-simplexml": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": ">=5.1.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.0"
+ },
+ "bin": [
+ "scripts/phpcs",
+ "scripts/phpcbf"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "CodeSniffer.php",
+ "CodeSniffer/CLI.php",
+ "CodeSniffer/Exception.php",
+ "CodeSniffer/File.php",
+ "CodeSniffer/Fixer.php",
+ "CodeSniffer/Report.php",
+ "CodeSniffer/Reporting.php",
+ "CodeSniffer/Sniff.php",
+ "CodeSniffer/Tokens.php",
+ "CodeSniffer/Reports/",
+ "CodeSniffer/Tokenizers/",
+ "CodeSniffer/DocGenerators/",
+ "CodeSniffer/Standards/AbstractPatternSniff.php",
+ "CodeSniffer/Standards/AbstractScopeSniff.php",
+ "CodeSniffer/Standards/AbstractVariableSniff.php",
+ "CodeSniffer/Standards/IncorrectPatternException.php",
+ "CodeSniffer/Standards/Generic/Sniffs/",
+ "CodeSniffer/Standards/MySource/Sniffs/",
+ "CodeSniffer/Standards/PEAR/Sniffs/",
+ "CodeSniffer/Standards/PSR1/Sniffs/",
+ "CodeSniffer/Standards/PSR2/Sniffs/",
+ "CodeSniffer/Standards/Squiz/Sniffs/",
+ "CodeSniffer/Standards/Zend/Sniffs/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Greg Sherwood",
+ "role": "lead"
+ }
+ ],
+ "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
+ "homepage": "http://www.squizlabs.com/php-codesniffer",
+ "keywords": [
+ "phpcs",
+ "standards"
+ ],
+ "time": "2017-05-22T02:43:20+00:00"
+ }
+ ],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": {
+ "php": ">=5.3.0",
+ "ext-mbstring": "*"
+ },
+ "platform-dev": [],
+ "platform-overrides": {
+ "php": "5.3.29"
+ }
+}
diff --git a/_include/calendar/johngrogg/ics-parser/examples/ICal.ics b/_include/calendar/johngrogg/ics-parser/examples/ICal.ics
new file mode 100644
index 0000000..af8aa94
--- /dev/null
+++ b/_include/calendar/johngrogg/ics-parser/examples/ICal.ics
@@ -0,0 +1,309 @@
+BEGIN:VCALENDAR
+PRODID:-//Google Inc//Google Calendar 70.9054//EN
+VERSION:2.0
+CALSCALE:GREGORIAN
+METHOD:PUBLISH
+X-WR-CALNAME:Testkalender
+X-WR-TIMEZONE:UTC
+X-WR-CALDESC:Nur zum testen vom Google Kalender
+BEGIN:VFREEBUSY
+UID:f06ff6b3564b2f696bf42d393f8dea59
+ORGANIZER:MAILTO:jane_smith@host1.com
+DTSTAMP:20170316T204607Z
+DTSTART:20170213T204607Z
+DTEND:20180517T204607Z
+URL:https://www.host.com/calendar/busytime/jsmith.ifb
+FREEBUSY;FBTYPE=BUSY:20170623T070000Z/20170223T110000Z
+FREEBUSY;FBTYPE=BUSY:20170624T131500Z/20170316T151500Z
+FREEBUSY;FBTYPE=BUSY:20170715T131500Z/20170416T150000Z
+FREEBUSY;FBTYPE=BUSY:20170716T131500Z/20170516T100500Z
+END:VFREEBUSY
+BEGIN:VEVENT
+DTSTART:20171032T000000
+DTEND:20171101T2300
+DESCRIPTION:Invalid date - parser will skip the event
+SUMMARY:Invalid date - parser will skip the event
+DTSTAMP:20170406T063924
+LOCATION:
+UID:f81b0b41a2e138ae0903daee0a966e1e
+SEQUENCE:0
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE;TZID=America/Los_Angeles:19410512
+DTEND;VALUE=DATE;TZID=America/Los_Angeles:19410512
+DTSTAMP;TZID=America/Los_Angeles:19410512T195741Z
+UID:dh3fki5du0opa7cs5n5s87ca02@google.com
+CREATED:20380101T141901Z
+DESCRIPTION;LANGUAGE=en-gb:
+LAST-MODIFIED:20380101T141901Z
+LOCATION:
+SEQUENCE:0
+STATUS:CONFIRMED
+SUMMARY;LANGUAGE=en-gb:Before 1970-Test: Konrad Zuse invents the Z3, the "first
+ digital Computer"
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20380201
+DTEND;VALUE=DATE:20380202
+DTSTAMP;TZID="GMT Standard Time":20380101T195741Z
+UID:dh3fki5du0opa7cs5n5s87ca01@google.com
+CREATED:20380101T141901Z
+DESCRIPTION;LANGUAGE=en-gb:
+LAST-MODIFIED:20380101T141901Z
+LOCATION:
+SEQUENCE:0
+STATUS:CONFIRMED
+SUMMARY;LANGUAGE=en-gb:Year 2038 problem test
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART:20160105T090000Z
+DTEND:20160107T173000Z
+DTSTAMP;TZID="Greenwich Mean Time:Dublin; Edinburgh; Lisbon; London":20110121T195741Z
+UID:15lc1nvupht8dtfiptenljoiv4@google.com
+CREATED:20110121T195616Z
+DESCRIPTION;LANGUAGE=en-gb:This is a short description\nwith a new line. Some "special" 's
+ igns' may be interesting\, too.
+LAST-MODIFIED:20150409T150000Z
+LOCATION:Kansas
+SEQUENCE:2
+STATUS:CONFIRMED
+SUMMARY;LANGUAGE=en-gb:My Holidays
+TRANSP:TRANSPARENT
+ORGANIZER;CN="My Name":mailto:my.name@mydomain.com
+END:VEVENT
+BEGIN:VEVENT
+ATTENDEE;CN="Page, Larry (l.page@google.com)";ROLE=REQ-PARTICIPANT;RSVP=FALSE:mailto:l.page@google.com
+ATTENDEE;CN="Brin, Sergey (s.brin@google.com)";ROLE=REQ-PARTICIPANT;RSVP=TRUE:mailto:s.brin@google.com
+DTSTART;VALUE=DATE:20160112
+DTEND;VALUE=DATE:20160116
+DTSTAMP;TZID="GMT Standard Time":20110121T195741Z
+UID:1koigufm110c5hnq6ln57murd4@google.com
+CREATED:20110119T142901Z
+DESCRIPTION;LANGUAGE=en-gb:Project xyz Review Meeting Minutes\n
+ Agenda\n1. Review of project version 1.0 requirements.\n2.
+ Definition
+ of project processes.\n3. Review of project schedule.\n
+ Participants: John Smith, Jane Doe, Jim Dandy\n-It was
+ decided that the requirements need to be signed off by
+ product marketing.\n-Project processes were accepted.\n
+ -Project schedule needs to account for scheduled holidays
+ and employee vacation time. Check with HR for specific
+ dates.\n-New schedule will be distributed by Friday.\n-
+ Next weeks meeting is cancelled. No meeting until 3/23.
+LAST-MODIFIED:20150409T150000Z
+LOCATION:
+SEQUENCE:2
+STATUS:CONFIRMED
+SUMMARY;LANGUAGE=en-gb:Test 2
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20160119
+DTEND;VALUE=DATE:20160120
+DTSTAMP;TZID="GMT Standard Time":20110121T195741Z
+UID:rq8jng4jgq0m1lvpj8486fttu0@google.com
+CREATED:20110119T141904Z
+DESCRIPTION;LANGUAGE=en-gb:
+LAST-MODIFIED:20150409T150000Z
+LOCATION:
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+SEQUENCE:0
+STATUS:CONFIRMED
+SUMMARY;LANGUAGE=en-gb:DST Change
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20160119
+DTEND;VALUE=DATE:20160120
+DTSTAMP;TZID="GMT Standard Time":20110121T195741Z
+UID:dh3fki5du0opa7cs5n5s87ca00@google.com
+CREATED:20110119T141901Z
+DESCRIPTION;LANGUAGE=en-gb:
+LAST-MODIFIED:20150409T150000Z
+LOCATION:
+RRULE:FREQ=WEEKLY;COUNT=5;INTERVAL=2;BYDAY=TU
+SEQUENCE:0
+STATUS:CONFIRMED
+SUMMARY;LANGUAGE=en-gb:Test 1
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+SUMMARY:Duration Test
+DTSTART:20160425T150000Z
+DTSTAMP:20160424T150000Z
+DURATION:PT1H15M5S
+RRULE:FREQ=DAILY;COUNT=2
+UID:calendar-62-e7c39bf02382917349672271dd781c89
+END:VEVENT
+BEGIN:VEVENT
+SUMMARY:BYMONTHDAY Test
+DTSTART:20160922T130000Z
+DTEND:20160922T150000Z
+DTSTAMP:20160921T130000Z
+RRULE:FREQ=MONTHLY;UNTIL=20170923T000000Z;INTERVAL=1;BYMONTHDAY=23
+UID:33844fe8df15fbfc13c97fc41c0c4b00392c6870@google.com
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;TZID=Europe/Paris:20160921T080000
+DTEND;TZID=Europe/Paris:20160921T090000
+RRULE:FREQ=WEEKLY;BYDAY=2WE
+DTSTAMP:20161117T165045Z
+UID:884bc8350185031337d9ec49d2e7e101dd5ae5fb@google.com
+CREATED:20160920T133918Z
+DESCRIPTION:
+LAST-MODIFIED:20160920T133923Z
+LOCATION:
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Paris Timezone Test
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART:20160215T080000Z
+DTEND:20160515T090000Z
+DTSTAMP:20161121T113027Z
+CREATED:20161121T113027Z
+UID:65323c541a30dd1f180e2bbfa2724995
+DESCRIPTION:
+LAST-MODIFIED:20161121T113027Z
+LOCATION:
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Long event covering the range from example with special chars:
+ ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÕÖÒÓÔØÙÚÛÜÝÞß
+ àáâãäåæçèéêėëìíîïðñòóôõöøùúûüūýþÿž
+ ‘ ’ ‚ ‛ “ ” „ ‟ – — …
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+CLASS:PUBLIC
+CREATED:20160706T161104Z
+DTEND;TZID="(UTC-05:00) Eastern Time (US & Canada)":20160409T110000
+DTSTAMP:20160706T150005Z
+DTSTART;TZID="(UTC-05:00) Eastern Time (US & Canada)":20160409T090000
+EXDATE;TZID="(UTC-05:00) Eastern Time (US & Canada)":
+ 20160528T090000,
+ 20160625T090000
+LAST-MODIFIED:20160707T182011Z
+EXDATE;TZID="(UTC-05:00) Eastern Time (US & Canada)":20160709T090000
+EXDATE;TZID="(UTC-05:00) Eastern Time (US & Canada)":20160723T090000
+LOCATION:Sanctuary
+PRIORITY:5
+RRULE:FREQ=WEEKLY;COUNT=15;BYDAY=SA
+SEQUENCE:0
+SUMMARY:Microsoft Unicode CLDR EXDATE Test
+TRANSP:OPAQUE
+UID:040000008200E00074C5B7101A82E0080000000020F6512D0B48CF0100000000000000001000000058BFB8CBB85D504CB99FBA637BCFD6BF
+X-MICROSOFT-CDO-BUSYSTATUS:BUSY
+X-MICROSOFT-CDO-IMPORTANCE:1
+X-MICROSOFT-DISALLOW-COUNTER:FALSE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20170118
+DTEND;VALUE=DATE:20170118
+DTSTAMP;TZID="GMT Standard Time":20170121T195741Z
+RRULE:FREQ=MONTHLY;BYSETPOS=3;BYDAY=WE;COUNT=5
+UID:4dnsuc3nknin15kv25cn7ridss@google.com
+CREATED:20170119T142059Z
+DESCRIPTION;LANGUAGE=en-gb:BYDAY Test 1
+LAST-MODIFIED:20170409T150000Z
+SEQUENCE:0
+STATUS:CONFIRMED
+SUMMARY;LANGUAGE=en-gb:BYDAY Test 1
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20170301
+DTEND;VALUE=DATE:20170301
+DTSTAMP;TZID="GMT Standard Time":20170121T195741Z
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=WE
+UID:h6f7sdjbpt47v3dkral8lnsgcc@google.com
+CREATED:20170119T142040Z
+DESCRIPTION;LANGUAGE=en-gb:BYDAY Test 2
+LAST-MODIFIED:20170409T150000Z
+SEQUENCE:0
+STATUS:CONFIRMED
+SUMMARY;LANGUAGE=en-gb:BYDAY Test 2
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20170111
+DTEND;VALUE=DATE:20170111
+DTSTAMP;TZID="GMT Standard Time":20170121T195741Z
+RRULE:FREQ=YEARLY;INTERVAL=2;COUNT=5;BYMONTH=1,2,3
+UID:f50e8b89a4a3b0070e0b687d03@google.com
+CREATED:20170119T142040Z
+DESCRIPTION;LANGUAGE=en-gb:BYMONTH Multiple Test 1
+LAST-MODIFIED:20170409T150000Z
+SEQUENCE:0
+STATUS:CONFIRMED
+SUMMARY;LANGUAGE=en-gb:BYMONTH Multiple Test 1
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20170405
+DTEND;VALUE=DATE:20170405
+DTSTAMP;TZID="GMT Standard Time":20170121T195741Z
+RRULE:FREQ=YEARLY;BYMONTH=4,5,6;BYDAY=WE;COUNT=5
+UID:675f06aa795665ae50904ebf0e@google.com
+CREATED:20170119T142040Z
+DESCRIPTION;LANGUAGE=en-gb:BYMONTH Multiple Test 2
+LAST-MODIFIED:20170409T150000Z
+SEQUENCE:0
+STATUS:CONFIRMED
+SUMMARY;LANGUAGE=en-gb:BYMONTH Multiple Test 2
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+BEGIN:VALARM
+TRIGGER;VALUE=DURATION:-PT30M
+ACTION:DISPLAY
+DESCRIPTION:Buzz buzz
+END:VALARM
+DTSTART;VALUE=DATE;TZID=Germany/Berlin:20170123
+DTEND;VALUE=DATE;TZID=Germany/Berlin:20170123
+DTSTAMP;TZID="GMT Standard Time":20170121T195741Z
+RRULE:FREQ=MONTHLY;BYDAY=-2MO;COUNT=5
+EXDATE;VALUE=DATE:20171020
+UID:d287b7ec808fcf084983f10837@google.com
+CREATED:20170119T142040Z
+DESCRIPTION;LANGUAGE=en-gb:Negative BYDAY
+LAST-MODIFIED:20170409T150000Z
+SEQUENCE:0
+STATUS:CONFIRMED
+SUMMARY;LANGUAGE=en-gb:Negative BYDAY
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;TZID=Australia/Sydney:20170813T190000
+DTEND;TZID=Australia/Sydney:20170813T213000
+RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=2SU;COUNT=2
+DTSTAMP:20170809T114431Z
+UID:testuid@google.com
+CREATED:20170802T135539Z
+DESCRIPTION:
+LAST-MODIFIED:20170802T135935Z
+LOCATION:
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Parent Recurrence Event
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;TZID=Australia/Sydney:20170813T190000
+DTEND;TZID=Australia/Sydney:20170813T213000
+DTSTAMP:20170809T114431Z
+UID:testuid@google.com
+RECURRENCE-ID;TZID=Australia/Sydney:20170813T190000
+CREATED:20170802T135539Z
+DESCRIPTION:
+LAST-MODIFIED:20170809T105604Z
+LOCATION:Melbourne VIC\, Australia
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Override Parent Recurrence Event
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
diff --git a/_include/calendar/johngrogg/ics-parser/examples/index.php b/_include/calendar/johngrogg/ics-parser/examples/index.php
new file mode 100644
index 0000000..abcbbb2
--- /dev/null
+++ b/_include/calendar/johngrogg/ics-parser/examples/index.php
@@ -0,0 +1,175 @@
+ 2, // Default value
+ 'defaultTimeZone' => 'UTC',
+ 'defaultWeekStart' => 'MO', // Default value
+ 'disableCharacterReplacement' => false, // Default value
+ 'skipRecurrence' => false, // Default value
+ 'useTimeZoneWithRRules' => false, // Default value
+ ));
+ // $ical->initFile('ICal.ics');
+ // $ical->initUrl('https://raw.githubusercontent.com/u01jmg3/ics-parser/master/examples/ICal.ics');
+} catch (\Exception $e) {
+ die($e);
+}
+
+$forceTimeZone = false;
+?>
+
+
+
+
+
+
+ PHP ICS Parser example
+
+
+
+
+
PHP ICS Parser example
+
+
+ eventCount ?>
+ The number of events
+
+
+ freeBusyCount ?>
+ The number of free/busy time slots
+
+
+ todoCount ?>
+ The number of todos
+
+
+ alarmCount ?>
+ The number of alarms
+
+
+
+ true,
+ 'range' => true,
+ 'all' => true,
+ );
+ ?>
+
+ eventsFromInterval('1 week');
+
+ if ($events) {
+ echo '
Events in the next 7 days: ';
+ }
+
+ $count = 1;
+ ?>
+
+
+
+
+
+
iCalDateToDateTime($event->dtstart_array[3], $forceTimeZone);
+ echo $event->summary . ' (' . $dtstart->format('d-m-Y H:i') . ')';
+ ?>
+ printData() ?>
+
+
+
+ 1 && $count % 3 === 0) {
+ echo '
';
+ }
+
+ $count++;
+ ?>
+
+
+
+
+ eventsFromRange('2017-03-01 12:00:00', '2017-04-31 17:00:00');
+
+ if ($events) {
+ echo '
Events March through April: ';
+ }
+
+ $count = 1;
+ ?>
+
+
+
+
+
+
iCalDateToDateTime($event->dtstart_array[3], $forceTimeZone);
+ echo $event->summary . ' (' . $dtstart->format('d-m-Y H:i') . ')';
+ ?>
+ printData() ?>
+
+
+
+ 1 && $count % 3 === 0) {
+ echo '
';
+ }
+
+ $count++;
+ ?>
+
+
+
+
+ sortEventsWithOrder($ical->events());
+
+ if ($events) {
+ echo '
All Events: ';
+ }
+ ?>
+
+
+
+
+
+
iCalDateToDateTime($event->dtstart_array[3], $forceTimeZone);
+ echo $event->summary . ' (' . $dtstart->format('d-m-Y H:i') . ')';
+ ?>
+ printData() ?>
+
+
+
+ 1 && $count % 3 === 0) {
+ echo '
';
+ }
+
+ $count++;
+ ?>
+
+
+
+
+
+
diff --git a/_include/calendar/johngrogg/ics-parser/src/ICal/Event.php b/_include/calendar/johngrogg/ics-parser/src/ICal/Event.php
new file mode 100644
index 0000000..8074ce4
--- /dev/null
+++ b/_include/calendar/johngrogg/ics-parser/src/ICal/Event.php
@@ -0,0 +1,204 @@
+%s: %s
';
+
+ /**
+ * https://www.kanzaki.com/docs/ical/summary.html
+ *
+ * @var $summary
+ */
+ public $summary;
+
+ /**
+ * https://www.kanzaki.com/docs/ical/dtstart.html
+ *
+ * @var $dtstart
+ */
+ public $dtstart;
+
+ /**
+ * https://www.kanzaki.com/docs/ical/dtend.html
+ *
+ * @var $dtend
+ */
+ public $dtend;
+
+ /**
+ * https://www.kanzaki.com/docs/ical/duration.html
+ *
+ * @var $duration
+ */
+ public $duration;
+
+ /**
+ * https://www.kanzaki.com/docs/ical/dtstamp.html
+ *
+ * @var $dtstamp
+ */
+ public $dtstamp;
+
+ /**
+ * https://www.kanzaki.com/docs/ical/uid.html
+ *
+ * @var $uid
+ */
+ public $uid;
+
+ /**
+ * https://www.kanzaki.com/docs/ical/created.html
+ *
+ * @var $created
+ */
+ public $created;
+
+ /**
+ * https://www.kanzaki.com/docs/ical/lastModified.html
+ *
+ * @var $lastmodified
+ */
+ public $lastmodified;
+
+ /**
+ * https://www.kanzaki.com/docs/ical/description.html
+ *
+ * @var $description
+ */
+ public $description;
+
+ /**
+ * https://www.kanzaki.com/docs/ical/location.html
+ *
+ * @var $location
+ */
+ public $location;
+
+ /**
+ * https://www.kanzaki.com/docs/ical/sequence.html
+ *
+ * @var $sequence
+ */
+ public $sequence;
+
+ /**
+ * https://www.kanzaki.com/docs/ical/status.html
+ *
+ * @var $status
+ */
+ public $status;
+
+ /**
+ * https://www.kanzaki.com/docs/ical/transp.html
+ *
+ * @var $transp
+ */
+ public $transp;
+
+ /**
+ * https://www.kanzaki.com/docs/ical/organizer.html
+ *
+ * @var $organizer
+ */
+ public $organizer;
+
+ /**
+ * https://www.kanzaki.com/docs/ical/attendee.html
+ *
+ * @var $attendee
+ */
+ public $attendee;
+
+ /**
+ * Creates the Event object
+ *
+ * @param array $data
+ * @return void
+ */
+ public function __construct(array $data = array())
+ {
+ if (!empty($data)) {
+ foreach ($data as $key => $value) {
+ $variable = self::snakeCase($key);
+ $this->{$variable} = self::prepareData($value);
+ }
+ }
+ }
+
+ /**
+ * Prepares the data for output
+ *
+ * @param mixed $value
+ * @return mixed
+ */
+ protected function prepareData($value)
+ {
+ if (is_string($value)) {
+ return stripslashes(trim(str_replace('\n', "\n", $value)));
+ } elseif (is_array($value)) {
+ return array_map('self::prepareData', $value);
+ }
+
+ return $value;
+ }
+
+ /**
+ * Returns Event data excluding anything blank
+ * within an HTML template
+ *
+ * @param string $html HTML template to use
+ * @return string
+ */
+ public function printData($html = self::HTML_TEMPLATE)
+ {
+ $data = array(
+ 'SUMMARY' => $this->summary,
+ 'DTSTART' => $this->dtstart,
+ 'DTEND' => $this->dtend,
+ 'DTSTART_TZ' => $this->dtstart_tz,
+ 'DTEND_TZ' => $this->dtend_tz,
+ 'DURATION' => $this->duration,
+ 'DTSTAMP' => $this->dtstamp,
+ 'UID' => $this->uid,
+ 'CREATED' => $this->created,
+ 'LAST-MODIFIED' => $this->lastmodified,
+ 'DESCRIPTION' => $this->description,
+ 'LOCATION' => $this->location,
+ 'SEQUENCE' => $this->sequence,
+ 'STATUS' => $this->status,
+ 'TRANSP' => $this->transp,
+ 'ORGANISER' => $this->organizer,
+ 'ATTENDEE(S)' => $this->attendee,
+ );
+
+ $data = array_filter($data); // Remove any blank values
+ $output = '';
+
+ foreach ($data as $key => $value) {
+ $output .= sprintf($html, $key, $value);
+ }
+
+ return $output;
+ }
+
+ /**
+ * Converts the given input to snake_case
+ *
+ * @param string $input
+ * @param string $glue
+ * @param string $separator
+ * @return string
+ */
+ protected static function snakeCase($input, $glue = '_', $separator = '-')
+ {
+ $input = preg_split('/(?<=[a-z])(?=[A-Z])/x', $input);
+ $input = join($input, $glue);
+ $input = str_replace($separator, $glue, $input);
+
+ return strtolower($input);
+ }
+}
diff --git a/_include/calendar/johngrogg/ics-parser/src/ICal/ICal.php b/_include/calendar/johngrogg/ics-parser/src/ICal/ICal.php
new file mode 100644
index 0000000..326f585
--- /dev/null
+++ b/_include/calendar/johngrogg/ics-parser/src/ICal/ICal.php
@@ -0,0 +1,2430 @@
+
+ * @license https://opensource.org/licenses/mit-license.php MIT License
+ * @version 2.1.4
+ */
+
+namespace ICal;
+
+use Carbon\Carbon;
+
+class ICal
+{
+ // phpcs:disable Generic.Arrays.DisallowLongArraySyntax.Found
+
+ const DATE_TIME_FORMAT = 'Ymd\THis';
+ const DATE_TIME_FORMAT_PRETTY = 'F Y H:i:s';
+ const ICAL_DATE_TIME_TEMPLATE = 'TZID=%s:';
+ const RECURRENCE_EVENT = 'Generated recurrence event';
+ const SECONDS_IN_A_WEEK = 604800;
+ const TIME_FORMAT = 'His';
+ const TIME_ZONE_UTC = 'UTC';
+ const UNIX_FORMAT = 'U';
+ const UNIX_MIN_YEAR = 1970;
+
+ /**
+ * Tracks the number of alarms in the current iCal feed
+ *
+ * @var integer
+ */
+ public $alarmCount = 0;
+
+ /**
+ * Tracks the number of events in the current iCal feed
+ *
+ * @var integer
+ */
+ public $eventCount = 0;
+
+ /**
+ * Tracks the free/busy count in the current iCal feed
+ *
+ * @var integer
+ */
+ public $freeBusyCount = 0;
+
+ /**
+ * Tracks the number of todos in the current iCal feed
+ *
+ * @var integer
+ */
+ public $todoCount = 0;
+
+ /**
+ * The value in years to use for indefinite, recurring events
+ *
+ * @var integer
+ */
+ public $defaultSpan = 2;
+
+ /**
+ * Enables customisation of the default time zone
+ *
+ * @var string
+ */
+ public $defaultTimeZone;
+
+ /**
+ * The two letter representation of the first day of the week
+ *
+ * @var string
+ */
+ public $defaultWeekStart = 'MO';
+
+ /**
+ * Toggles whether to skip the parsing of recurrence rules
+ *
+ * @var boolean
+ */
+ public $skipRecurrence = false;
+
+ /**
+ * Toggles whether to use time zone info when parsing recurrence rules
+ *
+ * @var boolean
+ */
+ public $useTimeZoneWithRRules = false;
+
+ /**
+ * Toggles whether to disable all character replacement.
+ *
+ * @var boolean
+ */
+ public $disableCharacterReplacement = false;
+
+ /**
+ * The parsed calendar
+ *
+ * @var array
+ */
+ public $cal = array();
+
+ /**
+ * Tracks the VFREEBUSY component
+ *
+ * @var integer
+ */
+ protected $freeBusyIndex = 0;
+
+ /**
+ * Variable to track the previous keyword
+ *
+ * @var string
+ */
+ protected $lastKeyword;
+
+ /**
+ * Cache valid time zones to avoid unnecessary lookups
+ *
+ * @var array
+ */
+ protected $validTimeZones = array();
+
+ /**
+ * Event recurrence instances that have been altered
+ *
+ * @var array
+ */
+ protected $alteredRecurrenceInstances = array();
+
+ /**
+ * An associative array containing ordinal data
+ *
+ * @var array
+ */
+ protected $dayOrdinals = array(
+ 1 => 'first',
+ 2 => 'second',
+ 3 => 'third',
+ 4 => 'fourth',
+ 5 => 'fifth',
+ );
+
+ /**
+ * An associative array containing weekday conversion data
+ *
+ * @var array
+ */
+ protected $weekdays = array(
+ 'SU' => 'sunday',
+ 'MO' => 'monday',
+ 'TU' => 'tuesday',
+ 'WE' => 'wednesday',
+ 'TH' => 'thursday',
+ 'FR' => 'friday',
+ 'SA' => 'saturday',
+ );
+
+ /**
+ * An associative array containing week conversion data
+ * (UK = SU, Europe = MO)
+ *
+ * @var array
+ */
+ protected $weeks = array(
+ 'SA' => array('SA', 'SU', 'MO', 'TU', 'WE', 'TH', 'FR'),
+ 'SU' => array('SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'),
+ 'MO' => array('MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU'),
+ );
+
+ /**
+ * An associative array containing month names
+ *
+ * @var array
+ */
+ protected $monthNames = array(
+ 1 => 'January',
+ 2 => 'February',
+ 3 => 'March',
+ 4 => 'April',
+ 5 => 'May',
+ 6 => 'June',
+ 7 => 'July',
+ 8 => 'August',
+ 9 => 'September',
+ 10 => 'October',
+ 11 => 'November',
+ 12 => 'December',
+ );
+
+ /**
+ * An associative array containing frequency conversion terms
+ *
+ * @var array
+ */
+ protected $frequencyConversion = array(
+ 'DAILY' => 'day',
+ 'WEEKLY' => 'week',
+ 'MONTHLY' => 'month',
+ 'YEARLY' => 'year',
+ );
+
+ /**
+ * Define which variables can be configured
+ *
+ * @var array
+ */
+ private static $configurableOptions = array(
+ 'defaultSpan',
+ 'defaultTimeZone',
+ 'defaultWeekStart',
+ 'disableCharacterReplacement',
+ 'skipRecurrence',
+ 'useTimeZoneWithRRules',
+ );
+
+ /**
+ * Creates the ICal object
+ *
+ * @param mixed $files
+ *
+ * @param array $options
+ * @return void
+ */
+ public function __construct($files = false, array $options = array())
+ {
+ ini_set('auto_detect_line_endings', '1');
+
+ foreach ($options as $option => $value) {
+ if (in_array($option, self::$configurableOptions)) {
+ $this->{$option} = $value;
+ }
+ }
+
+ // Fallback to use the system default time zone
+ if (!isset($this->defaultTimeZone) || !$this->isValidTimeZoneId($this->defaultTimeZone)) {
+ $this->defaultTimeZone = date_default_timezone_get();
+ }
+
+ if ($files !== false) {
+ $files = is_array($files) ? $files : array($files);
+
+ foreach ($files as $file) {
+ if ($this->isFileOrUrl($file)) {
+ $lines = $this->fileOrUrl($file);
+ } else {
+ $lines = is_array($file) ? $file : array($file);
+ }
+
+ $this->initLines($lines);
+ }
+ }
+ }
+
+ /**
+ * Initialises lines from a string
+ *
+ * @param string $string
+ * @return ICal
+ */
+ public function initString($string)
+ {
+ if (empty($this->cal)) {
+ $lines = explode(PHP_EOL, $string);
+
+ $this->initLines($lines);
+ } else {
+ trigger_error('ICal::initString: Calendar already initialised in constructor', E_USER_NOTICE);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Initialises lines from a file
+ *
+ * @param string $file
+ * @return ICal
+ */
+ public function initFile($file)
+ {
+ if (empty($this->cal)) {
+ $lines = $this->fileOrUrl($file);
+
+ $this->initLines($lines);
+ } else {
+ trigger_error('ICal::initFile: Calendar already initialised in constructor', E_USER_NOTICE);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Initialises lines from a URL
+ *
+ * @param string $url
+ * @return ICal
+ */
+ public function initUrl($url)
+ {
+ $this->initFile($url);
+
+ return $this;
+ }
+
+ /**
+ * Initialises the parser using an array
+ * containing each line of iCal content
+ *
+ * @param array $lines
+ * @return void
+ */
+ protected function initLines(array $lines)
+ {
+ $lines = $this->unfold($lines);
+
+ if (stristr($lines[0], 'BEGIN:VCALENDAR') !== false) {
+ $component = '';
+ foreach ($lines as $line) {
+ $line = rtrim($line); // Trim trailing whitespace
+ $line = $this->removeUnprintableChars($line);
+
+ if (!$this->disableCharacterReplacement) {
+ $line = $this->cleanData($line);
+ }
+
+ $add = $this->keyValueFromString($line);
+
+ $keyword = $add[0];
+ $values = $add[1]; // May be an array containing multiple values
+
+ if (!is_array($values)) {
+ if (!empty($values)) {
+ $values = array($values); // Make an array as not already
+ $blankArray = array(); // Empty placeholder array
+ array_push($values, $blankArray);
+ } else {
+ $values = array(); // Use blank array to ignore this line
+ }
+ } elseif (empty($values[0])) {
+ $values = array(); // Use blank array to ignore this line
+ }
+
+ // Reverse so that our array of properties is processed first
+ $values = array_reverse($values);
+
+ foreach ($values as $value) {
+ switch ($line) {
+ // https://www.kanzaki.com/docs/ical/vtodo.html
+ case 'BEGIN:VTODO':
+ if (!is_array($value)) {
+ $this->todoCount++;
+ }
+
+ $component = 'VTODO';
+ break;
+
+ // https://www.kanzaki.com/docs/ical/vevent.html
+ case 'BEGIN:VEVENT':
+ if (!is_array($value)) {
+ $this->eventCount++;
+ }
+
+ $component = 'VEVENT';
+ break;
+
+ // https://www.kanzaki.com/docs/ical/vfreebusy.html
+ case 'BEGIN:VFREEBUSY':
+ if (!is_array($value)) {
+ $this->freeBusyIndex++;
+ }
+
+ $component = 'VFREEBUSY';
+ break;
+
+ case 'BEGIN:VALARM':
+ if (!is_array($value)) {
+ $this->alarmCount++;
+ }
+
+ $component = 'VALARM';
+ break;
+
+ case 'END:VALARM':
+ $component = 'VEVENT';
+ break;
+
+ case 'BEGIN:DAYLIGHT':
+ case 'BEGIN:STANDARD':
+ case 'BEGIN:VCALENDAR':
+ case 'BEGIN:VTIMEZONE':
+ $component = $value;
+ break;
+
+ case 'END:DAYLIGHT':
+ case 'END:STANDARD':
+ case 'END:VCALENDAR':
+ case 'END:VEVENT':
+ case 'END:VFREEBUSY':
+ case 'END:VTIMEZONE':
+ case 'END:VTODO':
+ $component = 'VCALENDAR';
+ break;
+
+ default:
+ $this->addCalendarComponentWithKeyAndValue($component, $keyword, $value);
+ break;
+ }
+ }
+ }
+
+ $this->processEvents();
+
+ if (!$this->skipRecurrence) {
+ $this->processRecurrences();
+
+ // Apply changes to altered recurrence instances
+ if (!empty($this->alteredRecurrenceInstances)) {
+ $events = $this->cal['VEVENT'];
+
+ foreach ($this->alteredRecurrenceInstances as $alteredRecurrenceInstance) {
+ if (isset($alteredRecurrenceInstance['altered-event'])) {
+ $alteredEvent = $alteredRecurrenceInstance['altered-event'];
+ $key = key($alteredEvent);
+ $events[$key] = $alteredEvent[$key];
+ }
+ }
+
+ $this->cal['VEVENT'] = $events;
+ }
+ }
+
+ $this->processDateConversions();
+ }
+ }
+
+ /**
+ * Unfolds an iCal file in preparation for parsing
+ * (https://icalendar.org/iCalendar-RFC-5545/3-1-content-lines.html)
+ *
+ * @param array $lines
+ * @return string
+ */
+ protected function unfold(array $lines)
+ {
+ $string = implode(PHP_EOL, $lines);
+ $string = preg_replace('/' . PHP_EOL . '[ \t]/', '', $string);
+ $lines = explode(PHP_EOL, $string);
+
+ return $lines;
+ }
+
+ /**
+ * Add one key and value pair to the `$this->cal` array
+ *
+ * @param string $component
+ * @param string|boolean $keyword
+ * @param string $value
+ * @return void
+ */
+ protected function addCalendarComponentWithKeyAndValue($component, $keyword, $value)
+ {
+ if ($keyword == false) {
+ $keyword = $this->lastKeyword;
+ }
+
+ switch ($component) {
+ case 'VALARM':
+ $key1 = 'VEVENT';
+ $key2 = ($this->eventCount - 1);
+ $key3 = $component;
+
+ if (!isset($this->cal[$key1][$key2][$key3]["{$keyword}_array"])) {
+ $this->cal[$key1][$key2][$key3]["{$keyword}_array"] = array();
+ }
+
+ if (is_array($value)) {
+ // Add array of properties to the end
+ array_push($this->cal[$key1][$key2][$key3]["{$keyword}_array"], $value);
+ } else {
+ if (!isset($this->cal[$key1][$key2][$key3][$keyword])) {
+ $this->cal[$key1][$key2][$key3][$keyword] = $value;
+ }
+
+ if ($this->cal[$key1][$key2][$key3][$keyword] !== $value) {
+ $this->cal[$key1][$key2][$key3][$keyword] .= ',' . $value;
+ }
+ }
+ break;
+
+ case 'VEVENT':
+ $key1 = $component;
+ $key2 = ($this->eventCount - 1);
+
+ if (!isset($this->cal[$key1][$key2]["{$keyword}_array"])) {
+ $this->cal[$key1][$key2]["{$keyword}_array"] = array();
+ }
+
+ if (is_array($value)) {
+ // Add array of properties to the end
+ array_push($this->cal[$key1][$key2]["{$keyword}_array"], $value);
+ } else {
+ if (!isset($this->cal[$key1][$key2][$keyword])) {
+ $this->cal[$key1][$key2][$keyword] = $value;
+ }
+
+ if ($keyword === 'EXDATE') {
+ if (trim($value) === $value) {
+ $array = array_filter(explode(',', $value));
+ $this->cal[$key1][$key2]["{$keyword}_array"][] = $array;
+ } else {
+ $value = explode(',', implode(',', $this->cal[$key1][$key2]["{$keyword}_array"][1]) . trim($value));
+ $this->cal[$key1][$key2]["{$keyword}_array"][1] = $value;
+ }
+ } else {
+ $this->cal[$key1][$key2]["{$keyword}_array"][] = $value;
+
+ if ($keyword === 'DURATION') {
+ $duration = new \DateInterval($value);
+ array_push($this->cal[$key1][$key2]["{$keyword}_array"], $duration);
+ }
+ }
+
+ if ($this->cal[$key1][$key2][$keyword] !== $value) {
+ $this->cal[$key1][$key2][$keyword] .= ',' . $value;
+ }
+ }
+ break;
+
+ case 'VFREEBUSY':
+ $key1 = $component;
+ $key2 = ($this->freeBusyIndex - 1);
+ $key3 = $keyword;
+
+ if ($keyword === 'FREEBUSY') {
+ if (is_array($value)) {
+ $this->cal[$key1][$key2][$key3][][] = $value;
+ } else {
+ $this->freeBusyCount++;
+
+ end($this->cal[$key1][$key2][$key3]);
+ $key = key($this->cal[$key1][$key2][$key3]);
+
+ $value = explode('/', $value);
+ $this->cal[$key1][$key2][$key3][$key][] = $value;
+ }
+ } else {
+ $this->cal[$key1][$key2][$key3][] = $value;
+ }
+ break;
+
+ case 'VTODO':
+ $this->cal[$component][$this->todoCount - 1][$keyword] = $value;
+ break;
+
+ default:
+ $this->cal[$component][$keyword] = $value;
+ break;
+ }
+
+ $this->lastKeyword = $keyword;
+ }
+
+ /**
+ * Gets the key value pair from an iCal string
+ *
+ * @param string $text
+ * @return array
+ */
+ protected function keyValueFromString($text)
+ {
+ $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
+
+ $colon = strpos($text, ':');
+ $quote = strpos($text, '"');
+ if ($colon === false) {
+ $matches = array();
+ } elseif ($quote === false || $colon < $quote) {
+ list($before, $after) = explode(':', $text, 2);
+ $matches = array($text, $before, $after);
+ } else {
+ list($before, $text) = explode('"', $text, 2);
+ $text = '"' . $text;
+ $matches = str_getcsv($text, ':');
+ $combinedValue = '';
+
+ foreach ($matches as $key => $match) {
+ if ($key === 0) {
+ if (!empty($before)) {
+ $matches[$key] = $before . '"' . $matches[$key] . '"';
+ }
+ } else {
+ if ($key > 1) {
+ $combinedValue .= ':';
+ }
+
+ $combinedValue .= $matches[$key];
+ }
+ }
+
+ $matches = array_slice($matches, 0, 2);
+ $matches[1] = $combinedValue;
+ array_unshift($matches, $before . $text);
+ }
+
+ if (count($matches) === 0) {
+ return false;
+ }
+
+ if (preg_match('/^([A-Z-]+)([;][\w\W]*)?$/', $matches[1])) {
+ $matches = array_splice($matches, 1, 2); // Remove first match and re-align ordering
+
+ // Process properties
+ if (preg_match('/([A-Z-]+)[;]([\w\W]*)/', $matches[0], $properties)) {
+ // Remove first match
+ array_shift($properties);
+ // Fix to ignore everything in keyword after a ; (e.g. Language, TZID, etc.)
+ $matches[0] = $properties[0];
+ array_shift($properties); // Repeat removing first match
+
+ $formatted = array();
+ foreach ($properties as $property) {
+ // Match semicolon separator outside of quoted substrings
+ preg_match_all('~[^' . PHP_EOL . '";]+(?:"[^"\\\]*(?:\\\.[^"\\\]*)*"[^' . PHP_EOL . '";]*)*~', $property, $attributes);
+ // Remove multi-dimensional array and use the first key
+ $attributes = (sizeof($attributes) === 0) ? array($property) : reset($attributes);
+
+ if (is_array($attributes)) {
+ foreach ($attributes as $attribute) {
+ // Match equals sign separator outside of quoted substrings
+ preg_match_all(
+ '~[^' . PHP_EOL . '"=]+(?:"[^"\\\]*(?:\\\.[^"\\\]*)*"[^' . PHP_EOL . '"=]*)*~',
+ $attribute,
+ $values
+ );
+ // Remove multi-dimensional array and use the first key
+ $value = (sizeof($values) === 0) ? null : reset($values);
+
+ if (is_array($value) && isset($value[1])) {
+ // Remove double quotes from beginning and end only
+ $formatted[$value[0]] = trim($value[1], '"');
+ }
+ }
+ }
+ }
+
+ // Assign the keyword property information
+ $properties[0] = $formatted;
+
+ // Add match to beginning of array
+ array_unshift($properties, $matches[1]);
+ $matches[1] = $properties;
+ }
+
+ return $matches;
+ } else {
+ return false; // Ignore this match
+ }
+ }
+
+ /**
+ * Returns a `DateTime` object from an iCal date time format
+ *
+ * @param string $icalDate
+ * @param boolean $forceTimeZone
+ * @param boolean $forceUtc
+ * @return DateTime
+ * @throws \Exception
+ */
+ public function iCalDateToDateTime($icalDate, $forceTimeZone = false, $forceUtc = false)
+ {
+ /**
+ * iCal times may be in 3 formats, (https://www.kanzaki.com/docs/ical/dateTime.html)
+ *
+ * UTC: Has a trailing 'Z'
+ * Floating: No time zone reference specified, no trailing 'Z', use local time
+ * TZID: Set time zone as specified
+ *
+ * Use DateTime class objects to get around limitations with `mktime` and `gmmktime`.
+ * Must have a local time zone set to process floating times.
+ */
+ $pattern = '/\AT?Z?I?D?=?(.*):?'; // [1]: Time zone
+ $pattern .= '([0-9]{4})'; // [2]: YYYY
+ $pattern .= '([0-9]{2})'; // [3]: MM
+ $pattern .= '([0-9]{2})'; // [4]: DD
+ $pattern .= 'T?'; // Time delimiter
+ $pattern .= '([0-9]{0,2})'; // [5]: HH
+ $pattern .= '([0-9]{0,2})'; // [6]: MM
+ $pattern .= '([0-9]{0,2})'; // [7]: SS
+ $pattern .= '(Z?)/'; // [8]: UTC flag
+
+ preg_match($pattern, $icalDate, $date);
+
+ if (empty($date)) {
+ throw new \Exception('Invalid iCal date format.');
+ }
+
+ // A Unix timestamp cannot represent a date prior to 1 Jan 1970
+ $year = $date[2];
+ $isUtc = false;
+
+ if ($year <= self::UNIX_MIN_YEAR) {
+ $eventTimeZone = ltrim(strstr($icalDate, ':', true), 'TZID=');
+
+ if (empty($eventTimeZone)) {
+ $dateTime = new \DateTime($icalDate, new \DateTimeZone($this->defaultTimeZone));
+ } else {
+ $icalDate = ltrim(strstr($icalDate, ':'), ':');
+ $dateTime = new \DateTime($icalDate, new \DateTimeZone($eventTimeZone));
+ }
+ } else {
+ if ($forceTimeZone) {
+ // TZID={Time Zone}:
+ if (isset($date[1])) {
+ $eventTimeZone = rtrim($date[1], ':');
+ }
+
+ if ($date[8] === 'Z') {
+ $isUtc = true;
+ $dateTime = new \DateTime('now', new \DateTimeZone(self::TIME_ZONE_UTC));
+ } elseif (isset($eventTimeZone) && $this->isValidIanaTimeZoneId($eventTimeZone)) {
+ $dateTime = new \DateTime('now', new \DateTimeZone($eventTimeZone));
+ } elseif (isset($eventTimeZone) && $this->isValidCldrTimeZoneId($eventTimeZone)) {
+ $dateTime = new \DateTime('now', new \DateTimeZone($this->isValidCldrTimeZoneId($eventTimeZone, true)));
+ } else {
+ $dateTime = new \DateTime('now', new \DateTimeZone($this->defaultTimeZone));
+ }
+ } else {
+ if ($forceUtc) {
+ $dateTime = new \DateTime('now', new \DateTimeZone(self::TIME_ZONE_UTC));
+ } else {
+ $dateTime = new \DateTime('now');
+ }
+ }
+
+ $dateTime->setDate((int) $date[2], (int) $date[3], (int) $date[4]);
+ $dateTime->setTime((int) $date[5], (int) $date[6], (int) $date[7]);
+ }
+
+ if ($forceTimeZone && $isUtc) {
+ $dateTime->setTimezone(new \DateTimeZone($this->defaultTimeZone));
+ } elseif ($forceUtc) {
+ $dateTime->setTimezone(new \DateTimeZone(self::TIME_ZONE_UTC));
+ }
+
+ return $dateTime;
+ }
+
+ /**
+ * Returns a Unix timestamp from an iCal date time format
+ *
+ * @param string $icalDate
+ * @param boolean $forceTimeZone
+ * @param boolean $forceUtc
+ * @return integer
+ */
+ public function iCalDateToUnixTimestamp($icalDate, $forceTimeZone = false, $forceUtc = false)
+ {
+ $dateTime = $this->iCalDateToDateTime($icalDate, $forceTimeZone, $forceUtc);
+ $offset = 0;
+
+ if ($forceTimeZone) {
+ $offset = $dateTime->getOffset();
+ }
+
+ return $dateTime->getTimestamp() + $offset;
+ }
+
+ /**
+ * Returns a date adapted to the calendar time zone depending on the event `TZID`
+ *
+ * @param array $event
+ * @param string $key
+ * @param string $format
+ * @return string|boolean
+ */
+ public function iCalDateWithTimeZone(array $event, $key, $format = self::DATE_TIME_FORMAT)
+ {
+ if (!isset($event[$key . '_array']) || !isset($event[$key])) {
+ return false;
+ }
+
+ $dateArray = $event[$key . '_array'];
+ $date = $event[$key];
+
+ if ($key === 'DURATION') {
+ $duration = end($dateArray);
+ $dateTime = $this->parseDuration($event['DTSTART'], $duration, null);
+ } else {
+ $dateTime = new \DateTime($dateArray[1], new \DateTimeZone(self::TIME_ZONE_UTC));
+ $dateTime->setTimezone(new \DateTimeZone($this->calendarTimeZone()));
+ }
+
+ // Force time zone
+ if (isset($dateArray[0]['TZID'])) {
+ if ($this->isValidIanaTimeZoneId($dateArray[0]['TZID'])) {
+ $dateTime->setTimezone(new \DateTimeZone($dateArray[0]['TZID']));
+ } elseif ($this->isValidCldrTimeZoneId($dateArray[0]['TZID'])) {
+ $dateTime->setTimezone(new \DateTimeZone($this->isValidCldrTimeZoneId($dateArray[0]['TZID'], true)));
+ } else {
+ $dateTime->setTimezone(new \DateTimeZone($this->defaultTimeZone));
+ }
+ }
+
+ if (is_null($format)) {
+ $output = $dateTime;
+ } else {
+ if ($format === self::UNIX_FORMAT) {
+ $output = $dateTime->getTimestamp();
+ } else {
+ $output = $dateTime->format($format);
+ }
+ }
+
+ return $output;
+ }
+
+ /**
+ * Performs admin tasks on all events as read from the iCal file.
+ * Adds a Unix timestamp to all `{DTSTART|DTEND|RECURRENCE-ID}_array` arrays
+ * Tracks modified recurrence instances
+ *
+ * @return boolean|void
+ */
+ protected function processEvents()
+ {
+ $events = (isset($this->cal['VEVENT'])) ? $this->cal['VEVENT'] : array();
+
+ if (empty($events)) {
+ return false;
+ }
+
+ foreach ($events as $key => $anEvent) {
+ foreach (array('DTSTART', 'DTEND', 'RECURRENCE-ID') as $type) {
+ if (isset($anEvent[$type])) {
+ $date = $anEvent[$type . '_array'][1];
+
+ if (isset($anEvent[$type . '_array'][0]['TZID'])) {
+ $date = sprintf(self::ICAL_DATE_TIME_TEMPLATE, $anEvent[$type . '_array'][0]['TZID']) . $date;
+ }
+
+ $anEvent[$type . '_array'][2] = $this->iCalDateToUnixTimestamp($date, true, true);
+ $anEvent[$type . '_array'][3] = $date;
+ }
+ }
+
+ if (isset($anEvent['RECURRENCE-ID'])) {
+ $uid = $anEvent['UID'];
+
+ if (!isset($this->alteredRecurrenceInstances[$uid])) {
+ $this->alteredRecurrenceInstances[$uid] = array();
+ }
+
+ $recurrenceDateUtc = $this->iCalDateToUnixTimestamp($anEvent['RECURRENCE-ID_array'][3], true, true);
+ $this->alteredRecurrenceInstances[$uid][$key] = $recurrenceDateUtc;
+ }
+
+ $events[$key] = $anEvent;
+ }
+
+ $eventKeysToRemove = array();
+
+ foreach ($events as $key => $event) {
+ $checks[] = !isset($event['RECURRENCE-ID']);
+ $checks[] = isset($event['UID']);
+ $checks[] = isset($event['UID']) && isset($this->alteredRecurrenceInstances[$event['UID']]);
+
+ if ((bool) array_product($checks)) {
+ $eventDtstartUnix = $this->iCalDateToUnixTimestamp($event['DTSTART_array'][3], true, true);
+
+ if (false !== $alteredEventKey = array_search($eventDtstartUnix, $this->alteredRecurrenceInstances[$event['UID']])) {
+ $eventKeysToRemove[] = $alteredEventKey;
+
+ $alteredEvent = array_replace_recursive($events[$key], $events[$alteredEventKey]);
+ $this->alteredRecurrenceInstances[$event['UID']]['altered-event'] = array($key => $alteredEvent);
+ }
+ }
+
+ unset($checks);
+ }
+
+ if (!empty($eventKeysToRemove)) {
+ foreach ($eventKeysToRemove as $eventKeyToRemove) {
+ $events[$eventKeyToRemove] = null;
+ }
+ }
+
+ $this->cal['VEVENT'] = $events;
+ }
+
+ /**
+ * Processes recurrence rules
+ *
+ * @return boolean|void
+ */
+ protected function processRecurrences()
+ {
+ $events = (isset($this->cal['VEVENT'])) ? $this->cal['VEVENT'] : array();
+
+ $recurrenceEvents = array();
+ $allRecurrenceEvents = array();
+
+ if (empty($events)) {
+ return false;
+ }
+
+ foreach ($events as $anEvent) {
+ if (isset($anEvent['RRULE']) && $anEvent['RRULE'] !== '') {
+ // Tag as generated by a recurrence rule
+ $anEvent['RRULE_array'][2] = self::RECURRENCE_EVENT;
+
+ $isAllDayEvent = (strlen($anEvent['DTSTART_array'][1]) === 8) ? true : false;
+
+ $initialStart = new \DateTime($anEvent['DTSTART_array'][1]);
+ $initialStartOffset = $initialStart->getOffset();
+ $initialStartTimeZoneName = $initialStart->getTimezone()->getName();
+
+ if (isset($anEvent['DTEND'])) {
+ $initialEnd = new \DateTime($anEvent['DTEND_array'][1]);
+ $initialEndOffset = $initialEnd->getOffset();
+ $initialEndTimeZoneName = $initialEnd->getTimezone()->getName();
+ } else {
+ $initialEndTimeZoneName = $initialStartTimeZoneName;
+ }
+
+ // Recurring event, parse RRULE and add appropriate duplicate events
+ $rrules = array();
+ $rruleStrings = explode(';', $anEvent['RRULE']);
+
+ foreach ($rruleStrings as $s) {
+ list($k, $v) = explode('=', $s);
+ $rrules[$k] = $v;
+ }
+
+ // Get frequency
+ $frequency = $rrules['FREQ'];
+ // Get Start timestamp
+ $startTimestamp = $initialStart->getTimestamp();
+
+ if (isset($anEvent['DTEND'])) {
+ $endTimestamp = $initialEnd->getTimestamp();
+ } elseif (isset($anEvent['DURATION'])) {
+ $duration = end($anEvent['DURATION_array']);
+ $endTimestamp = $this->parseDuration($anEvent['DTSTART'], $duration);
+ } else {
+ $endTimestamp = $anEvent['DTSTART_array'][2];
+ }
+
+ $eventTimestampOffset = $endTimestamp - $startTimestamp;
+ // Get Interval
+ $interval = (isset($rrules['INTERVAL']) && $rrules['INTERVAL'] !== '') ? $rrules['INTERVAL'] : 1;
+
+ $dayNumber = null;
+ $weekday = null;
+
+ if (in_array($frequency, array('MONTHLY', 'YEARLY')) && isset($rrules['BYDAY']) && $rrules['BYDAY'] !== '') {
+ // Deal with BYDAY
+ $byDay = $rrules['BYDAY'];
+ $dayNumber = intval($byDay);
+
+ if (empty($dayNumber)) { // Returns 0 when no number defined in BYDAY
+ if (!isset($rrules['BYSETPOS'])) {
+ $dayNumber = 1; // Set first as default
+ } elseif (is_numeric($rrules['BYSETPOS'])) {
+ $dayNumber = $rrules['BYSETPOS'];
+ }
+ }
+
+ $weekday = substr($byDay, -2);
+ }
+
+ $untilDefault = date_create('now');
+ $untilDefault->modify($this->defaultSpan . ' year');
+ $untilDefault->setTime(23, 59, 59); // End of the day
+
+ // Compute EXDATEs
+ $exdates = $this->parseExdates($anEvent);
+
+ if (isset($rrules['UNTIL'])) {
+ // Get Until
+ $until = strtotime($rrules['UNTIL']);
+ } elseif (isset($rrules['COUNT'])) {
+ $countOrig = (is_numeric($rrules['COUNT']) && $rrules['COUNT'] > 1) ? $rrules['COUNT'] : 0;
+
+ // Increment count by the number of excluded dates
+ $countOrig += sizeof($exdates);
+
+ // Remove one to exclude the occurrence that initialises the rule
+ $count = ($countOrig - 1);
+
+ if ($interval >= 2) {
+ $count += ($count > 0) ? ($count * $interval) : 0;
+ }
+
+ $countNb = 1;
+ $offset = "+{$count} " . $this->frequencyConversion[$frequency];
+ $until = strtotime($offset, $startTimestamp);
+
+ if (in_array($frequency, array('MONTHLY', 'YEARLY'))
+ && isset($rrules['BYDAY']) && $rrules['BYDAY'] !== ''
+ ) {
+ $dtstart = date_create($anEvent['DTSTART']);
+
+ if (!$dtstart) {
+ continue;
+ }
+
+ for ($i = 1; $i <= $count; $i++) {
+ $dtstartClone = clone $dtstart;
+ $dtstartClone->modify('next ' . $this->frequencyConversion[$frequency]);
+ $offset = "{$this->convertDayOrdinalToPositive($dayNumber, $weekday, $dtstartClone)} {$this->weekdays[$weekday]} of " . $dtstartClone->format('F Y H:i:01');
+ $dtstart->modify($offset);
+ }
+
+ // Jumping X months forwards doesn't mean
+ // the end date will fall on the same day defined in BYDAY
+ // Use the largest of these to ensure we are going far enough
+ // in the future to capture our final end day
+ $until = max($until, $dtstart->format(self::UNIX_FORMAT));
+ }
+
+ unset($offset);
+ } else {
+ $until = $untilDefault->getTimestamp();
+ }
+
+ $until = intval($until);
+
+ // Decide how often to add events and do so
+ switch ($frequency) {
+ case 'DAILY':
+ // Simply add a new event each interval of days until UNTIL is reached
+ $offset = "+{$interval} day";
+ $recurringTimestamp = strtotime($offset, $startTimestamp);
+
+ while ($recurringTimestamp <= $until) {
+ $dayRecurringTimestamp = $recurringTimestamp;
+
+ // Adjust time zone from initial event
+ $dayRecurringOffset = 0;
+ if ($this->useTimeZoneWithRRules) {
+ $recurringTimeZone = \DateTime::createFromFormat(self::UNIX_FORMAT, $dayRecurringTimestamp);
+ $recurringTimeZone->setTimezone($initialStart->getTimezone());
+ $dayRecurringOffset = $recurringTimeZone->getOffset();
+ $dayRecurringTimestamp += $dayRecurringOffset;
+ }
+
+ // Add event
+ $anEvent['DTSTART'] = date(self::DATE_TIME_FORMAT, $dayRecurringTimestamp) . ($isAllDayEvent || ($initialStartTimeZoneName === 'Z') ? 'Z' : '');
+ $anEvent['DTSTART_array'][1] = $anEvent['DTSTART'];
+ $anEvent['DTSTART_array'][2] = $dayRecurringTimestamp;
+ $anEvent['DTEND_array'] = $anEvent['DTSTART_array'];
+ $anEvent['DTEND_array'][2] += $eventTimestampOffset;
+ $anEvent['DTEND'] = date(
+ self::DATE_TIME_FORMAT,
+ $anEvent['DTEND_array'][2]
+ ) . ($isAllDayEvent || ($initialEndTimeZoneName === 'Z') ? 'Z' : '');
+ $anEvent['DTEND_array'][1] = $anEvent['DTEND'];
+
+ // Exclusions
+ $isExcluded = array_filter($exdates, function ($exdate) use ($anEvent, $dayRecurringOffset) {
+ return self::isExdateMatch($exdate, $anEvent, $dayRecurringOffset);
+ });
+
+ if (isset($anEvent['UID'])) {
+ $searchDate = $anEvent['DTSTART'];
+ if (isset($anEvent['DTSTART_array'][0]['TZID'])) {
+ $searchDate = sprintf(self::ICAL_DATE_TIME_TEMPLATE, $anEvent['DTSTART_array'][0]['TZID']) . $searchDate;
+ }
+
+ if (isset($this->alteredRecurrenceInstances[$anEvent['UID']])) {
+ $searchDateUtc = $this->iCalDateToUnixTimestamp($searchDate, true, true);
+ if (in_array($searchDateUtc, $this->alteredRecurrenceInstances[$anEvent['UID']])) {
+ $isExcluded = true;
+ }
+ }
+ }
+
+ if (!$isExcluded) {
+ $anEvent = $this->processEventIcalDateTime($anEvent);
+ $recurrenceEvents[] = $anEvent;
+ $this->eventCount++;
+
+ // If RRULE[COUNT] is reached then break
+ if (isset($rrules['COUNT'])) {
+ $countNb++;
+
+ if ($countNb >= $countOrig) {
+ break;
+ }
+ }
+ }
+
+ // Move forwards
+ $recurringTimestamp = strtotime($offset, $recurringTimestamp);
+ }
+
+ $recurrenceEvents = $this->trimToRecurrenceCount($rrules, $recurrenceEvents);
+ $allRecurrenceEvents = array_merge($allRecurrenceEvents, $recurrenceEvents);
+ $recurrenceEvents = array(); // Reset
+ break;
+
+ case 'WEEKLY':
+ // Create offset
+ $offset = "+{$interval} week";
+
+ $wkst = (isset($rrules['WKST']) && in_array($rrules['WKST'], array('SA', 'SU', 'MO'))) ? $rrules['WKST'] : $this->defaultWeekStart;
+ $aWeek = $this->weeks[$wkst];
+ $days = array('SA' => 'Saturday', 'SU' => 'Sunday', 'MO' => 'Monday');
+
+ // Build list of days of week to add events
+ $weekdays = $aWeek;
+
+ if (isset($rrules['BYDAY']) && $rrules['BYDAY'] !== '') {
+ $byDays = explode(',', $rrules['BYDAY']);
+ } else {
+ // A textual representation of a day, two letters (e.g. SU)
+ $byDays = array(mb_substr(strtoupper($initialStart->format('D')), 0, 2));
+ }
+
+ // Get timestamp of first day of start week
+ $weekRecurringTimestamp = (strcasecmp($initialStart->format('l'), $this->weekdays[$wkst]) === 0)
+ ? $startTimestamp
+ : strtotime("last {$days[$wkst]} " . $initialStart->format('H:i:s'), $startTimestamp);
+
+ // Step through weeks
+ while ($weekRecurringTimestamp <= $until) {
+ $dayRecurringTimestamp = $weekRecurringTimestamp;
+
+ // Adjust time zone from initial event
+ $dayRecurringOffset = 0;
+ if ($this->useTimeZoneWithRRules) {
+ $dayRecurringTimeZone = \DateTime::createFromFormat(self::UNIX_FORMAT, $dayRecurringTimestamp);
+ $dayRecurringTimeZone->setTimezone($initialStart->getTimezone());
+ $dayRecurringOffset = $dayRecurringTimeZone->getOffset();
+ $dayRecurringTimestamp += $dayRecurringOffset;
+ }
+
+ foreach ($weekdays as $day) {
+ // Check if day should be added
+ if (in_array($day, $byDays) && $dayRecurringTimestamp > $startTimestamp
+ && $dayRecurringTimestamp <= $until
+ ) {
+ // Add event
+ $anEvent['DTSTART'] = date(self::DATE_TIME_FORMAT, $dayRecurringTimestamp) . ($isAllDayEvent || ($initialStartTimeZoneName === 'Z') ? 'Z' : '');
+ $anEvent['DTSTART_array'][1] = $anEvent['DTSTART'];
+ $anEvent['DTSTART_array'][2] = $dayRecurringTimestamp;
+ $anEvent['DTEND_array'] = $anEvent['DTSTART_array'];
+ $anEvent['DTEND_array'][2] += $eventTimestampOffset;
+ $anEvent['DTEND'] = date(
+ self::DATE_TIME_FORMAT,
+ $anEvent['DTEND_array'][2]
+ ) . ($isAllDayEvent || ($initialEndTimeZoneName === 'Z') ? 'Z' : '');
+ $anEvent['DTEND_array'][1] = $anEvent['DTEND'];
+
+ // Exclusions
+ $isExcluded = array_filter($exdates, function ($exdate) use ($anEvent, $dayRecurringOffset) {
+ return self::isExdateMatch($exdate, $anEvent, $dayRecurringOffset);
+ });
+
+ if (isset($anEvent['UID'])) {
+ $searchDate = $anEvent['DTSTART'];
+ if (isset($anEvent['DTSTART_array'][0]['TZID'])) {
+ $searchDate = sprintf(self::ICAL_DATE_TIME_TEMPLATE, $anEvent['DTSTART_array'][0]['TZID']) . $searchDate;
+ }
+
+ if (isset($this->alteredRecurrenceInstances[$anEvent['UID']])) {
+ $searchDateUtc = $this->iCalDateToUnixTimestamp($searchDate, true, true);
+ if (in_array($searchDateUtc, $this->alteredRecurrenceInstances[$anEvent['UID']])) {
+ $isExcluded = true;
+ }
+ }
+ }
+
+ if (!$isExcluded) {
+ $anEvent = $this->processEventIcalDateTime($anEvent);
+ $recurrenceEvents[] = $anEvent;
+ $this->eventCount++;
+
+ // If RRULE[COUNT] is reached then break
+ if (isset($rrules['COUNT'])) {
+ $countNb++;
+
+ if ($countNb >= $countOrig) {
+ break 2;
+ }
+ }
+ }
+ }
+
+ // Move forwards a day
+ $dayRecurringTimestamp = strtotime('+1 day', $dayRecurringTimestamp);
+ }
+
+ // Move forwards $interval weeks
+ $weekRecurringTimestamp = strtotime($offset, $weekRecurringTimestamp);
+ }
+
+ $recurrenceEvents = $this->trimToRecurrenceCount($rrules, $recurrenceEvents);
+ $allRecurrenceEvents = array_merge($allRecurrenceEvents, $recurrenceEvents);
+ $recurrenceEvents = array(); // Reset
+ break;
+
+ case 'MONTHLY':
+ // Create offset
+ $recurringTimestamp = $startTimestamp;
+ $offset = "+{$interval} month";
+
+ if (isset($rrules['BYMONTHDAY']) && $rrules['BYMONTHDAY'] !== '') {
+ // Deal with BYMONTHDAY
+ $monthdays = explode(',', $rrules['BYMONTHDAY']);
+
+ while ($recurringTimestamp <= $until) {
+ foreach ($monthdays as $key => $monthday) {
+ if ($key === 0) {
+ // Ensure original event conforms to monthday rule
+ $anEvent['DTSTART'] = gmdate(
+ 'Ym' . sprintf('%02d', $monthday) . '\T' . self::TIME_FORMAT,
+ strtotime($anEvent['DTSTART'])
+ ) . ($isAllDayEvent || ($initialStartTimeZoneName === 'Z') ? 'Z' : '');
+
+ $anEvent['DTEND'] = gmdate(
+ 'Ym' . sprintf('%02d', $monthday) . '\T' . self::TIME_FORMAT,
+ isset($anEvent['DURATION'])
+ ? $this->parseDuration($anEvent['DTSTART'], end($anEvent['DURATION_array']))
+ : strtotime($anEvent['DTEND'])
+ ) . ($isAllDayEvent || ($initialEndTimeZoneName === 'Z') ? 'Z' : '');
+
+ $anEvent['DTSTART_array'][1] = $anEvent['DTSTART'];
+ $anEvent['DTSTART_array'][2] = $this->iCalDateToUnixTimestamp($anEvent['DTSTART']);
+ $anEvent['DTEND_array'][1] = $anEvent['DTEND'];
+ $anEvent['DTEND_array'][2] = $this->iCalDateToUnixTimestamp($anEvent['DTEND']);
+
+ // Ensure recurring timestamp confirms to BYMONTHDAY rule
+ $monthRecurringTimestamp = $this->iCalDateToUnixTimestamp(
+ gmdate(
+ 'Ym' . sprintf('%02d', $monthday) . '\T' . self::TIME_FORMAT,
+ $recurringTimestamp
+ ) . ($isAllDayEvent || ($initialStartTimeZoneName === 'Z') ? 'Z' : '')
+ );
+ }
+
+ // Adjust time zone from initial event
+ $monthRecurringOffset = 0;
+ if ($this->useTimeZoneWithRRules) {
+ $recurringTimeZone = \DateTime::createFromFormat(self::UNIX_FORMAT, $monthRecurringTimestamp);
+ $recurringTimeZone->setTimezone($initialStart->getTimezone());
+ $monthRecurringOffset = $recurringTimeZone->getOffset();
+ $monthRecurringTimestamp += $monthRecurringOffset;
+ }
+
+ // Add event
+ $anEvent['DTSTART'] = date(
+ 'Ym' . sprintf('%02d', $monthday) . '\T' . self::TIME_FORMAT,
+ $monthRecurringTimestamp
+ ) . ($isAllDayEvent || ($initialStartTimeZoneName === 'Z') ? 'Z' : '');
+ $anEvent['DTSTART_array'][1] = $anEvent['DTSTART'];
+ $anEvent['DTSTART_array'][2] = $monthRecurringTimestamp;
+ $anEvent['DTEND_array'] = $anEvent['DTSTART_array'];
+ $anEvent['DTEND_array'][2] += $eventTimestampOffset;
+ $anEvent['DTEND'] = date(
+ self::DATE_TIME_FORMAT,
+ $anEvent['DTEND_array'][2]
+ ) . ($isAllDayEvent || ($initialEndTimeZoneName === 'Z') ? 'Z' : '');
+ $anEvent['DTEND_array'][1] = $anEvent['DTEND'];
+
+ // Exclusions
+ $isExcluded = array_filter($exdates, function ($exdate) use ($anEvent, $monthRecurringOffset) {
+ return self::isExdateMatch($exdate, $anEvent, $monthRecurringOffset);
+ });
+
+ if (isset($anEvent['UID'])) {
+ $searchDate = $anEvent['DTSTART'];
+ if (isset($anEvent['DTSTART_array'][0]['TZID'])) {
+ $searchDate = sprintf(self::ICAL_DATE_TIME_TEMPLATE, $anEvent['DTSTART_array'][0]['TZID']) . $searchDate;
+ }
+
+ if (isset($this->alteredRecurrenceInstances[$anEvent['UID']])) {
+ $searchDateUtc = $this->iCalDateToUnixTimestamp($searchDate, true, true);
+ if (in_array($searchDateUtc, $this->alteredRecurrenceInstances[$anEvent['UID']])) {
+ $isExcluded = true;
+ }
+ }
+ }
+
+ if (!$isExcluded) {
+ $anEvent = $this->processEventIcalDateTime($anEvent);
+ $recurrenceEvents[] = $anEvent;
+ $this->eventCount++;
+
+ // If RRULE[COUNT] is reached then break
+ if (isset($rrules['COUNT'])) {
+ $countNb++;
+
+ if ($countNb >= $countOrig) {
+ break 2;
+ }
+ }
+ }
+ }
+
+ // Move forwards
+ $recurringTimestamp = strtotime($offset, $recurringTimestamp);
+ }
+ } elseif (isset($rrules['BYDAY']) && $rrules['BYDAY'] !== '') {
+ while ($recurringTimestamp <= $until) {
+ $monthRecurringTimestamp = $recurringTimestamp;
+
+ // Adjust time zone from initial event
+ $monthRecurringOffset = 0;
+
+ if ($this->useTimeZoneWithRRules) {
+ $recurringTimeZone = \DateTime::createFromFormat(self::UNIX_FORMAT, $monthRecurringTimestamp);
+ $recurringTimeZone->setTimezone($initialStart->getTimezone());
+ $monthRecurringOffset = $recurringTimeZone->getOffset();
+ $monthRecurringTimestamp += $monthRecurringOffset;
+ }
+
+ $eventStartDesc = "{$this->convertDayOrdinalToPositive($dayNumber, $weekday, $monthRecurringTimestamp)} {$this->weekdays[$weekday]} of "
+ . date(self::DATE_TIME_FORMAT_PRETTY, $monthRecurringTimestamp);
+ $eventStartTimestamp = strtotime($eventStartDesc);
+
+ if (intval($rrules['BYDAY']) === 0) {
+ $lastDayDesc = "last {$this->weekdays[$weekday]} of "
+ . date(self::DATE_TIME_FORMAT_PRETTY, $monthRecurringTimestamp);
+ } else {
+ $lastDayDesc = "{$this->convertDayOrdinalToPositive($dayNumber, $weekday, $monthRecurringTimestamp)} {$this->weekdays[$weekday]} of "
+ . date(self::DATE_TIME_FORMAT_PRETTY, $monthRecurringTimestamp);
+ }
+
+ $lastDayTimestamp = strtotime($lastDayDesc);
+
+ do {
+ // Prevent 5th day of a month from showing up on the next month
+ // If BYDAY and the event falls outside the current month, skip the event
+
+ $compareCurrentMonth = date('F', $monthRecurringTimestamp);
+ $compareEventMonth = date('F', $eventStartTimestamp);
+
+ if ($compareCurrentMonth !== $compareEventMonth) {
+ $monthRecurringTimestamp = strtotime($offset, $monthRecurringTimestamp);
+ continue;
+ }
+
+ if ($eventStartTimestamp > $startTimestamp && $eventStartTimestamp < $until) {
+ $anEvent['DTSTART'] = date(self::DATE_TIME_FORMAT, $eventStartTimestamp) . ($isAllDayEvent || ($initialStartTimeZoneName === 'Z') ? 'Z' : '');
+ $anEvent['DTSTART_array'][1] = $anEvent['DTSTART'];
+ $anEvent['DTSTART_array'][2] = $eventStartTimestamp;
+ $anEvent['DTEND_array'] = $anEvent['DTSTART_array'];
+ $anEvent['DTEND_array'][2] += $eventTimestampOffset;
+ $anEvent['DTEND'] = date(
+ self::DATE_TIME_FORMAT,
+ $anEvent['DTEND_array'][2]
+ ) . ($isAllDayEvent || ($initialEndTimeZoneName === 'Z') ? 'Z' : '');
+ $anEvent['DTEND_array'][1] = $anEvent['DTEND'];
+
+ // Exclusions
+ $isExcluded = array_filter($exdates, function ($exdate) use ($anEvent, $monthRecurringOffset) {
+ return self::isExdateMatch($exdate, $anEvent, $monthRecurringOffset);
+ });
+
+ if (isset($anEvent['UID'])) {
+ $searchDate = $anEvent['DTSTART'];
+ if (isset($anEvent['DTSTART_array'][0]['TZID'])) {
+ $searchDate = sprintf(self::ICAL_DATE_TIME_TEMPLATE, $anEvent['DTSTART_array'][0]['TZID']) . $searchDate;
+ }
+
+ if (isset($this->alteredRecurrenceInstances[$anEvent['UID']])) {
+ $searchDateUtc = $this->iCalDateToUnixTimestamp($searchDate, true, true);
+ if (in_array($searchDateUtc, $this->alteredRecurrenceInstances[$anEvent['UID']])) {
+ $isExcluded = true;
+ }
+ }
+ }
+
+ if (!$isExcluded) {
+ $anEvent = $this->processEventIcalDateTime($anEvent);
+ $recurrenceEvents[] = $anEvent;
+ $this->eventCount++;
+
+ // If RRULE[COUNT] is reached then break
+ if (isset($rrules['COUNT'])) {
+ $countNb++;
+
+ if ($countNb >= $countOrig) {
+ break 2;
+ }
+ }
+ }
+ }
+
+ if (isset($rrules['BYSETPOS'])) {
+ // BYSETPOS is defined so skip
+ // looping through each week
+ $lastDayTimestamp = $eventStartTimestamp;
+ }
+
+ $eventStartTimestamp += self::SECONDS_IN_A_WEEK;
+ } while ($eventStartTimestamp <= $lastDayTimestamp);
+
+ // Move forwards
+ $recurringTimestamp = strtotime($offset, $recurringTimestamp);
+ }
+ }
+
+ $recurrenceEvents = $this->trimToRecurrenceCount($rrules, $recurrenceEvents);
+ $allRecurrenceEvents = array_merge($allRecurrenceEvents, $recurrenceEvents);
+ $recurrenceEvents = array(); // Reset
+ break;
+
+ case 'YEARLY':
+ // Create offset
+ $recurringTimestamp = $startTimestamp;
+ $offset = "+{$interval} year";
+
+ // Deal with BYMONTH
+ if (isset($rrules['BYMONTH']) && $rrules['BYMONTH'] !== '') {
+ $bymonths = explode(',', $rrules['BYMONTH']);
+ }
+
+ // Check if BYDAY rule exists
+ if (isset($rrules['BYDAY']) && $rrules['BYDAY'] !== '') {
+ while ($recurringTimestamp <= $until) {
+ $yearRecurringTimestamp = $recurringTimestamp;
+
+ // Adjust time zone from initial event
+ $yearRecurringOffset = 0;
+
+ if ($this->useTimeZoneWithRRules) {
+ $recurringTimeZone = \DateTime::createFromFormat(self::UNIX_FORMAT, $yearRecurringTimestamp);
+ $recurringTimeZone->setTimezone($initialStart->getTimezone());
+ $yearRecurringOffset = $recurringTimeZone->getOffset();
+ $yearRecurringTimestamp += $yearRecurringOffset;
+ }
+
+ foreach ($bymonths as $bymonth) {
+ $eventStartDesc = "{$this->convertDayOrdinalToPositive($dayNumber, $weekday, $yearRecurringTimestamp)} {$this->weekdays[$weekday]}"
+ . " of {$this->monthNames[$bymonth]} "
+ . gmdate('Y H:i:s', $yearRecurringTimestamp);
+ $eventStartTimestamp = strtotime($eventStartDesc);
+
+ if (intval($rrules['BYDAY']) === 0) {
+ $lastDayDesc = "last {$this->weekdays[$weekday]}"
+ . " of {$this->monthNames[$bymonth]} "
+ . gmdate('Y H:i:s', $yearRecurringTimestamp);
+ } else {
+ $lastDayDesc = "{$this->convertDayOrdinalToPositive($dayNumber, $weekday, $yearRecurringTimestamp)} {$this->weekdays[$weekday]}"
+ . " of {$this->monthNames[$bymonth]} "
+ . gmdate('Y H:i:s', $yearRecurringTimestamp);
+ }
+
+ $lastDayTimestamp = strtotime($lastDayDesc);
+
+ do {
+ if ($eventStartTimestamp > $startTimestamp && $eventStartTimestamp < $until) {
+ $anEvent['DTSTART'] = date(self::DATE_TIME_FORMAT, $eventStartTimestamp) . ($isAllDayEvent || ($initialStartTimeZoneName === 'Z') ? 'Z' : '');
+ $anEvent['DTSTART_array'][1] = $anEvent['DTSTART'];
+ $anEvent['DTSTART_array'][2] = $eventStartTimestamp;
+ $anEvent['DTEND_array'] = $anEvent['DTSTART_array'];
+ $anEvent['DTEND_array'][2] += $eventTimestampOffset;
+ $anEvent['DTEND'] = date(
+ self::DATE_TIME_FORMAT,
+ $anEvent['DTEND_array'][2]
+ ) . ($isAllDayEvent || ($initialEndTimeZoneName === 'Z') ? 'Z' : '');
+ $anEvent['DTEND_array'][1] = $anEvent['DTEND'];
+
+ // Exclusions
+ $isExcluded = array_filter($exdates, function ($exdate) use ($anEvent, $yearRecurringOffset) {
+ return self::isExdateMatch($exdate, $anEvent, $yearRecurringOffset);
+ });
+
+ if (isset($anEvent['UID'])) {
+ $searchDate = $anEvent['DTSTART'];
+ if (isset($anEvent['DTSTART_array'][0]['TZID'])) {
+ $searchDate = sprintf(self::ICAL_DATE_TIME_TEMPLATE, $anEvent['DTSTART_array'][0]['TZID']) . $searchDate;
+ }
+
+ if (isset($this->alteredRecurrenceInstances[$anEvent['UID']])) {
+ $searchDateUtc = $this->iCalDateToUnixTimestamp($searchDate, true, true);
+ if (in_array($searchDateUtc, $this->alteredRecurrenceInstances[$anEvent['UID']])) {
+ $isExcluded = true;
+ }
+ }
+ }
+
+ if (!$isExcluded) {
+ $anEvent = $this->processEventIcalDateTime($anEvent);
+ $recurrenceEvents[] = $anEvent;
+ $this->eventCount++;
+
+ // If RRULE[COUNT] is reached then break
+ if (isset($rrules['COUNT'])) {
+ $countNb++;
+
+ if ($countNb >= $countOrig) {
+ break 3;
+ }
+ }
+ }
+ }
+
+ $eventStartTimestamp += self::SECONDS_IN_A_WEEK;
+ } while ($eventStartTimestamp <= $lastDayTimestamp);
+ }
+
+ // Move forwards
+ $recurringTimestamp = strtotime($offset, $recurringTimestamp);
+ }
+ } else {
+ $day = $initialStart->format('d');
+
+ // Step through years
+ while ($recurringTimestamp <= $until) {
+ $yearRecurringTimestamp = $recurringTimestamp;
+
+ // Adjust time zone from initial event
+ $yearRecurringOffset = 0;
+ if ($this->useTimeZoneWithRRules) {
+ $recurringTimeZone = \DateTime::createFromFormat(self::UNIX_FORMAT, $yearRecurringTimestamp);
+ $recurringTimeZone->setTimezone($initialStart->getTimezone());
+ $yearRecurringOffset = $recurringTimeZone->getOffset();
+ $yearRecurringTimestamp += $yearRecurringOffset;
+ }
+
+ $eventStartDescs = array();
+ if (isset($rrules['BYMONTH']) && $rrules['BYMONTH'] !== '') {
+ foreach ($bymonths as $bymonth) {
+ array_push($eventStartDescs, "$day {$this->monthNames[$bymonth]} " . gmdate('Y H:i:s', $yearRecurringTimestamp));
+ }
+ } else {
+ array_push($eventStartDescs, $day . gmdate(self::DATE_TIME_FORMAT_PRETTY, $yearRecurringTimestamp));
+ }
+
+ foreach ($eventStartDescs as $eventStartDesc) {
+ $eventStartTimestamp = strtotime($eventStartDesc);
+
+ if ($eventStartTimestamp > $startTimestamp && $eventStartTimestamp < $until) {
+ $anEvent['DTSTART'] = date(self::DATE_TIME_FORMAT, $eventStartTimestamp) . ($isAllDayEvent || ($initialStartTimeZoneName === 'Z') ? 'Z' : '');
+ $anEvent['DTSTART_array'][1] = $anEvent['DTSTART'];
+ $anEvent['DTSTART_array'][2] = $eventStartTimestamp;
+ $anEvent['DTEND_array'] = $anEvent['DTSTART_array'];
+ $anEvent['DTEND_array'][2] += $eventTimestampOffset;
+ $anEvent['DTEND'] = date(
+ self::DATE_TIME_FORMAT,
+ $anEvent['DTEND_array'][2]
+ ) . ($isAllDayEvent || ($initialEndTimeZoneName === 'Z') ? 'Z' : '');
+ $anEvent['DTEND_array'][1] = $anEvent['DTEND'];
+
+ // Exclusions
+ $isExcluded = array_filter($exdates, function ($exdate) use ($anEvent, $yearRecurringOffset) {
+ return self::isExdateMatch($exdate, $anEvent, $yearRecurringOffset);
+ });
+
+ if (isset($anEvent['UID'])) {
+ $searchDate = $anEvent['DTSTART'];
+ if (isset($anEvent['DTSTART_array'][0]['TZID'])) {
+ $searchDate = sprintf(self::ICAL_DATE_TIME_TEMPLATE, $anEvent['DTSTART_array'][0]['TZID']) . $searchDate;
+ }
+
+ if (isset($this->alteredRecurrenceInstances[$anEvent['UID']])) {
+ $searchDateUtc = $this->iCalDateToUnixTimestamp($searchDate, true, true);
+ if (in_array($searchDateUtc, $this->alteredRecurrenceInstances[$anEvent['UID']])) {
+ $isExcluded = true;
+ }
+ }
+ }
+
+ if (!$isExcluded) {
+ $anEvent = $this->processEventIcalDateTime($anEvent);
+ $recurrenceEvents[] = $anEvent;
+ $this->eventCount++;
+
+ // If RRULE[COUNT] is reached then break
+ if (isset($rrules['COUNT'])) {
+ $countNb++;
+
+ if ($countNb >= $countOrig) {
+ break 2;
+ }
+ }
+ }
+ }
+ }
+
+ // Move forwards
+ $recurringTimestamp = strtotime($offset, $recurringTimestamp);
+ }
+ }
+
+ $recurrenceEvents = $this->trimToRecurrenceCount($rrules, $recurrenceEvents);
+ $allRecurrenceEvents = array_merge($allRecurrenceEvents, $recurrenceEvents);
+ $recurrenceEvents = array(); // Reset
+ break;
+ }
+ }
+ }
+
+ $events = array_merge($events, $allRecurrenceEvents);
+
+ $this->cal['VEVENT'] = $events;
+ }
+
+ /**
+ * Processes date conversions using the time zone
+ *
+ * Add keys `DTSTART_tz` and `DTEND_tz` to each Event
+ * These keys contain dates adapted to the calendar
+ * time zone depending on the event `TZID`.
+ *
+ * @return boolean|void
+ */
+ protected function processDateConversions()
+ {
+ $events = (isset($this->cal['VEVENT'])) ? $this->cal['VEVENT'] : array();
+
+ if (empty($events)) {
+ return false;
+ }
+
+ foreach ($events as $key => $anEvent) {
+ if (!$this->isValidDate($anEvent['DTSTART'])) {
+ unset($events[$key]);
+ $this->eventCount--;
+
+ continue;
+ }
+
+ if ($this->useTimeZoneWithRRules && isset($anEvent['RRULE_array'][2]) && $anEvent['RRULE_array'][2] === self::RECURRENCE_EVENT) {
+ $events[$key]['DTSTART_tz'] = $anEvent['DTSTART'];
+ $events[$key]['DTEND_tz'] = $anEvent['DTEND'];
+ } else {
+ $events[$key]['DTSTART_tz'] = $this->iCalDateWithTimeZone($anEvent, 'DTSTART');
+
+ if ($this->iCalDateWithTimeZone($anEvent, 'DTEND')) {
+ $events[$key]['DTEND_tz'] = $this->iCalDateWithTimeZone($anEvent, 'DTEND');
+ } elseif ($this->iCalDateWithTimeZone($anEvent, 'DURATION')) {
+ $events[$key]['DTEND_tz'] = $this->iCalDateWithTimeZone($anEvent, 'DURATION');
+ }
+ }
+ }
+
+ $this->cal['VEVENT'] = $events;
+ }
+
+ /**
+ * Extends the `{DTSTART|DTEND|RECURRENCE-ID}_array`
+ * array to include an iCal date time for each event
+ * (`TZID=Timezone:YYYYMMDD[T]HHMMSS`)
+ *
+ * @param array $event
+ * @param integer $index
+ * @return array
+ */
+ protected function processEventIcalDateTime(array $event, $index = 3)
+ {
+ $calendarTimeZone = $this->calendarTimeZone(true);
+
+ foreach (array('DTSTART', 'DTEND', 'RECURRENCE-ID') as $type) {
+ if (isset($event["{$type}_array"])) {
+ $timeZone = (isset($event["{$type}_array"][0]['TZID'])) ? $event["{$type}_array"][0]['TZID'] : $calendarTimeZone;
+ $event["{$type}_array"][$index] = ((is_null($timeZone)) ? '' : sprintf(self::ICAL_DATE_TIME_TEMPLATE, $timeZone)) . $event["{$type}_array"][1];
+ }
+ }
+
+ return $event;
+ }
+
+ /**
+ * Returns an array of Events.
+ * Every event is a class with the event
+ * details being properties within it.
+ *
+ * @return array
+ */
+ public function events()
+ {
+ $array = $this->cal;
+ $array = isset($array['VEVENT']) ? $array['VEVENT'] : array();
+ $events = array();
+
+ if (!empty($array)) {
+ foreach ($array as $event) {
+ $events[] = new Event($event);
+ }
+ }
+
+ return $events;
+ }
+
+ /**
+ * Returns the calendar name
+ *
+ * @return string
+ */
+ public function calendarName()
+ {
+ return isset($this->cal['VCALENDAR']['X-WR-CALNAME']) ? $this->cal['VCALENDAR']['X-WR-CALNAME'] : '';
+ }
+
+ /**
+ * Returns the calendar description
+ *
+ * @return string
+ */
+ public function calendarDescription()
+ {
+ return isset($this->cal['VCALENDAR']['X-WR-CALDESC']) ? $this->cal['VCALENDAR']['X-WR-CALDESC'] : '';
+ }
+
+ /**
+ * Returns the calendar time zone
+ *
+ * @param boolean $ignoreUtc
+ * @return string
+ */
+ public function calendarTimeZone($ignoreUtc = false)
+ {
+ if (isset($this->cal['VCALENDAR']['X-WR-TIMEZONE'])) {
+ $timeZone = $this->cal['VCALENDAR']['X-WR-TIMEZONE'];
+ } elseif (isset($this->cal['VTIMEZONE']['TZID'])) {
+ $timeZone = $this->cal['VTIMEZONE']['TZID'];
+ } else {
+ $timeZone = $this->defaultTimeZone;
+ }
+
+ // Use default time zone if the calendar's is invalid
+ if ($this->isValidIanaTimeZoneId($timeZone) === false) {
+ // phpcs:ignore CustomPHPCS.ControlStructures.AssignmentInCondition.Warning
+ if (($timeZone = $this->isValidCldrTimeZoneId($timeZone, true)) === false) {
+ $timeZone = $this->defaultTimeZone;
+ }
+ }
+
+ if ($ignoreUtc && strtoupper($timeZone) === self::TIME_ZONE_UTC) {
+ return null;
+ }
+
+ return $timeZone;
+ }
+
+ /**
+ * Returns an array of arrays with all free/busy events.
+ * Every event is an associative array and each property
+ * is an element it.
+ *
+ * @return array
+ */
+ public function freeBusyEvents()
+ {
+ $array = $this->cal;
+
+ return isset($array['VFREEBUSY']) ? $array['VFREEBUSY'] : '';
+ }
+
+ /**
+ * Returns a boolean value whether the
+ * current calendar has events or not
+ *
+ * @return boolean
+ */
+ public function hasEvents()
+ {
+ return (count($this->events()) > 0) ?: false;
+ }
+
+ /**
+ * Returns a sorted array of the events in a given range,
+ * or an empty array if no events exist in the range.
+ *
+ * Events will be returned if the start or end date is contained within the
+ * range (inclusive), or if the event starts before and end after the range.
+ *
+ * If a start date is not specified or of a valid format, then the start
+ * of the range will default to the current time and date of the server.
+ *
+ * If an end date is not specified or of a valid format, then the end of
+ * the range will default to the current time and date of the server,
+ * plus 20 years.
+ *
+ * Note that this function makes use of Unix timestamps. This might be a
+ * problem for events on, during, or after 29 Jan 2038.
+ * See https://en.wikipedia.org/wiki/Unix_time#Representing_the_number
+ *
+ * @param string $rangeStart
+ * @param string $rangeEnd
+ * @return array
+ * @throws \Exception
+ */
+ public function eventsFromRange($rangeStart = false, $rangeEnd = false)
+ {
+ // Sort events before processing range
+ $events = $this->sortEventsWithOrder($this->events(), SORT_ASC);
+
+ if (empty($events)) {
+ return array();
+ }
+
+ $extendedEvents = array();
+
+ if ($rangeStart) {
+ try {
+ $rangeStart = new \DateTime($rangeStart, new \DateTimeZone($this->defaultTimeZone));
+ } catch (\Exception $e) {
+ error_log("ICal::eventsFromRange: Invalid date passed ({$rangeStart})");
+ $rangeStart = false;
+ }
+ } else {
+ $rangeStart = new \DateTime('now', new \DateTimeZone($this->defaultTimeZone));
+ }
+
+ if ($rangeEnd) {
+ try {
+ $rangeEnd = new \DateTime($rangeEnd, new \DateTimeZone($this->defaultTimeZone));
+ } catch (\Exception $e) {
+ error_log("ICal::eventsFromRange: Invalid date passed ({$rangeEnd})");
+ $rangeEnd = false;
+ }
+ } else {
+ $rangeEnd = new \DateTime('now', new \DateTimeZone($this->defaultTimeZone));
+ $rangeEnd->modify('+20 years');
+ }
+
+ // If start and end are identical and are dates with no times...
+ if ($rangeEnd->format('His') == 0 && $rangeStart->getTimestamp() == $rangeEnd->getTimestamp()) {
+ $rangeEnd->modify('+1 day');
+ }
+
+ $rangeStart = $rangeStart->getTimestamp();
+ $rangeEnd = $rangeEnd->getTimestamp();
+
+ foreach ($events as $anEvent) {
+ $eventStart = $anEvent->dtstart_array[2];
+ $eventEnd = (isset($anEvent->dtend_array[2])) ? $anEvent->dtend_array[2] : null;
+
+ if (($eventStart >= $rangeStart && $eventStart < $rangeEnd) // Event start date contained in the range
+ || ($eventEnd !== null
+ && (
+ ($eventEnd > $rangeStart && $eventEnd <= $rangeEnd) // Event end date contained in the range
+ || ($eventStart < $rangeStart && $eventEnd > $rangeEnd) // Event starts before and finishes after range
+ )
+ )
+ ) {
+ $extendedEvents[] = $anEvent;
+ }
+ }
+
+ if (empty($extendedEvents)) {
+ return array();
+ }
+
+ return $extendedEvents;
+ }
+
+ /**
+ * Returns a sorted array of the events following a given string,
+ * or `false` if no events exist in the range.
+ *
+ * @param string $interval
+ * @return array
+ */
+ public function eventsFromInterval($interval)
+ {
+ $rangeStart = new \DateTime('now', new \DateTimeZone($this->defaultTimeZone));
+ $rangeEnd = new \DateTime('now', new \DateTimeZone($this->defaultTimeZone));
+
+ $dateInterval = \DateInterval::createFromDateString($interval);
+ $rangeEnd->add($dateInterval);
+
+ return $this->eventsFromRange($rangeStart->format('Y-m-d'), $rangeEnd->format('Y-m-d'));
+ }
+
+ /**
+ * Sorts events based on a given sort order
+ *
+ * @param array $events
+ * @param integer $sortOrder Either SORT_ASC, SORT_DESC, SORT_REGULAR, SORT_NUMERIC, SORT_STRING
+ * @return array
+ */
+ public function sortEventsWithOrder(array $events, $sortOrder = SORT_ASC)
+ {
+ $extendedEvents = array();
+ $timestamp = array();
+
+ foreach ($events as $key => $anEvent) {
+ $extendedEvents[] = $anEvent;
+ $timestamp[$key] = $anEvent->dtstart_array[2];
+ }
+
+ array_multisort($timestamp, $sortOrder, $extendedEvents);
+
+ return $extendedEvents;
+ }
+
+ /**
+ * Checks if a time zone is valid (IANA or CLDR)
+ *
+ * @param string $timeZone
+ * @return boolean
+ */
+ protected function isValidTimeZoneId($timeZone)
+ {
+ return ($this->isValidIanaTimeZoneId($timeZone) !== false || $this->isValidCldrTimeZoneId($timeZone) !== false);
+ }
+
+ /**
+ * Checks if a time zone is a valid IANA time zone
+ *
+ * @param string $timeZone
+ * @return boolean
+ */
+ protected function isValidIanaTimeZoneId($timeZone)
+ {
+ if (in_array($timeZone, $this->validTimeZones)) {
+ return true;
+ }
+
+ $valid = array();
+ $tza = timezone_abbreviations_list();
+
+ foreach ($tza as $zone) {
+ foreach ($zone as $item) {
+ $valid[$item['timezone_id']] = true;
+ }
+ }
+
+ unset($valid['']);
+
+ if (isset($valid[$timeZone]) || in_array($timeZone, timezone_identifiers_list(\DateTimeZone::ALL_WITH_BC))) {
+ $this->validTimeZones[] = $timeZone;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks if a time zone is a valid CLDR time zone
+ *
+ * @param string $timeZone
+ * @param boolean $doConversion
+ * @return boolean|string
+ */
+ public function isValidCldrTimeZoneId($timeZone, $doConversion = false)
+ {
+ $timeZone = html_entity_decode($timeZone);
+
+ $cldrTimeZones = array(
+ '(UTC-12:00) International Date Line West' => 'Etc/GMT+12',
+ '(UTC-11:00) Coordinated Universal Time-11' => 'Etc/GMT+11',
+ '(UTC-10:00) Hawaii' => 'Pacific/Honolulu',
+ '(UTC-09:00) Alaska' => 'America/Anchorage',
+ '(UTC-08:00) Pacific Time (US & Canada)' => 'America/Los_Angeles',
+ '(UTC-07:00) Arizona' => 'America/Phoenix',
+ '(UTC-07:00) Chihuahua, La Paz, Mazatlan' => 'America/Chihuahua',
+ '(UTC-07:00) Mountain Time (US & Canada)' => 'America/Denver',
+ '(UTC-06:00) Central America' => 'America/Guatemala',
+ '(UTC-06:00) Central Time (US & Canada)' => 'America/Chicago',
+ '(UTC-06:00) Guadalajara, Mexico City, Monterrey' => 'America/Mexico_City',
+ '(UTC-06:00) Saskatchewan' => 'America/Regina',
+ '(UTC-05:00) Bogota, Lima, Quito, Rio Branco' => 'America/Bogota',
+ '(UTC-05:00) Chetumal' => 'America/Cancun',
+ '(UTC-05:00) Eastern Time (US & Canada)' => 'America/New_York',
+ '(UTC-05:00) Indiana (East)' => 'America/Indianapolis',
+ '(UTC-04:00) Asuncion' => 'America/Asuncion',
+ '(UTC-04:00) Atlantic Time (Canada)' => 'America/Halifax',
+ '(UTC-04:00) Caracas' => 'America/Caracas',
+ '(UTC-04:00) Cuiaba' => 'America/Cuiaba',
+ '(UTC-04:00) Georgetown, La Paz, Manaus, San Juan' => 'America/La_Paz',
+ '(UTC-04:00) Santiago' => 'America/Santiago',
+ '(UTC-03:30) Newfoundland' => 'America/St_Johns',
+ '(UTC-03:00) Brasilia' => 'America/Sao_Paulo',
+ '(UTC-03:00) Cayenne, Fortaleza' => 'America/Cayenne',
+ '(UTC-03:00) City of Buenos Aires' => 'America/Buenos_Aires',
+ '(UTC-03:00) Greenland' => 'America/Godthab',
+ '(UTC-03:00) Montevideo' => 'America/Montevideo',
+ '(UTC-03:00) Salvador' => 'America/Bahia',
+ '(UTC-02:00) Coordinated Universal Time-02' => 'Etc/GMT+2',
+ '(UTC-01:00) Azores' => 'Atlantic/Azores',
+ '(UTC-01:00) Cabo Verde Is.' => 'Atlantic/Cape_Verde',
+ '(UTC) Coordinated Universal Time' => 'Etc/GMT',
+ '(UTC+00:00) Casablanca' => 'Africa/Casablanca',
+ '(UTC+00:00) Dublin, Edinburgh, Lisbon, London' => 'Europe/London',
+ '(UTC+00:00) Monrovia, Reykjavik' => 'Atlantic/Reykjavik',
+ '(UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna' => 'Europe/Berlin',
+ '(UTC+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague' => 'Europe/Budapest',
+ '(UTC+01:00) Brussels, Copenhagen, Madrid, Paris' => 'Europe/Paris',
+ '(UTC+01:00) Sarajevo, Skopje, Warsaw, Zagreb' => 'Europe/Warsaw',
+ '(UTC+01:00) West Central Africa' => 'Africa/Lagos',
+ '(UTC+02:00) Amman' => 'Asia/Amman',
+ '(UTC+02:00) Athens, Bucharest' => 'Europe/Bucharest',
+ '(UTC+02:00) Beirut' => 'Asia/Beirut',
+ '(UTC+02:00) Cairo' => 'Africa/Cairo',
+ '(UTC+02:00) Chisinau' => 'Europe/Chisinau',
+ '(UTC+02:00) Damascus' => 'Asia/Damascus',
+ '(UTC+02:00) Harare, Pretoria' => 'Africa/Johannesburg',
+ '(UTC+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius' => 'Europe/Kiev',
+ '(UTC+02:00) Jerusalem' => 'Asia/Jerusalem',
+ '(UTC+02:00) Kaliningrad' => 'Europe/Kaliningrad',
+ '(UTC+02:00) Tripoli' => 'Africa/Tripoli',
+ '(UTC+02:00) Windhoek' => 'Africa/Windhoek',
+ '(UTC+03:00) Baghdad' => 'Asia/Baghdad',
+ '(UTC+03:00) Istanbul' => 'Europe/Istanbul',
+ '(UTC+03:00) Kuwait, Riyadh' => 'Asia/Riyadh',
+ '(UTC+03:00) Minsk' => 'Europe/Minsk',
+ '(UTC+03:00) Moscow, St. Petersburg, Volgograd' => 'Europe/Moscow',
+ '(UTC+03:00) Nairobi' => 'Africa/Nairobi',
+ '(UTC+03:30) Tehran' => 'Asia/Tehran',
+ '(UTC+04:00) Abu Dhabi, Muscat' => 'Asia/Dubai',
+ '(UTC+04:00) Baku' => 'Asia/Baku',
+ '(UTC+04:00) Izhevsk, Samara' => 'Europe/Samara',
+ '(UTC+04:00) Port Louis' => 'Indian/Mauritius',
+ '(UTC+04:00) Tbilisi' => 'Asia/Tbilisi',
+ '(UTC+04:00) Yerevan' => 'Asia/Yerevan',
+ '(UTC+04:30) Kabul' => 'Asia/Kabul',
+ '(UTC+05:00) Ashgabat, Tashkent' => 'Asia/Tashkent',
+ '(UTC+05:00) Ekaterinburg' => 'Asia/Yekaterinburg',
+ '(UTC+05:00) Islamabad, Karachi' => 'Asia/Karachi',
+ '(UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi' => 'Asia/Calcutta',
+ '(UTC+05:30) Sri Jayawardenepura' => 'Asia/Colombo',
+ '(UTC+05:45) Kathmandu' => 'Asia/Katmandu',
+ '(UTC+06:00) Astana' => 'Asia/Almaty',
+ '(UTC+06:00) Dhaka' => 'Asia/Dhaka',
+ '(UTC+06:30) Yangon (Rangoon)' => 'Asia/Rangoon',
+ '(UTC+07:00) Bangkok, Hanoi, Jakarta' => 'Asia/Bangkok',
+ '(UTC+07:00) Krasnoyarsk' => 'Asia/Krasnoyarsk',
+ '(UTC+07:00) Novosibirsk' => 'Asia/Novosibirsk',
+ '(UTC+08:00) Beijing, Chongqing, Hong Kong, Urumqi' => 'Asia/Shanghai',
+ '(UTC+08:00) Irkutsk' => 'Asia/Irkutsk',
+ '(UTC+08:00) Kuala Lumpur, Singapore' => 'Asia/Singapore',
+ '(UTC+08:00) Perth' => 'Australia/Perth',
+ '(UTC+08:00) Taipei' => 'Asia/Taipei',
+ '(UTC+08:00) Ulaanbaatar' => 'Asia/Ulaanbaatar',
+ '(UTC+09:00) Osaka, Sapporo, Tokyo' => 'Asia/Tokyo',
+ '(UTC+09:00) Pyongyang' => 'Asia/Pyongyang',
+ '(UTC+09:00) Seoul' => 'Asia/Seoul',
+ '(UTC+09:00) Yakutsk' => 'Asia/Yakutsk',
+ '(UTC+09:30) Adelaide' => 'Australia/Adelaide',
+ '(UTC+09:30) Darwin' => 'Australia/Darwin',
+ '(UTC+10:00) Brisbane' => 'Australia/Brisbane',
+ '(UTC+10:00) Canberra, Melbourne, Sydney' => 'Australia/Sydney',
+ '(UTC+10:00) Guam, Port Moresby' => 'Pacific/Port_Moresby',
+ '(UTC+10:00) Hobart' => 'Australia/Hobart',
+ '(UTC+10:00) Vladivostok' => 'Asia/Vladivostok',
+ '(UTC+11:00) Chokurdakh' => 'Asia/Srednekolymsk',
+ '(UTC+11:00) Magadan' => 'Asia/Magadan',
+ '(UTC+11:00) Solomon Is., New Caledonia' => 'Pacific/Guadalcanal',
+ '(UTC+12:00) Anadyr, Petropavlovsk-Kamchatsky' => 'Asia/Kamchatka',
+ '(UTC+12:00) Auckland, Wellington' => 'Pacific/Auckland',
+ '(UTC+12:00) Coordinated Universal Time+12' => 'Etc/GMT-12',
+ '(UTC+12:00) Fiji' => 'Pacific/Fiji',
+ "(UTC+13:00) Nuku'alofa" => 'Pacific/Tongatapu',
+ '(UTC+13:00) Samoa' => 'Pacific/Apia',
+ '(UTC+14:00) Kiritimati Island' => 'Pacific/Kiritimati',
+ );
+
+ if (array_key_exists($timeZone, $cldrTimeZones)) {
+ if ($doConversion) {
+ return $cldrTimeZones[$timeZone];
+ } else {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Parses a duration and applies it to a date
+ *
+ * @param string $date
+ * @param string $duration
+ * @param string $format
+ * @return integer|DateTime
+ */
+ protected function parseDuration($date, $duration, $format = self::UNIX_FORMAT)
+ {
+ $dateTime = date_create($date);
+ $dateTime->modify($duration->y . ' year');
+ $dateTime->modify($duration->m . ' month');
+ $dateTime->modify($duration->d . ' day');
+ $dateTime->modify($duration->h . ' hour');
+ $dateTime->modify($duration->i . ' minute');
+ $dateTime->modify($duration->s . ' second');
+
+ if (is_null($format)) {
+ $output = $dateTime;
+ } else {
+ if ($format === self::UNIX_FORMAT) {
+ $output = $dateTime->getTimestamp();
+ } else {
+ $output = $dateTime->format($format);
+ }
+ }
+
+ return $output;
+ }
+
+ /**
+ * Gets the number of days between a start and end date
+ *
+ * @param integer $days
+ * @param integer $start
+ * @param integer $end
+ * @return integer
+ */
+ protected function numberOfDays($days, $start, $end)
+ {
+ $w = array(date('w', $start), date('w', $end));
+ $oneWeek = self::SECONDS_IN_A_WEEK;
+ $x = floor(($end - $start) / $oneWeek);
+ $sum = 0;
+
+ for ($day = 0; $day < 7; ++$day) {
+ if ($days & pow(2, $day)) {
+ $sum += $x + (($w[0] > $w[1]) ? $w[0] <= $day || $day <= $w[1] : $w[0] <= $day && $day <= $w[1]);
+ }
+ }
+
+ return $sum;
+ }
+
+ /**
+ * Converts a negative day ordinal to
+ * its equivalent positive form
+ *
+ * @param integer $dayNumber
+ * @param integer $weekday
+ * @param integer $timestamp
+ * @return string
+ */
+ protected function convertDayOrdinalToPositive($dayNumber, $weekday, $timestamp)
+ {
+ $dayNumber = empty($dayNumber) ? 1 : $dayNumber; // Returns 0 when no number defined in BYDAY
+
+ $dayOrdinals = $this->dayOrdinals;
+
+ // We only care about negative BYDAY values
+ if ($dayNumber >= 1) {
+ return $dayOrdinals[$dayNumber];
+ }
+
+ $timestamp = (is_object($timestamp)) ? $timestamp : \DateTime::createFromFormat(self::UNIX_FORMAT, $timestamp);
+ $start = strtotime('first day of ' . $timestamp->format(self::DATE_TIME_FORMAT_PRETTY));
+ $end = strtotime('last day of ' . $timestamp->format(self::DATE_TIME_FORMAT_PRETTY));
+
+ // Used with pow(2, X) so pow(2, 4) is THURSDAY
+ $weekdays = array_flip(array_keys($this->weekdays));
+
+ $numberOfDays = $this->numberOfDays(pow(2, $weekdays[$weekday]), $start, $end);
+
+ // Create subset
+ $dayOrdinals = array_slice($dayOrdinals, 0, $numberOfDays, true);
+
+ // Reverse only the values
+ $dayOrdinals = array_combine(array_keys($dayOrdinals), array_reverse(array_values($dayOrdinals)));
+
+ return $dayOrdinals[$dayNumber * -1];
+ }
+
+ /**
+ * Removes unprintable ASCII and UTF-8 characters
+ *
+ * @param string $data
+ * @return string
+ */
+ protected function removeUnprintableChars($data)
+ {
+ return preg_replace('/[\x00-\x1F\x7F\xA0]/u', '', $data);
+ }
+
+ /**
+ * Provides a polyfill for PHP 7.2's `mb_chr()`, which is a multibyte safe version of `chr()`.
+ * Multibyte safe.
+ *
+ * @param integer $code
+ * @return string
+ */
+ protected function mb_chr($code)
+ {
+ if (function_exists('mb_chr')) {
+ return mb_chr($code);
+ } else {
+ if (0x80 > $code %= 0x200000) {
+ $s = chr($code);
+ } elseif (0x800 > $code) {
+ $s = chr(0xc0 | $code >> 6) . chr(0x80 | $code & 0x3f);
+ } elseif (0x10000 > $code) {
+ $s = chr(0xe0 | $code >> 12) . chr(0x80 | $code >> 6 & 0x3f) . chr(0x80 | $code & 0x3f);
+ } else {
+ $s = chr(0xf0 | $code >> 18) . chr(0x80 | $code >> 12 & 0x3f) . chr(0x80 | $code >> 6 & 0x3f) . chr(0x80 | $code & 0x3f);
+ }
+
+ return $s;
+ }
+ }
+
+ /**
+ * Replaces all occurrences of a search string with a given replacement string.
+ * Multibyte safe.
+ *
+ * @param string|array $search
+ * @param string|array $replace
+ * @param string|array $subject
+ * @param integer $count
+ * @return array|string
+ */
+ protected function mb_str_replace($search, $replace, $subject, &$count = 0)
+ {
+ if (!is_array($subject)) {
+ // Normalize `$search` and `$replace` so they are both arrays of the same length
+ $searches = is_array($search) ? array_values($search) : array($search);
+ $replacements = is_array($replace) ? array_values($replace) : array($replace);
+ $replacements = array_pad($replacements, count($searches), '');
+
+ foreach ($searches as $key => $search) {
+ $parts = mb_split(preg_quote($search), $subject);
+ $count += count($parts) - 1;
+ $subject = implode($replacements[$key], $parts);
+ }
+ } else {
+ // Call `mb_str_replace` for each subject in array, recursively
+ foreach ($subject as $key => $value) {
+ $subject[$key] = $this->mb_str_replace($search, $replace, $value, $count);
+ }
+ }
+
+ return $subject;
+ }
+
+ /**
+ * Replaces curly quotes and other special characters
+ * with their standard equivalents
+ *
+ * @param string $data
+ * @return string
+ */
+ protected function cleanData($data)
+ {
+ $replacementChars = array(
+ "\xe2\x80\x98" => "'", // ‘
+ "\xe2\x80\x99" => "'", // ’
+ "\xe2\x80\x9a" => "'", // ‚
+ "\xe2\x80\x9b" => "'", // ‛
+ "\xe2\x80\x9c" => '"', // “
+ "\xe2\x80\x9d" => '"', // ”
+ "\xe2\x80\x9e" => '"', // „
+ "\xe2\x80\x9f" => '"', // ‟
+ "\xe2\x80\x93" => '-', // –
+ "\xe2\x80\x94" => '--', // —
+ "\xe2\x80\xa6" => '...', // …
+ "\xc2\xa0" => ' ',
+ );
+ // Replace UTF-8 characters
+ $cleanedData = strtr($data, $replacementChars);
+
+ // Replace Windows-1252 equivalents
+ $charsToReplace = array_map(function ($code) {
+ return $this->mb_chr($code);
+ }, array(133, 145, 146, 147, 148, 150, 151, 194));
+ $cleanedData = $this->mb_str_replace($charsToReplace, $replacementChars, $cleanedData);
+
+ return $cleanedData;
+ }
+
+ /**
+ * Parses a list of excluded dates
+ * to be applied to an Event
+ *
+ * @param array $event
+ * @return array
+ */
+ public function parseExdates(array $event)
+ {
+ if (empty($event['EXDATE_array'])) {
+ return array();
+ } else {
+ $exdates = $event['EXDATE_array'];
+ }
+
+ $output = array();
+ $currentTimeZone = $this->defaultTimeZone;
+
+ foreach ($exdates as $subArray) {
+ end($subArray);
+ $finalKey = key($subArray);
+
+ foreach ($subArray as $key => $value) {
+ if ($key === 'TZID') {
+ $checkTimeZone = $subArray[$key];
+
+ if ($this->isValidIanaTimeZoneId($checkTimeZone)) {
+ $currentTimeZone = $checkTimeZone;
+ } elseif ($this->isValidCldrTimeZoneId($checkTimeZone)) {
+ $currentTimeZone = $this->isValidCldrTimeZoneId($checkTimeZone, true);
+ } else {
+ $currentTimeZone = $this->defaultTimeZone;
+ }
+ } elseif (is_numeric($key)) {
+ $icalDate = $subArray[$key];
+
+ if (substr($icalDate, -1) === 'Z') {
+ $currentTimeZone = self::TIME_ZONE_UTC;
+ }
+
+ $output[] = new Carbon($icalDate, $currentTimeZone);
+
+ if ($key === $finalKey) {
+ // Reset to default
+ $currentTimeZone = $this->defaultTimeZone;
+ }
+ }
+ }
+ }
+
+ return $output;
+ }
+
+ /**
+ * Checks if a date string is a valid date
+ *
+ * @param string $value
+ * @return boolean
+ * @throws \Exception
+ */
+ public function isValidDate($value)
+ {
+ if (!$value) {
+ return false;
+ }
+
+ try {
+ new \DateTime($value);
+
+ return true;
+ } catch (\Exception $e) {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if a filename exists as a file or URL
+ *
+ * @param string $filename
+ * @return boolean
+ */
+ protected function isFileOrUrl($filename)
+ {
+ return (file_exists($filename) || filter_var($filename, FILTER_VALIDATE_URL)) ?: false;
+ }
+
+ /**
+ * Reads an entire file or URL into an array
+ *
+ * @param string $filename
+ * @return array
+ * @throws \Exception
+ */
+ protected function fileOrUrl($filename)
+ {
+ if (!$lines = file($filename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES)) {
+ throw new \Exception("The file path or URL '{$filename}' does not exist.");
+ }
+
+ return $lines;
+ }
+
+ /**
+ * Ensures the recurrence count is enforced against generated recurrence events.
+ *
+ * @param array $rrules
+ * @param array $recurrenceEvents
+ * @return array
+ */
+ protected function trimToRecurrenceCount(array $rrules, array $recurrenceEvents)
+ {
+ if (isset($rrules['COUNT'])) {
+ $recurrenceCount = (intval($rrules['COUNT']) - 1);
+ $surplusCount = (sizeof($recurrenceEvents) - $recurrenceCount);
+
+ if ($surplusCount > 0) {
+ $recurrenceEvents = array_slice($recurrenceEvents, 0, $recurrenceCount);
+ $this->eventCount -= $surplusCount;
+ }
+ }
+
+ return $recurrenceEvents;
+ }
+
+ /**
+ * Checks if an excluded date matches a given date by reconciling time zones.
+ *
+ * @param integer $exdate
+ * @param array $anEvent
+ * @param integer $recurringOffset
+ * @return boolean
+ */
+ protected function isExdateMatch($exdate, array $anEvent, $recurringOffset)
+ {
+ $searchDate = $anEvent['DTSTART'];
+
+ if (substr($searchDate, -1) === 'Z') {
+ $timeZone = self::TIME_ZONE_UTC;
+ } else {
+ if (isset($anEvent['DTSTART_array'][0]['TZID'])) {
+ $checkTimeZone = $anEvent['DTSTART_array'][0]['TZID'];
+
+ if ($this->isValidIanaTimeZoneId($checkTimeZone)) {
+ $timeZone = $checkTimeZone;
+ } elseif ($this->isValidCldrTimeZoneId($checkTimeZone)) {
+ $timeZone = $this->isValidCldrTimeZoneId($checkTimeZone, true);
+ } else {
+ $timeZone = $this->defaultTimeZone;
+ }
+ } else {
+ $timeZone = $this->defaultTimeZone;
+ }
+ }
+
+ $a = new Carbon($searchDate, $timeZone);
+ $b = $exdate->addSeconds($recurringOffset);
+
+ return $a->eq($b);
+ }
+}
diff --git a/_include/calendar/nesbot/carbon/LICENSE b/_include/calendar/nesbot/carbon/LICENSE
new file mode 100644
index 0000000..6de45eb
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/LICENSE
@@ -0,0 +1,19 @@
+Copyright (C) Brian Nesbitt
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/_include/calendar/nesbot/carbon/composer.json b/_include/calendar/nesbot/carbon/composer.json
new file mode 100644
index 0000000..40be3f5
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/composer.json
@@ -0,0 +1,60 @@
+{
+ "name": "nesbot/carbon",
+ "type": "library",
+ "description": "A simple API extension for DateTime.",
+ "keywords": [
+ "date",
+ "time",
+ "DateTime"
+ ],
+ "homepage": "http://carbon.nesbot.com",
+ "support": {
+ "issues": "https://github.com/briannesbitt/Carbon/issues",
+ "source": "https://github.com/briannesbitt/Carbon"
+ },
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Brian Nesbitt",
+ "email": "brian@nesbot.com",
+ "homepage": "http://nesbot.com"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.9",
+ "symfony/translation": "~2.6 || ~3.0 || ~4.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "~2",
+ "phpunit/phpunit": "^4.8.35 || ^5.7"
+ },
+ "autoload": {
+ "psr-4": {
+ "": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Tests\\": "tests/"
+ }
+ },
+ "config": {
+ "sort-packages": true
+ },
+ "scripts": {
+ "test": [
+ "@phpunit",
+ "@phpcs"
+ ],
+ "phpunit": "phpunit --verbose --coverage-clover=coverage.xml",
+ "phpcs": "php-cs-fixer fix -v --diff --dry-run",
+ "phpstan": "phpstan analyse --configuration phpstan.neon --level 3 src tests"
+ },
+ "extra": {
+ "laravel": {
+ "providers": [
+ "Carbon\\Laravel\\ServiceProvider"
+ ]
+ }
+ }
+}
diff --git a/_include/calendar/nesbot/carbon/readme.md b/_include/calendar/nesbot/carbon/readme.md
new file mode 100644
index 0000000..5e9d1cc
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/readme.md
@@ -0,0 +1,94 @@
+# Carbon
+
+[![Latest Stable Version](https://poser.pugx.org/nesbot/carbon/v/stable.png)](https://packagist.org/packages/nesbot/carbon)
+[![Total Downloads](https://poser.pugx.org/nesbot/carbon/downloads.png)](https://packagist.org/packages/nesbot/carbon)
+[![Build Status](https://travis-ci.org/briannesbitt/Carbon.svg?branch=master)](https://travis-ci.org/briannesbitt/Carbon)
+[![StyleCI](https://styleci.io/repos/5724990/shield?style=flat)](https://styleci.io/repos/5724990)
+[![codecov.io](https://codecov.io/github/briannesbitt/Carbon/coverage.svg?branch=master)](https://codecov.io/github/briannesbitt/Carbon?branch=master)
+[![PHP-Eye](https://php-eye.com/badge/nesbot/carbon/tested.svg?style=flat)](https://php-eye.com/package/nesbot/carbon)
+[![PHPStan](https://img.shields.io/badge/PHPStan-enabled-brightgreen.svg?style=flat)](https://github.com/phpstan/phpstan)
+
+A simple PHP API extension for DateTime. [http://carbon.nesbot.com](http://carbon.nesbot.com)
+
+```php
+use Carbon\Carbon;
+
+printf("Right now is %s", Carbon::now()->toDateTimeString());
+printf("Right now in Vancouver is %s", Carbon::now('America/Vancouver')); //implicit __toString()
+$tomorrow = Carbon::now()->addDay();
+$lastWeek = Carbon::now()->subWeek();
+$nextSummerOlympics = Carbon::createFromDate(2016)->addYears(4);
+
+$officialDate = Carbon::now()->toRfc2822String();
+
+$howOldAmI = Carbon::createFromDate(1975, 5, 21)->age;
+
+$noonTodayLondonTime = Carbon::createFromTime(12, 0, 0, 'Europe/London');
+
+$internetWillBlowUpOn = Carbon::create(2038, 01, 19, 3, 14, 7, 'GMT');
+
+// Don't really want this to happen so mock now
+Carbon::setTestNow(Carbon::createFromDate(2000, 1, 1));
+
+// comparisons are always done in UTC
+if (Carbon::now()->gte($internetWillBlowUpOn)) {
+ die();
+}
+
+// Phew! Return to normal behaviour
+Carbon::setTestNow();
+
+if (Carbon::now()->isWeekend()) {
+ echo 'Party!';
+}
+echo Carbon::now()->subMinutes(2)->diffForHumans(); // '2 minutes ago'
+
+// ... but also does 'from now', 'after' and 'before'
+// rolling up to seconds, minutes, hours, days, months, years
+
+$daysSinceEpoch = Carbon::createFromTimestamp(0)->diffInDays();
+```
+
+## Installation
+
+### With Composer
+
+```
+$ composer require nesbot/carbon
+```
+
+```json
+{
+ "require": {
+ "nesbot/carbon": "~1.21"
+ }
+}
+```
+
+```php
+
+
+### Without Composer
+
+Why are you not using [composer](http://getcomposer.org/)? Download [Carbon.php](https://github.com/briannesbitt/Carbon/blob/master/src/Carbon/Carbon.php) from the repo and save the file into your project path somewhere.
+
+```php
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon;
+
+use Carbon\Exceptions\InvalidDateException;
+use Closure;
+use DatePeriod;
+use DateTime;
+use DateTimeInterface;
+use DateTimeZone;
+use InvalidArgumentException;
+use JsonSerializable;
+use Symfony\Component\Translation\TranslatorInterface;
+
+/**
+ * A simple API extension for DateTime
+ *
+ * @property int $year
+ * @property int $yearIso
+ * @property int $month
+ * @property int $day
+ * @property int $hour
+ * @property int $minute
+ * @property int $second
+ * @property int $timestamp seconds since the Unix Epoch
+ * @property \DateTimeZone $timezone the current timezone
+ * @property \DateTimeZone $tz alias of timezone
+ * @property-read int $micro
+ * @property-read int $dayOfWeek 0 (for Sunday) through 6 (for Saturday)
+ * @property-read int $dayOfWeekIso 1 (for Monday) through 7 (for Sunday)
+ * @property-read int $dayOfYear 0 through 365
+ * @property-read int $weekOfMonth 1 through 5
+ * @property-read int $weekNumberInMonth 1 through 5
+ * @property-read int $weekOfYear ISO-8601 week number of year, weeks starting on Monday
+ * @property-read int $daysInMonth number of days in the given month
+ * @property-read int $age does a diffInYears() with default parameters
+ * @property-read int $quarter the quarter of this instance, 1 - 4
+ * @property-read int $offset the timezone offset in seconds from UTC
+ * @property-read int $offsetHours the timezone offset in hours from UTC
+ * @property-read bool $dst daylight savings time indicator, true if DST, false otherwise
+ * @property-read bool $local checks if the timezone is local, true if local, false otherwise
+ * @property-read bool $utc checks if the timezone is UTC, true if UTC, false otherwise
+ * @property-read string $timezoneName
+ * @property-read string $tzName
+ * @property-read string $englishDayOfWeek the day of week in English
+ * @property-read string $shortEnglishDayOfWeek the abbreviated day of week in English
+ * @property-read string $englishMonth the day of week in English
+ * @property-read string $shortEnglishMonth the abbreviated day of week in English
+ * @property-read string $localeDayOfWeek the day of week in current locale LC_TIME
+ * @property-read string $shortLocaleDayOfWeek the abbreviated day of week in current locale LC_TIME
+ * @property-read string $localeMonth the month in current locale LC_TIME
+ * @property-read string $shortLocaleMonth the abbreviated month in current locale LC_TIME
+ */
+class Carbon extends DateTime implements JsonSerializable
+{
+ const NO_ZERO_DIFF = 01;
+ const JUST_NOW = 02;
+ const ONE_DAY_WORDS = 04;
+ const TWO_DAY_WORDS = 010;
+
+ /**
+ * The day constants.
+ */
+ const SUNDAY = 0;
+ const MONDAY = 1;
+ const TUESDAY = 2;
+ const WEDNESDAY = 3;
+ const THURSDAY = 4;
+ const FRIDAY = 5;
+ const SATURDAY = 6;
+
+ /**
+ * Names of days of the week.
+ *
+ * @var array
+ */
+ protected static $days = array(
+ self::SUNDAY => 'Sunday',
+ self::MONDAY => 'Monday',
+ self::TUESDAY => 'Tuesday',
+ self::WEDNESDAY => 'Wednesday',
+ self::THURSDAY => 'Thursday',
+ self::FRIDAY => 'Friday',
+ self::SATURDAY => 'Saturday',
+ );
+
+ /**
+ * Number of X in Y.
+ */
+ const YEARS_PER_CENTURY = 100;
+ const YEARS_PER_DECADE = 10;
+ const MONTHS_PER_YEAR = 12;
+ const MONTHS_PER_QUARTER = 3;
+ const WEEKS_PER_YEAR = 52;
+ const WEEKS_PER_MONTH = 4;
+ const DAYS_PER_WEEK = 7;
+ const HOURS_PER_DAY = 24;
+ const MINUTES_PER_HOUR = 60;
+ const SECONDS_PER_MINUTE = 60;
+
+ /**
+ * RFC7231 DateTime format.
+ *
+ * @var string
+ */
+ const RFC7231_FORMAT = 'D, d M Y H:i:s \G\M\T';
+
+ /**
+ * Default format to use for __toString method when type juggling occurs.
+ *
+ * @var string
+ */
+ const DEFAULT_TO_STRING_FORMAT = 'Y-m-d H:i:s';
+
+ /**
+ * Format for converting mocked time, includes microseconds.
+ *
+ * @var string
+ */
+ const MOCK_DATETIME_FORMAT = 'Y-m-d H:i:s.u';
+
+ /**
+ * Customizable PHP_INT_SIZE override.
+ *
+ * @var int
+ */
+ public static $PHPIntSize = PHP_INT_SIZE;
+
+ /**
+ * Format to use for __toString method when type juggling occurs.
+ *
+ * @var string
+ */
+ protected static $toStringFormat = self::DEFAULT_TO_STRING_FORMAT;
+
+ /**
+ * First day of week.
+ *
+ * @var int
+ */
+ protected static $weekStartsAt = self::MONDAY;
+
+ /**
+ * Last day of week.
+ *
+ * @var int
+ */
+ protected static $weekEndsAt = self::SUNDAY;
+
+ /**
+ * Days of weekend.
+ *
+ * @var array
+ */
+ protected static $weekendDays = array(
+ self::SATURDAY,
+ self::SUNDAY,
+ );
+
+ /**
+ * Midday/noon hour.
+ *
+ * @var int
+ */
+ protected static $midDayAt = 12;
+
+ /**
+ * Format regex patterns.
+ *
+ * @var array
+ */
+ protected static $regexFormats = array(
+ 'd' => '(3[01]|[12][0-9]|0[1-9])',
+ 'D' => '([a-zA-Z]{3})',
+ 'j' => '([123][0-9]|[1-9])',
+ 'l' => '([a-zA-Z]{2,})',
+ 'N' => '([1-7])',
+ 'S' => '([a-zA-Z]{2})',
+ 'w' => '([0-6])',
+ 'z' => '(36[0-5]|3[0-5][0-9]|[12][0-9]{2}|[1-9]?[0-9])',
+ 'W' => '(5[012]|[1-4][0-9]|[1-9])',
+ 'F' => '([a-zA-Z]{2,})',
+ 'm' => '(1[012]|0[1-9])',
+ 'M' => '([a-zA-Z]{3})',
+ 'n' => '(1[012]|[1-9])',
+ 't' => '(2[89]|3[01])',
+ 'L' => '(0|1)',
+ 'o' => '([1-9][0-9]{0,4})',
+ 'Y' => '([1-9][0-9]{0,4})',
+ 'y' => '([0-9]{2})',
+ 'a' => '(am|pm)',
+ 'A' => '(AM|PM)',
+ 'B' => '([0-9]{3})',
+ 'g' => '(1[012]|[1-9])',
+ 'G' => '(2[0-3]|1?[0-9])',
+ 'h' => '(1[012]|0[1-9])',
+ 'H' => '(2[0-3]|[01][0-9])',
+ 'i' => '([0-5][0-9])',
+ 's' => '([0-5][0-9])',
+ 'u' => '([0-9]{1,6})',
+ 'v' => '([0-9]{1,3})',
+ 'e' => '([a-zA-Z]{1,5})|([a-zA-Z]*\/[a-zA-Z]*)',
+ 'I' => '(0|1)',
+ 'O' => '([\+\-](1[012]|0[0-9])[0134][05])',
+ 'P' => '([\+\-](1[012]|0[0-9]):[0134][05])',
+ 'T' => '([a-zA-Z]{1,5})',
+ 'Z' => '(-?[1-5]?[0-9]{1,4})',
+ 'U' => '([0-9]*)',
+
+ // The formats below are combinations of the above formats.
+ 'c' => '(([1-9][0-9]{0,4})\-(1[012]|0[1-9])\-(3[01]|[12][0-9]|0[1-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])[\+\-](1[012]|0[0-9]):([0134][05]))', // Y-m-dTH:i:sP
+ 'r' => '(([a-zA-Z]{3}), ([123][0-9]|[1-9]) ([a-zA-Z]{3}) ([1-9][0-9]{0,4}) (2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9]) [\+\-](1[012]|0[0-9])([0134][05]))', // D, j M Y H:i:s O
+ );
+
+ /**
+ * A test Carbon instance to be returned when now instances are created.
+ *
+ * @var \Carbon\Carbon
+ */
+ protected static $testNow;
+
+ /**
+ * A translator to ... er ... translate stuff.
+ *
+ * @var \Symfony\Component\Translation\TranslatorInterface
+ */
+ protected static $translator;
+
+ /**
+ * The errors that can occur.
+ *
+ * @var array
+ */
+ protected static $lastErrors;
+
+ /**
+ * The custom Carbon JSON serializer.
+ *
+ * @var callable|null
+ */
+ protected static $serializer;
+
+ /**
+ * The registered string macros.
+ *
+ * @var array
+ */
+ protected static $localMacros = array();
+
+ /**
+ * Will UTF8 encoding be used to print localized date/time ?
+ *
+ * @var bool
+ */
+ protected static $utf8 = false;
+
+ /**
+ * Add microseconds to now on PHP < 7.1 and 7.1.3. true by default.
+ *
+ * @var bool
+ */
+ protected static $microsecondsFallback = true;
+
+ /**
+ * Indicates if months should be calculated with overflow.
+ *
+ * @var bool
+ */
+ protected static $monthsOverflow = true;
+
+ /**
+ * Indicates if years should be calculated with overflow.
+ *
+ * @var bool
+ */
+ protected static $yearsOverflow = true;
+
+ /**
+ * Indicates if years are compared with month by default so isSameMonth and isSameQuarter have $ofSameYear set
+ * to true by default.
+ *
+ * @var bool
+ */
+ protected static $compareYearWithMonth = false;
+
+ /**
+ * Options for diffForHumans().
+ *
+ * @var int
+ */
+ protected static $humanDiffOptions = self::NO_ZERO_DIFF;
+
+ /**
+ * @param int $humanDiffOptions
+ */
+ public static function setHumanDiffOptions($humanDiffOptions)
+ {
+ static::$humanDiffOptions = $humanDiffOptions;
+ }
+
+ /**
+ * @param int $humanDiffOption
+ */
+ public static function enableHumanDiffOption($humanDiffOption)
+ {
+ static::$humanDiffOptions = static::getHumanDiffOptions() | $humanDiffOption;
+ }
+
+ /**
+ * @param int $humanDiffOption
+ */
+ public static function disableHumanDiffOption($humanDiffOption)
+ {
+ static::$humanDiffOptions = static::getHumanDiffOptions() & ~$humanDiffOption;
+ }
+
+ /**
+ * @return int
+ */
+ public static function getHumanDiffOptions()
+ {
+ return static::$humanDiffOptions;
+ }
+
+ /**
+ * Add microseconds to now on PHP < 7.1 and 7.1.3 if set to true,
+ * let microseconds to 0 on those PHP versions if false.
+ *
+ * @param bool $microsecondsFallback
+ */
+ public static function useMicrosecondsFallback($microsecondsFallback = true)
+ {
+ static::$microsecondsFallback = $microsecondsFallback;
+ }
+
+ /**
+ * Return true if microseconds fallback on PHP < 7.1 and 7.1.3 is
+ * enabled. false if disabled.
+ *
+ * @return bool
+ */
+ public static function isMicrosecondsFallbackEnabled()
+ {
+ return static::$microsecondsFallback;
+ }
+
+ /**
+ * Indicates if months should be calculated with overflow.
+ *
+ * @param bool $monthsOverflow
+ *
+ * @return void
+ */
+ public static function useMonthsOverflow($monthsOverflow = true)
+ {
+ static::$monthsOverflow = $monthsOverflow;
+ }
+
+ /**
+ * Reset the month overflow behavior.
+ *
+ * @return void
+ */
+ public static function resetMonthsOverflow()
+ {
+ static::$monthsOverflow = true;
+ }
+
+ /**
+ * Get the month overflow behavior.
+ *
+ * @return bool
+ */
+ public static function shouldOverflowMonths()
+ {
+ return static::$monthsOverflow;
+ }
+
+ /**
+ * Indicates if years should be calculated with overflow.
+ *
+ * @param bool $yearsOverflow
+ *
+ * @return void
+ */
+ public static function useYearsOverflow($yearsOverflow = true)
+ {
+ static::$yearsOverflow = $yearsOverflow;
+ }
+
+ /**
+ * Reset the month overflow behavior.
+ *
+ * @return void
+ */
+ public static function resetYearsOverflow()
+ {
+ static::$yearsOverflow = true;
+ }
+
+ /**
+ * Get the month overflow behavior.
+ *
+ * @return bool
+ */
+ public static function shouldOverflowYears()
+ {
+ return static::$yearsOverflow;
+ }
+
+ /**
+ * Get the month comparison default behavior.
+ *
+ * @return bool
+ */
+ public static function compareYearWithMonth($compareYearWithMonth = true)
+ {
+ static::$compareYearWithMonth = $compareYearWithMonth;
+ }
+
+ /**
+ * Get the month comparison default behavior.
+ *
+ * @return bool
+ */
+ public static function shouldCompareYearWithMonth()
+ {
+ return static::$compareYearWithMonth;
+ }
+
+ /**
+ * Creates a DateTimeZone from a string, DateTimeZone or integer offset.
+ *
+ * @param \DateTimeZone|string|int|null $object
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return \DateTimeZone
+ */
+ protected static function safeCreateDateTimeZone($object)
+ {
+ if ($object === null) {
+ // Don't return null... avoid Bug #52063 in PHP <5.3.6
+ return new DateTimeZone(date_default_timezone_get());
+ }
+
+ if ($object instanceof DateTimeZone) {
+ return $object;
+ }
+
+ if (is_numeric($object)) {
+ $tzName = timezone_name_from_abbr(null, $object * 3600, true);
+
+ if ($tzName === false) {
+ throw new InvalidArgumentException('Unknown or bad timezone ('.$object.')');
+ }
+
+ $object = $tzName;
+ }
+
+ $tz = @timezone_open($object = (string) $object);
+
+ if ($tz !== false) {
+ return $tz;
+ }
+
+ // Work-around for a bug fixed in PHP 5.5.10 https://bugs.php.net/bug.php?id=45528
+ // See: https://stackoverflow.com/q/14068594/2646927
+ // @codeCoverageIgnoreStart
+ if (strpos($object, ':') !== false) {
+ try {
+ return static::createFromFormat('O', $object)->getTimezone();
+ } catch (InvalidArgumentException $e) {
+ //
+ }
+ }
+ // @codeCoverageIgnoreEnd
+
+ throw new InvalidArgumentException('Unknown or bad timezone ('.$object.')');
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //////////////////////////// CONSTRUCTORS /////////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Create a new Carbon instance.
+ *
+ * Please see the testing aids section (specifically static::setTestNow())
+ * for more on the possibility of this constructor returning a test instance.
+ *
+ * @param string|null $time
+ * @param \DateTimeZone|string|null $tz
+ */
+ public function __construct($time = null, $tz = null)
+ {
+ // If the class has a test now set and we are trying to create a now()
+ // instance then override as required
+ $isNow = empty($time) || $time === 'now';
+ if (static::hasTestNow() && ($isNow || static::hasRelativeKeywords($time))) {
+ $testInstance = clone static::getTestNow();
+
+ //shift the time according to the given time zone
+ if ($tz !== null && $tz !== static::getTestNow()->getTimezone()) {
+ $testInstance->setTimezone($tz);
+ } else {
+ $tz = $testInstance->getTimezone();
+ }
+
+ if (static::hasRelativeKeywords($time)) {
+ $testInstance->modify($time);
+ }
+
+ $time = $testInstance->format(static::MOCK_DATETIME_FORMAT);
+ }
+
+ $timezone = static::safeCreateDateTimeZone($tz);
+ // @codeCoverageIgnoreStart
+ if ($isNow && !isset($testInstance) && static::isMicrosecondsFallbackEnabled() && (
+ version_compare(PHP_VERSION, '7.1.0-dev', '<')
+ ||
+ version_compare(PHP_VERSION, '7.1.3-dev', '>=') && version_compare(PHP_VERSION, '7.1.4-dev', '<')
+ )
+ ) {
+ // Get microseconds from microtime() if "now" asked and PHP < 7.1 and PHP 7.1.3 if fallback enabled.
+ list($microTime, $timeStamp) = explode(' ', microtime());
+ $dateTime = new DateTime('now', $timezone);
+ $dateTime->setTimestamp($timeStamp); // Use the timestamp returned by microtime as now can happen in the next second
+ $time = $dateTime->format(static::DEFAULT_TO_STRING_FORMAT).substr($microTime, 1, 7);
+ }
+ // @codeCoverageIgnoreEnd
+
+ // Work-around for PHP bug https://bugs.php.net/bug.php?id=67127
+ if (strpos((string) .1, '.') === false) {
+ $locale = setlocale(LC_NUMERIC, '0');
+ setlocale(LC_NUMERIC, 'C');
+ }
+ parent::__construct($time, $timezone);
+ if (isset($locale)) {
+ setlocale(LC_NUMERIC, $locale);
+ }
+ static::setLastErrors(parent::getLastErrors());
+ }
+
+ /**
+ * Create a Carbon instance from a DateTime one.
+ *
+ * @param \DateTime|\DateTimeInterface $date
+ *
+ * @return static
+ */
+ public static function instance($date)
+ {
+ if ($date instanceof static) {
+ return clone $date;
+ }
+
+ static::expectDateTime($date);
+
+ return new static($date->format('Y-m-d H:i:s.u'), $date->getTimezone());
+ }
+
+ /**
+ * Create a carbon instance from a string.
+ *
+ * This is an alias for the constructor that allows better fluent syntax
+ * as it allows you to do Carbon::parse('Monday next week')->fn() rather
+ * than (new Carbon('Monday next week'))->fn().
+ *
+ * @param string|null $time
+ * @param \DateTimeZone|string|null $tz
+ *
+ * @return static
+ */
+ public static function parse($time = null, $tz = null)
+ {
+ return new static($time, $tz);
+ }
+
+ /**
+ * Get a Carbon instance for the current date and time.
+ *
+ * @param \DateTimeZone|string|null $tz
+ *
+ * @return static
+ */
+ public static function now($tz = null)
+ {
+ return new static(null, $tz);
+ }
+
+ /**
+ * Create a Carbon instance for today.
+ *
+ * @param \DateTimeZone|string|null $tz
+ *
+ * @return static
+ */
+ public static function today($tz = null)
+ {
+ return static::parse('today', $tz);
+ }
+
+ /**
+ * Create a Carbon instance for tomorrow.
+ *
+ * @param \DateTimeZone|string|null $tz
+ *
+ * @return static
+ */
+ public static function tomorrow($tz = null)
+ {
+ return static::parse('tomorrow', $tz);
+ }
+
+ /**
+ * Create a Carbon instance for yesterday.
+ *
+ * @param \DateTimeZone|string|null $tz
+ *
+ * @return static
+ */
+ public static function yesterday($tz = null)
+ {
+ return static::parse('yesterday', $tz);
+ }
+
+ /**
+ * Create a Carbon instance for the greatest supported date.
+ *
+ * @return static
+ */
+ public static function maxValue()
+ {
+ if (self::$PHPIntSize === 4) {
+ // 32 bit
+ return static::createFromTimestamp(PHP_INT_MAX); // @codeCoverageIgnore
+ }
+
+ // 64 bit
+ return static::create(9999, 12, 31, 23, 59, 59);
+ }
+
+ /**
+ * Create a Carbon instance for the lowest supported date.
+ *
+ * @return static
+ */
+ public static function minValue()
+ {
+ if (self::$PHPIntSize === 4) {
+ // 32 bit
+ return static::createFromTimestamp(~PHP_INT_MAX); // @codeCoverageIgnore
+ }
+
+ // 64 bit
+ return static::create(1, 1, 1, 0, 0, 0);
+ }
+
+ /**
+ * Create a new Carbon instance from a specific date and time.
+ *
+ * If any of $year, $month or $day are set to null their now() values will
+ * be used.
+ *
+ * If $hour is null it will be set to its now() value and the default
+ * values for $minute and $second will be their now() values.
+ *
+ * If $hour is not null then the default values for $minute and $second
+ * will be 0.
+ *
+ * @param int|null $year
+ * @param int|null $month
+ * @param int|null $day
+ * @param int|null $hour
+ * @param int|null $minute
+ * @param int|null $second
+ * @param \DateTimeZone|string|null $tz
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return static
+ */
+ public static function create($year = null, $month = null, $day = null, $hour = null, $minute = null, $second = null, $tz = null)
+ {
+ $now = static::hasTestNow() ? static::getTestNow() : static::now($tz);
+
+ $defaults = array_combine(array(
+ 'year',
+ 'month',
+ 'day',
+ 'hour',
+ 'minute',
+ 'second',
+ ), explode('-', $now->format('Y-n-j-G-i-s')));
+
+ $year = $year === null ? $defaults['year'] : $year;
+ $month = $month === null ? $defaults['month'] : $month;
+ $day = $day === null ? $defaults['day'] : $day;
+
+ if ($hour === null) {
+ $hour = $defaults['hour'];
+ $minute = $minute === null ? $defaults['minute'] : $minute;
+ $second = $second === null ? $defaults['second'] : $second;
+ } else {
+ $minute = $minute === null ? 0 : $minute;
+ $second = $second === null ? 0 : $second;
+ }
+
+ $fixYear = null;
+
+ if ($year < 0) {
+ $fixYear = $year;
+ $year = 0;
+ } elseif ($year > 9999) {
+ $fixYear = $year - 9999;
+ $year = 9999;
+ }
+
+ $instance = static::createFromFormat('!Y-n-j G:i:s', sprintf('%s-%s-%s %s:%02s:%02s', $year, $month, $day, $hour, $minute, $second), $tz);
+
+ if ($fixYear !== null) {
+ $instance->addYears($fixYear);
+ }
+
+ return $instance;
+ }
+
+ /**
+ * Create a new safe Carbon instance from a specific date and time.
+ *
+ * If any of $year, $month or $day are set to null their now() values will
+ * be used.
+ *
+ * If $hour is null it will be set to its now() value and the default
+ * values for $minute and $second will be their now() values.
+ *
+ * If $hour is not null then the default values for $minute and $second
+ * will be 0.
+ *
+ * If one of the set values is not valid, an \InvalidArgumentException
+ * will be thrown.
+ *
+ * @param int|null $year
+ * @param int|null $month
+ * @param int|null $day
+ * @param int|null $hour
+ * @param int|null $minute
+ * @param int|null $second
+ * @param \DateTimeZone|string|null $tz
+ *
+ * @throws \Carbon\Exceptions\InvalidDateException|\InvalidArgumentException
+ *
+ * @return static
+ */
+ public static function createSafe($year = null, $month = null, $day = null, $hour = null, $minute = null, $second = null, $tz = null)
+ {
+ $fields = array(
+ 'year' => array(0, 9999),
+ 'month' => array(0, 12),
+ 'day' => array(0, 31),
+ 'hour' => array(0, 24),
+ 'minute' => array(0, 59),
+ 'second' => array(0, 59),
+ );
+
+ foreach ($fields as $field => $range) {
+ if ($$field !== null && (!is_int($$field) || $$field < $range[0] || $$field > $range[1])) {
+ throw new InvalidDateException($field, $$field);
+ }
+ }
+
+ $instance = static::create($year, $month, $day, $hour, $minute, $second, $tz);
+
+ foreach (array_reverse($fields) as $field => $range) {
+ if ($$field !== null && (!is_int($$field) || $$field !== $instance->$field)) {
+ throw new InvalidDateException($field, $$field);
+ }
+ }
+
+ return $instance;
+ }
+
+ /**
+ * Create a Carbon instance from just a date. The time portion is set to now.
+ *
+ * @param int|null $year
+ * @param int|null $month
+ * @param int|null $day
+ * @param \DateTimeZone|string|null $tz
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return static
+ */
+ public static function createFromDate($year = null, $month = null, $day = null, $tz = null)
+ {
+ return static::create($year, $month, $day, null, null, null, $tz);
+ }
+
+ /**
+ * Create a Carbon instance from just a date. The time portion is set to midnight.
+ *
+ * @param int|null $year
+ * @param int|null $month
+ * @param int|null $day
+ * @param \DateTimeZone|string|null $tz
+ *
+ * @return static
+ */
+ public static function createMidnightDate($year = null, $month = null, $day = null, $tz = null)
+ {
+ return static::create($year, $month, $day, 0, 0, 0, $tz);
+ }
+
+ /**
+ * Create a Carbon instance from just a time. The date portion is set to today.
+ *
+ * @param int|null $hour
+ * @param int|null $minute
+ * @param int|null $second
+ * @param \DateTimeZone|string|null $tz
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return static
+ */
+ public static function createFromTime($hour = null, $minute = null, $second = null, $tz = null)
+ {
+ return static::create(null, null, null, $hour, $minute, $second, $tz);
+ }
+
+ /**
+ * Create a Carbon instance from a time string. The date portion is set to today.
+ *
+ * @param string $time
+ * @param \DateTimeZone|string|null $tz
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return static
+ */
+ public static function createFromTimeString($time, $tz = null)
+ {
+ return static::today($tz)->setTimeFromTimeString($time);
+ }
+
+ private static function createFromFormatAndTimezone($format, $time, $tz)
+ {
+ return $tz !== null
+ ? parent::createFromFormat($format, $time, static::safeCreateDateTimeZone($tz))
+ : parent::createFromFormat($format, $time);
+ }
+
+ /**
+ * Create a Carbon instance from a specific format.
+ *
+ * @param string $format Datetime format
+ * @param string $time
+ * @param \DateTimeZone|string|null $tz
+ *
+ * @throws InvalidArgumentException
+ *
+ * @return static
+ */
+ public static function createFromFormat($format, $time, $tz = null)
+ {
+ // First attempt to create an instance, so that error messages are based on the unmodified format.
+ $date = self::createFromFormatAndTimezone($format, $time, $tz);
+ $lastErrors = parent::getLastErrors();
+
+ if (($mock = static::getTestNow()) && ($date instanceof DateTime || $date instanceof DateTimeInterface)) {
+ // Set timezone from mock if custom timezone was neither given directly nor as a part of format.
+ // First let's skip the part that will be ignored by the parser.
+ $nonEscaped = '(?getTimezone();
+ }
+
+ // Prepend mock datetime only if the format does not contain non escaped unix epoch reset flag.
+ if (!preg_match("/{$nonEscaped}[!|]/", $format)) {
+ $format = static::MOCK_DATETIME_FORMAT.' '.$format;
+ $time = $mock->format(static::MOCK_DATETIME_FORMAT).' '.$time;
+ }
+
+ // Regenerate date from the modified format to base result on the mocked instance instead of now.
+ $date = self::createFromFormatAndTimezone($format, $time, $tz);
+ }
+
+ if ($date instanceof DateTime || $date instanceof DateTimeInterface) {
+ $instance = static::instance($date);
+ $instance::setLastErrors($lastErrors);
+
+ return $instance;
+ }
+
+ throw new InvalidArgumentException(implode(PHP_EOL, $lastErrors['errors']));
+ }
+
+ /**
+ * Set last errors.
+ *
+ * @param array $lastErrors
+ *
+ * @return void
+ */
+ private static function setLastErrors(array $lastErrors)
+ {
+ static::$lastErrors = $lastErrors;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function getLastErrors()
+ {
+ return static::$lastErrors;
+ }
+
+ /**
+ * Create a Carbon instance from a timestamp.
+ *
+ * @param int $timestamp
+ * @param \DateTimeZone|string|null $tz
+ *
+ * @return static
+ */
+ public static function createFromTimestamp($timestamp, $tz = null)
+ {
+ return static::today($tz)->setTimestamp($timestamp);
+ }
+
+ /**
+ * Create a Carbon instance from a timestamp in milliseconds.
+ *
+ * @param int $timestamp
+ * @param \DateTimeZone|string|null $tz
+ *
+ * @return static
+ */
+ public static function createFromTimestampMs($timestamp, $tz = null)
+ {
+ return static::createFromFormat('U.u', sprintf('%F', $timestamp / 1000))
+ ->setTimezone($tz);
+ }
+
+ /**
+ * Create a Carbon instance from an UTC timestamp.
+ *
+ * @param int $timestamp
+ *
+ * @return static
+ */
+ public static function createFromTimestampUTC($timestamp)
+ {
+ return new static('@'.$timestamp);
+ }
+
+ /**
+ * Make a Carbon instance from given variable if possible.
+ *
+ * Always return a new instance. Parse only strings and only these likely to be dates (skip intervals
+ * and recurrences). Throw an exception for invalid format, but otherwise return null.
+ *
+ * @param mixed $var
+ *
+ * @return static|null
+ */
+ public static function make($var)
+ {
+ if ($var instanceof DateTime || $var instanceof DateTimeInterface) {
+ return static::instance($var);
+ }
+
+ if (is_string($var)) {
+ $var = trim($var);
+ $first = substr($var, 0, 1);
+
+ if (is_string($var) && $first !== 'P' && $first !== 'R' && preg_match('/[a-z0-9]/i', $var)) {
+ return static::parse($var);
+ }
+ }
+ }
+
+ /**
+ * Get a copy of the instance.
+ *
+ * @return static
+ */
+ public function copy()
+ {
+ return clone $this;
+ }
+
+ /**
+ * Returns a present instance in the same timezone.
+ *
+ * @return static
+ */
+ public function nowWithSameTz()
+ {
+ return static::now($this->getTimezone());
+ }
+
+ /**
+ * Throws an exception if the given object is not a DateTime and does not implement DateTimeInterface
+ * and not in $other.
+ *
+ * @param mixed $date
+ * @param string|array $other
+ *
+ * @throws \InvalidArgumentException
+ */
+ protected static function expectDateTime($date, $other = array())
+ {
+ $message = 'Expected ';
+ foreach ((array) $other as $expect) {
+ $message .= "{$expect}, ";
+ }
+
+ if (!$date instanceof DateTime && !$date instanceof DateTimeInterface) {
+ throw new InvalidArgumentException(
+ $message.'DateTime or DateTimeInterface, '.
+ (is_object($date) ? get_class($date) : gettype($date)).' given'
+ );
+ }
+ }
+
+ /**
+ * Return the Carbon instance passed through, a now instance in the same timezone
+ * if null given or parse the input if string given.
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|string|null $date
+ *
+ * @return static
+ */
+ protected function resolveCarbon($date = null)
+ {
+ if (!$date) {
+ return $this->nowWithSameTz();
+ }
+
+ if (is_string($date)) {
+ return static::parse($date, $this->getTimezone());
+ }
+
+ static::expectDateTime($date, array('null', 'string'));
+
+ return $date instanceof self ? $date : static::instance($date);
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ ///////////////////////// GETTERS AND SETTERS /////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Get a part of the Carbon object
+ *
+ * @param string $name
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return string|int|bool|\DateTimeZone
+ */
+ public function __get($name)
+ {
+ static $formats = array(
+ 'year' => 'Y',
+ 'yearIso' => 'o',
+ 'month' => 'n',
+ 'day' => 'j',
+ 'hour' => 'G',
+ 'minute' => 'i',
+ 'second' => 's',
+ 'micro' => 'u',
+ 'dayOfWeek' => 'w',
+ 'dayOfWeekIso' => 'N',
+ 'dayOfYear' => 'z',
+ 'weekOfYear' => 'W',
+ 'daysInMonth' => 't',
+ 'timestamp' => 'U',
+ 'englishDayOfWeek' => 'l',
+ 'shortEnglishDayOfWeek' => 'D',
+ 'englishMonth' => 'F',
+ 'shortEnglishMonth' => 'M',
+ 'localeDayOfWeek' => '%A',
+ 'shortLocaleDayOfWeek' => '%a',
+ 'localeMonth' => '%B',
+ 'shortLocaleMonth' => '%b',
+ );
+
+ switch (true) {
+ case isset($formats[$name]):
+ $format = $formats[$name];
+ $method = substr($format, 0, 1) === '%' ? 'formatLocalized' : 'format';
+ $value = $this->$method($format);
+
+ return is_numeric($value) ? (int) $value : $value;
+
+ case $name === 'weekOfMonth':
+ return (int) ceil($this->day / static::DAYS_PER_WEEK);
+
+ case $name === 'weekNumberInMonth':
+ return (int) ceil(($this->day + $this->copy()->startOfMonth()->dayOfWeek - 1) / static::DAYS_PER_WEEK);
+
+ case $name === 'age':
+ return $this->diffInYears();
+
+ case $name === 'quarter':
+ return (int) ceil($this->month / static::MONTHS_PER_QUARTER);
+
+ case $name === 'offset':
+ return $this->getOffset();
+
+ case $name === 'offsetHours':
+ return $this->getOffset() / static::SECONDS_PER_MINUTE / static::MINUTES_PER_HOUR;
+
+ case $name === 'dst':
+ return $this->format('I') === '1';
+
+ case $name === 'local':
+ return $this->getOffset() === $this->copy()->setTimezone(date_default_timezone_get())->getOffset();
+
+ case $name === 'utc':
+ return $this->getOffset() === 0;
+
+ case $name === 'timezone' || $name === 'tz':
+ return $this->getTimezone();
+
+ case $name === 'timezoneName' || $name === 'tzName':
+ return $this->getTimezone()->getName();
+
+ default:
+ throw new InvalidArgumentException(sprintf("Unknown getter '%s'", $name));
+ }
+ }
+
+ /**
+ * Check if an attribute exists on the object
+ *
+ * @param string $name
+ *
+ * @return bool
+ */
+ public function __isset($name)
+ {
+ try {
+ $this->__get($name);
+ } catch (InvalidArgumentException $e) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Set a part of the Carbon object
+ *
+ * @param string $name
+ * @param string|int|\DateTimeZone $value
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return void
+ */
+ public function __set($name, $value)
+ {
+ switch ($name) {
+ case 'year':
+ case 'month':
+ case 'day':
+ case 'hour':
+ case 'minute':
+ case 'second':
+ list($year, $month, $day, $hour, $minute, $second) = explode('-', $this->format('Y-n-j-G-i-s'));
+ $$name = $value;
+ $this->setDateTime($year, $month, $day, $hour, $minute, $second);
+ break;
+
+ case 'timestamp':
+ parent::setTimestamp($value);
+ break;
+
+ case 'timezone':
+ case 'tz':
+ $this->setTimezone($value);
+ break;
+
+ default:
+ throw new InvalidArgumentException(sprintf("Unknown setter '%s'", $name));
+ }
+ }
+
+ /**
+ * Set the instance's year
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function year($value)
+ {
+ $this->year = $value;
+
+ return $this;
+ }
+
+ /**
+ * Set the instance's month
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function month($value)
+ {
+ $this->month = $value;
+
+ return $this;
+ }
+
+ /**
+ * Set the instance's day
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function day($value)
+ {
+ $this->day = $value;
+
+ return $this;
+ }
+
+ /**
+ * Set the instance's hour
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function hour($value)
+ {
+ $this->hour = $value;
+
+ return $this;
+ }
+
+ /**
+ * Set the instance's minute
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function minute($value)
+ {
+ $this->minute = $value;
+
+ return $this;
+ }
+
+ /**
+ * Set the instance's second
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function second($value)
+ {
+ $this->second = $value;
+
+ return $this;
+ }
+
+ /**
+ * Sets the current date of the DateTime object to a different date.
+ * Calls modify as a workaround for a php bug
+ *
+ * @param int $year
+ * @param int $month
+ * @param int $day
+ *
+ * @return static
+ *
+ * @see https://github.com/briannesbitt/Carbon/issues/539
+ * @see https://bugs.php.net/bug.php?id=63863
+ */
+ public function setDate($year, $month, $day)
+ {
+ $this->modify('+0 day');
+
+ return parent::setDate($year, $month, $day);
+ }
+
+ /**
+ * Set the date and time all together
+ *
+ * @param int $year
+ * @param int $month
+ * @param int $day
+ * @param int $hour
+ * @param int $minute
+ * @param int $second
+ *
+ * @return static
+ */
+ public function setDateTime($year, $month, $day, $hour, $minute, $second = 0)
+ {
+ return $this->setDate($year, $month, $day)->setTime($hour, $minute, $second);
+ }
+
+ /**
+ * Set the time by time string
+ *
+ * @param string $time
+ *
+ * @return static
+ */
+ public function setTimeFromTimeString($time)
+ {
+ if (strpos($time, ':') === false) {
+ $time .= ':0';
+ }
+
+ return $this->modify($time);
+ }
+
+ /**
+ * Set the instance's timestamp
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function timestamp($value)
+ {
+ return $this->setTimestamp($value);
+ }
+
+ /**
+ * Alias for setTimezone()
+ *
+ * @param \DateTimeZone|string $value
+ *
+ * @return static
+ */
+ public function timezone($value)
+ {
+ return $this->setTimezone($value);
+ }
+
+ /**
+ * Alias for setTimezone()
+ *
+ * @param \DateTimeZone|string $value
+ *
+ * @return static
+ */
+ public function tz($value)
+ {
+ return $this->setTimezone($value);
+ }
+
+ /**
+ * Set the instance's timezone from a string or object
+ *
+ * @param \DateTimeZone|string $value
+ *
+ * @return static
+ */
+ public function setTimezone($value)
+ {
+ parent::setTimezone(static::safeCreateDateTimeZone($value));
+ // https://bugs.php.net/bug.php?id=72338
+ // just workaround on this bug
+ $this->getTimestamp();
+
+ return $this;
+ }
+
+ /**
+ * Set the year, month, and date for this instance to that of the passed instance.
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface $date
+ *
+ * @return static
+ */
+ public function setDateFrom($date)
+ {
+ $date = static::instance($date);
+
+ $this->setDate($date->year, $date->month, $date->day);
+
+ return $this;
+ }
+
+ /**
+ * Set the hour, day, and time for this instance to that of the passed instance.
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface $date
+ *
+ * @return static
+ */
+ public function setTimeFrom($date)
+ {
+ $date = static::instance($date);
+
+ $this->setTime($date->hour, $date->minute, $date->second);
+
+ return $this;
+ }
+
+ /**
+ * Get the days of the week
+ *
+ * @return array
+ */
+ public static function getDays()
+ {
+ return static::$days;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ /////////////////////// WEEK SPECIAL DAYS /////////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Get the first day of week
+ *
+ * @return int
+ */
+ public static function getWeekStartsAt()
+ {
+ return static::$weekStartsAt;
+ }
+
+ /**
+ * Set the first day of week
+ *
+ * @param int $day week start day
+ *
+ * @throws InvalidArgumentException
+ *
+ * @return void
+ */
+ public static function setWeekStartsAt($day)
+ {
+ if ($day > static::SATURDAY || $day < static::SUNDAY) {
+ throw new InvalidArgumentException('Day of a week should be greater than or equal to 0 and less than or equal to 6.');
+ }
+
+ static::$weekStartsAt = $day;
+ }
+
+ /**
+ * Get the last day of week
+ *
+ * @return int
+ */
+ public static function getWeekEndsAt()
+ {
+ return static::$weekEndsAt;
+ }
+
+ /**
+ * Set the last day of week
+ *
+ * @param int $day
+ *
+ * @throws InvalidArgumentException
+ *
+ * @return void
+ */
+ public static function setWeekEndsAt($day)
+ {
+ if ($day > static::SATURDAY || $day < static::SUNDAY) {
+ throw new InvalidArgumentException('Day of a week should be greater than or equal to 0 and less than or equal to 6.');
+ }
+
+ static::$weekEndsAt = $day;
+ }
+
+ /**
+ * Get weekend days
+ *
+ * @return array
+ */
+ public static function getWeekendDays()
+ {
+ return static::$weekendDays;
+ }
+
+ /**
+ * Set weekend days
+ *
+ * @param array $days
+ *
+ * @return void
+ */
+ public static function setWeekendDays($days)
+ {
+ static::$weekendDays = $days;
+ }
+
+ /**
+ * get midday/noon hour
+ *
+ * @return int
+ */
+ public static function getMidDayAt()
+ {
+ return static::$midDayAt;
+ }
+
+ /**
+ * Set midday/noon hour
+ *
+ * @param int $hour midday hour
+ *
+ * @return void
+ */
+ public static function setMidDayAt($hour)
+ {
+ static::$midDayAt = $hour;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ ///////////////////////// TESTING AIDS ////////////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Set a Carbon instance (real or mock) to be returned when a "now"
+ * instance is created. The provided instance will be returned
+ * specifically under the following conditions:
+ * - A call to the static now() method, ex. Carbon::now()
+ * - When a null (or blank string) is passed to the constructor or parse(), ex. new Carbon(null)
+ * - When the string "now" is passed to the constructor or parse(), ex. new Carbon('now')
+ * - When a string containing the desired time is passed to Carbon::parse().
+ *
+ * Note the timezone parameter was left out of the examples above and
+ * has no affect as the mock value will be returned regardless of its value.
+ *
+ * To clear the test instance call this method using the default
+ * parameter of null.
+ *
+ * @param \Carbon\Carbon|null $testNow real or mock Carbon instance
+ * @param \Carbon\Carbon|string|null $testNow
+ */
+ public static function setTestNow($testNow = null)
+ {
+ static::$testNow = is_string($testNow) ? static::parse($testNow) : $testNow;
+ }
+
+ /**
+ * Get the Carbon instance (real or mock) to be returned when a "now"
+ * instance is created.
+ *
+ * @return static the current instance used for testing
+ */
+ public static function getTestNow()
+ {
+ return static::$testNow;
+ }
+
+ /**
+ * Determine if there is a valid test instance set. A valid test instance
+ * is anything that is not null.
+ *
+ * @return bool true if there is a test instance, otherwise false
+ */
+ public static function hasTestNow()
+ {
+ return static::getTestNow() !== null;
+ }
+
+ /**
+ * Determine if a time string will produce a relative date.
+ *
+ * @param string $time
+ *
+ * @return bool true if time match a relative date, false if absolute or invalid time string
+ */
+ public static function hasRelativeKeywords($time)
+ {
+ if (strtotime($time) === false) {
+ return false;
+ }
+
+ $date1 = new DateTime('2000-01-01T00:00:00Z');
+ $date1->modify($time);
+ $date2 = new DateTime('2001-12-25T00:00:00Z');
+ $date2->modify($time);
+
+ return $date1 != $date2;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ /////////////////////// LOCALIZATION //////////////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Initialize the translator instance if necessary.
+ *
+ * @return \Symfony\Component\Translation\TranslatorInterface
+ */
+ protected static function translator()
+ {
+ if (static::$translator === null) {
+ static::$translator = Translator::get();
+ }
+
+ return static::$translator;
+ }
+
+ /**
+ * Get the translator instance in use
+ *
+ * @return \Symfony\Component\Translation\TranslatorInterface
+ */
+ public static function getTranslator()
+ {
+ return static::translator();
+ }
+
+ /**
+ * Set the translator instance to use
+ *
+ * @param \Symfony\Component\Translation\TranslatorInterface $translator
+ *
+ * @return void
+ */
+ public static function setTranslator(TranslatorInterface $translator)
+ {
+ static::$translator = $translator;
+ }
+
+ /**
+ * Get the current translator locale
+ *
+ * @return string
+ */
+ public static function getLocale()
+ {
+ return static::translator()->getLocale();
+ }
+
+ /**
+ * Set the current translator locale and indicate if the source locale file exists
+ *
+ * @param string $locale locale ex. en
+ *
+ * @return bool
+ */
+ public static function setLocale($locale)
+ {
+ return static::translator()->setLocale($locale) !== false;
+ }
+
+ /**
+ * Set the current locale to the given, execute the passed function, reset the locale to previous one,
+ * then return the result of the closure (or null if the closure was void).
+ *
+ * @param string $locale locale ex. en
+ *
+ * @return mixed
+ */
+ public static function executeWithLocale($locale, $func)
+ {
+ $currentLocale = static::getLocale();
+ $result = call_user_func($func, static::setLocale($locale) ? static::getLocale() : false, static::translator());
+ static::setLocale($currentLocale);
+
+ return $result;
+ }
+
+ /**
+ * Returns true if the given locale is internally supported and has short-units support.
+ * Support is considered enabled if either year, day or hour has a short variant translated.
+ *
+ * @param string $locale locale ex. en
+ *
+ * @return bool
+ */
+ public static function localeHasShortUnits($locale)
+ {
+ return static::executeWithLocale($locale, function ($newLocale, TranslatorInterface $translator) {
+ return $newLocale &&
+ (
+ ($y = $translator->trans('y')) !== 'y' &&
+ $y !== $translator->trans('year')
+ ) || (
+ ($y = $translator->trans('d')) !== 'd' &&
+ $y !== $translator->trans('day')
+ ) || (
+ ($y = $translator->trans('h')) !== 'h' &&
+ $y !== $translator->trans('hour')
+ );
+ });
+ }
+
+ /**
+ * Returns true if the given locale is internally supported and has diff syntax support (ago, from now, before, after).
+ * Support is considered enabled if the 4 sentences are translated in the given locale.
+ *
+ * @param string $locale locale ex. en
+ *
+ * @return bool
+ */
+ public static function localeHasDiffSyntax($locale)
+ {
+ return static::executeWithLocale($locale, function ($newLocale, TranslatorInterface $translator) {
+ return $newLocale &&
+ $translator->trans('ago') !== 'ago' &&
+ $translator->trans('from_now') !== 'from_now' &&
+ $translator->trans('before') !== 'before' &&
+ $translator->trans('after') !== 'after';
+ });
+ }
+
+ /**
+ * Returns true if the given locale is internally supported and has words for 1-day diff (just now, yesterday, tomorrow).
+ * Support is considered enabled if the 3 words are translated in the given locale.
+ *
+ * @param string $locale locale ex. en
+ *
+ * @return bool
+ */
+ public static function localeHasDiffOneDayWords($locale)
+ {
+ return static::executeWithLocale($locale, function ($newLocale, TranslatorInterface $translator) {
+ return $newLocale &&
+ $translator->trans('diff_now') !== 'diff_now' &&
+ $translator->trans('diff_yesterday') !== 'diff_yesterday' &&
+ $translator->trans('diff_tomorrow') !== 'diff_tomorrow';
+ });
+ }
+
+ /**
+ * Returns true if the given locale is internally supported and has words for 2-days diff (before yesterday, after tomorrow).
+ * Support is considered enabled if the 2 words are translated in the given locale.
+ *
+ * @param string $locale locale ex. en
+ *
+ * @return bool
+ */
+ public static function localeHasDiffTwoDayWords($locale)
+ {
+ return static::executeWithLocale($locale, function ($newLocale, TranslatorInterface $translator) {
+ return $newLocale &&
+ $translator->trans('diff_before_yesterday') !== 'diff_before_yesterday' &&
+ $translator->trans('diff_after_tomorrow') !== 'diff_after_tomorrow';
+ });
+ }
+
+ /**
+ * Returns true if the given locale is internally supported and has period syntax support (X times, every X, from X, to X).
+ * Support is considered enabled if the 4 sentences are translated in the given locale.
+ *
+ * @param string $locale locale ex. en
+ *
+ * @return bool
+ */
+ public static function localeHasPeriodSyntax($locale)
+ {
+ return static::executeWithLocale($locale, function ($newLocale, TranslatorInterface $translator) {
+ return $newLocale &&
+ $translator->trans('period_recurrences') !== 'period_recurrences' &&
+ $translator->trans('period_interval') !== 'period_interval' &&
+ $translator->trans('period_start_date') !== 'period_start_date' &&
+ $translator->trans('period_end_date') !== 'period_end_date';
+ });
+ }
+
+ /**
+ * Returns the list of internally available locales and already loaded custom locales.
+ * (It will ignore custom translator dynamic loading.)
+ *
+ * @return array
+ */
+ public static function getAvailableLocales()
+ {
+ $translator = static::translator();
+ $locales = array();
+ if ($translator instanceof Translator) {
+ foreach (glob(__DIR__.'/Lang/*.php') as $file) {
+ $locales[] = substr($file, strrpos($file, '/') + 1, -4);
+ }
+
+ $locales = array_unique(array_merge($locales, array_keys($translator->getMessages())));
+ }
+
+ return $locales;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ /////////////////////// STRING FORMATTING /////////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Set if UTF8 will be used for localized date/time
+ *
+ * @param bool $utf8
+ */
+ public static function setUtf8($utf8)
+ {
+ static::$utf8 = $utf8;
+ }
+
+ /**
+ * Format the instance with the current locale. You can set the current
+ * locale using setlocale() http://php.net/setlocale.
+ *
+ * @param string $format
+ *
+ * @return string
+ */
+ public function formatLocalized($format)
+ {
+ // Check for Windows to find and replace the %e modifier correctly.
+ if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
+ $format = preg_replace('#(?toDateTimeString()));
+
+ return static::$utf8 ? utf8_encode($formatted) : $formatted;
+ }
+
+ /**
+ * Reset the format used to the default when type juggling a Carbon instance to a string
+ *
+ * @return void
+ */
+ public static function resetToStringFormat()
+ {
+ static::setToStringFormat(static::DEFAULT_TO_STRING_FORMAT);
+ }
+
+ /**
+ * Set the default format used when type juggling a Carbon instance to a string
+ *
+ * @param string|Closure $format
+ *
+ * @return void
+ */
+ public static function setToStringFormat($format)
+ {
+ static::$toStringFormat = $format;
+ }
+
+ /**
+ * Format the instance as a string using the set format
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ $format = static::$toStringFormat;
+
+ return $this->format($format instanceof Closure ? $format($this) : $format);
+ }
+
+ /**
+ * Format the instance as date
+ *
+ * @return string
+ */
+ public function toDateString()
+ {
+ return $this->format('Y-m-d');
+ }
+
+ /**
+ * Format the instance as a readable date
+ *
+ * @return string
+ */
+ public function toFormattedDateString()
+ {
+ return $this->format('M j, Y');
+ }
+
+ /**
+ * Format the instance as time
+ *
+ * @return string
+ */
+ public function toTimeString()
+ {
+ return $this->format('H:i:s');
+ }
+
+ /**
+ * Format the instance as date and time
+ *
+ * @return string
+ */
+ public function toDateTimeString()
+ {
+ return $this->format('Y-m-d H:i:s');
+ }
+
+ /**
+ * Format the instance with day, date and time
+ *
+ * @return string
+ */
+ public function toDayDateTimeString()
+ {
+ return $this->format('D, M j, Y g:i A');
+ }
+
+ /**
+ * Format the instance as ATOM
+ *
+ * @return string
+ */
+ public function toAtomString()
+ {
+ return $this->format(static::ATOM);
+ }
+
+ /**
+ * Format the instance as COOKIE
+ *
+ * @return string
+ */
+ public function toCookieString()
+ {
+ return $this->format(static::COOKIE);
+ }
+
+ /**
+ * Format the instance as ISO8601
+ *
+ * @return string
+ */
+ public function toIso8601String()
+ {
+ return $this->toAtomString();
+ }
+
+ /**
+ * Format the instance as RFC822
+ *
+ * @return string
+ */
+ public function toRfc822String()
+ {
+ return $this->format(static::RFC822);
+ }
+
+ /**
+ * Convert the instance to UTC and return as Zulu ISO8601
+ *
+ * @return string
+ */
+ public function toIso8601ZuluString()
+ {
+ return $this->copy()->setTimezone('UTC')->format('Y-m-d\TH:i:s\Z');
+ }
+
+ /**
+ * Format the instance as RFC850
+ *
+ * @return string
+ */
+ public function toRfc850String()
+ {
+ return $this->format(static::RFC850);
+ }
+
+ /**
+ * Format the instance as RFC1036
+ *
+ * @return string
+ */
+ public function toRfc1036String()
+ {
+ return $this->format(static::RFC1036);
+ }
+
+ /**
+ * Format the instance as RFC1123
+ *
+ * @return string
+ */
+ public function toRfc1123String()
+ {
+ return $this->format(static::RFC1123);
+ }
+
+ /**
+ * Format the instance as RFC2822
+ *
+ * @return string
+ */
+ public function toRfc2822String()
+ {
+ return $this->format(static::RFC2822);
+ }
+
+ /**
+ * Format the instance as RFC3339
+ *
+ * @return string
+ */
+ public function toRfc3339String()
+ {
+ return $this->format(static::RFC3339);
+ }
+
+ /**
+ * Format the instance as RSS
+ *
+ * @return string
+ */
+ public function toRssString()
+ {
+ return $this->format(static::RSS);
+ }
+
+ /**
+ * Format the instance as W3C
+ *
+ * @return string
+ */
+ public function toW3cString()
+ {
+ return $this->format(static::W3C);
+ }
+
+ /**
+ * Format the instance as RFC7231
+ *
+ * @return string
+ */
+ public function toRfc7231String()
+ {
+ return $this->copy()
+ ->setTimezone('GMT')
+ ->format(static::RFC7231_FORMAT);
+ }
+
+ /**
+ * Get default array representation
+ *
+ * @return array
+ */
+ public function toArray()
+ {
+ return array(
+ 'year' => $this->year,
+ 'month' => $this->month,
+ 'day' => $this->day,
+ 'dayOfWeek' => $this->dayOfWeek,
+ 'dayOfYear' => $this->dayOfYear,
+ 'hour' => $this->hour,
+ 'minute' => $this->minute,
+ 'second' => $this->second,
+ 'micro' => $this->micro,
+ 'timestamp' => $this->timestamp,
+ 'formatted' => $this->format(self::DEFAULT_TO_STRING_FORMAT),
+ 'timezone' => $this->timezone,
+ );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ ////////////////////////// COMPARISONS ////////////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Determines if the instance is equal to another
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @return bool
+ */
+ public function eq($date)
+ {
+ return $this == $date;
+ }
+
+ /**
+ * Determines if the instance is equal to another
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @see eq()
+ *
+ * @return bool
+ */
+ public function equalTo($date)
+ {
+ return $this->eq($date);
+ }
+
+ /**
+ * Determines if the instance is not equal to another
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @return bool
+ */
+ public function ne($date)
+ {
+ return !$this->eq($date);
+ }
+
+ /**
+ * Determines if the instance is not equal to another
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @see ne()
+ *
+ * @return bool
+ */
+ public function notEqualTo($date)
+ {
+ return $this->ne($date);
+ }
+
+ /**
+ * Determines if the instance is greater (after) than another
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @return bool
+ */
+ public function gt($date)
+ {
+ return $this > $date;
+ }
+
+ /**
+ * Determines if the instance is greater (after) than another
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @see gt()
+ *
+ * @return bool
+ */
+ public function greaterThan($date)
+ {
+ return $this->gt($date);
+ }
+
+ /**
+ * Determines if the instance is greater (after) than or equal to another
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @return bool
+ */
+ public function gte($date)
+ {
+ return $this >= $date;
+ }
+
+ /**
+ * Determines if the instance is greater (after) than or equal to another
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @see gte()
+ *
+ * @return bool
+ */
+ public function greaterThanOrEqualTo($date)
+ {
+ return $this->gte($date);
+ }
+
+ /**
+ * Determines if the instance is less (before) than another
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @return bool
+ */
+ public function lt($date)
+ {
+ return $this < $date;
+ }
+
+ /**
+ * Determines if the instance is less (before) than another
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @see lt()
+ *
+ * @return bool
+ */
+ public function lessThan($date)
+ {
+ return $this->lt($date);
+ }
+
+ /**
+ * Determines if the instance is less (before) or equal to another
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @return bool
+ */
+ public function lte($date)
+ {
+ return $this <= $date;
+ }
+
+ /**
+ * Determines if the instance is less (before) or equal to another
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @see lte()
+ *
+ * @return bool
+ */
+ public function lessThanOrEqualTo($date)
+ {
+ return $this->lte($date);
+ }
+
+ /**
+ * Determines if the instance is between two others
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2
+ * @param bool $equal Indicates if an equal to comparison should be done
+ *
+ * @return bool
+ */
+ public function between($date1, $date2, $equal = true)
+ {
+ if ($date1->gt($date2)) {
+ $temp = $date1;
+ $date1 = $date2;
+ $date2 = $temp;
+ }
+
+ if ($equal) {
+ return $this->gte($date1) && $this->lte($date2);
+ }
+
+ return $this->gt($date1) && $this->lt($date2);
+ }
+
+ /**
+ * Get the closest date from the instance.
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2
+ *
+ * @return static
+ */
+ public function closest($date1, $date2)
+ {
+ return $this->diffInSeconds($date1) < $this->diffInSeconds($date2) ? $date1 : $date2;
+ }
+
+ /**
+ * Get the farthest date from the instance.
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2
+ *
+ * @return static
+ */
+ public function farthest($date1, $date2)
+ {
+ return $this->diffInSeconds($date1) > $this->diffInSeconds($date2) ? $date1 : $date2;
+ }
+
+ /**
+ * Get the minimum instance between a given instance (default now) and the current instance.
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|string|null $date
+ *
+ * @return static
+ */
+ public function min($date = null)
+ {
+ $date = $this->resolveCarbon($date);
+
+ return $this->lt($date) ? $this : $date;
+ }
+
+ /**
+ * Get the minimum instance between a given instance (default now) and the current instance.
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @see min()
+ *
+ * @return static
+ */
+ public function minimum($date = null)
+ {
+ return $this->min($date);
+ }
+
+ /**
+ * Get the maximum instance between a given instance (default now) and the current instance.
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|string|null $date
+ *
+ * @return static
+ */
+ public function max($date = null)
+ {
+ $date = $this->resolveCarbon($date);
+
+ return $this->gt($date) ? $this : $date;
+ }
+
+ /**
+ * Get the maximum instance between a given instance (default now) and the current instance.
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @see max()
+ *
+ * @return static
+ */
+ public function maximum($date = null)
+ {
+ return $this->max($date);
+ }
+
+ /**
+ * Determines if the instance is a weekday.
+ *
+ * @return bool
+ */
+ public function isWeekday()
+ {
+ return !$this->isWeekend();
+ }
+
+ /**
+ * Determines if the instance is a weekend day.
+ *
+ * @return bool
+ */
+ public function isWeekend()
+ {
+ return in_array($this->dayOfWeek, static::$weekendDays);
+ }
+
+ /**
+ * Determines if the instance is yesterday.
+ *
+ * @return bool
+ */
+ public function isYesterday()
+ {
+ return $this->toDateString() === static::yesterday($this->getTimezone())->toDateString();
+ }
+
+ /**
+ * Determines if the instance is today.
+ *
+ * @return bool
+ */
+ public function isToday()
+ {
+ return $this->toDateString() === $this->nowWithSameTz()->toDateString();
+ }
+
+ /**
+ * Determines if the instance is tomorrow.
+ *
+ * @return bool
+ */
+ public function isTomorrow()
+ {
+ return $this->toDateString() === static::tomorrow($this->getTimezone())->toDateString();
+ }
+
+ /**
+ * Determines if the instance is within the next week.
+ *
+ * @return bool
+ */
+ public function isNextWeek()
+ {
+ return $this->weekOfYear === $this->nowWithSameTz()->addWeek()->weekOfYear;
+ }
+
+ /**
+ * Determines if the instance is within the last week.
+ *
+ * @return bool
+ */
+ public function isLastWeek()
+ {
+ return $this->weekOfYear === $this->nowWithSameTz()->subWeek()->weekOfYear;
+ }
+
+ /**
+ * Determines if the instance is within the next quarter.
+ *
+ * @return bool
+ */
+ public function isNextQuarter()
+ {
+ return $this->quarter === $this->nowWithSameTz()->addQuarter()->quarter;
+ }
+
+ /**
+ * Determines if the instance is within the last quarter.
+ *
+ * @return bool
+ */
+ public function isLastQuarter()
+ {
+ return $this->quarter === $this->nowWithSameTz()->subQuarter()->quarter;
+ }
+
+ /**
+ * Determines if the instance is within the next month.
+ *
+ * @return bool
+ */
+ public function isNextMonth()
+ {
+ return $this->month === $this->nowWithSameTz()->addMonthNoOverflow()->month;
+ }
+
+ /**
+ * Determines if the instance is within the last month.
+ *
+ * @return bool
+ */
+ public function isLastMonth()
+ {
+ return $this->month === $this->nowWithSameTz()->subMonthNoOverflow()->month;
+ }
+
+ /**
+ * Determines if the instance is within next year.
+ *
+ * @return bool
+ */
+ public function isNextYear()
+ {
+ return $this->year === $this->nowWithSameTz()->addYear()->year;
+ }
+
+ /**
+ * Determines if the instance is within the previous year.
+ *
+ * @return bool
+ */
+ public function isLastYear()
+ {
+ return $this->year === $this->nowWithSameTz()->subYear()->year;
+ }
+
+ /**
+ * Determines if the instance is in the future, ie. greater (after) than now.
+ *
+ * @return bool
+ */
+ public function isFuture()
+ {
+ return $this->gt($this->nowWithSameTz());
+ }
+
+ /**
+ * Determines if the instance is in the past, ie. less (before) than now.
+ *
+ * @return bool
+ */
+ public function isPast()
+ {
+ return $this->lt($this->nowWithSameTz());
+ }
+
+ /**
+ * Determines if the instance is a leap year.
+ *
+ * @return bool
+ */
+ public function isLeapYear()
+ {
+ return $this->format('L') === '1';
+ }
+
+ /**
+ * Determines if the instance is a long year
+ *
+ * @see https://en.wikipedia.org/wiki/ISO_8601#Week_dates
+ *
+ * @return bool
+ */
+ public function isLongYear()
+ {
+ return static::create($this->year, 12, 28, 0, 0, 0, $this->tz)->weekOfYear === 53;
+ }
+
+ /**
+ * Compares the formatted values of the two dates.
+ *
+ * @param string $format The date formats to compare.
+ * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use current day.
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return bool
+ */
+ public function isSameAs($format, $date = null)
+ {
+ $date = $date ?: static::now($this->tz);
+
+ static::expectDateTime($date, 'null');
+
+ return $this->format($format) === $date->format($format);
+ }
+
+ /**
+ * Determines if the instance is in the current year.
+ *
+ * @return bool
+ */
+ public function isCurrentYear()
+ {
+ return $this->isSameYear();
+ }
+
+ /**
+ * Checks if the passed in date is in the same year as the instance year.
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use current day.
+ *
+ * @return bool
+ */
+ public function isSameYear($date = null)
+ {
+ return $this->isSameAs('Y', $date);
+ }
+
+ /**
+ * Determines if the instance is in the current month.
+ *
+ * @return bool
+ */
+ public function isCurrentQuarter()
+ {
+ return $this->isSameQuarter();
+ }
+
+ /**
+ * Checks if the passed in date is in the same quarter as the instance quarter (and year if needed).
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use current day.
+ * @param bool $ofSameYear Check if it is the same month in the same year.
+ *
+ * @return bool
+ */
+ public function isSameQuarter($date = null, $ofSameYear = null)
+ {
+ $date = $date ? static::instance($date) : static::now($this->tz);
+
+ static::expectDateTime($date, 'null');
+
+ $ofSameYear = is_null($ofSameYear) ? static::shouldCompareYearWithMonth() : $ofSameYear;
+
+ return $this->quarter === $date->quarter && (!$ofSameYear || $this->isSameYear($date));
+ }
+
+ /**
+ * Determines if the instance is in the current month.
+ *
+ * @param bool $ofSameYear Check if it is the same month in the same year.
+ *
+ * @return bool
+ */
+ public function isCurrentMonth($ofSameYear = null)
+ {
+ return $this->isSameMonth($ofSameYear);
+ }
+
+ /**
+ * Checks if the passed in date is in the same month as the instance´s month.
+ *
+ * Note that this defaults to only comparing the month while ignoring the year.
+ * To test if it is the same exact month of the same year, pass in true as the second parameter.
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use the current date.
+ * @param bool $ofSameYear Check if it is the same month in the same year.
+ *
+ * @return bool
+ */
+ public function isSameMonth($date = null, $ofSameYear = null)
+ {
+ $ofSameYear = is_null($ofSameYear) ? static::shouldCompareYearWithMonth() : $ofSameYear;
+
+ return $this->isSameAs($ofSameYear ? 'Y-m' : 'm', $date);
+ }
+
+ /**
+ * Determines if the instance is in the current day.
+ *
+ * @return bool
+ */
+ public function isCurrentDay()
+ {
+ return $this->isSameDay();
+ }
+
+ /**
+ * Checks if the passed in date is the same exact day as the instance´s day.
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use the current date.
+ *
+ * @return bool
+ */
+ public function isSameDay($date = null)
+ {
+ return $this->isSameAs('Y-m-d', $date);
+ }
+
+ /**
+ * Determines if the instance is in the current hour.
+ *
+ * @return bool
+ */
+ public function isCurrentHour()
+ {
+ return $this->isSameHour();
+ }
+
+ /**
+ * Checks if the passed in date is the same exact hour as the instance´s hour.
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use the current date.
+ *
+ * @return bool
+ */
+ public function isSameHour($date = null)
+ {
+ return $this->isSameAs('Y-m-d H', $date);
+ }
+
+ /**
+ * Determines if the instance is in the current minute.
+ *
+ * @return bool
+ */
+ public function isCurrentMinute()
+ {
+ return $this->isSameMinute();
+ }
+
+ /**
+ * Checks if the passed in date is the same exact minute as the instance´s minute.
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use the current date.
+ *
+ * @return bool
+ */
+ public function isSameMinute($date = null)
+ {
+ return $this->isSameAs('Y-m-d H:i', $date);
+ }
+
+ /**
+ * Determines if the instance is in the current second.
+ *
+ * @return bool
+ */
+ public function isCurrentSecond()
+ {
+ return $this->isSameSecond();
+ }
+
+ /**
+ * Checks if the passed in date is the same exact second as the instance´s second.
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use the current date.
+ *
+ * @return bool
+ */
+ public function isSameSecond($date = null)
+ {
+ return $this->isSameAs('Y-m-d H:i:s', $date);
+ }
+
+ /**
+ * Checks if this day is a specific day of the week.
+ *
+ * @param int $dayOfWeek
+ *
+ * @return bool
+ */
+ public function isDayOfWeek($dayOfWeek)
+ {
+ return $this->dayOfWeek === $dayOfWeek;
+ }
+
+ /**
+ * Checks if this day is a Sunday.
+ *
+ * @return bool
+ */
+ public function isSunday()
+ {
+ return $this->dayOfWeek === static::SUNDAY;
+ }
+
+ /**
+ * Checks if this day is a Monday.
+ *
+ * @return bool
+ */
+ public function isMonday()
+ {
+ return $this->dayOfWeek === static::MONDAY;
+ }
+
+ /**
+ * Checks if this day is a Tuesday.
+ *
+ * @return bool
+ */
+ public function isTuesday()
+ {
+ return $this->dayOfWeek === static::TUESDAY;
+ }
+
+ /**
+ * Checks if this day is a Wednesday.
+ *
+ * @return bool
+ */
+ public function isWednesday()
+ {
+ return $this->dayOfWeek === static::WEDNESDAY;
+ }
+
+ /**
+ * Checks if this day is a Thursday.
+ *
+ * @return bool
+ */
+ public function isThursday()
+ {
+ return $this->dayOfWeek === static::THURSDAY;
+ }
+
+ /**
+ * Checks if this day is a Friday.
+ *
+ * @return bool
+ */
+ public function isFriday()
+ {
+ return $this->dayOfWeek === static::FRIDAY;
+ }
+
+ /**
+ * Checks if this day is a Saturday.
+ *
+ * @return bool
+ */
+ public function isSaturday()
+ {
+ return $this->dayOfWeek === static::SATURDAY;
+ }
+
+ /**
+ * Check if its the birthday. Compares the date/month values of the two dates.
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use current day.
+ *
+ * @return bool
+ */
+ public function isBirthday($date = null)
+ {
+ return $this->isSameAs('md', $date);
+ }
+
+ /**
+ * Check if today is the last day of the Month
+ *
+ * @return bool
+ */
+ public function isLastOfMonth()
+ {
+ return $this->day === $this->daysInMonth;
+ }
+
+ /**
+ * Check if the instance is start of day / midnight.
+ *
+ * @param bool $checkMicroseconds check time at microseconds precision
+ * /!\ Warning, this is not reliable with PHP < 7.1.4
+ *
+ * @return bool
+ */
+ public function isStartOfDay($checkMicroseconds = false)
+ {
+ return $checkMicroseconds
+ ? $this->format('H:i:s.u') === '00:00:00.000000'
+ : $this->format('H:i:s') === '00:00:00';
+ }
+
+ /**
+ * Check if the instance is end of day.
+ *
+ * @param bool $checkMicroseconds check time at microseconds precision
+ * /!\ Warning, this is not reliable with PHP < 7.1.4
+ *
+ * @return bool
+ */
+ public function isEndOfDay($checkMicroseconds = false)
+ {
+ return $checkMicroseconds
+ ? $this->format('H:i:s.u') === '23:59:59.999999'
+ : $this->format('H:i:s') === '23:59:59';
+ }
+
+ /**
+ * Check if the instance is start of day / midnight.
+ *
+ * @return bool
+ */
+ public function isMidnight()
+ {
+ return $this->isStartOfDay();
+ }
+
+ /**
+ * Check if the instance is midday.
+ *
+ * @return bool
+ */
+ public function isMidday()
+ {
+ return $this->format('G:i:s') === static::$midDayAt.':00:00';
+ }
+
+ /**
+ * Checks if the (date)time string is in a given format.
+ *
+ * @param string $date
+ * @param string $format
+ *
+ * @return bool
+ */
+ public static function hasFormat($date, $format)
+ {
+ try {
+ // Try to create a DateTime object. Throws an InvalidArgumentException if the provided time string
+ // doesn't match the format in any way.
+ static::createFromFormat($format, $date);
+
+ // createFromFormat() is known to handle edge cases silently.
+ // E.g. "1975-5-1" (Y-n-j) will still be parsed correctly when "Y-m-d" is supplied as the format.
+ // To ensure we're really testing against our desired format, perform an additional regex validation.
+ $regex = strtr(
+ preg_quote($format, '/'),
+ static::$regexFormats
+ );
+
+ return (bool) preg_match('/^'.$regex.'$/', $date);
+ } catch (InvalidArgumentException $e) {
+ }
+
+ return false;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ /////////////////// ADDITIONS AND SUBTRACTIONS ////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Add centuries to the instance. Positive $value travels forward while
+ * negative $value travels into the past.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addCenturies($value)
+ {
+ return $this->addYears(static::YEARS_PER_CENTURY * $value);
+ }
+
+ /**
+ * Add a century to the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addCentury($value = 1)
+ {
+ return $this->addCenturies($value);
+ }
+
+ /**
+ * Remove centuries from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subCenturies($value)
+ {
+ return $this->addCenturies(-1 * $value);
+ }
+
+ /**
+ * Remove a century from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subCentury($value = 1)
+ {
+ return $this->subCenturies($value);
+ }
+
+ /**
+ * Add years to the instance. Positive $value travel forward while
+ * negative $value travel into the past.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addYears($value)
+ {
+ if ($this->shouldOverflowYears()) {
+ return $this->addYearsWithOverflow($value);
+ }
+
+ return $this->addYearsNoOverflow($value);
+ }
+
+ /**
+ * Add a year to the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addYear($value = 1)
+ {
+ return $this->addYears($value);
+ }
+
+ /**
+ * Add years to the instance with no overflow of months
+ * Positive $value travel forward while
+ * negative $value travel into the past.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addYearsNoOverflow($value)
+ {
+ return $this->addMonthsNoOverflow($value * static::MONTHS_PER_YEAR);
+ }
+
+ /**
+ * Add year with overflow months set to false
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addYearNoOverflow($value = 1)
+ {
+ return $this->addYearsNoOverflow($value);
+ }
+
+ /**
+ * Add years to the instance.
+ * Positive $value travel forward while
+ * negative $value travel into the past.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addYearsWithOverflow($value)
+ {
+ return $this->modify((int) $value.' year');
+ }
+
+ /**
+ * Add year with overflow.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addYearWithOverflow($value = 1)
+ {
+ return $this->addYearsWithOverflow($value);
+ }
+
+ /**
+ * Remove years from the instance.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subYears($value)
+ {
+ return $this->addYears(-1 * $value);
+ }
+
+ /**
+ * Remove a year from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subYear($value = 1)
+ {
+ return $this->subYears($value);
+ }
+
+ /**
+ * Remove years from the instance with no month overflow.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subYearsNoOverflow($value)
+ {
+ return $this->subMonthsNoOverflow($value * static::MONTHS_PER_YEAR);
+ }
+
+ /**
+ * Remove year from the instance with no month overflow
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subYearNoOverflow($value = 1)
+ {
+ return $this->subYearsNoOverflow($value);
+ }
+
+ /**
+ * Remove years from the instance.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subYearsWithOverflow($value)
+ {
+ return $this->subMonthsWithOverflow($value * static::MONTHS_PER_YEAR);
+ }
+
+ /**
+ * Remove year from the instance.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subYearWithOverflow($value = 1)
+ {
+ return $this->subYearsWithOverflow($value);
+ }
+
+ /**
+ * Add quarters to the instance. Positive $value travels forward while
+ * negative $value travels into the past.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addQuarters($value)
+ {
+ return $this->addMonths(static::MONTHS_PER_QUARTER * $value);
+ }
+
+ /**
+ * Add a quarter to the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addQuarter($value = 1)
+ {
+ return $this->addQuarters($value);
+ }
+
+ /**
+ * Remove quarters from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subQuarters($value)
+ {
+ return $this->addQuarters(-1 * $value);
+ }
+
+ /**
+ * Remove a quarter from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subQuarter($value = 1)
+ {
+ return $this->subQuarters($value);
+ }
+
+ /**
+ * Add months to the instance. Positive $value travels forward while
+ * negative $value travels into the past.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addMonths($value)
+ {
+ if (static::shouldOverflowMonths()) {
+ return $this->addMonthsWithOverflow($value);
+ }
+
+ return $this->addMonthsNoOverflow($value);
+ }
+
+ /**
+ * Add a month to the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addMonth($value = 1)
+ {
+ return $this->addMonths($value);
+ }
+
+ /**
+ * Remove months from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subMonths($value)
+ {
+ return $this->addMonths(-1 * $value);
+ }
+
+ /**
+ * Remove a month from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subMonth($value = 1)
+ {
+ return $this->subMonths($value);
+ }
+
+ /**
+ * Add months to the instance. Positive $value travels forward while
+ * negative $value travels into the past.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addMonthsWithOverflow($value)
+ {
+ return $this->modify((int) $value.' month');
+ }
+
+ /**
+ * Add a month to the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addMonthWithOverflow($value = 1)
+ {
+ return $this->addMonthsWithOverflow($value);
+ }
+
+ /**
+ * Remove months from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subMonthsWithOverflow($value)
+ {
+ return $this->addMonthsWithOverflow(-1 * $value);
+ }
+
+ /**
+ * Remove a month from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subMonthWithOverflow($value = 1)
+ {
+ return $this->subMonthsWithOverflow($value);
+ }
+
+ /**
+ * Add months without overflowing to the instance. Positive $value
+ * travels forward while negative $value travels into the past.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addMonthsNoOverflow($value)
+ {
+ $day = $this->day;
+
+ $this->modify((int) $value.' month');
+
+ if ($day !== $this->day) {
+ $this->modify('last day of previous month');
+ }
+
+ return $this;
+ }
+
+ /**
+ * Add a month with no overflow to the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addMonthNoOverflow($value = 1)
+ {
+ return $this->addMonthsNoOverflow($value);
+ }
+
+ /**
+ * Remove months with no overflow from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subMonthsNoOverflow($value)
+ {
+ return $this->addMonthsNoOverflow(-1 * $value);
+ }
+
+ /**
+ * Remove a month with no overflow from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subMonthNoOverflow($value = 1)
+ {
+ return $this->subMonthsNoOverflow($value);
+ }
+
+ /**
+ * Add days to the instance. Positive $value travels forward while
+ * negative $value travels into the past.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addDays($value)
+ {
+ return $this->modify((int) $value.' day');
+ }
+
+ /**
+ * Add a day to the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addDay($value = 1)
+ {
+ return $this->addDays($value);
+ }
+
+ /**
+ * Remove days from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subDays($value)
+ {
+ return $this->addDays(-1 * $value);
+ }
+
+ /**
+ * Remove a day from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subDay($value = 1)
+ {
+ return $this->subDays($value);
+ }
+
+ /**
+ * Add weekdays to the instance. Positive $value travels forward while
+ * negative $value travels into the past.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addWeekdays($value)
+ {
+ // Fix for weekday bug https://bugs.php.net/bug.php?id=54909
+ $t = $this->toTimeString();
+ $this->modify((int) $value.' weekday');
+
+ return $this->setTimeFromTimeString($t);
+ }
+
+ /**
+ * Add a weekday to the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addWeekday($value = 1)
+ {
+ return $this->addWeekdays($value);
+ }
+
+ /**
+ * Remove weekdays from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subWeekdays($value)
+ {
+ return $this->addWeekdays(-1 * $value);
+ }
+
+ /**
+ * Remove a weekday from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subWeekday($value = 1)
+ {
+ return $this->subWeekdays($value);
+ }
+
+ /**
+ * Add weeks to the instance. Positive $value travels forward while
+ * negative $value travels into the past.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addWeeks($value)
+ {
+ return $this->modify((int) $value.' week');
+ }
+
+ /**
+ * Add a week to the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addWeek($value = 1)
+ {
+ return $this->addWeeks($value);
+ }
+
+ /**
+ * Remove weeks to the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subWeeks($value)
+ {
+ return $this->addWeeks(-1 * $value);
+ }
+
+ /**
+ * Remove a week from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subWeek($value = 1)
+ {
+ return $this->subWeeks($value);
+ }
+
+ /**
+ * Add hours to the instance. Positive $value travels forward while
+ * negative $value travels into the past.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addHours($value)
+ {
+ return $this->modify((int) $value.' hour');
+ }
+
+ /**
+ * Add hours to the instance using timestamp. Positive $value travels
+ * forward while negative $value travels into the past.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addRealHours($value)
+ {
+ return $this->addRealMinutes($value * static::MINUTES_PER_HOUR);
+ }
+
+ /**
+ * Add an hour to the instance.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addHour($value = 1)
+ {
+ return $this->addHours($value);
+ }
+
+ /**
+ * Add an hour to the instance using timestamp.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addRealHour($value = 1)
+ {
+ return $this->addRealHours($value);
+ }
+
+ /**
+ * Remove hours from the instance.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subHours($value)
+ {
+ return $this->addHours(-1 * $value);
+ }
+
+ /**
+ * Remove hours from the instance using timestamp.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subRealHours($value)
+ {
+ return $this->addRealHours(-1 * $value);
+ }
+
+ /**
+ * Remove an hour from the instance.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subHour($value = 1)
+ {
+ return $this->subHours($value);
+ }
+
+ /**
+ * Remove an hour from the instance.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subRealHour($value = 1)
+ {
+ return $this->subRealHours($value);
+ }
+
+ /**
+ * Add minutes to the instance using timestamp. Positive $value
+ * travels forward while negative $value travels into the past.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addMinutes($value)
+ {
+ return $this->modify((int) $value.' minute');
+ }
+
+ /**
+ * Add minutes to the instance using timestamp. Positive $value travels
+ * forward while negative $value travels into the past.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addRealMinutes($value)
+ {
+ return $this->addRealSeconds($value * static::SECONDS_PER_MINUTE);
+ }
+
+ /**
+ * Add a minute to the instance.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addMinute($value = 1)
+ {
+ return $this->addMinutes($value);
+ }
+
+ /**
+ * Add a minute to the instance using timestamp.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addRealMinute($value = 1)
+ {
+ return $this->addRealMinutes($value);
+ }
+
+ /**
+ * Remove a minute from the instance.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subMinute($value = 1)
+ {
+ return $this->subMinutes($value);
+ }
+
+ /**
+ * Remove a minute from the instance using timestamp.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subRealMinute($value = 1)
+ {
+ return $this->addRealMinutes(-1 * $value);
+ }
+
+ /**
+ * Remove minutes from the instance.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subMinutes($value)
+ {
+ return $this->addMinutes(-1 * $value);
+ }
+
+ /**
+ * Remove a minute from the instance using timestamp.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subRealMinutes($value = 1)
+ {
+ return $this->subRealMinute($value);
+ }
+
+ /**
+ * Add seconds to the instance. Positive $value travels forward while
+ * negative $value travels into the past.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addSeconds($value)
+ {
+ return $this->modify((int) $value.' second');
+ }
+
+ /**
+ * Add seconds to the instance using timestamp. Positive $value travels
+ * forward while negative $value travels into the past.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addRealSeconds($value)
+ {
+ return $this->setTimestamp($this->getTimestamp() + $value);
+ }
+
+ /**
+ * Add a second to the instance.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addSecond($value = 1)
+ {
+ return $this->addSeconds($value);
+ }
+
+ /**
+ * Add a second to the instance using timestamp.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addRealSecond($value = 1)
+ {
+ return $this->addRealSeconds($value);
+ }
+
+ /**
+ * Remove seconds from the instance.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subSeconds($value)
+ {
+ return $this->addSeconds(-1 * $value);
+ }
+
+ /**
+ * Remove seconds from the instance using timestamp.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subRealSeconds($value)
+ {
+ return $this->addRealSeconds(-1 * $value);
+ }
+
+ /**
+ * Remove a second from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subSecond($value = 1)
+ {
+ return $this->subSeconds($value);
+ }
+
+ /**
+ * Remove a second from the instance using timestamp.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subRealSecond($value = 1)
+ {
+ return $this->subRealSeconds($value);
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ /////////////////////////// DIFFERENCES ///////////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Get the difference as a CarbonInterval instance
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return CarbonInterval
+ */
+ public function diffAsCarbonInterval($date = null, $absolute = true)
+ {
+ return CarbonInterval::instance($this->diff($this->resolveCarbon($date), $absolute));
+ }
+
+ /**
+ * Get the difference in years
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInYears($date = null, $absolute = true)
+ {
+ return (int) $this->diff($this->resolveCarbon($date), $absolute)->format('%r%y');
+ }
+
+ /**
+ * Get the difference in months
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInMonths($date = null, $absolute = true)
+ {
+ $date = $this->resolveCarbon($date);
+
+ return $this->diffInYears($date, $absolute) * static::MONTHS_PER_YEAR + (int) $this->diff($date, $absolute)->format('%r%m');
+ }
+
+ /**
+ * Get the difference in weeks
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInWeeks($date = null, $absolute = true)
+ {
+ return (int) ($this->diffInDays($date, $absolute) / static::DAYS_PER_WEEK);
+ }
+
+ /**
+ * Get the difference in days
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInDays($date = null, $absolute = true)
+ {
+ return (int) $this->diff($this->resolveCarbon($date), $absolute)->format('%r%a');
+ }
+
+ /**
+ * Get the difference in days using a filter closure
+ *
+ * @param Closure $callback
+ * @param \Carbon\Carbon|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInDaysFiltered(Closure $callback, $date = null, $absolute = true)
+ {
+ return $this->diffFiltered(CarbonInterval::day(), $callback, $date, $absolute);
+ }
+
+ /**
+ * Get the difference in hours using a filter closure
+ *
+ * @param Closure $callback
+ * @param \Carbon\Carbon|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInHoursFiltered(Closure $callback, $date = null, $absolute = true)
+ {
+ return $this->diffFiltered(CarbonInterval::hour(), $callback, $date, $absolute);
+ }
+
+ /**
+ * Get the difference by the given interval using a filter closure
+ *
+ * @param CarbonInterval $ci An interval to traverse by
+ * @param Closure $callback
+ * @param \Carbon\Carbon|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffFiltered(CarbonInterval $ci, Closure $callback, $date = null, $absolute = true)
+ {
+ $start = $this;
+ $end = $this->resolveCarbon($date);
+ $inverse = false;
+
+ if ($end < $start) {
+ $start = $end;
+ $end = $this;
+ $inverse = true;
+ }
+
+ $period = new DatePeriod($start, $ci, $end);
+ $values = array_filter(iterator_to_array($period), function ($date) use ($callback) {
+ return call_user_func($callback, Carbon::instance($date));
+ });
+
+ $diff = count($values);
+
+ return $inverse && !$absolute ? -$diff : $diff;
+ }
+
+ /**
+ * Get the difference in weekdays
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInWeekdays($date = null, $absolute = true)
+ {
+ return $this->diffInDaysFiltered(function (Carbon $date) {
+ return $date->isWeekday();
+ }, $date, $absolute);
+ }
+
+ /**
+ * Get the difference in weekend days using a filter
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInWeekendDays($date = null, $absolute = true)
+ {
+ return $this->diffInDaysFiltered(function (Carbon $date) {
+ return $date->isWeekend();
+ }, $date, $absolute);
+ }
+
+ /**
+ * Get the difference in hours.
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInHours($date = null, $absolute = true)
+ {
+ return (int) ($this->diffInSeconds($date, $absolute) / static::SECONDS_PER_MINUTE / static::MINUTES_PER_HOUR);
+ }
+
+ /**
+ * Get the difference in hours using timestamps.
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInRealHours($date = null, $absolute = true)
+ {
+ return (int) ($this->diffInRealSeconds($date, $absolute) / static::SECONDS_PER_MINUTE / static::MINUTES_PER_HOUR);
+ }
+
+ /**
+ * Get the difference in minutes.
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInMinutes($date = null, $absolute = true)
+ {
+ return (int) ($this->diffInSeconds($date, $absolute) / static::SECONDS_PER_MINUTE);
+ }
+
+ /**
+ * Get the difference in minutes using timestamps.
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInRealMinutes($date = null, $absolute = true)
+ {
+ return (int) ($this->diffInRealSeconds($date, $absolute) / static::SECONDS_PER_MINUTE);
+ }
+
+ /**
+ * Get the difference in seconds.
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInSeconds($date = null, $absolute = true)
+ {
+ $diff = $this->diff($this->resolveCarbon($date));
+ $value = $diff->days * static::HOURS_PER_DAY * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE +
+ $diff->h * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE +
+ $diff->i * static::SECONDS_PER_MINUTE +
+ $diff->s;
+
+ return $absolute || !$diff->invert ? $value : -$value;
+ }
+
+ /**
+ * Get the difference in seconds using timestamps.
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInRealSeconds($date = null, $absolute = true)
+ {
+ $date = $this->resolveCarbon($date);
+ $value = $date->getTimestamp() - $this->getTimestamp();
+
+ return $absolute ? abs($value) : $value;
+ }
+
+ /**
+ * The number of seconds since midnight.
+ *
+ * @return int
+ */
+ public function secondsSinceMidnight()
+ {
+ return $this->diffInSeconds($this->copy()->startOfDay());
+ }
+
+ /**
+ * The number of seconds until 23:59:59.
+ *
+ * @return int
+ */
+ public function secondsUntilEndOfDay()
+ {
+ return $this->diffInSeconds($this->copy()->endOfDay());
+ }
+
+ /**
+ * Get the difference in a human readable format in the current locale.
+ *
+ * When comparing a value in the past to default now:
+ * 1 hour ago
+ * 5 months ago
+ *
+ * When comparing a value in the future to default now:
+ * 1 hour from now
+ * 5 months from now
+ *
+ * When comparing a value in the past to another value:
+ * 1 hour before
+ * 5 months before
+ *
+ * When comparing a value in the future to another value:
+ * 1 hour after
+ * 5 months after
+ *
+ * @param Carbon|null $other
+ * @param bool $absolute removes time difference modifiers ago, after, etc
+ * @param bool $short displays short format of time units
+ * @param int $parts displays number of parts in the interval
+ *
+ * @return string
+ */
+ public function diffForHumans($other = null, $absolute = false, $short = false, $parts = 1)
+ {
+ $isNow = $other === null;
+ $interval = array();
+
+ $parts = min(6, max(1, (int) $parts));
+ $count = 1;
+ $unit = $short ? 's' : 'second';
+
+ if ($isNow) {
+ $other = $this->nowWithSameTz();
+ } elseif (!$other instanceof DateTime && !$other instanceof DateTimeInterface) {
+ $other = static::parse($other);
+ }
+
+ $diffInterval = $this->diff($other);
+
+ $diffIntervalArray = array(
+ array('value' => $diffInterval->y, 'unit' => 'year', 'unitShort' => 'y'),
+ array('value' => $diffInterval->m, 'unit' => 'month', 'unitShort' => 'm'),
+ array('value' => $diffInterval->d, 'unit' => 'day', 'unitShort' => 'd'),
+ array('value' => $diffInterval->h, 'unit' => 'hour', 'unitShort' => 'h'),
+ array('value' => $diffInterval->i, 'unit' => 'minute', 'unitShort' => 'min'),
+ array('value' => $diffInterval->s, 'unit' => 'second', 'unitShort' => 's'),
+ );
+
+ foreach ($diffIntervalArray as $diffIntervalData) {
+ if ($diffIntervalData['value'] > 0) {
+ $unit = $short ? $diffIntervalData['unitShort'] : $diffIntervalData['unit'];
+ $count = $diffIntervalData['value'];
+
+ if ($diffIntervalData['unit'] === 'day' && $count >= static::DAYS_PER_WEEK) {
+ $unit = $short ? 'w' : 'week';
+ $count = (int) ($count / static::DAYS_PER_WEEK);
+
+ $interval[] = static::translator()->transChoice($unit, $count, array(':count' => $count));
+
+ // get the count days excluding weeks (might be zero)
+ $numOfDaysCount = (int) ($diffIntervalData['value'] - ($count * static::DAYS_PER_WEEK));
+
+ if ($numOfDaysCount > 0 && count($interval) < $parts) {
+ $unit = $short ? 'd' : 'day';
+ $count = $numOfDaysCount;
+ $interval[] = static::translator()->transChoice($unit, $count, array(':count' => $count));
+ }
+ } else {
+ $interval[] = static::translator()->transChoice($unit, $count, array(':count' => $count));
+ }
+ }
+
+ // break the loop after we get the required number of parts in array
+ if (count($interval) >= $parts) {
+ break;
+ }
+ }
+
+ if (count($interval) === 0) {
+ if ($isNow && static::getHumanDiffOptions() & self::JUST_NOW) {
+ $key = 'diff_now';
+ $translation = static::translator()->trans($key);
+ if ($translation !== $key) {
+ return $translation;
+ }
+ }
+ $count = static::getHumanDiffOptions() & self::NO_ZERO_DIFF ? 1 : 0;
+ $unit = $short ? 's' : 'second';
+ $interval[] = static::translator()->transChoice($unit, $count, array(':count' => $count));
+ }
+
+ // join the interval parts by a space
+ $time = implode(' ', $interval);
+
+ unset($diffIntervalArray, $interval);
+
+ if ($absolute) {
+ return $time;
+ }
+
+ $isFuture = $diffInterval->invert === 1;
+
+ $transId = $isNow ? ($isFuture ? 'from_now' : 'ago') : ($isFuture ? 'after' : 'before');
+
+ if ($parts === 1) {
+ if ($isNow && $unit === 'day') {
+ if ($count === 1 && static::getHumanDiffOptions() & self::ONE_DAY_WORDS) {
+ $key = $isFuture ? 'diff_tomorrow' : 'diff_yesterday';
+ $translation = static::translator()->trans($key);
+ if ($translation !== $key) {
+ return $translation;
+ }
+ }
+ if ($count === 2 && static::getHumanDiffOptions() & self::TWO_DAY_WORDS) {
+ $key = $isFuture ? 'diff_after_tomorrow' : 'diff_before_yesterday';
+ $translation = static::translator()->trans($key);
+ if ($translation !== $key) {
+ return $translation;
+ }
+ }
+ }
+ // Some languages have special pluralization for past and future tense.
+ $key = $unit.'_'.$transId;
+ if ($key !== static::translator()->transChoice($key, $count)) {
+ $time = static::translator()->transChoice($key, $count, array(':count' => $count));
+ }
+ }
+
+ return static::translator()->trans($transId, array(':time' => $time));
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //////////////////////////// MODIFIERS ////////////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Resets the time to 00:00:00 start of day
+ *
+ * @return static
+ */
+ public function startOfDay()
+ {
+ return $this->modify('00:00:00.000000');
+ }
+
+ /**
+ * Resets the time to 23:59:59 end of day
+ *
+ * @return static
+ */
+ public function endOfDay()
+ {
+ return $this->modify('23:59:59.999999');
+ }
+
+ /**
+ * Resets the date to the first day of the month and the time to 00:00:00
+ *
+ * @return static
+ */
+ public function startOfMonth()
+ {
+ return $this->setDate($this->year, $this->month, 1)->startOfDay();
+ }
+
+ /**
+ * Resets the date to end of the month and time to 23:59:59
+ *
+ * @return static
+ */
+ public function endOfMonth()
+ {
+ return $this->setDate($this->year, $this->month, $this->daysInMonth)->endOfDay();
+ }
+
+ /**
+ * Resets the date to the first day of the quarter and the time to 00:00:00
+ *
+ * @return static
+ */
+ public function startOfQuarter()
+ {
+ $month = ($this->quarter - 1) * static::MONTHS_PER_QUARTER + 1;
+
+ return $this->setDate($this->year, $month, 1)->startOfDay();
+ }
+
+ /**
+ * Resets the date to end of the quarter and time to 23:59:59
+ *
+ * @return static
+ */
+ public function endOfQuarter()
+ {
+ return $this->startOfQuarter()->addMonths(static::MONTHS_PER_QUARTER - 1)->endOfMonth();
+ }
+
+ /**
+ * Resets the date to the first day of the year and the time to 00:00:00
+ *
+ * @return static
+ */
+ public function startOfYear()
+ {
+ return $this->setDate($this->year, 1, 1)->startOfDay();
+ }
+
+ /**
+ * Resets the date to end of the year and time to 23:59:59
+ *
+ * @return static
+ */
+ public function endOfYear()
+ {
+ return $this->setDate($this->year, 12, 31)->endOfDay();
+ }
+
+ /**
+ * Resets the date to the first day of the decade and the time to 00:00:00
+ *
+ * @return static
+ */
+ public function startOfDecade()
+ {
+ $year = $this->year - $this->year % static::YEARS_PER_DECADE;
+
+ return $this->setDate($year, 1, 1)->startOfDay();
+ }
+
+ /**
+ * Resets the date to end of the decade and time to 23:59:59
+ *
+ * @return static
+ */
+ public function endOfDecade()
+ {
+ $year = $this->year - $this->year % static::YEARS_PER_DECADE + static::YEARS_PER_DECADE - 1;
+
+ return $this->setDate($year, 12, 31)->endOfDay();
+ }
+
+ /**
+ * Resets the date to the first day of the century and the time to 00:00:00
+ *
+ * @return static
+ */
+ public function startOfCentury()
+ {
+ $year = $this->year - ($this->year - 1) % static::YEARS_PER_CENTURY;
+
+ return $this->setDate($year, 1, 1)->startOfDay();
+ }
+
+ /**
+ * Resets the date to end of the century and time to 23:59:59
+ *
+ * @return static
+ */
+ public function endOfCentury()
+ {
+ $year = $this->year - 1 - ($this->year - 1) % static::YEARS_PER_CENTURY + static::YEARS_PER_CENTURY;
+
+ return $this->setDate($year, 12, 31)->endOfDay();
+ }
+
+ /**
+ * Resets the date to the first day of week (defined in $weekStartsAt) and the time to 00:00:00
+ *
+ * @return static
+ */
+ public function startOfWeek()
+ {
+ while ($this->dayOfWeek !== static::$weekStartsAt) {
+ $this->subDay();
+ }
+
+ return $this->startOfDay();
+ }
+
+ /**
+ * Resets the date to end of week (defined in $weekEndsAt) and time to 23:59:59
+ *
+ * @return static
+ */
+ public function endOfWeek()
+ {
+ while ($this->dayOfWeek !== static::$weekEndsAt) {
+ $this->addDay();
+ }
+
+ return $this->endOfDay();
+ }
+
+ /**
+ * Modify to start of current hour, minutes and seconds become 0
+ *
+ * @return static
+ */
+ public function startOfHour()
+ {
+ return $this->setTime($this->hour, 0, 0);
+ }
+
+ /**
+ * Modify to end of current hour, minutes and seconds become 59
+ *
+ * @return static
+ */
+ public function endOfHour()
+ {
+ return $this->modify("$this->hour:59:59.999999");
+ }
+
+ /**
+ * Modify to start of current minute, seconds become 0
+ *
+ * @return static
+ */
+ public function startOfMinute()
+ {
+ return $this->setTime($this->hour, $this->minute, 0);
+ }
+
+ /**
+ * Modify to end of current minute, seconds become 59
+ *
+ * @return static
+ */
+ public function endOfMinute()
+ {
+ return $this->modify("$this->hour:$this->minute:59.999999");
+ }
+
+ /**
+ * Modify to start of current minute, seconds become 0
+ *
+ * @return static
+ */
+ public function startOfSecond()
+ {
+ return $this->modify("$this->hour:$this->minute:$this->second.0");
+ }
+
+ /**
+ * Modify to end of current minute, seconds become 59
+ *
+ * @return static
+ */
+ public function endOfSecond()
+ {
+ return $this->modify("$this->hour:$this->minute:$this->second.999999");
+ }
+
+ /**
+ * Modify to midday, default to self::$midDayAt
+ *
+ * @return static
+ */
+ public function midDay()
+ {
+ return $this->setTime(self::$midDayAt, 0, 0);
+ }
+
+ /**
+ * Modify to the next occurrence of a given day of the week.
+ * If no dayOfWeek is provided, modify to the next occurrence
+ * of the current day of the week. Use the supplied constants
+ * to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int|null $dayOfWeek
+ *
+ * @return static
+ */
+ public function next($dayOfWeek = null)
+ {
+ if ($dayOfWeek === null) {
+ $dayOfWeek = $this->dayOfWeek;
+ }
+
+ return $this->startOfDay()->modify('next '.static::$days[$dayOfWeek]);
+ }
+
+ /**
+ * Go forward or backward to the next week- or weekend-day.
+ *
+ * @param bool $weekday
+ * @param bool $forward
+ *
+ * @return $this
+ */
+ private function nextOrPreviousDay($weekday = true, $forward = true)
+ {
+ $step = $forward ? 1 : -1;
+
+ do {
+ $this->addDay($step);
+ } while ($weekday ? $this->isWeekend() : $this->isWeekday());
+
+ return $this;
+ }
+
+ /**
+ * Go forward to the next weekday.
+ *
+ * @return $this
+ */
+ public function nextWeekday()
+ {
+ return $this->nextOrPreviousDay();
+ }
+
+ /**
+ * Go backward to the previous weekday.
+ *
+ * @return $this
+ */
+ public function previousWeekday()
+ {
+ return $this->nextOrPreviousDay(true, false);
+ }
+
+ /**
+ * Go forward to the next weekend day.
+ *
+ * @return $this
+ */
+ public function nextWeekendDay()
+ {
+ return $this->nextOrPreviousDay(false);
+ }
+
+ /**
+ * Go backward to the previous weekend day.
+ *
+ * @return $this
+ */
+ public function previousWeekendDay()
+ {
+ return $this->nextOrPreviousDay(false, false);
+ }
+
+ /**
+ * Modify to the previous occurrence of a given day of the week.
+ * If no dayOfWeek is provided, modify to the previous occurrence
+ * of the current day of the week. Use the supplied constants
+ * to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int|null $dayOfWeek
+ *
+ * @return static
+ */
+ public function previous($dayOfWeek = null)
+ {
+ if ($dayOfWeek === null) {
+ $dayOfWeek = $this->dayOfWeek;
+ }
+
+ return $this->startOfDay()->modify('last '.static::$days[$dayOfWeek]);
+ }
+
+ /**
+ * Modify to the first occurrence of a given day of the week
+ * in the current month. If no dayOfWeek is provided, modify to the
+ * first day of the current month. Use the supplied constants
+ * to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int|null $dayOfWeek
+ *
+ * @return static
+ */
+ public function firstOfMonth($dayOfWeek = null)
+ {
+ $this->startOfDay();
+
+ if ($dayOfWeek === null) {
+ return $this->day(1);
+ }
+
+ return $this->modify('first '.static::$days[$dayOfWeek].' of '.$this->format('F').' '.$this->year);
+ }
+
+ /**
+ * Modify to the last occurrence of a given day of the week
+ * in the current month. If no dayOfWeek is provided, modify to the
+ * last day of the current month. Use the supplied constants
+ * to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int|null $dayOfWeek
+ *
+ * @return static
+ */
+ public function lastOfMonth($dayOfWeek = null)
+ {
+ $this->startOfDay();
+
+ if ($dayOfWeek === null) {
+ return $this->day($this->daysInMonth);
+ }
+
+ return $this->modify('last '.static::$days[$dayOfWeek].' of '.$this->format('F').' '.$this->year);
+ }
+
+ /**
+ * Modify to the given occurrence of a given day of the week
+ * in the current month. If the calculated occurrence is outside the scope
+ * of the current month, then return false and no modifications are made.
+ * Use the supplied constants to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int $nth
+ * @param int $dayOfWeek
+ *
+ * @return mixed
+ */
+ public function nthOfMonth($nth, $dayOfWeek)
+ {
+ $date = $this->copy()->firstOfMonth();
+ $check = $date->format('Y-m');
+ $date->modify('+'.$nth.' '.static::$days[$dayOfWeek]);
+
+ return $date->format('Y-m') === $check ? $this->modify($date) : false;
+ }
+
+ /**
+ * Modify to the first occurrence of a given day of the week
+ * in the current quarter. If no dayOfWeek is provided, modify to the
+ * first day of the current quarter. Use the supplied constants
+ * to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int|null $dayOfWeek day of the week default null
+ *
+ * @return static
+ */
+ public function firstOfQuarter($dayOfWeek = null)
+ {
+ return $this->setDate($this->year, $this->quarter * static::MONTHS_PER_QUARTER - 2, 1)->firstOfMonth($dayOfWeek);
+ }
+
+ /**
+ * Modify to the last occurrence of a given day of the week
+ * in the current quarter. If no dayOfWeek is provided, modify to the
+ * last day of the current quarter. Use the supplied constants
+ * to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int|null $dayOfWeek day of the week default null
+ *
+ * @return static
+ */
+ public function lastOfQuarter($dayOfWeek = null)
+ {
+ return $this->setDate($this->year, $this->quarter * static::MONTHS_PER_QUARTER, 1)->lastOfMonth($dayOfWeek);
+ }
+
+ /**
+ * Modify to the given occurrence of a given day of the week
+ * in the current quarter. If the calculated occurrence is outside the scope
+ * of the current quarter, then return false and no modifications are made.
+ * Use the supplied constants to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int $nth
+ * @param int $dayOfWeek
+ *
+ * @return mixed
+ */
+ public function nthOfQuarter($nth, $dayOfWeek)
+ {
+ $date = $this->copy()->day(1)->month($this->quarter * static::MONTHS_PER_QUARTER);
+ $lastMonth = $date->month;
+ $year = $date->year;
+ $date->firstOfQuarter()->modify('+'.$nth.' '.static::$days[$dayOfWeek]);
+
+ return ($lastMonth < $date->month || $year !== $date->year) ? false : $this->modify($date);
+ }
+
+ /**
+ * Modify to the first occurrence of a given day of the week
+ * in the current year. If no dayOfWeek is provided, modify to the
+ * first day of the current year. Use the supplied constants
+ * to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int|null $dayOfWeek day of the week default null
+ *
+ * @return static
+ */
+ public function firstOfYear($dayOfWeek = null)
+ {
+ return $this->month(1)->firstOfMonth($dayOfWeek);
+ }
+
+ /**
+ * Modify to the last occurrence of a given day of the week
+ * in the current year. If no dayOfWeek is provided, modify to the
+ * last day of the current year. Use the supplied constants
+ * to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int|null $dayOfWeek day of the week default null
+ *
+ * @return static
+ */
+ public function lastOfYear($dayOfWeek = null)
+ {
+ return $this->month(static::MONTHS_PER_YEAR)->lastOfMonth($dayOfWeek);
+ }
+
+ /**
+ * Modify to the given occurrence of a given day of the week
+ * in the current year. If the calculated occurrence is outside the scope
+ * of the current year, then return false and no modifications are made.
+ * Use the supplied constants to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int $nth
+ * @param int $dayOfWeek
+ *
+ * @return mixed
+ */
+ public function nthOfYear($nth, $dayOfWeek)
+ {
+ $date = $this->copy()->firstOfYear()->modify('+'.$nth.' '.static::$days[$dayOfWeek]);
+
+ return $this->year === $date->year ? $this->modify($date) : false;
+ }
+
+ /**
+ * Modify the current instance to the average of a given instance (default now) and the current instance.
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|string|null $date
+ *
+ * @return static
+ */
+ public function average($date = null)
+ {
+ return $this->addSeconds((int) ($this->diffInSeconds($this->resolveCarbon($date), false) / 2));
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ /////////////////////////// SERIALIZATION /////////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Return a serialized string of the instance.
+ *
+ * @return string
+ */
+ public function serialize()
+ {
+ return serialize($this);
+ }
+
+ /**
+ * Create an instance from a serialized string.
+ *
+ * @param string $value
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return static
+ */
+ public static function fromSerialized($value)
+ {
+ $instance = @unserialize($value);
+
+ if (!$instance instanceof static) {
+ throw new InvalidArgumentException('Invalid serialized value.');
+ }
+
+ return $instance;
+ }
+
+ /**
+ * The __set_state handler.
+ *
+ * @param array $array
+ *
+ * @return static
+ */
+ public static function __set_state($array)
+ {
+ return static::instance(parent::__set_state($array));
+ }
+
+ /**
+ * Prepare the object for JSON serialization.
+ *
+ * @return array|string
+ */
+ public function jsonSerialize()
+ {
+ if (static::$serializer) {
+ return call_user_func(static::$serializer, $this);
+ }
+
+ $carbon = $this;
+
+ return call_user_func(function () use ($carbon) {
+ return get_object_vars($carbon);
+ });
+ }
+
+ /**
+ * JSON serialize all Carbon instances using the given callback.
+ *
+ * @param callable $callback
+ *
+ * @return void
+ */
+ public static function serializeUsing($callback)
+ {
+ static::$serializer = $callback;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ /////////////////////////////// MACRO /////////////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Register a custom macro.
+ *
+ * @param string $name
+ * @param object|callable $macro
+ *
+ * @return void
+ */
+ public static function macro($name, $macro)
+ {
+ static::$localMacros[$name] = $macro;
+ }
+
+ /**
+ * Mix another object into the class.
+ *
+ * @param object $mixin
+ *
+ * @return void
+ */
+ public static function mixin($mixin)
+ {
+ $reflection = new \ReflectionClass($mixin);
+ $methods = $reflection->getMethods(
+ \ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED
+ );
+
+ foreach ($methods as $method) {
+ $method->setAccessible(true);
+
+ static::macro($method->name, $method->invoke($mixin));
+ }
+ }
+
+ /**
+ * Checks if macro is registered.
+ *
+ * @param string $name
+ *
+ * @return bool
+ */
+ public static function hasMacro($name)
+ {
+ return isset(static::$localMacros[$name]);
+ }
+
+ /**
+ * Dynamically handle calls to the class.
+ *
+ * @param string $method
+ * @param array $parameters
+ *
+ * @throws \BadMethodCallException
+ *
+ * @return mixed
+ */
+ public static function __callStatic($method, $parameters)
+ {
+ if (!static::hasMacro($method)) {
+ throw new \BadMethodCallException("Method $method does not exist.");
+ }
+
+ if (static::$localMacros[$method] instanceof Closure && method_exists('Closure', 'bind')) {
+ return call_user_func_array(Closure::bind(static::$localMacros[$method], null, get_called_class()), $parameters);
+ }
+
+ return call_user_func_array(static::$localMacros[$method], $parameters);
+ }
+
+ /**
+ * Dynamically handle calls to the class.
+ *
+ * @param string $method
+ * @param array $parameters
+ *
+ * @throws \BadMethodCallException|\ReflectionException
+ *
+ * @return mixed
+ */
+ public function __call($method, $parameters)
+ {
+ if (!static::hasMacro($method)) {
+ throw new \BadMethodCallException("Method $method does not exist.");
+ }
+
+ $macro = static::$localMacros[$method];
+
+ $reflexion = new \ReflectionFunction($macro);
+ $reflectionParameters = $reflexion->getParameters();
+ $expectedCount = count($reflectionParameters);
+ $actualCount = count($parameters);
+ if ($expectedCount > $actualCount && $reflectionParameters[$expectedCount - 1]->name === 'self') {
+ for ($i = $actualCount; $i < $expectedCount - 1; $i++) {
+ $parameters[] = $reflectionParameters[$i]->getDefaultValue();
+ }
+ $parameters[] = $this;
+ }
+
+ if ($macro instanceof Closure && method_exists($macro, 'bindTo')) {
+ return call_user_func_array($macro->bindTo($this, get_class($this)), $parameters);
+ }
+
+ return call_user_func_array($macro, $parameters);
+ }
+}
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/CarbonInterval.php b/_include/calendar/nesbot/carbon/src/Carbon/CarbonInterval.php
new file mode 100644
index 0000000..a4d7850
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/CarbonInterval.php
@@ -0,0 +1,1130 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon;
+
+use Closure;
+use DateInterval;
+use InvalidArgumentException;
+use ReflectionClass;
+use ReflectionFunction;
+use ReflectionMethod;
+use Symfony\Component\Translation\TranslatorInterface;
+
+/**
+ * A simple API extension for DateInterval.
+ * The implementation provides helpers to handle weeks but only days are saved.
+ * Weeks are calculated based on the total days of the current instance.
+ *
+ * @property int $years Total years of the current interval.
+ * @property int $months Total months of the current interval.
+ * @property int $weeks Total weeks of the current interval calculated from the days.
+ * @property int $dayz Total days of the current interval (weeks * 7 + days).
+ * @property int $hours Total hours of the current interval.
+ * @property int $minutes Total minutes of the current interval.
+ * @property int $seconds Total seconds of the current interval.
+ * @property-read int $dayzExcludeWeeks Total days remaining in the final week of the current instance (days % 7).
+ * @property-read int $daysExcludeWeeks alias of dayzExcludeWeeks
+ * @property-read float $totalYears Number of years equivalent to the interval.
+ * @property-read float $totalMonths Number of months equivalent to the interval.
+ * @property-read float $totalWeeks Number of weeks equivalent to the interval.
+ * @property-read float $totalDays Number of days equivalent to the interval.
+ * @property-read float $totalDayz Alias for totalDays.
+ * @property-read float $totalHours Number of hours equivalent to the interval.
+ * @property-read float $totalMinutes Number of minutes equivalent to the interval.
+ * @property-read float $totalSeconds Number of seconds equivalent to the interval.
+ *
+ * @method static CarbonInterval years($years = 1) Create instance specifying a number of years.
+ * @method static CarbonInterval year($years = 1) Alias for years()
+ * @method static CarbonInterval months($months = 1) Create instance specifying a number of months.
+ * @method static CarbonInterval month($months = 1) Alias for months()
+ * @method static CarbonInterval weeks($weeks = 1) Create instance specifying a number of weeks.
+ * @method static CarbonInterval week($weeks = 1) Alias for weeks()
+ * @method static CarbonInterval days($days = 1) Create instance specifying a number of days.
+ * @method static CarbonInterval dayz($days = 1) Alias for days()
+ * @method static CarbonInterval day($days = 1) Alias for days()
+ * @method static CarbonInterval hours($hours = 1) Create instance specifying a number of hours.
+ * @method static CarbonInterval hour($hours = 1) Alias for hours()
+ * @method static CarbonInterval minutes($minutes = 1) Create instance specifying a number of minutes.
+ * @method static CarbonInterval minute($minutes = 1) Alias for minutes()
+ * @method static CarbonInterval seconds($seconds = 1) Create instance specifying a number of seconds.
+ * @method static CarbonInterval second($seconds = 1) Alias for seconds()
+ * @method CarbonInterval years($years = 1) Set the years portion of the current interval.
+ * @method CarbonInterval year($years = 1) Alias for years().
+ * @method CarbonInterval months($months = 1) Set the months portion of the current interval.
+ * @method CarbonInterval month($months = 1) Alias for months().
+ * @method CarbonInterval weeks($weeks = 1) Set the weeks portion of the current interval. Will overwrite dayz value.
+ * @method CarbonInterval week($weeks = 1) Alias for weeks().
+ * @method CarbonInterval days($days = 1) Set the days portion of the current interval.
+ * @method CarbonInterval dayz($days = 1) Alias for days().
+ * @method CarbonInterval day($days = 1) Alias for days().
+ * @method CarbonInterval hours($hours = 1) Set the hours portion of the current interval.
+ * @method CarbonInterval hour($hours = 1) Alias for hours().
+ * @method CarbonInterval minutes($minutes = 1) Set the minutes portion of the current interval.
+ * @method CarbonInterval minute($minutes = 1) Alias for minutes().
+ * @method CarbonInterval seconds($seconds = 1) Set the seconds portion of the current interval.
+ * @method CarbonInterval second($seconds = 1) Alias for seconds().
+ */
+class CarbonInterval extends DateInterval
+{
+ /**
+ * Interval spec period designators
+ */
+ const PERIOD_PREFIX = 'P';
+ const PERIOD_YEARS = 'Y';
+ const PERIOD_MONTHS = 'M';
+ const PERIOD_DAYS = 'D';
+ const PERIOD_TIME_PREFIX = 'T';
+ const PERIOD_HOURS = 'H';
+ const PERIOD_MINUTES = 'M';
+ const PERIOD_SECONDS = 'S';
+
+ /**
+ * A translator to ... er ... translate stuff
+ *
+ * @var \Symfony\Component\Translation\TranslatorInterface
+ */
+ protected static $translator;
+
+ /**
+ * @var array|null
+ */
+ protected static $cascadeFactors;
+
+ /**
+ * @var array|null
+ */
+ private static $flipCascadeFactors;
+
+ /**
+ * The registered macros.
+ *
+ * @var array
+ */
+ protected static $macros = array();
+
+ /**
+ * Before PHP 5.4.20/5.5.4 instead of FALSE days will be set to -99999 when the interval instance
+ * was created by DateTime::diff().
+ */
+ const PHP_DAYS_FALSE = -99999;
+
+ /**
+ * Mapping of units and factors for cascading.
+ *
+ * Should only be modified by changing the factors or referenced constants.
+ *
+ * @return array
+ */
+ public static function getCascadeFactors()
+ {
+ return static::$cascadeFactors ?: array(
+ 'minutes' => array(Carbon::SECONDS_PER_MINUTE, 'seconds'),
+ 'hours' => array(Carbon::MINUTES_PER_HOUR, 'minutes'),
+ 'dayz' => array(Carbon::HOURS_PER_DAY, 'hours'),
+ 'months' => array(Carbon::DAYS_PER_WEEK * Carbon::WEEKS_PER_MONTH, 'dayz'),
+ 'years' => array(Carbon::MONTHS_PER_YEAR, 'months'),
+ );
+ }
+
+ private static function standardizeUnit($unit)
+ {
+ $unit = rtrim($unit, 'sz').'s';
+
+ return $unit === 'days' ? 'dayz' : $unit;
+ }
+
+ private static function getFlipCascadeFactors()
+ {
+ if (!self::$flipCascadeFactors) {
+ self::$flipCascadeFactors = array();
+ foreach (static::getCascadeFactors() as $to => $tuple) {
+ list($factor, $from) = $tuple;
+
+ self::$flipCascadeFactors[self::standardizeUnit($from)] = array(self::standardizeUnit($to), $factor);
+ }
+ }
+
+ return self::$flipCascadeFactors;
+ }
+
+ /**
+ * @param array $cascadeFactors
+ */
+ public static function setCascadeFactors(array $cascadeFactors)
+ {
+ self::$flipCascadeFactors = null;
+ static::$cascadeFactors = $cascadeFactors;
+ }
+
+ /**
+ * Determine if the interval was created via DateTime:diff() or not.
+ *
+ * @param DateInterval $interval
+ *
+ * @return bool
+ */
+ private static function wasCreatedFromDiff(DateInterval $interval)
+ {
+ return $interval->days !== false && $interval->days !== static::PHP_DAYS_FALSE;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //////////////////////////// CONSTRUCTORS /////////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Create a new CarbonInterval instance.
+ *
+ * @param int $years
+ * @param int $months
+ * @param int $weeks
+ * @param int $days
+ * @param int $hours
+ * @param int $minutes
+ * @param int $seconds
+ */
+ public function __construct($years = 1, $months = null, $weeks = null, $days = null, $hours = null, $minutes = null, $seconds = null)
+ {
+ $spec = $years;
+
+ if (!is_string($spec) || floatval($years) || preg_match('/^[0-9.]/', $years)) {
+ $spec = static::PERIOD_PREFIX;
+
+ $spec .= $years > 0 ? $years.static::PERIOD_YEARS : '';
+ $spec .= $months > 0 ? $months.static::PERIOD_MONTHS : '';
+
+ $specDays = 0;
+ $specDays += $weeks > 0 ? $weeks * static::getDaysPerWeek() : 0;
+ $specDays += $days > 0 ? $days : 0;
+
+ $spec .= $specDays > 0 ? $specDays.static::PERIOD_DAYS : '';
+
+ if ($hours > 0 || $minutes > 0 || $seconds > 0) {
+ $spec .= static::PERIOD_TIME_PREFIX;
+ $spec .= $hours > 0 ? $hours.static::PERIOD_HOURS : '';
+ $spec .= $minutes > 0 ? $minutes.static::PERIOD_MINUTES : '';
+ $spec .= $seconds > 0 ? $seconds.static::PERIOD_SECONDS : '';
+ }
+
+ if ($spec === static::PERIOD_PREFIX) {
+ // Allow the zero interval.
+ $spec .= '0'.static::PERIOD_YEARS;
+ }
+ }
+
+ parent::__construct($spec);
+ }
+
+ /**
+ * Returns the factor for a given source-to-target couple.
+ *
+ * @param string $source
+ * @param string $target
+ *
+ * @return int|null
+ */
+ public static function getFactor($source, $target)
+ {
+ $source = self::standardizeUnit($source);
+ $target = self::standardizeUnit($target);
+ $factors = static::getFlipCascadeFactors();
+ if (isset($factors[$source])) {
+ list($to, $factor) = $factors[$source];
+ if ($to === $target) {
+ return $factor;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns current config for days per week.
+ *
+ * @return int
+ */
+ public static function getDaysPerWeek()
+ {
+ return static::getFactor('dayz', 'weeks') ?: Carbon::DAYS_PER_WEEK;
+ }
+
+ /**
+ * Returns current config for hours per day.
+ *
+ * @return int
+ */
+ public static function getHoursPerDay()
+ {
+ return static::getFactor('hours', 'dayz') ?: Carbon::HOURS_PER_DAY;
+ }
+
+ /**
+ * Returns current config for minutes per hour.
+ *
+ * @return int
+ */
+ public static function getMinutesPerHours()
+ {
+ return static::getFactor('minutes', 'hours') ?: Carbon::MINUTES_PER_HOUR;
+ }
+
+ /**
+ * Returns current config for seconds per minute.
+ *
+ * @return int
+ */
+ public static function getSecondsPerMinutes()
+ {
+ return static::getFactor('seconds', 'minutes') ?: Carbon::SECONDS_PER_MINUTE;
+ }
+
+ /**
+ * Create a new CarbonInterval instance from specific values.
+ * This is an alias for the constructor that allows better fluent
+ * syntax as it allows you to do CarbonInterval::create(1)->fn() rather than
+ * (new CarbonInterval(1))->fn().
+ *
+ * @param int $years
+ * @param int $months
+ * @param int $weeks
+ * @param int $days
+ * @param int $hours
+ * @param int $minutes
+ * @param int $seconds
+ *
+ * @return static
+ */
+ public static function create($years = 1, $months = null, $weeks = null, $days = null, $hours = null, $minutes = null, $seconds = null)
+ {
+ return new static($years, $months, $weeks, $days, $hours, $minutes, $seconds);
+ }
+
+ /**
+ * Get a copy of the instance.
+ *
+ * @return static
+ */
+ public function copy()
+ {
+ $date = new static($this->spec());
+ $date->invert = $this->invert;
+
+ return $date;
+ }
+
+ /**
+ * Provide static helpers to create instances. Allows CarbonInterval::years(3).
+ *
+ * Note: This is done using the magic method to allow static and instance methods to
+ * have the same names.
+ *
+ * @param string $name
+ * @param array $args
+ *
+ * @return static
+ */
+ public static function __callStatic($name, $args)
+ {
+ $arg = count($args) === 0 ? 1 : $args[0];
+
+ switch ($name) {
+ case 'years':
+ case 'year':
+ return new static($arg);
+
+ case 'months':
+ case 'month':
+ return new static(null, $arg);
+
+ case 'weeks':
+ case 'week':
+ return new static(null, null, $arg);
+
+ case 'days':
+ case 'dayz':
+ case 'day':
+ return new static(null, null, null, $arg);
+
+ case 'hours':
+ case 'hour':
+ return new static(null, null, null, null, $arg);
+
+ case 'minutes':
+ case 'minute':
+ return new static(null, null, null, null, null, $arg);
+
+ case 'seconds':
+ case 'second':
+ return new static(null, null, null, null, null, null, $arg);
+ }
+
+ if (static::hasMacro($name)) {
+ return call_user_func_array(
+ array(new static(0), $name), $args
+ );
+ }
+ }
+
+ /**
+ * Creates a CarbonInterval from string.
+ *
+ * Format:
+ *
+ * Suffix | Unit | Example | DateInterval expression
+ * -------|---------|---------|------------------------
+ * y | years | 1y | P1Y
+ * mo | months | 3mo | P3M
+ * w | weeks | 2w | P2W
+ * d | days | 28d | P28D
+ * h | hours | 4h | PT4H
+ * m | minutes | 12m | PT12M
+ * s | seconds | 59s | PT59S
+ *
+ * e. g. `1w 3d 4h 32m 23s` is converted to 10 days 4 hours 32 minutes and 23 seconds.
+ *
+ * Special cases:
+ * - An empty string will return a zero interval
+ * - Fractions are allowed for weeks, days, hours and minutes and will be converted
+ * and rounded to the next smaller value (caution: 0.5w = 4d)
+ *
+ * @param string $intervalDefinition
+ *
+ * @return static
+ */
+ public static function fromString($intervalDefinition)
+ {
+ if (empty($intervalDefinition)) {
+ return new static(0);
+ }
+
+ $years = 0;
+ $months = 0;
+ $weeks = 0;
+ $days = 0;
+ $hours = 0;
+ $minutes = 0;
+ $seconds = 0;
+
+ $pattern = '/(\d+(?:\.\d+)?)\h*([^\d\h]*)/i';
+ preg_match_all($pattern, $intervalDefinition, $parts, PREG_SET_ORDER);
+ while ($match = array_shift($parts)) {
+ list($part, $value, $unit) = $match;
+ $intValue = intval($value);
+ $fraction = floatval($value) - $intValue;
+ switch (strtolower($unit)) {
+ case 'year':
+ case 'years':
+ case 'y':
+ $years += $intValue;
+ break;
+
+ case 'month':
+ case 'months':
+ case 'mo':
+ $months += $intValue;
+ break;
+
+ case 'week':
+ case 'weeks':
+ case 'w':
+ $weeks += $intValue;
+ if ($fraction) {
+ $parts[] = array(null, $fraction * static::getDaysPerWeek(), 'd');
+ }
+ break;
+
+ case 'day':
+ case 'days':
+ case 'd':
+ $days += $intValue;
+ if ($fraction) {
+ $parts[] = array(null, $fraction * static::getHoursPerDay(), 'h');
+ }
+ break;
+
+ case 'hour':
+ case 'hours':
+ case 'h':
+ $hours += $intValue;
+ if ($fraction) {
+ $parts[] = array(null, $fraction * static::getMinutesPerHours(), 'm');
+ }
+ break;
+
+ case 'minute':
+ case 'minutes':
+ case 'm':
+ $minutes += $intValue;
+ if ($fraction) {
+ $seconds += round($fraction * static::getSecondsPerMinutes());
+ }
+ break;
+
+ case 'second':
+ case 'seconds':
+ case 's':
+ $seconds += $intValue;
+ break;
+
+ default:
+ throw new InvalidArgumentException(
+ sprintf('Invalid part %s in definition %s', $part, $intervalDefinition)
+ );
+ }
+ }
+
+ return new static($years, $months, $weeks, $days, $hours, $minutes, $seconds);
+ }
+
+ /**
+ * Create a CarbonInterval instance from a DateInterval one. Can not instance
+ * DateInterval objects created from DateTime::diff() as you can't externally
+ * set the $days field.
+ *
+ * @param DateInterval $di
+ *
+ * @return static
+ */
+ public static function instance(DateInterval $di)
+ {
+ $instance = new static(static::getDateIntervalSpec($di));
+ $instance->invert = $di->invert;
+
+ return $instance;
+ }
+
+ /**
+ * Make a CarbonInterval instance from given variable if possible.
+ *
+ * Always return a new instance. Parse only strings and only these likely to be intervals (skip dates
+ * and recurrences). Throw an exception for invalid format, but otherwise return null.
+ *
+ * @param mixed $var
+ *
+ * @return static|null
+ */
+ public static function make($var)
+ {
+ if ($var instanceof DateInterval) {
+ return static::instance($var);
+ }
+
+ if (is_string($var)) {
+ $var = trim($var);
+
+ if (substr($var, 0, 1) === 'P') {
+ return new static($var);
+ }
+
+ if (preg_match('/^(?:\h*\d+(?:\.\d+)?\h*[a-z]+)+$/i', $var)) {
+ return static::fromString($var);
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ /////////////////////// LOCALIZATION //////////////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Initialize the translator instance if necessary.
+ *
+ * @return \Symfony\Component\Translation\TranslatorInterface
+ */
+ protected static function translator()
+ {
+ if (static::$translator === null) {
+ static::$translator = Translator::get();
+ }
+
+ return static::$translator;
+ }
+
+ /**
+ * Get the translator instance in use.
+ *
+ * @return \Symfony\Component\Translation\TranslatorInterface
+ */
+ public static function getTranslator()
+ {
+ return static::translator();
+ }
+
+ /**
+ * Set the translator instance to use.
+ *
+ * @param TranslatorInterface $translator
+ */
+ public static function setTranslator(TranslatorInterface $translator)
+ {
+ static::$translator = $translator;
+ }
+
+ /**
+ * Get the current translator locale.
+ *
+ * @return string
+ */
+ public static function getLocale()
+ {
+ return static::translator()->getLocale();
+ }
+
+ /**
+ * Set the current translator locale.
+ *
+ * @param string $locale
+ */
+ public static function setLocale($locale)
+ {
+ return static::translator()->setLocale($locale) !== false;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ ///////////////////////// GETTERS AND SETTERS /////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Get a part of the CarbonInterval object.
+ *
+ * @param string $name
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return int|float
+ */
+ public function __get($name)
+ {
+ if (substr($name, 0, 5) === 'total') {
+ return $this->total(substr($name, 5));
+ }
+
+ switch ($name) {
+ case 'years':
+ return $this->y;
+
+ case 'months':
+ return $this->m;
+
+ case 'dayz':
+ return $this->d;
+
+ case 'hours':
+ return $this->h;
+
+ case 'minutes':
+ return $this->i;
+
+ case 'seconds':
+ return $this->s;
+
+ case 'weeks':
+ return (int) floor($this->d / static::getDaysPerWeek());
+
+ case 'daysExcludeWeeks':
+ case 'dayzExcludeWeeks':
+ return $this->d % static::getDaysPerWeek();
+
+ default:
+ throw new InvalidArgumentException(sprintf("Unknown getter '%s'", $name));
+ }
+ }
+
+ /**
+ * Set a part of the CarbonInterval object.
+ *
+ * @param string $name
+ * @param int $val
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function __set($name, $val)
+ {
+ switch ($name) {
+ case 'years':
+ $this->y = $val;
+ break;
+
+ case 'months':
+ $this->m = $val;
+ break;
+
+ case 'weeks':
+ $this->d = $val * static::getDaysPerWeek();
+ break;
+
+ case 'dayz':
+ $this->d = $val;
+ break;
+
+ case 'hours':
+ $this->h = $val;
+ break;
+
+ case 'minutes':
+ $this->i = $val;
+ break;
+
+ case 'seconds':
+ $this->s = $val;
+ break;
+ }
+ }
+
+ /**
+ * Allow setting of weeks and days to be cumulative.
+ *
+ * @param int $weeks Number of weeks to set
+ * @param int $days Number of days to set
+ *
+ * @return static
+ */
+ public function weeksAndDays($weeks, $days)
+ {
+ $this->dayz = ($weeks * static::getDaysPerWeek()) + $days;
+
+ return $this;
+ }
+
+ /**
+ * Register a custom macro.
+ *
+ * @param string $name
+ * @param object|callable $macro
+ *
+ * @return void
+ */
+ public static function macro($name, $macro)
+ {
+ static::$macros[$name] = $macro;
+ }
+
+ /**
+ * Register macros from a mixin object.
+ *
+ * @param object $mixin
+ *
+ * @throws \ReflectionException
+ *
+ * @return void
+ */
+ public static function mixin($mixin)
+ {
+ $reflection = new ReflectionClass($mixin);
+
+ $methods = $reflection->getMethods(
+ ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED
+ );
+
+ foreach ($methods as $method) {
+ $method->setAccessible(true);
+
+ static::macro($method->name, $method->invoke($mixin));
+ }
+ }
+
+ /**
+ * Check if macro is registered.
+ *
+ * @param string $name
+ *
+ * @return bool
+ */
+ public static function hasMacro($name)
+ {
+ return isset(static::$macros[$name]);
+ }
+
+ /**
+ * Call given macro.
+ *
+ * @param string $name
+ * @param array $parameters
+ *
+ * @return mixed
+ */
+ protected function callMacro($name, $parameters)
+ {
+ $macro = static::$macros[$name];
+
+ $reflection = new ReflectionFunction($macro);
+
+ $reflectionParameters = $reflection->getParameters();
+
+ $expectedCount = count($reflectionParameters);
+ $actualCount = count($parameters);
+
+ if ($expectedCount > $actualCount && $reflectionParameters[$expectedCount - 1]->name === 'self') {
+ for ($i = $actualCount; $i < $expectedCount - 1; $i++) {
+ $parameters[] = $reflectionParameters[$i]->getDefaultValue();
+ }
+
+ $parameters[] = $this;
+ }
+
+ if ($macro instanceof Closure && method_exists($macro, 'bindTo')) {
+ $macro = $macro->bindTo($this, get_class($this));
+ }
+
+ return call_user_func_array($macro, $parameters);
+ }
+
+ /**
+ * Allow fluent calls on the setters... CarbonInterval::years(3)->months(5)->day().
+ *
+ * Note: This is done using the magic method to allow static and instance methods to
+ * have the same names.
+ *
+ * @param string $name
+ * @param array $args
+ *
+ * @return static
+ */
+ public function __call($name, $args)
+ {
+ if (static::hasMacro($name)) {
+ return $this->callMacro($name, $args);
+ }
+
+ $arg = count($args) === 0 ? 1 : $args[0];
+
+ switch ($name) {
+ case 'years':
+ case 'year':
+ $this->years = $arg;
+ break;
+
+ case 'months':
+ case 'month':
+ $this->months = $arg;
+ break;
+
+ case 'weeks':
+ case 'week':
+ $this->dayz = $arg * static::getDaysPerWeek();
+ break;
+
+ case 'days':
+ case 'dayz':
+ case 'day':
+ $this->dayz = $arg;
+ break;
+
+ case 'hours':
+ case 'hour':
+ $this->hours = $arg;
+ break;
+
+ case 'minutes':
+ case 'minute':
+ $this->minutes = $arg;
+ break;
+
+ case 'seconds':
+ case 'second':
+ $this->seconds = $arg;
+ break;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get the current interval in a human readable format in the current locale.
+ *
+ * @param bool $short (false by default), returns short units if true
+ *
+ * @return string
+ */
+ public function forHumans($short = false)
+ {
+ $periods = array(
+ 'year' => array('y', $this->years),
+ 'month' => array('m', $this->months),
+ 'week' => array('w', $this->weeks),
+ 'day' => array('d', $this->daysExcludeWeeks),
+ 'hour' => array('h', $this->hours),
+ 'minute' => array('min', $this->minutes),
+ 'second' => array('s', $this->seconds),
+ );
+
+ $parts = array();
+ foreach ($periods as $unit => $options) {
+ list($shortUnit, $count) = $options;
+ if ($count > 0) {
+ $parts[] = static::translator()->transChoice($short ? $shortUnit : $unit, $count, array(':count' => $count));
+ }
+ }
+
+ return implode(' ', $parts);
+ }
+
+ /**
+ * Format the instance as a string using the forHumans() function.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->forHumans();
+ }
+
+ /**
+ * Convert the interval to a CarbonPeriod.
+ *
+ * @return CarbonPeriod
+ */
+ public function toPeriod()
+ {
+ return CarbonPeriod::createFromArray(
+ array_merge(array($this), func_get_args())
+ );
+ }
+
+ /**
+ * Invert the interval.
+ *
+ * @return $this
+ */
+ public function invert()
+ {
+ $this->invert = $this->invert ? 0 : 1;
+
+ return $this;
+ }
+
+ /**
+ * Add the passed interval to the current instance.
+ *
+ * @param DateInterval $interval
+ *
+ * @return static
+ */
+ public function add(DateInterval $interval)
+ {
+ $sign = $interval->invert === 1 ? -1 : 1;
+
+ if (static::wasCreatedFromDiff($interval)) {
+ $this->dayz += $interval->days * $sign;
+ } else {
+ $this->years += $interval->y * $sign;
+ $this->months += $interval->m * $sign;
+ $this->dayz += $interval->d * $sign;
+ $this->hours += $interval->h * $sign;
+ $this->minutes += $interval->i * $sign;
+ $this->seconds += $interval->s * $sign;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Multiply current instance given number of times
+ *
+ * @param float $factor
+ *
+ * @return $this
+ */
+ public function times($factor)
+ {
+ if ($factor < 0) {
+ $this->invert = $this->invert ? 0 : 1;
+ $factor = -$factor;
+ }
+
+ $this->years = (int) round($this->years * $factor);
+ $this->months = (int) round($this->months * $factor);
+ $this->dayz = (int) round($this->dayz * $factor);
+ $this->hours = (int) round($this->hours * $factor);
+ $this->minutes = (int) round($this->minutes * $factor);
+ $this->seconds = (int) round($this->seconds * $factor);
+
+ return $this;
+ }
+
+ /**
+ * Get the interval_spec string of a date interval.
+ *
+ * @param DateInterval $interval
+ *
+ * @return string
+ */
+ public static function getDateIntervalSpec(DateInterval $interval)
+ {
+ $date = array_filter(array(
+ static::PERIOD_YEARS => $interval->y,
+ static::PERIOD_MONTHS => $interval->m,
+ static::PERIOD_DAYS => $interval->d,
+ ));
+
+ $time = array_filter(array(
+ static::PERIOD_HOURS => $interval->h,
+ static::PERIOD_MINUTES => $interval->i,
+ static::PERIOD_SECONDS => $interval->s,
+ ));
+
+ $specString = static::PERIOD_PREFIX;
+
+ foreach ($date as $key => $value) {
+ $specString .= $value.$key;
+ }
+
+ if (count($time) > 0) {
+ $specString .= static::PERIOD_TIME_PREFIX;
+ foreach ($time as $key => $value) {
+ $specString .= $value.$key;
+ }
+ }
+
+ return $specString === static::PERIOD_PREFIX ? 'PT0S' : $specString;
+ }
+
+ /**
+ * Get the interval_spec string.
+ *
+ * @return string
+ */
+ public function spec()
+ {
+ return static::getDateIntervalSpec($this);
+ }
+
+ /**
+ * Comparing 2 date intervals.
+ *
+ * @param DateInterval $a
+ * @param DateInterval $b
+ *
+ * @return int
+ */
+ public static function compareDateIntervals(DateInterval $a, DateInterval $b)
+ {
+ $current = Carbon::now();
+ $passed = $current->copy()->add($b);
+ $current->add($a);
+
+ if ($current < $passed) {
+ return -1;
+ }
+ if ($current > $passed) {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ /**
+ * Comparing with passed interval.
+ *
+ * @param DateInterval $interval
+ *
+ * @return int
+ */
+ public function compare(DateInterval $interval)
+ {
+ return static::compareDateIntervals($this, $interval);
+ }
+
+ /**
+ * Convert overflowed values into bigger units.
+ *
+ * @return $this
+ */
+ public function cascade()
+ {
+ foreach (static::getFlipCascadeFactors() as $source => $cascade) {
+ list($target, $factor) = $cascade;
+
+ if ($source === 'dayz' && $target === 'weeks') {
+ continue;
+ }
+
+ $value = $this->$source;
+ $this->$source = $modulo = $value % $factor;
+ $this->$target += ($value - $modulo) / $factor;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get amount of given unit equivalent to the interval.
+ *
+ * @param string $unit
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return float
+ */
+ public function total($unit)
+ {
+ $realUnit = $unit = strtolower($unit);
+
+ if (in_array($unit, array('days', 'weeks'))) {
+ $realUnit = 'dayz';
+ } elseif (!in_array($unit, array('seconds', 'minutes', 'hours', 'dayz', 'months', 'years'))) {
+ throw new InvalidArgumentException("Unknown unit '$unit'.");
+ }
+
+ $result = 0;
+ $cumulativeFactor = 0;
+ $unitFound = false;
+
+ foreach (static::getFlipCascadeFactors() as $source => $cascade) {
+ list($target, $factor) = $cascade;
+
+ if ($source === $realUnit) {
+ $unitFound = true;
+ $result += $this->$source;
+ $cumulativeFactor = 1;
+ }
+
+ if ($factor === false) {
+ if ($unitFound) {
+ break;
+ }
+
+ $result = 0;
+ $cumulativeFactor = 0;
+
+ continue;
+ }
+
+ if ($target === $realUnit) {
+ $unitFound = true;
+ }
+
+ if ($cumulativeFactor) {
+ $cumulativeFactor *= $factor;
+ $result += $this->$target * $cumulativeFactor;
+
+ continue;
+ }
+
+ $result = ($result + $this->$source) / $factor;
+ }
+
+ if (isset($target) && !$cumulativeFactor) {
+ $result += $this->$target;
+ }
+
+ if (!$unitFound) {
+ throw new \InvalidArgumentException("Unit $unit have no configuration to get total from other units.");
+ }
+
+ if ($unit === 'weeks') {
+ return $result / static::getDaysPerWeek();
+ }
+
+ return $result;
+ }
+}
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/CarbonPeriod.php b/_include/calendar/nesbot/carbon/src/Carbon/CarbonPeriod.php
new file mode 100644
index 0000000..ae52c0b
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/CarbonPeriod.php
@@ -0,0 +1,1445 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon;
+
+use BadMethodCallException;
+use Closure;
+use Countable;
+use DateInterval;
+use DateTime;
+use DateTimeInterface;
+use InvalidArgumentException;
+use Iterator;
+use ReflectionClass;
+use ReflectionFunction;
+use ReflectionMethod;
+use RuntimeException;
+
+/**
+ * Substitution of DatePeriod with some modifications and many more features.
+ * Fully compatible with PHP 5.3+!
+ *
+ * @method static CarbonPeriod start($date, $inclusive = null) Create instance specifying start date.
+ * @method static CarbonPeriod since($date, $inclusive = null) Alias for start().
+ * @method static CarbonPeriod sinceNow($inclusive = null) Create instance with start date set to now.
+ * @method static CarbonPeriod end($date = null, $inclusive = null) Create instance specifying end date.
+ * @method static CarbonPeriod until($date = null, $inclusive = null) Alias for end().
+ * @method static CarbonPeriod untilNow($inclusive = null) Create instance with end date set to now.
+ * @method static CarbonPeriod dates($start, $end = null) Create instance with start and end date.
+ * @method static CarbonPeriod between($start, $end = null) Create instance with start and end date.
+ * @method static CarbonPeriod recurrences($recurrences = null) Create instance with maximum number of recurrences.
+ * @method static CarbonPeriod times($recurrences = null) Alias for recurrences().
+ * @method static CarbonPeriod options($options = null) Create instance with options.
+ * @method static CarbonPeriod toggle($options, $state = null) Create instance with options toggled on or off.
+ * @method static CarbonPeriod filter($callback, $name = null) Create instance with filter added to the stack.
+ * @method static CarbonPeriod push($callback, $name = null) Alias for filter().
+ * @method static CarbonPeriod prepend($callback, $name = null) Create instance with filter prepened to the stack.
+ * @method static CarbonPeriod filters(array $filters) Create instance with filters stack.
+ * @method static CarbonPeriod interval($interval) Create instance with given date interval.
+ * @method static CarbonPeriod each($interval) Create instance with given date interval.
+ * @method static CarbonPeriod every($interval) Create instance with given date interval.
+ * @method static CarbonPeriod step($interval) Create instance with given date interval.
+ * @method static CarbonPeriod stepBy($interval) Create instance with given date interval.
+ * @method static CarbonPeriod invert() Create instance with inverted date interval.
+ * @method static CarbonPeriod years($years = 1) Create instance specifying a number of years for date interval.
+ * @method static CarbonPeriod year($years = 1) Alias for years().
+ * @method static CarbonPeriod months($months = 1) Create instance specifying a number of months for date interval.
+ * @method static CarbonPeriod month($months = 1) Alias for months().
+ * @method static CarbonPeriod weeks($weeks = 1) Create instance specifying a number of weeks for date interval.
+ * @method static CarbonPeriod week($weeks = 1) Alias for weeks().
+ * @method static CarbonPeriod days($days = 1) Create instance specifying a number of days for date interval.
+ * @method static CarbonPeriod dayz($days = 1) Alias for days().
+ * @method static CarbonPeriod day($days = 1) Alias for days().
+ * @method static CarbonPeriod hours($hours = 1) Create instance specifying a number of hours for date interval.
+ * @method static CarbonPeriod hour($hours = 1) Alias for hours().
+ * @method static CarbonPeriod minutes($minutes = 1) Create instance specifying a number of minutes for date interval.
+ * @method static CarbonPeriod minute($minutes = 1) Alias for minutes().
+ * @method static CarbonPeriod seconds($seconds = 1) Create instance specifying a number of seconds for date interval.
+ * @method static CarbonPeriod second($seconds = 1) Alias for seconds().
+ * @method CarbonPeriod start($date, $inclusive = null) Change the period start date.
+ * @method CarbonPeriod since($date, $inclusive = null) Alias for start().
+ * @method CarbonPeriod sinceNow($inclusive = null) Change the period start date to now.
+ * @method CarbonPeriod end($date = null, $inclusive = null) Change the period end date.
+ * @method CarbonPeriod until($date = null, $inclusive = null) Alias for end().
+ * @method CarbonPeriod untilNow($inclusive = null) Change the period end date to now.
+ * @method CarbonPeriod dates($start, $end = null) Change the period start and end date.
+ * @method CarbonPeriod recurrences($recurrences = null) Change the maximum number of recurrences.
+ * @method CarbonPeriod times($recurrences = null) Alias for recurrences().
+ * @method CarbonPeriod options($options = null) Change the period options.
+ * @method CarbonPeriod toggle($options, $state = null) Toggle given options on or off.
+ * @method CarbonPeriod filter($callback, $name = null) Add a filter to the stack.
+ * @method CarbonPeriod push($callback, $name = null) Alias for filter().
+ * @method CarbonPeriod prepend($callback, $name = null) Prepend a filter to the stack.
+ * @method CarbonPeriod filters(array $filters = array()) Set filters stack.
+ * @method CarbonPeriod interval($interval) Change the period date interval.
+ * @method CarbonPeriod invert() Invert the period date interval.
+ * @method CarbonPeriod years($years = 1) Set the years portion of the date interval.
+ * @method CarbonPeriod year($years = 1) Alias for years().
+ * @method CarbonPeriod months($months = 1) Set the months portion of the date interval.
+ * @method CarbonPeriod month($months = 1) Alias for months().
+ * @method CarbonPeriod weeks($weeks = 1) Set the weeks portion of the date interval.
+ * @method CarbonPeriod week($weeks = 1) Alias for weeks().
+ * @method CarbonPeriod days($days = 1) Set the days portion of the date interval.
+ * @method CarbonPeriod dayz($days = 1) Alias for days().
+ * @method CarbonPeriod day($days = 1) Alias for days().
+ * @method CarbonPeriod hours($hours = 1) Set the hours portion of the date interval.
+ * @method CarbonPeriod hour($hours = 1) Alias for hours().
+ * @method CarbonPeriod minutes($minutes = 1) Set the minutes portion of the date interval.
+ * @method CarbonPeriod minute($minutes = 1) Alias for minutes().
+ * @method CarbonPeriod seconds($seconds = 1) Set the seconds portion of the date interval.
+ * @method CarbonPeriod second($seconds = 1) Alias for seconds().
+ */
+class CarbonPeriod implements Iterator, Countable
+{
+ /**
+ * Built-in filters.
+ *
+ * @var string
+ */
+ const RECURRENCES_FILTER = 'Carbon\CarbonPeriod::filterRecurrences';
+ const END_DATE_FILTER = 'Carbon\CarbonPeriod::filterEndDate';
+
+ /**
+ * Special value which can be returned by filters to end iteration. Also a filter.
+ *
+ * @var string
+ */
+ const END_ITERATION = 'Carbon\CarbonPeriod::endIteration';
+
+ /**
+ * Available options.
+ *
+ * @var int
+ */
+ const EXCLUDE_START_DATE = 1;
+ const EXCLUDE_END_DATE = 2;
+
+ /**
+ * Number of maximum attempts before giving up on finding next valid date.
+ *
+ * @var int
+ */
+ const NEXT_MAX_ATTEMPTS = 1000;
+
+ /**
+ * The registered macros.
+ *
+ * @var array
+ */
+ protected static $macros = array();
+
+ /**
+ * Underlying date interval instance. Always present, one day by default.
+ *
+ * @var CarbonInterval
+ */
+ protected $dateInterval;
+
+ /**
+ * Whether current date interval was set by default.
+ *
+ * @var bool
+ */
+ protected $isDefaultInterval;
+
+ /**
+ * The filters stack.
+ *
+ * @var array
+ */
+ protected $filters = array();
+
+ /**
+ * Period start date. Applied on rewind. Always present, now by default.
+ *
+ * @var Carbon
+ */
+ protected $startDate;
+
+ /**
+ * Period end date. For inverted interval should be before the start date. Applied via a filter.
+ *
+ * @var Carbon|null
+ */
+ protected $endDate;
+
+ /**
+ * Limit for number of recurrences. Applied via a filter.
+ *
+ * @var int|null
+ */
+ protected $recurrences;
+
+ /**
+ * Iteration options.
+ *
+ * @var int
+ */
+ protected $options;
+
+ /**
+ * Index of current date. Always sequential, even if some dates are skipped by filters.
+ * Equal to null only before the first iteration.
+ *
+ * @var int
+ */
+ protected $key;
+
+ /**
+ * Current date. May temporarily hold unaccepted value when looking for a next valid date.
+ * Equal to null only before the first iteration.
+ *
+ * @var Carbon
+ */
+ protected $current;
+
+ /**
+ * Timezone of current date. Taken from the start date.
+ *
+ * @var \DateTimeZone|null
+ */
+ protected $timezone;
+
+ /**
+ * The cached validation result for current date.
+ *
+ * @var bool|string|null
+ */
+ protected $validationResult;
+
+ /**
+ * Create a new instance.
+ *
+ * @return static
+ */
+ public static function create()
+ {
+ return static::createFromArray(func_get_args());
+ }
+
+ /**
+ * Create a new instance from an array of parameters.
+ *
+ * @param array $params
+ *
+ * @return static
+ */
+ public static function createFromArray(array $params)
+ {
+ // PHP 5.3 equivalent of new static(...$params).
+ $reflection = new ReflectionClass(get_class());
+ /** @var static $instance */
+ $instance = $reflection->newInstanceArgs($params);
+
+ return $instance;
+ }
+
+ /**
+ * Create CarbonPeriod from ISO 8601 string.
+ *
+ * @param string $iso
+ * @param int|null $options
+ *
+ * @return static
+ */
+ public static function createFromIso($iso, $options = null)
+ {
+ $params = static::parseIso8601($iso);
+
+ $instance = static::createFromArray($params);
+
+ if ($options !== null) {
+ $instance->setOptions($options);
+ }
+
+ return $instance;
+ }
+
+ /**
+ * Return whether given interval contains non zero value of any time unit.
+ *
+ * @param \DateInterval $interval
+ *
+ * @return bool
+ */
+ protected static function intervalHasTime(DateInterval $interval)
+ {
+ // The array_key_exists and get_object_vars are used as a workaround to check microsecond support.
+ // Both isset and property_exists will fail on PHP 7.0.14 - 7.0.21 due to the following bug:
+ // https://bugs.php.net/bug.php?id=74852
+ return $interval->h || $interval->i || $interval->s || array_key_exists('f', get_object_vars($interval)) && $interval->f;
+ }
+
+ /**
+ * Return whether given callable is a string pointing to one of Carbon's is* methods
+ * and should be automatically converted to a filter callback.
+ *
+ * @param callable $callable
+ *
+ * @return bool
+ */
+ protected static function isCarbonPredicateMethod($callable)
+ {
+ return is_string($callable) && substr($callable, 0, 2) === 'is' && (method_exists('Carbon\Carbon', $callable) || Carbon::hasMacro($callable));
+ }
+
+ /**
+ * Return whether given variable is an ISO 8601 specification.
+ *
+ * Note: Check is very basic, as actual validation will be done later when parsing.
+ * We just want to ensure that variable is not any other type of a valid parameter.
+ *
+ * @param mixed $var
+ *
+ * @return bool
+ */
+ protected static function isIso8601($var)
+ {
+ if (!is_string($var)) {
+ return false;
+ }
+
+ // Match slash but not within a timezone name.
+ $part = '[a-z]+(?:[_-][a-z]+)*';
+
+ preg_match("#\b$part/$part\b|(/)#i", $var, $match);
+
+ return isset($match[1]);
+ }
+
+ /**
+ * Parse given ISO 8601 string into an array of arguments.
+ *
+ * @param string $iso
+ *
+ * @return array
+ */
+ protected static function parseIso8601($iso)
+ {
+ $result = array();
+
+ $interval = null;
+ $start = null;
+ $end = null;
+
+ foreach (explode('/', $iso) as $key => $part) {
+ if ($key === 0 && preg_match('/^R([0-9]*)$/', $part, $match)) {
+ $parsed = strlen($match[1]) ? (int) $match[1] : null;
+ } elseif ($interval === null && $parsed = CarbonInterval::make($part)) {
+ $interval = $part;
+ } elseif ($start === null && $parsed = Carbon::make($part)) {
+ $start = $part;
+ } elseif ($end === null && $parsed = Carbon::make(static::addMissingParts($start, $part))) {
+ $end = $part;
+ } else {
+ throw new InvalidArgumentException("Invalid ISO 8601 specification: $iso.");
+ }
+
+ $result[] = $parsed;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Add missing parts of the target date from the soure date.
+ *
+ * @param string $source
+ * @param string $target
+ *
+ * @return string
+ */
+ protected static function addMissingParts($source, $target)
+ {
+ $pattern = '/'.preg_replace('/[0-9]+/', '[0-9]+', preg_quote($target, '/')).'$/';
+
+ $result = preg_replace($pattern, $target, $source, 1, $count);
+
+ return $count ? $result : $target;
+ }
+
+ /**
+ * Register a custom macro.
+ *
+ * @param string $name
+ * @param object|callable $macro
+ *
+ * @return void
+ */
+ public static function macro($name, $macro)
+ {
+ static::$macros[$name] = $macro;
+ }
+
+ /**
+ * Register macros from a mixin object.
+ *
+ * @param object $mixin
+ *
+ * @throws \ReflectionException
+ *
+ * @return void
+ */
+ public static function mixin($mixin)
+ {
+ $reflection = new ReflectionClass($mixin);
+
+ $methods = $reflection->getMethods(
+ ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED
+ );
+
+ foreach ($methods as $method) {
+ $method->setAccessible(true);
+
+ static::macro($method->name, $method->invoke($mixin));
+ }
+ }
+
+ /**
+ * Check if macro is registered.
+ *
+ * @param string $name
+ *
+ * @return bool
+ */
+ public static function hasMacro($name)
+ {
+ return isset(static::$macros[$name]);
+ }
+
+ /**
+ * Provide static proxy for instance aliases.
+ *
+ * @param string $method
+ * @param array $parameters
+ *
+ * @return mixed
+ */
+ public static function __callStatic($method, $parameters)
+ {
+ return call_user_func_array(
+ array(new static, $method), $parameters
+ );
+ }
+
+ /**
+ * CarbonPeriod constructor.
+ *
+ * @throws InvalidArgumentException
+ */
+ public function __construct()
+ {
+ // Parse and assign arguments one by one. First argument may be an ISO 8601 spec,
+ // which will be first parsed into parts and then processed the same way.
+ $arguments = func_get_args();
+
+ if (count($arguments) && static::isIso8601($iso = $arguments[0])) {
+ array_splice($arguments, 0, 1, static::parseIso8601($iso));
+ }
+
+ foreach ($arguments as $argument) {
+ if ($this->dateInterval === null && $parsed = CarbonInterval::make($argument)) {
+ $this->setDateInterval($parsed);
+ } elseif ($this->startDate === null && $parsed = Carbon::make($argument)) {
+ $this->setStartDate($parsed);
+ } elseif ($this->endDate === null && $parsed = Carbon::make($argument)) {
+ $this->setEndDate($parsed);
+ } elseif ($this->recurrences === null && $this->endDate === null && is_numeric($argument)) {
+ $this->setRecurrences($argument);
+ } elseif ($this->options === null && (is_int($argument) || $argument === null)) {
+ $this->setOptions($argument);
+ } else {
+ throw new InvalidArgumentException('Invalid constructor parameters.');
+ }
+ }
+
+ if ($this->startDate === null) {
+ $this->setStartDate(Carbon::now());
+ }
+
+ if ($this->dateInterval === null) {
+ $this->setDateInterval(CarbonInterval::day());
+
+ $this->isDefaultInterval = true;
+ }
+
+ if ($this->options === null) {
+ $this->setOptions(0);
+ }
+ }
+
+ /**
+ * Change the period date interval.
+ *
+ * @param DateInterval|string $interval
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return $this
+ */
+ public function setDateInterval($interval)
+ {
+ if (!$interval = CarbonInterval::make($interval)) {
+ throw new InvalidArgumentException('Invalid interval.');
+ }
+
+ if ($interval->spec() === 'PT0S') {
+ throw new InvalidArgumentException('Empty interval is not accepted.');
+ }
+
+ $this->dateInterval = $interval;
+
+ $this->isDefaultInterval = false;
+
+ $this->handleChangedParameters();
+
+ return $this;
+ }
+
+ /**
+ * Invert the period date interval.
+ *
+ * @return $this
+ */
+ public function invertDateInterval()
+ {
+ $interval = $this->dateInterval->invert();
+
+ return $this->setDateInterval($interval);
+ }
+
+ /**
+ * Set start and end date.
+ *
+ * @param DateTime|DateTimeInterface|string $start
+ * @param DateTime|DateTimeInterface|string|null $end
+ *
+ * @return $this
+ */
+ public function setDates($start, $end)
+ {
+ $this->setStartDate($start);
+ $this->setEndDate($end);
+
+ return $this;
+ }
+
+ /**
+ * Change the period options.
+ *
+ * @param int|null $options
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return $this
+ */
+ public function setOptions($options)
+ {
+ if (!is_int($options) && !is_null($options)) {
+ throw new InvalidArgumentException('Invalid options.');
+ }
+
+ $this->options = $options ?: 0;
+
+ $this->handleChangedParameters();
+
+ return $this;
+ }
+
+ /**
+ * Get the period options.
+ *
+ * @return int
+ */
+ public function getOptions()
+ {
+ return $this->options;
+ }
+
+ /**
+ * Toggle given options on or off.
+ *
+ * @param int $options
+ * @param bool|null $state
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return $this
+ */
+ public function toggleOptions($options, $state = null)
+ {
+ if ($state === null) {
+ $state = ($this->options & $options) !== $options;
+ }
+
+ return $this->setOptions($state ?
+ $this->options | $options :
+ $this->options & ~$options
+ );
+ }
+
+ /**
+ * Toggle EXCLUDE_START_DATE option.
+ *
+ * @param bool $state
+ *
+ * @return $this
+ */
+ public function excludeStartDate($state = true)
+ {
+ return $this->toggleOptions(static::EXCLUDE_START_DATE, $state);
+ }
+
+ /**
+ * Toggle EXCLUDE_END_DATE option.
+ *
+ * @param bool $state
+ *
+ * @return $this
+ */
+ public function excludeEndDate($state = true)
+ {
+ return $this->toggleOptions(static::EXCLUDE_END_DATE, $state);
+ }
+
+ /**
+ * Get the underlying date interval.
+ *
+ * @return CarbonInterval
+ */
+ public function getDateInterval()
+ {
+ return $this->dateInterval->copy();
+ }
+
+ /**
+ * Get start date of the period.
+ *
+ * @return Carbon
+ */
+ public function getStartDate()
+ {
+ return $this->startDate->copy();
+ }
+
+ /**
+ * Get end date of the period.
+ *
+ * @return Carbon|null
+ */
+ public function getEndDate()
+ {
+ if ($this->endDate) {
+ return $this->endDate->copy();
+ }
+ }
+
+ /**
+ * Get number of recurrences.
+ *
+ * @return int|null
+ */
+ public function getRecurrences()
+ {
+ return $this->recurrences;
+ }
+
+ /**
+ * Returns true if the start date should be excluded.
+ *
+ * @return bool
+ */
+ public function isStartExcluded()
+ {
+ return ($this->options & static::EXCLUDE_START_DATE) !== 0;
+ }
+
+ /**
+ * Returns true if the end date should be excluded.
+ *
+ * @return bool
+ */
+ public function isEndExcluded()
+ {
+ return ($this->options & static::EXCLUDE_END_DATE) !== 0;
+ }
+
+ /**
+ * Add a filter to the stack.
+ *
+ * @param callable $callback
+ * @param string $name
+ *
+ * @return $this
+ */
+ public function addFilter($callback, $name = null)
+ {
+ $tuple = $this->createFilterTuple(func_get_args());
+
+ $this->filters[] = $tuple;
+
+ $this->handleChangedParameters();
+
+ return $this;
+ }
+
+ /**
+ * Prepend a filter to the stack.
+ *
+ * @param callable $callback
+ * @param string $name
+ *
+ * @return $this
+ */
+ public function prependFilter($callback, $name = null)
+ {
+ $tuple = $this->createFilterTuple(func_get_args());
+
+ array_unshift($this->filters, $tuple);
+
+ $this->handleChangedParameters();
+
+ return $this;
+ }
+
+ /**
+ * Create a filter tuple from raw parameters.
+ *
+ * Will create an automatic filter callback for one of Carbon's is* methods.
+ *
+ * @param array $parameters
+ *
+ * @return array
+ */
+ protected function createFilterTuple(array $parameters)
+ {
+ $method = array_shift($parameters);
+
+ if (!$this->isCarbonPredicateMethod($method)) {
+ return array($method, array_shift($parameters));
+ }
+
+ return array(function ($date) use ($method, $parameters) {
+ return call_user_func_array(array($date, $method), $parameters);
+ }, $method);
+ }
+
+ /**
+ * Remove a filter by instance or name.
+ *
+ * @param callable|string $filter
+ *
+ * @return $this
+ */
+ public function removeFilter($filter)
+ {
+ $key = is_callable($filter) ? 0 : 1;
+
+ $this->filters = array_values(array_filter(
+ $this->filters,
+ function ($tuple) use ($key, $filter) {
+ return $tuple[$key] !== $filter;
+ }
+ ));
+
+ $this->updateInternalState();
+
+ $this->handleChangedParameters();
+
+ return $this;
+ }
+
+ /**
+ * Return whether given instance or name is in the filter stack.
+ *
+ * @param callable|string $filter
+ *
+ * @return bool
+ */
+ public function hasFilter($filter)
+ {
+ $key = is_callable($filter) ? 0 : 1;
+
+ foreach ($this->filters as $tuple) {
+ if ($tuple[$key] === $filter) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Get filters stack.
+ *
+ * @return array
+ */
+ public function getFilters()
+ {
+ return $this->filters;
+ }
+
+ /**
+ * Set filters stack.
+ *
+ * @param array $filters
+ *
+ * @return $this
+ */
+ public function setFilters(array $filters)
+ {
+ $this->filters = $filters;
+
+ $this->updateInternalState();
+
+ $this->handleChangedParameters();
+
+ return $this;
+ }
+
+ /**
+ * Reset filters stack.
+ *
+ * @return $this
+ */
+ public function resetFilters()
+ {
+ $this->filters = array();
+
+ if ($this->endDate !== null) {
+ $this->filters[] = array(static::END_DATE_FILTER, null);
+ }
+
+ if ($this->recurrences !== null) {
+ $this->filters[] = array(static::RECURRENCES_FILTER, null);
+ }
+
+ $this->handleChangedParameters();
+
+ return $this;
+ }
+
+ /**
+ * Update properties after removing built-in filters.
+ *
+ * @return void
+ */
+ protected function updateInternalState()
+ {
+ if (!$this->hasFilter(static::END_DATE_FILTER)) {
+ $this->endDate = null;
+ }
+
+ if (!$this->hasFilter(static::RECURRENCES_FILTER)) {
+ $this->recurrences = null;
+ }
+ }
+
+ /**
+ * Add a recurrences filter (set maximum number of recurrences).
+ *
+ * @param int|null $recurrences
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return $this
+ */
+ public function setRecurrences($recurrences)
+ {
+ if (!is_numeric($recurrences) && !is_null($recurrences) || $recurrences < 0) {
+ throw new InvalidArgumentException('Invalid number of recurrences.');
+ }
+
+ if ($recurrences === null) {
+ return $this->removeFilter(static::RECURRENCES_FILTER);
+ }
+
+ $this->recurrences = (int) $recurrences;
+
+ if (!$this->hasFilter(static::RECURRENCES_FILTER)) {
+ return $this->addFilter(static::RECURRENCES_FILTER);
+ }
+
+ $this->handleChangedParameters();
+
+ return $this;
+ }
+
+ /**
+ * Recurrences filter callback (limits number of recurrences).
+ *
+ * @param \Carbon\Carbon $current
+ * @param int $key
+ *
+ * @return bool|string
+ */
+ protected function filterRecurrences($current, $key)
+ {
+ if ($key < $this->recurrences) {
+ return true;
+ }
+
+ return static::END_ITERATION;
+ }
+
+ /**
+ * Change the period start date.
+ *
+ * @param DateTime|DateTimeInterface|string $date
+ * @param bool|null $inclusive
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return $this
+ */
+ public function setStartDate($date, $inclusive = null)
+ {
+ if (!$date = Carbon::make($date)) {
+ throw new InvalidArgumentException('Invalid start date.');
+ }
+
+ $this->startDate = $date;
+
+ if ($inclusive !== null) {
+ $this->toggleOptions(static::EXCLUDE_START_DATE, !$inclusive);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Change the period end date.
+ *
+ * @param DateTime|DateTimeInterface|string|null $date
+ * @param bool|null $inclusive
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return $this
+ */
+ public function setEndDate($date, $inclusive = null)
+ {
+ if (!is_null($date) && !$date = Carbon::make($date)) {
+ throw new InvalidArgumentException('Invalid end date.');
+ }
+
+ if (!$date) {
+ return $this->removeFilter(static::END_DATE_FILTER);
+ }
+
+ $this->endDate = $date;
+
+ if ($inclusive !== null) {
+ $this->toggleOptions(static::EXCLUDE_END_DATE, !$inclusive);
+ }
+
+ if (!$this->hasFilter(static::END_DATE_FILTER)) {
+ return $this->addFilter(static::END_DATE_FILTER);
+ }
+
+ $this->handleChangedParameters();
+
+ return $this;
+ }
+
+ /**
+ * End date filter callback.
+ *
+ * @param \Carbon\Carbon $current
+ *
+ * @return bool|string
+ */
+ protected function filterEndDate($current)
+ {
+ if (!$this->isEndExcluded() && $current == $this->endDate) {
+ return true;
+ }
+
+ if ($this->dateInterval->invert ? $current > $this->endDate : $current < $this->endDate) {
+ return true;
+ }
+
+ return static::END_ITERATION;
+ }
+
+ /**
+ * End iteration filter callback.
+ *
+ * @return string
+ */
+ protected function endIteration()
+ {
+ return static::END_ITERATION;
+ }
+
+ /**
+ * Handle change of the parameters.
+ */
+ protected function handleChangedParameters()
+ {
+ $this->validationResult = null;
+ }
+
+ /**
+ * Validate current date and stop iteration when necessary.
+ *
+ * Returns true when current date is valid, false if it is not, or static::END_ITERATION
+ * when iteration should be stopped.
+ *
+ * @return bool|static::END_ITERATION
+ */
+ protected function validateCurrentDate()
+ {
+ if ($this->current === null) {
+ $this->rewind();
+ }
+
+ // Check after the first rewind to avoid repeating the initial validation.
+ if ($this->validationResult !== null) {
+ return $this->validationResult;
+ }
+
+ return $this->validationResult = $this->checkFilters();
+ }
+
+ /**
+ * Check whether current value and key pass all the filters.
+ *
+ * @return bool|string
+ */
+ protected function checkFilters()
+ {
+ $current = $this->prepareForReturn($this->current);
+
+ foreach ($this->filters as $tuple) {
+ $result = call_user_func(
+ $tuple[0], $current->copy(), $this->key, $this
+ );
+
+ if ($result === static::END_ITERATION) {
+ return static::END_ITERATION;
+ }
+
+ if (!$result) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Prepare given date to be returned to the external logic.
+ *
+ * @param Carbon $date
+ *
+ * @return Carbon
+ */
+ protected function prepareForReturn(Carbon $date)
+ {
+ $date = $date->copy();
+
+ if ($this->timezone) {
+ $date->setTimezone($this->timezone);
+ }
+
+ return $date;
+ }
+
+ /**
+ * Check if the current position is valid.
+ *
+ * @return bool
+ */
+ public function valid()
+ {
+ return $this->validateCurrentDate() === true;
+ }
+
+ /**
+ * Return the current key.
+ *
+ * @return int|null
+ */
+ public function key()
+ {
+ if ($this->valid()) {
+ return $this->key;
+ }
+ }
+
+ /**
+ * Return the current date.
+ *
+ * @return Carbon|null
+ */
+ public function current()
+ {
+ if ($this->valid()) {
+ return $this->prepareForReturn($this->current);
+ }
+ }
+
+ /**
+ * Move forward to the next date.
+ *
+ * @throws \RuntimeException
+ *
+ * @return void
+ */
+ public function next()
+ {
+ if ($this->current === null) {
+ $this->rewind();
+ }
+
+ if ($this->validationResult !== static::END_ITERATION) {
+ $this->key++;
+
+ $this->incrementCurrentDateUntilValid();
+ }
+ }
+
+ /**
+ * Rewind to the start date.
+ *
+ * Iterating over a date in the UTC timezone avoids bug during backward DST change.
+ *
+ * @see https://bugs.php.net/bug.php?id=72255
+ * @see https://bugs.php.net/bug.php?id=74274
+ * @see https://wiki.php.net/rfc/datetime_and_daylight_saving_time
+ *
+ * @throws \RuntimeException
+ *
+ * @return void
+ */
+ public function rewind()
+ {
+ $this->key = 0;
+ $this->current = $this->startDate->copy();
+ $this->timezone = static::intervalHasTime($this->dateInterval) ? $this->current->getTimezone() : null;
+
+ if ($this->timezone) {
+ $this->current->setTimezone('UTC');
+ }
+
+ $this->validationResult = null;
+
+ if ($this->isStartExcluded() || $this->validateCurrentDate() === false) {
+ $this->incrementCurrentDateUntilValid();
+ }
+ }
+
+ /**
+ * Skip iterations and returns iteration state (false if ended, true if still valid).
+ *
+ * @param int $count steps number to skip (1 by default)
+ *
+ * @return bool
+ */
+ public function skip($count = 1)
+ {
+ for ($i = $count; $this->valid() && $i > 0; $i--) {
+ $this->next();
+ }
+
+ return $this->valid();
+ }
+
+ /**
+ * Keep incrementing the current date until a valid date is found or the iteration is ended.
+ *
+ * @throws \RuntimeException
+ *
+ * @return void
+ */
+ protected function incrementCurrentDateUntilValid()
+ {
+ $attempts = 0;
+
+ do {
+ $this->current->add($this->dateInterval);
+
+ $this->validationResult = null;
+
+ if (++$attempts > static::NEXT_MAX_ATTEMPTS) {
+ throw new RuntimeException('Could not find next valid date.');
+ }
+ } while ($this->validateCurrentDate() === false);
+ }
+
+ /**
+ * Format the date period as ISO 8601.
+ *
+ * @return string
+ */
+ public function toIso8601String()
+ {
+ $parts = array();
+
+ if ($this->recurrences !== null) {
+ $parts[] = 'R'.$this->recurrences;
+ }
+
+ $parts[] = $this->startDate->toIso8601String();
+
+ $parts[] = $this->dateInterval->spec();
+
+ if ($this->endDate !== null) {
+ $parts[] = $this->endDate->toIso8601String();
+ }
+
+ return implode('/', $parts);
+ }
+
+ /**
+ * Convert the date period into a string.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ $translator = Carbon::getTranslator();
+
+ $parts = array();
+
+ $format = !$this->startDate->isStartOfDay() || $this->endDate && !$this->endDate->isStartOfDay()
+ ? 'Y-m-d H:i:s'
+ : 'Y-m-d';
+
+ if ($this->recurrences !== null) {
+ $parts[] = $translator->transChoice('period_recurrences', $this->recurrences, array(':count' => $this->recurrences));
+ }
+
+ $parts[] = $translator->trans('period_interval', array(':interval' => $this->dateInterval->forHumans()));
+
+ $parts[] = $translator->trans('period_start_date', array(':date' => $this->startDate->format($format)));
+
+ if ($this->endDate !== null) {
+ $parts[] = $translator->trans('period_end_date', array(':date' => $this->endDate->format($format)));
+ }
+
+ $result = implode(' ', $parts);
+
+ return mb_strtoupper(mb_substr($result, 0, 1)).mb_substr($result, 1);
+ }
+
+ /**
+ * Format the date period as ISO 8601.
+ *
+ * @return string
+ */
+ public function spec()
+ {
+ return $this->toIso8601String();
+ }
+
+ /**
+ * Convert the date period into an array without changing current iteration state.
+ *
+ * @return array
+ */
+ public function toArray()
+ {
+ $state = array(
+ $this->key,
+ $this->current ? $this->current->copy() : null,
+ $this->validationResult,
+ );
+
+ $result = iterator_to_array($this);
+
+ list(
+ $this->key,
+ $this->current,
+ $this->validationResult
+ ) = $state;
+
+ return $result;
+ }
+
+ /**
+ * Count dates in the date period.
+ *
+ * @return int
+ */
+ public function count()
+ {
+ return count($this->toArray());
+ }
+
+ /**
+ * Return the first date in the date period.
+ *
+ * @return Carbon|null
+ */
+ public function first()
+ {
+ if ($array = $this->toArray()) {
+ return $array[0];
+ }
+ }
+
+ /**
+ * Return the last date in the date period.
+ *
+ * @return Carbon|null
+ */
+ public function last()
+ {
+ if ($array = $this->toArray()) {
+ return $array[count($array) - 1];
+ }
+ }
+
+ /**
+ * Call given macro.
+ *
+ * @param string $name
+ * @param array $parameters
+ *
+ * @return mixed
+ */
+ protected function callMacro($name, $parameters)
+ {
+ $macro = static::$macros[$name];
+
+ $reflection = new ReflectionFunction($macro);
+
+ $reflectionParameters = $reflection->getParameters();
+
+ $expectedCount = count($reflectionParameters);
+ $actualCount = count($parameters);
+
+ if ($expectedCount > $actualCount && $reflectionParameters[$expectedCount - 1]->name === 'self') {
+ for ($i = $actualCount; $i < $expectedCount - 1; $i++) {
+ $parameters[] = $reflectionParameters[$i]->getDefaultValue();
+ }
+
+ $parameters[] = $this;
+ }
+
+ if ($macro instanceof Closure && method_exists($macro, 'bindTo')) {
+ $macro = $macro->bindTo($this, get_class($this));
+ }
+
+ return call_user_func_array($macro, $parameters);
+ }
+
+ /**
+ * Convert the date period into a string.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->toString();
+ }
+
+ /**
+ * Add aliases for setters.
+ *
+ * CarbonPeriod::days(3)->hours(5)->invert()
+ * ->sinceNow()->until('2010-01-10')
+ * ->filter(...)
+ * ->count()
+ *
+ * Note: We use magic method to let static and instance aliases with the same names.
+ *
+ * @param string $method
+ * @param array $parameters
+ *
+ * @return mixed
+ */
+ public function __call($method, $parameters)
+ {
+ if (static::hasMacro($method)) {
+ return $this->callMacro($method, $parameters);
+ }
+
+ $first = count($parameters) >= 1 ? $parameters[0] : null;
+ $second = count($parameters) >= 2 ? $parameters[1] : null;
+
+ switch ($method) {
+ case 'start':
+ case 'since':
+ return $this->setStartDate($first, $second);
+
+ case 'sinceNow':
+ return $this->setStartDate(new Carbon, $first);
+
+ case 'end':
+ case 'until':
+ return $this->setEndDate($first, $second);
+
+ case 'untilNow':
+ return $this->setEndDate(new Carbon, $first);
+
+ case 'dates':
+ case 'between':
+ return $this->setDates($first, $second);
+
+ case 'recurrences':
+ case 'times':
+ return $this->setRecurrences($first);
+
+ case 'options':
+ return $this->setOptions($first);
+
+ case 'toggle':
+ return $this->toggleOptions($first, $second);
+
+ case 'filter':
+ case 'push':
+ return $this->addFilter($first, $second);
+
+ case 'prepend':
+ return $this->prependFilter($first, $second);
+
+ case 'filters':
+ return $this->setFilters($first ?: array());
+
+ case 'interval':
+ case 'each':
+ case 'every':
+ case 'step':
+ case 'stepBy':
+ return $this->setDateInterval($first);
+
+ case 'invert':
+ return $this->invertDateInterval();
+
+ case 'years':
+ case 'year':
+ case 'months':
+ case 'month':
+ case 'weeks':
+ case 'week':
+ case 'days':
+ case 'dayz':
+ case 'day':
+ case 'hours':
+ case 'hour':
+ case 'minutes':
+ case 'minute':
+ case 'seconds':
+ case 'second':
+ return $this->setDateInterval(call_user_func(
+ // Override default P1D when instantiating via fluent setters.
+ array($this->isDefaultInterval ? new CarbonInterval('PT0S') : $this->dateInterval, $method),
+ count($parameters) === 0 ? 1 : $first
+ ));
+ }
+
+ throw new BadMethodCallException("Method $method does not exist.");
+ }
+}
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Exceptions/InvalidDateException.php b/_include/calendar/nesbot/carbon/src/Carbon/Exceptions/InvalidDateException.php
new file mode 100644
index 0000000..1b0d473
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Exceptions/InvalidDateException.php
@@ -0,0 +1,67 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Exceptions;
+
+use Exception;
+use InvalidArgumentException;
+
+class InvalidDateException extends InvalidArgumentException
+{
+ /**
+ * The invalid field.
+ *
+ * @var string
+ */
+ private $field;
+
+ /**
+ * The invalid value.
+ *
+ * @var mixed
+ */
+ private $value;
+
+ /**
+ * Constructor.
+ *
+ * @param string $field
+ * @param mixed $value
+ * @param int $code
+ * @param \Exception|null $previous
+ */
+ public function __construct($field, $value, $code = 0, Exception $previous = null)
+ {
+ $this->field = $field;
+ $this->value = $value;
+ parent::__construct($field.' : '.$value.' is not a valid value.', $code, $previous);
+ }
+
+ /**
+ * Get the invalid field.
+ *
+ * @return string
+ */
+ public function getField()
+ {
+ return $this->field;
+ }
+
+ /**
+ * Get the invalid value.
+ *
+ * @return mixed
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+}
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/af.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/af.php
new file mode 100644
index 0000000..5cf6a8d
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/af.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count jaar|:count jare',
+ 'y' => ':count jaar|:count jare',
+ 'month' => ':count maand|:count maande',
+ 'm' => ':count maand|:count maande',
+ 'week' => ':count week|:count weke',
+ 'w' => ':count week|:count weke',
+ 'day' => ':count dag|:count dae',
+ 'd' => ':count dag|:count dae',
+ 'hour' => ':count uur|:count ure',
+ 'h' => ':count uur|:count ure',
+ 'minute' => ':count minuut|:count minute',
+ 'min' => ':count minuut|:count minute',
+ 'second' => ':count sekond|:count sekondes',
+ 's' => ':count sekond|:count sekondes',
+ 'ago' => ':time terug',
+ 'from_now' => ':time van nou af',
+ 'after' => ':time na',
+ 'before' => ':time voor',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/ar.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/ar.php
new file mode 100644
index 0000000..de8f6b8
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/ar.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '{0}سنة|{1}سنة|{2}سنتين|[3,10]:count سنوات|[11,Inf]:count سنة',
+ 'y' => '{0}سنة|{1}سنة|{2}سنتين|[3,10]:count سنوات|[11,Inf]:count سنة',
+ 'month' => '{0}شهر|{1} شهر|{2}شهرين|[3,10]:count أشهر|[11,Inf]:count شهر',
+ 'm' => '{0}شهر|{1} شهر|{2}شهرين|[3,10]:count أشهر|[11,Inf]:count شهر',
+ 'week' => '{0}أسبوع|{1}أسبوع|{2}أسبوعين|[3,10]:count أسابيع|[11,Inf]:count أسبوع',
+ 'w' => '{0}أسبوع|{1}أسبوع|{2}أسبوعين|[3,10]:count أسابيع|[11,Inf]:count أسبوع',
+ 'day' => '{0}يوم|{1}يوم|{2}يومين|[3,10]:count أيام|[11,Inf] يوم',
+ 'd' => '{0}يوم|{1}يوم|{2}يومين|[3,10]:count أيام|[11,Inf] يوم',
+ 'hour' => '{0}ساعة|{1}ساعة|{2}ساعتين|[3,10]:count ساعات|[11,Inf]:count ساعة',
+ 'h' => '{0}ساعة|{1}ساعة|{2}ساعتين|[3,10]:count ساعات|[11,Inf]:count ساعة',
+ 'minute' => '{0}دقيقة|{1}دقيقة|{2}دقيقتين|[3,10]:count دقائق|[11,Inf]:count دقيقة',
+ 'min' => '{0}دقيقة|{1}دقيقة|{2}دقيقتين|[3,10]:count دقائق|[11,Inf]:count دقيقة',
+ 'second' => '{0}ثانية|{1}ثانية|{2}ثانيتين|[3,10]:count ثوان|[11,Inf]:count ثانية',
+ 's' => '{0}ثانية|{1}ثانية|{2}ثانيتين|[3,10]:count ثوان|[11,Inf]:count ثانية',
+ 'ago' => 'منذ :time',
+ 'from_now' => ':time من الآن',
+ 'after' => 'بعد :time',
+ 'before' => 'قبل :time',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/ar_Shakl.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/ar_Shakl.php
new file mode 100644
index 0000000..846ae02
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/ar_Shakl.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '[0,1] سَنَة|{2} سَنَتَيْن|[3,10]:count سَنَوَات|[11,Inf]:count سَنَة',
+ 'y' => '[0,1] سَنَة|{2} سَنَتَيْن|[3,10]:count سَنَوَات|[11,Inf]:count سَنَة',
+ 'month' => '[0,1] شَهْرَ|{2} شَهْرَيْن|[3,10]:count أَشْهُر|[11,Inf]:count شَهْرَ',
+ 'm' => '[0,1] شَهْرَ|{2} شَهْرَيْن|[3,10]:count أَشْهُر|[11,Inf]:count شَهْرَ',
+ 'week' => '[0,1] أُسْبُوع|{2} أُسْبُوعَيْن|[3,10]:count أَسَابِيع|[11,Inf]:count أُسْبُوع',
+ 'w' => '[0,1] أُسْبُوع|{2} أُسْبُوعَيْن|[3,10]:count أَسَابِيع|[11,Inf]:count أُسْبُوع',
+ 'day' => '[0,1] يَوْم|{2} يَوْمَيْن|[3,10]:count أَيَّام|[11,Inf] يَوْم',
+ 'd' => '[0,1] يَوْم|{2} يَوْمَيْن|[3,10]:count أَيَّام|[11,Inf] يَوْم',
+ 'hour' => '[0,1] سَاعَة|{2} سَاعَتَيْن|[3,10]:count سَاعَات|[11,Inf]:count سَاعَة',
+ 'h' => '[0,1] سَاعَة|{2} سَاعَتَيْن|[3,10]:count سَاعَات|[11,Inf]:count سَاعَة',
+ 'minute' => '[0,1] دَقِيقَة|{2} دَقِيقَتَيْن|[3,10]:count دَقَائِق|[11,Inf]:count دَقِيقَة',
+ 'min' => '[0,1] دَقِيقَة|{2} دَقِيقَتَيْن|[3,10]:count دَقَائِق|[11,Inf]:count دَقِيقَة',
+ 'second' => '[0,1] ثَانِيَة|{2} ثَانِيَتَيْن|[3,10]:count ثَوَان|[11,Inf]:count ثَانِيَة',
+ 's' => '[0,1] ثَانِيَة|{2} ثَانِيَتَيْن|[3,10]:count ثَوَان|[11,Inf]:count ثَانِيَة',
+ 'ago' => 'مُنْذُ :time',
+ 'from_now' => 'مِنَ الْآن :time',
+ 'after' => 'بَعْدَ :time',
+ 'before' => 'قَبْلَ :time',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/az.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/az.php
new file mode 100644
index 0000000..25f5c4a
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/az.php
@@ -0,0 +1,40 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count il',
+ 'y' => ':count il',
+ 'month' => ':count ay',
+ 'm' => ':count ay',
+ 'week' => ':count həftə',
+ 'w' => ':count həftə',
+ 'day' => ':count gün',
+ 'd' => ':count gün',
+ 'hour' => ':count saat',
+ 'h' => ':count saat',
+ 'minute' => ':count dəqiqə',
+ 'min' => ':count dəqiqə',
+ 'second' => ':count saniyə',
+ 's' => ':count saniyə',
+ 'ago' => ':time əvvəl',
+ 'from_now' => ':time sonra',
+ 'after' => ':time sonra',
+ 'before' => ':time əvvəl',
+ 'diff_now' => 'indi',
+ 'diff_yesterday' => 'dünən',
+ 'diff_tomorrow' => 'sabah',
+ 'diff_before_yesterday' => 'srağagün',
+ 'diff_after_tomorrow' => 'birisi gün',
+ 'period_recurrences' => ':count dəfədən bir',
+ 'period_interval' => 'hər :interval',
+ 'period_start_date' => ':date tarixindən başlayaraq',
+ 'period_end_date' => ':date tarixinədək',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/bg.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/bg.php
new file mode 100644
index 0000000..d9e510b
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/bg.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count година|:count години',
+ 'y' => ':count година|:count години',
+ 'month' => ':count месец|:count месеца',
+ 'm' => ':count месец|:count месеца',
+ 'week' => ':count седмица|:count седмици',
+ 'w' => ':count седмица|:count седмици',
+ 'day' => ':count ден|:count дни',
+ 'd' => ':count ден|:count дни',
+ 'hour' => ':count час|:count часа',
+ 'h' => ':count час|:count часа',
+ 'minute' => ':count минута|:count минути',
+ 'min' => ':count минута|:count минути',
+ 'second' => ':count секунда|:count секунди',
+ 's' => ':count секунда|:count секунди',
+ 'ago' => 'преди :time',
+ 'from_now' => ':time от сега',
+ 'after' => 'след :time',
+ 'before' => 'преди :time',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/bn.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/bn.php
new file mode 100644
index 0000000..5817599
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/bn.php
@@ -0,0 +1,38 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '১ বছর|:count বছর',
+ 'y' => '১ বছর|:count বছর',
+ 'month' => '১ মাস|:count মাস',
+ 'm' => '১ মাস|:count মাস',
+ 'week' => '১ সপ্তাহ|:count সপ্তাহ',
+ 'w' => '১ সপ্তাহ|:count সপ্তাহ',
+ 'day' => '১ দিন|:count দিন',
+ 'd' => '১ দিন|:count দিন',
+ 'hour' => '১ ঘন্টা|:count ঘন্টা',
+ 'h' => '১ ঘন্টা|:count ঘন্টা',
+ 'minute' => '১ মিনিট|:count মিনিট',
+ 'min' => '১ মিনিট|:count মিনিট',
+ 'second' => '১ সেকেন্ড|:count সেকেন্ড',
+ 's' => '১ সেকেন্ড|:count সেকেন্ড',
+ 'ago' => ':time পূর্বে',
+ 'from_now' => 'এখন থেকে :time',
+ 'after' => ':time পরে',
+ 'before' => ':time আগে',
+ 'diff_now' => 'এখন',
+ 'diff_yesterday' => 'গতকাল',
+ 'diff_tomorrow' => 'আগামীকাল',
+ 'period_recurrences' => ':count বার|:count বার',
+ 'period_interval' => 'প্রতি :interval',
+ 'period_start_date' => ':date থেকে',
+ 'period_end_date' => ':date পর্যন্ত',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/bs_BA.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/bs_BA.php
new file mode 100644
index 0000000..7a9b05a
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/bs_BA.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count godina|:count godine|:count godina',
+ 'y' => ':count godina|:count godine|:count godina',
+ 'month' => ':count mjesec|:count mjeseca|:count mjeseci',
+ 'm' => ':count mjesec|:count mjeseca|:count mjeseci',
+ 'week' => ':count nedjelja|:count nedjelje|:count nedjelja',
+ 'w' => ':count nedjelja|:count nedjelje|:count nedjelja',
+ 'day' => ':count dan|:count dana|:count dana',
+ 'd' => ':count dan|:count dana|:count dana',
+ 'hour' => ':count sat|:count sata|:count sati',
+ 'h' => ':count sat|:count sata|:count sati',
+ 'minute' => ':count minut|:count minuta|:count minuta',
+ 'min' => ':count minut|:count minuta|:count minuta',
+ 'second' => ':count sekund|:count sekunda|:count sekundi',
+ 's' => ':count sekund|:count sekunda|:count sekundi',
+ 'ago' => 'prije :time',
+ 'from_now' => 'za :time',
+ 'after' => 'nakon :time',
+ 'before' => ':time ranije',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/ca.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/ca.php
new file mode 100644
index 0000000..c854b5a
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/ca.php
@@ -0,0 +1,40 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count any|:count anys',
+ 'y' => ':count any|:count anys',
+ 'month' => ':count mes|:count mesos',
+ 'm' => ':count mes|:count mesos',
+ 'week' => ':count setmana|:count setmanes',
+ 'w' => ':count setmana|:count setmanes',
+ 'day' => ':count dia|:count dies',
+ 'd' => ':count dia|:count dies',
+ 'hour' => ':count hora|:count hores',
+ 'h' => ':count hora|:count hores',
+ 'minute' => ':count minut|:count minuts',
+ 'min' => ':count minut|:count minuts',
+ 'second' => ':count segon|:count segons',
+ 's' => ':count segon|:count segons',
+ 'ago' => 'fa :time',
+ 'from_now' => 'd\'aquí :time',
+ 'after' => ':time després',
+ 'before' => ':time abans',
+ 'diff_now' => 'ara mateix',
+ 'diff_yesterday' => 'ahir',
+ 'diff_tomorrow' => 'demà',
+ 'diff_before_yesterday' => "abans d'ahir",
+ 'diff_after_tomorrow' => 'demà passat',
+ 'period_recurrences' => ':count cop|:count cops',
+ 'period_interval' => 'cada :interval',
+ 'period_start_date' => 'de :date',
+ 'period_end_date' => 'fins a :date',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/cs.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/cs.php
new file mode 100644
index 0000000..a447ce2
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/cs.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count rok|:count roky|:count let',
+ 'y' => ':count rok|:count roky|:count let',
+ 'month' => ':count měsíc|:count měsíce|:count měsíců',
+ 'm' => ':count měsíc|:count měsíce|:count měsíců',
+ 'week' => ':count týden|:count týdny|:count týdnů',
+ 'w' => ':count týden|:count týdny|:count týdnů',
+ 'day' => ':count den|:count dny|:count dní',
+ 'd' => ':count den|:count dny|:count dní',
+ 'hour' => ':count hodinu|:count hodiny|:count hodin',
+ 'h' => ':count hodinu|:count hodiny|:count hodin',
+ 'minute' => ':count minutu|:count minuty|:count minut',
+ 'min' => ':count minutu|:count minuty|:count minut',
+ 'second' => ':count sekundu|:count sekundy|:count sekund',
+ 's' => ':count sekundu|:count sekundy|:count sekund',
+ 'ago' => ':time nazpět',
+ 'from_now' => 'za :time',
+ 'after' => ':time později',
+ 'before' => ':time předtím',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/cy.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/cy.php
new file mode 100644
index 0000000..c93750e
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/cy.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+return array(
+ 'year' => '1 flwyddyn|:count blynedd',
+ 'y' => ':countbl',
+ 'month' => '1 mis|:count fis',
+ 'm' => ':countmi',
+ 'week' => ':count wythnos',
+ 'w' => ':countw',
+ 'day' => ':count diwrnod',
+ 'd' => ':countd',
+ 'hour' => ':count awr',
+ 'h' => ':counth',
+ 'minute' => ':count munud',
+ 'min' => ':countm',
+ 'second' => ':count eiliad',
+ 's' => ':counts',
+ 'ago' => ':time yn ôl',
+ 'from_now' => ':time o hyn ymlaen',
+ 'after' => ':time ar ôl',
+ 'before' => ':time o\'r blaen',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/da.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/da.php
new file mode 100644
index 0000000..86507b0
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/da.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count år|:count år',
+ 'y' => ':count år|:count år',
+ 'month' => ':count måned|:count måneder',
+ 'm' => ':count måned|:count måneder',
+ 'week' => ':count uge|:count uger',
+ 'w' => ':count uge|:count uger',
+ 'day' => ':count dag|:count dage',
+ 'd' => ':count dag|:count dage',
+ 'hour' => ':count time|:count timer',
+ 'h' => ':count time|:count timer',
+ 'minute' => ':count minut|:count minutter',
+ 'min' => ':count minut|:count minutter',
+ 'second' => ':count sekund|:count sekunder',
+ 's' => ':count sekund|:count sekunder',
+ 'ago' => ':time siden',
+ 'from_now' => 'om :time',
+ 'after' => ':time efter',
+ 'before' => ':time før',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/de.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/de.php
new file mode 100644
index 0000000..5ea2a03
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/de.php
@@ -0,0 +1,46 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count Jahr|:count Jahre',
+ 'y' => ':countJ|:countJ',
+ 'month' => ':count Monat|:count Monate',
+ 'm' => ':countMon|:countMon',
+ 'week' => ':count Woche|:count Wochen',
+ 'w' => ':countWo|:countWo',
+ 'day' => ':count Tag|:count Tage',
+ 'd' => ':countTg|:countTg',
+ 'hour' => ':count Stunde|:count Stunden',
+ 'h' => ':countStd|:countStd',
+ 'minute' => ':count Minute|:count Minuten',
+ 'min' => ':countMin|:countMin',
+ 'second' => ':count Sekunde|:count Sekunden',
+ 's' => ':countSek|:countSek',
+ 'ago' => 'vor :time',
+ 'from_now' => 'in :time',
+ 'after' => ':time später',
+ 'before' => ':time zuvor',
+
+ 'year_from_now' => ':count Jahr|:count Jahren',
+ 'month_from_now' => ':count Monat|:count Monaten',
+ 'week_from_now' => ':count Woche|:count Wochen',
+ 'day_from_now' => ':count Tag|:count Tagen',
+ 'year_ago' => ':count Jahr|:count Jahren',
+ 'month_ago' => ':count Monat|:count Monaten',
+ 'week_ago' => ':count Woche|:count Wochen',
+ 'day_ago' => ':count Tag|:count Tagen',
+
+ 'diff_now' => 'Gerade eben',
+ 'diff_yesterday' => 'Gestern',
+ 'diff_tomorrow' => 'Heute',
+ 'diff_before_yesterday' => 'Vorgestern',
+ 'diff_after_tomorrow' => 'Übermorgen',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/dv_MV.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/dv_MV.php
new file mode 100644
index 0000000..e3c50b3
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/dv_MV.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '{0}އަހަރެއް|[1,Inf]:count އަހަރު',
+ 'y' => '{0}އަހަރެއް|[1,Inf]:count އަހަރު',
+ 'month' => '{0}މައްސަރެއް|[1,Inf]:count މަސް',
+ 'm' => '{0}މައްސަރެއް|[1,Inf]:count މަސް',
+ 'week' => '{0}ހަފްތާއެއް|[1,Inf]:count ހަފްތާ',
+ 'w' => '{0}ހަފްތާއެއް|[1,Inf]:count ހަފްތާ',
+ 'day' => '{0}ދުވަސް|[1,Inf]:count ދުވަސް',
+ 'd' => '{0}ދުވަސް|[1,Inf]:count ދުވަސް',
+ 'hour' => '{0}ގަޑިއިރެއް|[1,Inf]:count ގަޑި',
+ 'h' => '{0}ގަޑިއިރެއް|[1,Inf]:count ގަޑި',
+ 'minute' => '{0}މިނެޓެއް|[1,Inf]:count މިނެޓް',
+ 'min' => '{0}މިނެޓެއް|[1,Inf]:count މިނެޓް',
+ 'second' => '{0}ސިކުންތެއް|[1,Inf]:count ސިކުންތު',
+ 's' => '{0}ސިކުންތެއް|[1,Inf]:count ސިކުންތު',
+ 'ago' => ':time ކުރިން',
+ 'from_now' => ':time ފަހުން',
+ 'after' => ':time ފަހުން',
+ 'before' => ':time ކުރި',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/el.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/el.php
new file mode 100644
index 0000000..16b3f44
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/el.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count χρόνος|:count χρόνια',
+ 'y' => ':count χρόνος|:count χρόνια',
+ 'month' => ':count μήνας|:count μήνες',
+ 'm' => ':count μήνας|:count μήνες',
+ 'week' => ':count εβδομάδα|:count εβδομάδες',
+ 'w' => ':count εβδομάδα|:count εβδομάδες',
+ 'day' => ':count μέρα|:count μέρες',
+ 'd' => ':count μέρα|:count μέρες',
+ 'hour' => ':count ώρα|:count ώρες',
+ 'h' => ':count ώρα|:count ώρες',
+ 'minute' => ':count λεπτό|:count λεπτά',
+ 'min' => ':count λεπτό|:count λεπτά',
+ 'second' => ':count δευτερόλεπτο|:count δευτερόλεπτα',
+ 's' => ':count δευτερόλεπτο|:count δευτερόλεπτα',
+ 'ago' => 'πριν από :time',
+ 'from_now' => 'σε :time από τώρα',
+ 'after' => ':time μετά',
+ 'before' => ':time πριν',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/en.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/en.php
new file mode 100644
index 0000000..a15c131
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/en.php
@@ -0,0 +1,40 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count year|:count years',
+ 'y' => ':countyr|:countyrs',
+ 'month' => ':count month|:count months',
+ 'm' => ':countmo|:countmos',
+ 'week' => ':count week|:count weeks',
+ 'w' => ':countw|:countw',
+ 'day' => ':count day|:count days',
+ 'd' => ':countd|:countd',
+ 'hour' => ':count hour|:count hours',
+ 'h' => ':counth|:counth',
+ 'minute' => ':count minute|:count minutes',
+ 'min' => ':countm|:countm',
+ 'second' => ':count second|:count seconds',
+ 's' => ':counts|:counts',
+ 'ago' => ':time ago',
+ 'from_now' => ':time from now',
+ 'after' => ':time after',
+ 'before' => ':time before',
+ 'diff_now' => 'just now',
+ 'diff_yesterday' => 'yesterday',
+ 'diff_tomorrow' => 'tomorrow',
+ 'diff_before_yesterday' => 'before yesterday',
+ 'diff_after_tomorrow' => 'after tomorrow',
+ 'period_recurrences' => 'once|:count times',
+ 'period_interval' => 'every :interval',
+ 'period_start_date' => 'from :date',
+ 'period_end_date' => 'to :date',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/eo.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/eo.php
new file mode 100644
index 0000000..c5b90b3
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/eo.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count jaro|:count jaroj',
+ 'y' => ':count jaro|:count jaroj',
+ 'month' => ':count monato|:count monatoj',
+ 'm' => ':count monato|:count monatoj',
+ 'week' => ':count semajno|:count semajnoj',
+ 'w' => ':count semajno|:count semajnoj',
+ 'day' => ':count tago|:count tagoj',
+ 'd' => ':count tago|:count tagoj',
+ 'hour' => ':count horo|:count horoj',
+ 'h' => ':count horo|:count horoj',
+ 'minute' => ':count minuto|:count minutoj',
+ 'min' => ':count minuto|:count minutoj',
+ 'second' => ':count sekundo|:count sekundoj',
+ 's' => ':count sekundo|:count sekundoj',
+ 'ago' => 'antaŭ :time',
+ 'from_now' => 'je :time',
+ 'after' => ':time poste',
+ 'before' => ':time antaŭe',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/es.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/es.php
new file mode 100644
index 0000000..567a280
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/es.php
@@ -0,0 +1,36 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count año|:count años',
+ 'y' => ':count año|:count años',
+ 'month' => ':count mes|:count meses',
+ 'm' => ':count mes|:count meses',
+ 'week' => ':count semana|:count semanas',
+ 'w' => ':count semana|:count semanas',
+ 'day' => ':count día|:count días',
+ 'd' => ':count día|:count días',
+ 'hour' => ':count hora|:count horas',
+ 'h' => ':count hora|:count horas',
+ 'minute' => ':count minuto|:count minutos',
+ 'min' => ':count minuto|:count minutos',
+ 'second' => ':count segundo|:count segundos',
+ 's' => ':count segundo|:count segundos',
+ 'ago' => 'hace :time',
+ 'from_now' => 'dentro de :time',
+ 'after' => ':time después',
+ 'before' => ':time antes',
+ 'diff_now' => 'ahora mismo',
+ 'diff_yesterday' => 'ayer',
+ 'diff_tomorrow' => 'mañana',
+ 'diff_before_yesterday' => 'antier',
+ 'diff_after_tomorrow' => 'pasado mañana',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/et.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/et.php
new file mode 100644
index 0000000..2d9291e
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/et.php
@@ -0,0 +1,38 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count aasta|:count aastat',
+ 'y' => ':count aasta|:count aastat',
+ 'month' => ':count kuu|:count kuud',
+ 'm' => ':count kuu|:count kuud',
+ 'week' => ':count nädal|:count nädalat',
+ 'w' => ':count nädal|:count nädalat',
+ 'day' => ':count päev|:count päeva',
+ 'd' => ':count päev|:count päeva',
+ 'hour' => ':count tund|:count tundi',
+ 'h' => ':count tund|:count tundi',
+ 'minute' => ':count minut|:count minutit',
+ 'min' => ':count minut|:count minutit',
+ 'second' => ':count sekund|:count sekundit',
+ 's' => ':count sekund|:count sekundit',
+ 'ago' => ':time tagasi',
+ 'from_now' => ':time pärast',
+ 'after' => ':time pärast',
+ 'before' => ':time enne',
+ 'year_from_now' => ':count aasta',
+ 'month_from_now' => ':count kuu',
+ 'week_from_now' => ':count nädala',
+ 'day_from_now' => ':count päeva',
+ 'hour_from_now' => ':count tunni',
+ 'minute_from_now' => ':count minuti',
+ 'second_from_now' => ':count sekundi',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/eu.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/eu.php
new file mode 100644
index 0000000..1cb6b7c
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/eu.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => 'Urte 1|:count urte',
+ 'y' => 'Urte 1|:count urte',
+ 'month' => 'Hile 1|:count hile',
+ 'm' => 'Hile 1|:count hile',
+ 'week' => 'Aste 1|:count aste',
+ 'w' => 'Aste 1|:count aste',
+ 'day' => 'Egun 1|:count egun',
+ 'd' => 'Egun 1|:count egun',
+ 'hour' => 'Ordu 1|:count ordu',
+ 'h' => 'Ordu 1|:count ordu',
+ 'minute' => 'Minutu 1|:count minutu',
+ 'min' => 'Minutu 1|:count minutu',
+ 'second' => 'Segundu 1|:count segundu',
+ 's' => 'Segundu 1|:count segundu',
+ 'ago' => 'Orain dela :time',
+ 'from_now' => ':time barru',
+ 'after' => ':time geroago',
+ 'before' => ':time lehenago',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/fa.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/fa.php
new file mode 100644
index 0000000..31bec88
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/fa.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count سال',
+ 'y' => ':count سال',
+ 'month' => ':count ماه',
+ 'm' => ':count ماه',
+ 'week' => ':count هفته',
+ 'w' => ':count هفته',
+ 'day' => ':count روز',
+ 'd' => ':count روز',
+ 'hour' => ':count ساعت',
+ 'h' => ':count ساعت',
+ 'minute' => ':count دقیقه',
+ 'min' => ':count دقیقه',
+ 'second' => ':count ثانیه',
+ 's' => ':count ثانیه',
+ 'ago' => ':time پیش',
+ 'from_now' => ':time بعد',
+ 'after' => ':time پس از',
+ 'before' => ':time پیش از',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/fi.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/fi.php
new file mode 100644
index 0000000..4818804
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/fi.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count vuosi|:count vuotta',
+ 'y' => ':count vuosi|:count vuotta',
+ 'month' => ':count kuukausi|:count kuukautta',
+ 'm' => ':count kuukausi|:count kuukautta',
+ 'week' => ':count viikko|:count viikkoa',
+ 'w' => ':count viikko|:count viikkoa',
+ 'day' => ':count päivä|:count päivää',
+ 'd' => ':count päivä|:count päivää',
+ 'hour' => ':count tunti|:count tuntia',
+ 'h' => ':count tunti|:count tuntia',
+ 'minute' => ':count minuutti|:count minuuttia',
+ 'min' => ':count minuutti|:count minuuttia',
+ 'second' => ':count sekunti|:count sekuntia',
+ 's' => ':count sekunti|:count sekuntia',
+ 'ago' => ':time sitten',
+ 'from_now' => ':time tästä hetkestä',
+ 'after' => ':time sen jälkeen',
+ 'before' => ':time ennen',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/fo.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/fo.php
new file mode 100644
index 0000000..d91104b
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/fo.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count ár|:count ár',
+ 'y' => ':count ár|:count ár',
+ 'month' => ':count mánaður|:count mánaðir',
+ 'm' => ':count mánaður|:count mánaðir',
+ 'week' => ':count vika|:count vikur',
+ 'w' => ':count vika|:count vikur',
+ 'day' => ':count dag|:count dagar',
+ 'd' => ':count dag|:count dagar',
+ 'hour' => ':count tími|:count tímar',
+ 'h' => ':count tími|:count tímar',
+ 'minute' => ':count minutt|:count minuttir',
+ 'min' => ':count minutt|:count minuttir',
+ 'second' => ':count sekund|:count sekundir',
+ 's' => ':count sekund|:count sekundir',
+ 'ago' => ':time síðan',
+ 'from_now' => 'um :time',
+ 'after' => ':time aftaná',
+ 'before' => ':time áðrenn',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/fr.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/fr.php
new file mode 100644
index 0000000..0b20cdd
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/fr.php
@@ -0,0 +1,40 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count an|:count ans',
+ 'y' => ':count an|:count ans',
+ 'month' => ':count mois',
+ 'm' => ':count mois',
+ 'week' => ':count semaine|:count semaines',
+ 'w' => ':count sem.',
+ 'day' => ':count jour|:count jours',
+ 'd' => ':count j.',
+ 'hour' => ':count heure|:count heures',
+ 'h' => ':count h.',
+ 'minute' => ':count minute|:count minutes',
+ 'min' => ':count min.',
+ 'second' => ':count seconde|:count secondes',
+ 's' => ':count sec.',
+ 'ago' => 'il y a :time',
+ 'from_now' => 'dans :time',
+ 'after' => ':time après',
+ 'before' => ':time avant',
+ 'diff_now' => "à l'instant",
+ 'diff_yesterday' => 'hier',
+ 'diff_tomorrow' => 'demain',
+ 'diff_before_yesterday' => 'avant-hier',
+ 'diff_after_tomorrow' => 'après-demain',
+ 'period_recurrences' => ':count fois',
+ 'period_interval' => 'tous les :interval',
+ 'period_start_date' => 'de :date',
+ 'period_end_date' => 'à :date',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/gl.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/gl.php
new file mode 100644
index 0000000..cd22a31
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/gl.php
@@ -0,0 +1,24 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count ano|:count anos',
+ 'month' => ':count mes|:count meses',
+ 'week' => ':count semana|:count semanas',
+ 'day' => ':count día|:count días',
+ 'hour' => ':count hora|:count horas',
+ 'minute' => ':count minuto|:count minutos',
+ 'second' => ':count segundo|:count segundos',
+ 'ago' => 'fai :time',
+ 'from_now' => 'dentro de :time',
+ 'after' => ':time despois',
+ 'before' => ':time antes',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/gu.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/gu.php
new file mode 100644
index 0000000..7759dfc
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/gu.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count વર્ષ|:count વર્ષો',
+ 'y' => ':countવર્ષ|:countવર્ષો',
+ 'month' => ':count મહિનો|:count મહિના',
+ 'm' => ':countમહિનો|:countમહિના',
+ 'week' => ':count અઠવાડિયું|:count અઠવાડિયા',
+ 'w' => ':countઅઠ.|:countઅઠ.',
+ 'day' => ':count દિવસ|:count દિવસો',
+ 'd' => ':countદિ.|:countદિ.',
+ 'hour' => ':count કલાક|:count કલાકો',
+ 'h' => ':countક.|:countક.',
+ 'minute' => ':count મિનિટ|:count મિનિટ',
+ 'min' => ':countમિ.|:countમિ.',
+ 'second' => ':count સેકેન્ડ|:count સેકેન્ડ',
+ 's' => ':countસે.|:countસે.',
+ 'ago' => ':time પહેલા',
+ 'from_now' => ':time અત્યારથી',
+ 'after' => ':time પછી',
+ 'before' => ':time પહેલા',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/he.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/he.php
new file mode 100644
index 0000000..2d4f4f8
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/he.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => 'שנה|{2}שנתיים|:count שנים',
+ 'y' => 'שנה|{2}שנתיים|:count שנים',
+ 'month' => 'חודש|{2}חודשיים|:count חודשים',
+ 'm' => 'חודש|{2}חודשיים|:count חודשים',
+ 'week' => 'שבוע|{2}שבועיים|:count שבועות',
+ 'w' => 'שבוע|{2}שבועיים|:count שבועות',
+ 'day' => 'יום|{2}יומיים|:count ימים',
+ 'd' => 'יום|{2}יומיים|:count ימים',
+ 'hour' => 'שעה|{2}שעתיים|:count שעות',
+ 'h' => 'שעה|{2}שעתיים|:count שעות',
+ 'minute' => 'דקה|{2}דקותיים|:count דקות',
+ 'min' => 'דקה|{2}דקותיים|:count דקות',
+ 'second' => 'שניה|:count שניות',
+ 's' => 'שניה|:count שניות',
+ 'ago' => 'לפני :time',
+ 'from_now' => 'בעוד :time',
+ 'after' => 'אחרי :time',
+ 'before' => 'לפני :time',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/hi.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/hi.php
new file mode 100644
index 0000000..6c670ee
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/hi.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '1 वर्ष|:count वर्षों',
+ 'y' => '1 वर्ष|:count वर्षों',
+ 'month' => '1 माह|:count महीने',
+ 'm' => '1 माह|:count महीने',
+ 'week' => '1 सप्ताह|:count सप्ताह',
+ 'w' => '1 सप्ताह|:count सप्ताह',
+ 'day' => '1 दिन|:count दिनों',
+ 'd' => '1 दिन|:count दिनों',
+ 'hour' => '1 घंटा|:count घंटे',
+ 'h' => '1 घंटा|:count घंटे',
+ 'minute' => '1 मिनट|:count मिनटों',
+ 'min' => '1 मिनट|:count मिनटों',
+ 'second' => '1 सेकंड|:count सेकंड',
+ 's' => '1 सेकंड|:count सेकंड',
+ 'ago' => ':time पूर्व',
+ 'from_now' => ':time से',
+ 'after' => ':time के बाद',
+ 'before' => ':time के पहले',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/hr.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/hr.php
new file mode 100644
index 0000000..1a339de
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/hr.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count godinu|:count godine|:count godina',
+ 'y' => ':count godinu|:count godine|:count godina',
+ 'month' => ':count mjesec|:count mjeseca|:count mjeseci',
+ 'm' => ':count mjesec|:count mjeseca|:count mjeseci',
+ 'week' => ':count tjedan|:count tjedna|:count tjedana',
+ 'w' => ':count tjedan|:count tjedna|:count tjedana',
+ 'day' => ':count dan|:count dana|:count dana',
+ 'd' => ':count dan|:count dana|:count dana',
+ 'hour' => ':count sat|:count sata|:count sati',
+ 'h' => ':count sat|:count sata|:count sati',
+ 'minute' => ':count minutu|:count minute |:count minuta',
+ 'min' => ':count minutu|:count minute |:count minuta',
+ 'second' => ':count sekundu|:count sekunde|:count sekundi',
+ 's' => ':count sekundu|:count sekunde|:count sekundi',
+ 'ago' => 'prije :time',
+ 'from_now' => 'za :time',
+ 'after' => 'za :time',
+ 'before' => 'prije :time',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/hu.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/hu.php
new file mode 100644
index 0000000..45daf41
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/hu.php
@@ -0,0 +1,52 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count év',
+ 'y' => ':count év',
+ 'month' => ':count hónap',
+ 'm' => ':count hónap',
+ 'week' => ':count hét',
+ 'w' => ':count hét',
+ 'day' => ':count nap',
+ 'd' => ':count nap',
+ 'hour' => ':count óra',
+ 'h' => ':count óra',
+ 'minute' => ':count perc',
+ 'min' => ':count perc',
+ 'second' => ':count másodperc',
+ 's' => ':count másodperc',
+ 'ago' => ':time',
+ 'from_now' => ':time múlva',
+ 'after' => ':time később',
+ 'before' => ':time korábban',
+ 'year_ago' => ':count éve',
+ 'month_ago' => ':count hónapja',
+ 'week_ago' => ':count hete',
+ 'day_ago' => ':count napja',
+ 'hour_ago' => ':count órája',
+ 'minute_ago' => ':count perce',
+ 'second_ago' => ':count másodperce',
+ 'year_after' => ':count évvel',
+ 'month_after' => ':count hónappal',
+ 'week_after' => ':count héttel',
+ 'day_after' => ':count nappal',
+ 'hour_after' => ':count órával',
+ 'minute_after' => ':count perccel',
+ 'second_after' => ':count másodperccel',
+ 'year_before' => ':count évvel',
+ 'month_before' => ':count hónappal',
+ 'week_before' => ':count héttel',
+ 'day_before' => ':count nappal',
+ 'hour_before' => ':count órával',
+ 'minute_before' => ':count perccel',
+ 'second_before' => ':count másodperccel',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/hy.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/hy.php
new file mode 100644
index 0000000..d2665f2
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/hy.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count տարի',
+ 'y' => ':countտ',
+ 'month' => ':count ամիս',
+ 'm' => ':countամ',
+ 'week' => ':count շաբաթ',
+ 'w' => ':countշ',
+ 'day' => ':count օր',
+ 'd' => ':countօր',
+ 'hour' => ':count ժամ',
+ 'h' => ':countժ',
+ 'minute' => ':count րոպե',
+ 'min' => ':countր',
+ 'second' => ':count վարկյան',
+ 's' => ':countվրկ',
+ 'ago' => ':time առաջ',
+ 'from_now' => ':time ներկա պահից',
+ 'after' => ':time հետո',
+ 'before' => ':time առաջ',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/id.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/id.php
new file mode 100644
index 0000000..7f7114f
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/id.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count tahun',
+ 'y' => ':count tahun',
+ 'month' => ':count bulan',
+ 'm' => ':count bulan',
+ 'week' => ':count minggu',
+ 'w' => ':count minggu',
+ 'day' => ':count hari',
+ 'd' => ':count hari',
+ 'hour' => ':count jam',
+ 'h' => ':count jam',
+ 'minute' => ':count menit',
+ 'min' => ':count menit',
+ 'second' => ':count detik',
+ 's' => ':count detik',
+ 'ago' => ':time yang lalu',
+ 'from_now' => ':time dari sekarang',
+ 'after' => ':time setelah',
+ 'before' => ':time sebelum',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/is.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/is.php
new file mode 100644
index 0000000..94c76a7
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/is.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '1 ár|:count ár',
+ 'y' => '1 ár|:count ár',
+ 'month' => '1 mánuður|:count mánuðir',
+ 'm' => '1 mánuður|:count mánuðir',
+ 'week' => '1 vika|:count vikur',
+ 'w' => '1 vika|:count vikur',
+ 'day' => '1 dagur|:count dagar',
+ 'd' => '1 dagur|:count dagar',
+ 'hour' => '1 klukkutími|:count klukkutímar',
+ 'h' => '1 klukkutími|:count klukkutímar',
+ 'minute' => '1 mínúta|:count mínútur',
+ 'min' => '1 mínúta|:count mínútur',
+ 'second' => '1 sekúnda|:count sekúndur',
+ 's' => '1 sekúnda|:count sekúndur',
+ 'ago' => ':time síðan',
+ 'from_now' => ':time síðan',
+ 'after' => ':time eftir',
+ 'before' => ':time fyrir',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/it.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/it.php
new file mode 100644
index 0000000..70bc6d7
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/it.php
@@ -0,0 +1,36 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count anno|:count anni',
+ 'y' => ':count anno|:count anni',
+ 'month' => ':count mese|:count mesi',
+ 'm' => ':count mese|:count mesi',
+ 'week' => ':count settimana|:count settimane',
+ 'w' => ':count settimana|:count settimane',
+ 'day' => ':count giorno|:count giorni',
+ 'd' => ':count giorno|:count giorni',
+ 'hour' => ':count ora|:count ore',
+ 'h' => ':count ora|:count ore',
+ 'minute' => ':count minuto|:count minuti',
+ 'min' => ':count minuto|:count minuti',
+ 'second' => ':count secondo|:count secondi',
+ 's' => ':count secondo|:count secondi',
+ 'ago' => ':time fa',
+ 'from_now' => 'tra :time',
+ 'after' => ':time dopo',
+ 'before' => ':time prima',
+ 'diff_now' => 'proprio ora',
+ 'diff_yesterday' => 'ieri',
+ 'diff_tomorrow' => 'domani',
+ 'diff_before_yesterday' => "l'altro ieri",
+ 'diff_after_tomorrow' => 'dopodomani',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/ja.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/ja.php
new file mode 100644
index 0000000..7119547
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/ja.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count年',
+ 'y' => ':count年',
+ 'month' => ':countヶ月',
+ 'm' => ':countヶ月',
+ 'week' => ':count週間',
+ 'w' => ':count週間',
+ 'day' => ':count日',
+ 'd' => ':count日',
+ 'hour' => ':count時間',
+ 'h' => ':count時間',
+ 'minute' => ':count分',
+ 'min' => ':count分',
+ 'second' => ':count秒',
+ 's' => ':count秒',
+ 'ago' => ':time前',
+ 'from_now' => '今から:time',
+ 'after' => ':time後',
+ 'before' => ':time前',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/ka.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/ka.php
new file mode 100644
index 0000000..a8dde7e
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/ka.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count წლის',
+ 'y' => ':count წლის',
+ 'month' => ':count თვის',
+ 'm' => ':count თვის',
+ 'week' => ':count კვირის',
+ 'w' => ':count კვირის',
+ 'day' => ':count დღის',
+ 'd' => ':count დღის',
+ 'hour' => ':count საათის',
+ 'h' => ':count საათის',
+ 'minute' => ':count წუთის',
+ 'min' => ':count წუთის',
+ 'second' => ':count წამის',
+ 's' => ':count წამის',
+ 'ago' => ':time უკან',
+ 'from_now' => ':time შემდეგ',
+ 'after' => ':time შემდეგ',
+ 'before' => ':time უკან',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/kk.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/kk.php
new file mode 100644
index 0000000..8d113af
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/kk.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+return array(
+ 'year' => ':count жыл',
+ 'y' => ':count жыл',
+ 'month' => ':count ай',
+ 'm' => ':count ай',
+ 'week' => ':count апта',
+ 'w' => ':count апта',
+ 'day' => ':count күн',
+ 'd' => ':count күн',
+ 'hour' => ':count сағат',
+ 'h' => ':count сағат',
+ 'minute' => ':count минут',
+ 'min' => ':count минут',
+ 'second' => ':count секунд',
+ 's' => ':count секунд',
+ 'ago' => ':time бұрын',
+ 'from_now' => ':time кейін',
+ 'after' => ':time кейін',
+ 'before' => ':time бұрын',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/km.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/km.php
new file mode 100644
index 0000000..a104e06
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/km.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count ឆ្នាំ',
+ 'y' => ':count ឆ្នាំ',
+ 'month' => ':count ខែ',
+ 'm' => ':count ខែ',
+ 'week' => ':count សប្ដាហ៍',
+ 'w' => ':count សប្ដាហ៍',
+ 'day' => ':count ថ្ងៃ',
+ 'd' => ':count ថ្ងៃ',
+ 'hour' => ':count ម៉ោង',
+ 'h' => ':count ម៉ោង',
+ 'minute' => ':count នាទី',
+ 'min' => ':count នាទី',
+ 'second' => ':count វិនាទី',
+ 's' => ':count វិនាទី',
+ 'ago' => ':timeមុន',
+ 'from_now' => ':timeពីឥឡូវ',
+ 'after' => 'នៅក្រោយ :time',
+ 'before' => 'នៅមុន :time',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/ko.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/ko.php
new file mode 100644
index 0000000..0209164
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/ko.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count 년',
+ 'y' => ':count 년',
+ 'month' => ':count 개월',
+ 'm' => ':count 개월',
+ 'week' => ':count 주일',
+ 'w' => ':count 주일',
+ 'day' => ':count 일',
+ 'd' => ':count 일',
+ 'hour' => ':count 시간',
+ 'h' => ':count 시간',
+ 'minute' => ':count 분',
+ 'min' => ':count 분',
+ 'second' => ':count 초',
+ 's' => ':count 초',
+ 'ago' => ':time 전',
+ 'from_now' => ':time 후',
+ 'after' => ':time 이후',
+ 'before' => ':time 이전',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/lt.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/lt.php
new file mode 100644
index 0000000..3f2fd1e
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/lt.php
@@ -0,0 +1,38 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count metus|:count metus|:count metų',
+ 'y' => ':count metus|:count metus|:count metų',
+ 'month' => ':count mėnesį|:count mėnesius|:count mėnesių',
+ 'm' => ':count mėnesį|:count mėnesius|:count mėnesių',
+ 'week' => ':count savaitę|:count savaites|:count savaičių',
+ 'w' => ':count savaitę|:count savaites|:count savaičių',
+ 'day' => ':count dieną|:count dienas|:count dienų',
+ 'd' => ':count dieną|:count dienas|:count dienų',
+ 'hour' => ':count valandą|:count valandas|:count valandų',
+ 'h' => ':count valandą|:count valandas|:count valandų',
+ 'minute' => ':count minutę|:count minutes|:count minučių',
+ 'min' => ':count minutę|:count minutes|:count minučių',
+ 'second' => ':count sekundę|:count sekundes|:count sekundžių',
+ 's' => ':count sekundę|:count sekundes|:count sekundžių',
+ 'second_from_now' => ':count sekundės|:count sekundžių|:count sekundžių',
+ 'minute_from_now' => ':count minutės|:count minučių|:count minučių',
+ 'hour_from_now' => ':count valandos|:count valandų|:count valandų',
+ 'day_from_now' => ':count dienos|:count dienų|:count dienų',
+ 'week_from_now' => ':count savaitės|:count savaičių|:count savaičių',
+ 'month_from_now' => ':count mėnesio|:count mėnesių|:count mėnesių',
+ 'year_from_now' => ':count metų',
+ 'ago' => 'prieš :time',
+ 'from_now' => 'už :time',
+ 'after' => 'po :time',
+ 'before' => ':time nuo dabar',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/lv.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/lv.php
new file mode 100644
index 0000000..363193d
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/lv.php
@@ -0,0 +1,47 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '0 gadiem|:count gada|:count gadiem',
+ 'y' => '0 gadiem|:count gada|:count gadiem',
+ 'month' => '0 mēnešiem|:count mēneša|:count mēnešiem',
+ 'm' => '0 mēnešiem|:count mēneša|:count mēnešiem',
+ 'week' => '0 nedēļām|:count nedēļas|:count nedēļām',
+ 'w' => '0 nedēļām|:count nedēļas|:count nedēļām',
+ 'day' => '0 dienām|:count dienas|:count dienām',
+ 'd' => '0 dienām|:count dienas|:count dienām',
+ 'hour' => '0 stundām|:count stundas|:count stundām',
+ 'h' => '0 stundām|:count stundas|:count stundām',
+ 'minute' => '0 minūtēm|:count minūtes|:count minūtēm',
+ 'min' => '0 minūtēm|:count minūtes|:count minūtēm',
+ 'second' => '0 sekundēm|:count sekundes|:count sekundēm',
+ 's' => '0 sekundēm|:count sekundes|:count sekundēm',
+ 'ago' => 'pirms :time',
+ 'from_now' => 'pēc :time',
+ 'after' => ':time vēlāk',
+ 'before' => ':time pirms',
+
+ 'year_after' => '0 gadus|:count gadu|:count gadus',
+ 'month_after' => '0 mēnešus|:count mēnesi|:count mēnešus',
+ 'week_after' => '0 nedēļas|:count nedēļu|:count nedēļas',
+ 'day_after' => '0 dienas|:count dienu|:count dienas',
+ 'hour_after' => '0 stundas|:count stundu|:count stundas',
+ 'minute_after' => '0 minūtes|:count minūti|:count minūtes',
+ 'second_after' => '0 sekundes|:count sekundi|:count sekundes',
+
+ 'year_before' => '0 gadus|:count gadu|:count gadus',
+ 'month_before' => '0 mēnešus|:count mēnesi|:count mēnešus',
+ 'week_before' => '0 nedēļas|:count nedēļu|:count nedēļas',
+ 'day_before' => '0 dienas|:count dienu|:count dienas',
+ 'hour_before' => '0 stundas|:count stundu|:count stundas',
+ 'minute_before' => '0 minūtes|:count minūti|:count minūtes',
+ 'second_before' => '0 sekundes|:count sekundi|:count sekundes',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/mk.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/mk.php
new file mode 100644
index 0000000..c5ec12d
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/mk.php
@@ -0,0 +1,24 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count година|:count години',
+ 'month' => ':count месец|:count месеци',
+ 'week' => ':count седмица|:count седмици',
+ 'day' => ':count ден|:count дена',
+ 'hour' => ':count час|:count часа',
+ 'minute' => ':count минута|:count минути',
+ 'second' => ':count секунда|:count секунди',
+ 'ago' => 'пред :time',
+ 'from_now' => ':time од сега',
+ 'after' => 'по :time',
+ 'before' => 'пред :time',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/mn.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/mn.php
new file mode 100644
index 0000000..b26dce5
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/mn.php
@@ -0,0 +1,62 @@
+
+ *
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @translator Batmandakh Erdenebileg
+ */
+
+return array(
+ 'year' => ':count жил',
+ 'y' => ':count жил',
+ 'month' => ':count сар',
+ 'm' => ':count сар',
+ 'week' => ':count долоо хоног',
+ 'w' => ':count долоо хоног',
+ 'day' => ':count өдөр',
+ 'd' => ':count өдөр',
+ 'hour' => ':count цаг',
+ 'h' => ':countц',
+ 'minute' => ':count минут',
+ 'min' => ':countм',
+ 'second' => ':count секунд',
+ 's' => ':countс',
+
+ 'ago' => ':timeн өмнө',
+ 'year_ago' => ':count жилий',
+ 'month_ago' => ':count сары',
+ 'day_ago' => ':count хоногий',
+ 'hour_ago' => ':count цагий',
+ 'minute_ago' => ':count минуты',
+ 'second_ago' => ':count секунды',
+
+ 'from_now' => 'одоогоос :time',
+ 'year_from_now' => ':count жилийн дараа',
+ 'month_from_now' => ':count сарын дараа',
+ 'day_from_now' => ':count хоногийн дараа',
+ 'hour_from_now' => ':count цагийн дараа',
+ 'minute_from_now' => ':count минутын дараа',
+ 'second_from_now' => ':count секундын дараа',
+
+ // Does it required to make translation for before, after as follows? hmm, I think we've made it with ago and from now keywords already. Anyway, I've included it just in case of undesired action...
+ 'after' => ':timeн дараа',
+ 'year_after' => ':count жилий',
+ 'month_after' => ':count сары',
+ 'day_after' => ':count хоногий',
+ 'hour_after' => ':count цагий',
+ 'minute_after' => ':count минуты',
+ 'second_after' => ':count секунды',
+ 'before' => ':timeн өмнө',
+ 'year_before' => ':count жилий',
+ 'month_before' => ':count сары',
+ 'day_before' => ':count хоногий',
+ 'hour_before' => ':count цагий',
+ 'minute_before' => ':count минуты',
+ 'second_before' => ':count секунды',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/ms.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/ms.php
new file mode 100644
index 0000000..ef57422
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/ms.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count tahun',
+ 'y' => ':count tahun',
+ 'month' => ':count bulan',
+ 'm' => ':count bulan',
+ 'week' => ':count minggu',
+ 'w' => ':count minggu',
+ 'day' => ':count hari',
+ 'd' => ':count hari',
+ 'hour' => ':count jam',
+ 'h' => ':count jam',
+ 'minute' => ':count minit',
+ 'min' => ':count minit',
+ 'second' => ':count saat',
+ 's' => ':count saat',
+ 'ago' => ':time yang lalu',
+ 'from_now' => ':time dari sekarang',
+ 'after' => ':time selepas',
+ 'before' => ':time sebelum',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/my.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/my.php
new file mode 100644
index 0000000..e8e491e
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/my.php
@@ -0,0 +1,37 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count နှစ်|:count နှစ်',
+ 'y' => ':count နှစ်|:count နှစ်',
+ 'month' => ':count လ|:count လ',
+ 'm' => ':count လ|:count လ',
+ 'week' => ':count ပတ်|:count ပတ်',
+ 'w' => ':count ပတ်|:count ပတ်',
+ 'day' => ':count ရက်|:count ရက်',
+ 'd' => ':count ရက်|:count ရက်',
+ 'hour' => ':count နာရီ|:count နာရီ',
+ 'h' => ':count နာရီ|:count နာရီ',
+ 'minute' => ':count မိနစ်|:count မိနစ်',
+ 'min' => ':count မိနစ်|:count မိနစ်',
+ 'second' => ':count စက္ကန့်|:count စက္ကန့်',
+ 's' => ':count စက္ကန့်|:count စက္ကန့်',
+ 'ago' => 'လွန်ခဲ့သော :time က',
+ 'from_now' => 'ယခုမှစ၍နောက် :time အကြာ',
+ 'after' => ':time ကြာပြီးနောက်',
+ 'before' => ':time မတိုင်ခင်',
+ 'diff_now' => 'အခုလေးတင်',
+ 'diff_yesterday' => 'မနေ့က',
+ 'diff_tomorrow' => 'မနက်ဖြန်',
+ 'diff_before_yesterday' => 'တမြန်နေ့က',
+ 'diff_after_tomorrow' => 'တဘက်ခါ',
+ 'period_recurrences' => ':count ကြိမ်',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/ne.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/ne.php
new file mode 100644
index 0000000..0b528df
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/ne.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count वर्ष',
+ 'y' => ':count वर्ष',
+ 'month' => ':count महिना',
+ 'm' => ':count महिना',
+ 'week' => ':count हप्ता',
+ 'w' => ':count हप्ता',
+ 'day' => ':count दिन',
+ 'd' => ':count दिन',
+ 'hour' => ':count घण्टा',
+ 'h' => ':count घण्टा',
+ 'minute' => ':count मिनेट',
+ 'min' => ':count मिनेट',
+ 'second' => ':count सेकेण्ड',
+ 's' => ':count सेकेण्ड',
+ 'ago' => ':time पहिले',
+ 'from_now' => ':time देखि',
+ 'after' => ':time पछि',
+ 'before' => ':time अघि',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/nl.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/nl.php
new file mode 100644
index 0000000..ec5a88e
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/nl.php
@@ -0,0 +1,36 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count jaar',
+ 'y' => ':count jaar',
+ 'month' => ':count maand|:count maanden',
+ 'm' => ':count maand|:count maanden',
+ 'week' => ':count week|:count weken',
+ 'w' => ':count week|:count weken',
+ 'day' => ':count dag|:count dagen',
+ 'd' => ':count dag|:count dagen',
+ 'hour' => ':count uur',
+ 'h' => ':count uur',
+ 'minute' => ':count minuut|:count minuten',
+ 'min' => ':count minuut|:count minuten',
+ 'second' => ':count seconde|:count seconden',
+ 's' => ':count seconde|:count seconden',
+ 'ago' => ':time geleden',
+ 'from_now' => 'over :time',
+ 'after' => ':time later',
+ 'before' => ':time eerder',
+ 'diff_now' => 'nu',
+ 'diff_yesterday' => 'gisteren',
+ 'diff_tomorrow' => 'morgen',
+ 'diff_after_tomorrow' => 'overmorgen',
+ 'diff_before_yesterday' => 'eergisteren',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/no.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/no.php
new file mode 100644
index 0000000..a6ece06
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/no.php
@@ -0,0 +1,36 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count år|:count år',
+ 'y' => ':count år|:count år',
+ 'month' => ':count måned|:count måneder',
+ 'm' => ':count måned|:count måneder',
+ 'week' => ':count uke|:count uker',
+ 'w' => ':count uke|:count uker',
+ 'day' => ':count dag|:count dager',
+ 'd' => ':count dag|:count dager',
+ 'hour' => ':count time|:count timer',
+ 'h' => ':count time|:count timer',
+ 'minute' => ':count minutt|:count minutter',
+ 'min' => ':count minutt|:count minutter',
+ 'second' => ':count sekund|:count sekunder',
+ 's' => ':count sekund|:count sekunder',
+ 'ago' => ':time siden',
+ 'from_now' => 'om :time',
+ 'after' => ':time etter',
+ 'before' => ':time før',
+ 'diff_now' => 'akkurat nå',
+ 'diff_yesterday' => 'i går',
+ 'diff_tomorrow' => 'i morgen',
+ 'diff_before_yesterday' => 'i forgårs',
+ 'diff_after_tomorrow' => 'i overmorgen',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/oc.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/oc.php
new file mode 100644
index 0000000..e89e94c
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/oc.php
@@ -0,0 +1,44 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+\Symfony\Component\Translation\PluralizationRules::set(function ($number) {
+ return $number == 1 ? 0 : 1;
+}, 'oc');
+
+return array(
+ 'year' => ':count an|:count ans',
+ 'y' => ':count an|:count ans',
+ 'month' => ':count mes|:count meses',
+ 'm' => ':count mes|:count meses',
+ 'week' => ':count setmana|:count setmanas',
+ 'w' => ':count setmana|:count setmanas',
+ 'day' => ':count jorn|:count jorns',
+ 'd' => ':count jorn|:count jorns',
+ 'hour' => ':count ora|:count oras',
+ 'h' => ':count ora|:count oras',
+ 'minute' => ':count minuta|:count minutas',
+ 'min' => ':count minuta|:count minutas',
+ 'second' => ':count segonda|:count segondas',
+ 's' => ':count segonda|:count segondas',
+ 'ago' => 'fa :time',
+ 'from_now' => 'dins :time',
+ 'after' => ':time aprèp',
+ 'before' => ':time abans',
+ 'diff_now' => 'ara meteis',
+ 'diff_yesterday' => 'ièr',
+ 'diff_tomorrow' => 'deman',
+ 'diff_before_yesterday' => 'ièr delà',
+ 'diff_after_tomorrow' => 'deman passat',
+ 'period_recurrences' => ':count còp|:count còps',
+ 'period_interval' => 'cada :interval',
+ 'period_start_date' => 'de :date',
+ 'period_end_date' => 'fins a :date',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/pl.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/pl.php
new file mode 100644
index 0000000..2308af2
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/pl.php
@@ -0,0 +1,36 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count rok|:count lata|:count lat',
+ 'y' => ':countr|:countl',
+ 'month' => ':count miesiąc|:count miesiące|:count miesięcy',
+ 'm' => ':countmies',
+ 'week' => ':count tydzień|:count tygodnie|:count tygodni',
+ 'w' => ':counttyg',
+ 'day' => ':count dzień|:count dni|:count dni',
+ 'd' => ':countd',
+ 'hour' => ':count godzina|:count godziny|:count godzin',
+ 'h' => ':countg',
+ 'minute' => ':count minuta|:count minuty|:count minut',
+ 'min' => ':countm',
+ 'second' => ':count sekunda|:count sekundy|:count sekund',
+ 's' => ':counts',
+ 'ago' => ':time temu',
+ 'from_now' => ':time od teraz',
+ 'after' => ':time po',
+ 'before' => ':time przed',
+ 'diff_now' => 'przed chwilą',
+ 'diff_yesterday' => 'wczoraj',
+ 'diff_tomorrow' => 'jutro',
+ 'diff_before_yesterday' => 'przedwczoraj',
+ 'diff_after_tomorrow' => 'pojutrze',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/ps.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/ps.php
new file mode 100644
index 0000000..15c3296
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/ps.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count کال|:count کاله',
+ 'y' => ':countکال|:countکاله',
+ 'month' => ':count مياشت|:count مياشتي',
+ 'm' => ':countمياشت|:countمياشتي',
+ 'week' => ':count اونۍ|:count اونۍ',
+ 'w' => ':countاونۍ|:countاونۍ',
+ 'day' => ':count ورځ|:count ورځي',
+ 'd' => ':countورځ|:countورځي',
+ 'hour' => ':count ساعت|:count ساعته',
+ 'h' => ':countساعت|:countساعته',
+ 'minute' => ':count دقيقه|:count دقيقې',
+ 'min' => ':countدقيقه|:countدقيقې',
+ 'second' => ':count ثانيه|:count ثانيې',
+ 's' => ':countثانيه|:countثانيې',
+ 'ago' => ':time دمخه',
+ 'from_now' => ':time له اوس څخه',
+ 'after' => ':time وروسته',
+ 'before' => ':time دمخه',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/pt.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/pt.php
new file mode 100644
index 0000000..392b121
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/pt.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count ano|:count anos',
+ 'y' => ':count ano|:count anos',
+ 'month' => ':count mês|:count meses',
+ 'm' => ':count mês|:count meses',
+ 'week' => ':count semana|:count semanas',
+ 'w' => ':count semana|:count semanas',
+ 'day' => ':count dia|:count dias',
+ 'd' => ':count dia|:count dias',
+ 'hour' => ':count hora|:count horas',
+ 'h' => ':count hora|:count horas',
+ 'minute' => ':count minuto|:count minutos',
+ 'min' => ':count minuto|:count minutos',
+ 'second' => ':count segundo|:count segundos',
+ 's' => ':count segundo|:count segundos',
+ 'ago' => ':time atrás',
+ 'from_now' => 'em :time',
+ 'after' => ':time depois',
+ 'before' => ':time antes',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/pt_BR.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/pt_BR.php
new file mode 100644
index 0000000..1f84eac
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/pt_BR.php
@@ -0,0 +1,40 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count ano|:count anos',
+ 'y' => ':counta|:counta',
+ 'month' => ':count mês|:count meses',
+ 'm' => ':countm|:countm',
+ 'week' => ':count semana|:count semanas',
+ 'w' => ':countsem|:countsem',
+ 'day' => ':count dia|:count dias',
+ 'd' => ':countd|:countd',
+ 'hour' => ':count hora|:count horas',
+ 'h' => ':counth|:counth',
+ 'minute' => ':count minuto|:count minutos',
+ 'min' => ':countmin|:countmin',
+ 'second' => ':count segundo|:count segundos',
+ 's' => ':counts|:counts',
+ 'ago' => 'há :time',
+ 'from_now' => 'em :time',
+ 'after' => 'após :time',
+ 'before' => ':time atrás',
+ 'diff_now' => 'agora',
+ 'diff_yesterday' => 'ontem',
+ 'diff_tomorrow' => 'amanhã',
+ 'diff_before_yesterday' => 'anteontem',
+ 'diff_after_tomorrow' => 'depois de amanhã',
+ 'period_recurrences' => 'uma|:count vez',
+ 'period_interval' => 'toda :interval',
+ 'period_start_date' => 'de :date',
+ 'period_end_date' => 'até :date',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/ro.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/ro.php
new file mode 100644
index 0000000..cc16724
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/ro.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => 'un an|:count ani|:count ani',
+ 'y' => 'un an|:count ani|:count ani',
+ 'month' => 'o lună|:count luni|:count luni',
+ 'm' => 'o lună|:count luni|:count luni',
+ 'week' => 'o săptămână|:count săptămâni|:count săptămâni',
+ 'w' => 'o săptămână|:count săptămâni|:count săptămâni',
+ 'day' => 'o zi|:count zile|:count zile',
+ 'd' => 'o zi|:count zile|:count zile',
+ 'hour' => 'o oră|:count ore|:count ore',
+ 'h' => 'o oră|:count ore|:count ore',
+ 'minute' => 'un minut|:count minute|:count minute',
+ 'min' => 'un minut|:count minute|:count minute',
+ 'second' => 'o secundă|:count secunde|:count secunde',
+ 's' => 'o secundă|:count secunde|:count secunde',
+ 'ago' => 'acum :time',
+ 'from_now' => ':time de acum',
+ 'after' => 'peste :time',
+ 'before' => 'acum :time',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/ru.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/ru.php
new file mode 100644
index 0000000..6a83fb1
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/ru.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count год|:count года|:count лет',
+ 'y' => ':count г|:count г|:count л',
+ 'month' => ':count месяц|:count месяца|:count месяцев',
+ 'm' => ':count м|:count м|:count м',
+ 'week' => ':count неделю|:count недели|:count недель',
+ 'w' => ':count н|:count н|:count н',
+ 'day' => ':count день|:count дня|:count дней',
+ 'd' => ':count д|:count д|:count д',
+ 'hour' => ':count час|:count часа|:count часов',
+ 'h' => ':count ч|:count ч|:count ч',
+ 'minute' => ':count минуту|:count минуты|:count минут',
+ 'min' => ':count мин|:count мин|:count мин',
+ 'second' => ':count секунду|:count секунды|:count секунд',
+ 's' => ':count с|:count с|:count с',
+ 'ago' => ':time назад',
+ 'from_now' => 'через :time',
+ 'after' => ':time после',
+ 'before' => ':time до',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/sh.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/sh.php
new file mode 100644
index 0000000..57f287a
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/sh.php
@@ -0,0 +1,35 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+\Symfony\Component\Translation\PluralizationRules::set(function ($number) {
+ return ((1 == $number % 10) && (11 != $number % 100)) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2);
+}, 'sh');
+
+return array(
+ 'year' => ':count godina|:count godine|:count godina',
+ 'y' => ':count godina|:count godine|:count godina',
+ 'month' => ':count mesec|:count meseca|:count meseci',
+ 'm' => ':count mesec|:count meseca|:count meseci',
+ 'week' => ':count nedelja|:count nedelje|:count nedelja',
+ 'w' => ':count nedelja|:count nedelje|:count nedelja',
+ 'day' => ':count dan|:count dana|:count dana',
+ 'd' => ':count dan|:count dana|:count dana',
+ 'hour' => ':count čas|:count časa|:count časova',
+ 'h' => ':count čas|:count časa|:count časova',
+ 'minute' => ':count minut|:count minuta|:count minuta',
+ 'min' => ':count minut|:count minuta|:count minuta',
+ 'second' => ':count sekund|:count sekunda|:count sekundi',
+ 's' => ':count sekund|:count sekunda|:count sekundi',
+ 'ago' => 'pre :time',
+ 'from_now' => 'za :time',
+ 'after' => 'nakon :time',
+ 'before' => ':time raniјe',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/sk.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/sk.php
new file mode 100644
index 0000000..6101344
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/sk.php
@@ -0,0 +1,38 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => 'rok|:count roky|:count rokov',
+ 'y' => 'rok|:count roky|:count rokov',
+ 'month' => 'mesiac|:count mesiace|:count mesiacov',
+ 'm' => 'mesiac|:count mesiace|:count mesiacov',
+ 'week' => 'týždeň|:count týždne|:count týždňov',
+ 'w' => 'týždeň|:count týždne|:count týždňov',
+ 'day' => 'deň|:count dni|:count dní',
+ 'd' => 'deň|:count dni|:count dní',
+ 'hour' => 'hodinu|:count hodiny|:count hodín',
+ 'h' => 'hodinu|:count hodiny|:count hodín',
+ 'minute' => 'minútu|:count minúty|:count minút',
+ 'min' => 'minútu|:count minúty|:count minút',
+ 'second' => 'sekundu|:count sekundy|:count sekúnd',
+ 's' => 'sekundu|:count sekundy|:count sekúnd',
+ 'ago' => 'pred :time',
+ 'from_now' => 'za :time',
+ 'after' => 'o :time neskôr',
+ 'before' => ':time predtým',
+ 'year_ago' => 'rokom|:count rokmi|:count rokmi',
+ 'month_ago' => 'mesiacom|:count mesiacmi|:count mesiacmi',
+ 'week_ago' => 'týždňom|:count týždňami|:count týždňami',
+ 'day_ago' => 'dňom|:count dňami|:count dňami',
+ 'hour_ago' => 'hodinou|:count hodinami|:count hodinami',
+ 'minute_ago' => 'minútou|:count minútami|:count minútami',
+ 'second_ago' => 'sekundou|:count sekundami|:count sekundami',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/sl.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/sl.php
new file mode 100644
index 0000000..06686d1
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/sl.php
@@ -0,0 +1,43 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count leto|:count leti|:count leta|:count let',
+ 'y' => ':count leto|:count leti|:count leta|:count let',
+ 'month' => ':count mesec|:count meseca|:count mesece|:count mesecev',
+ 'm' => ':count mesec|:count meseca|:count mesece|:count mesecev',
+ 'week' => ':count teden|:count tedna|:count tedne|:count tednov',
+ 'w' => ':count teden|:count tedna|:count tedne|:count tednov',
+ 'day' => ':count dan|:count dni|:count dni|:count dni',
+ 'd' => ':count dan|:count dni|:count dni|:count dni',
+ 'hour' => ':count uro|:count uri|:count ure|:count ur',
+ 'h' => ':count uro|:count uri|:count ure|:count ur',
+ 'minute' => ':count minuto|:count minuti|:count minute|:count minut',
+ 'min' => ':count minuto|:count minuti|:count minute|:count minut',
+ 'second' => ':count sekundo|:count sekundi|:count sekunde|:count sekund',
+ 's' => ':count sekundo|:count sekundi|:count sekunde|:count sekund',
+ 'year_ago' => ':count letom|:count leti|:count leti|:count leti',
+ 'month_ago' => ':count mesecem|:count meseci|:count meseci|:count meseci',
+ 'week_ago' => ':count tednom|:count tednoma|:count tedni|:count tedni',
+ 'day_ago' => ':count dnem|:count dnevoma|:count dnevi|:count dnevi',
+ 'hour_ago' => ':count uro|:count urama|:count urami|:count urami',
+ 'minute_ago' => ':count minuto|:count minutama|:count minutami|:count minutami',
+ 'second_ago' => ':count sekundo|:count sekundama|:count sekundami|:count sekundami',
+ 'ago' => 'pred :time',
+ 'from_now' => 'čez :time',
+ 'after' => 'čez :time',
+ 'before' => 'pred :time',
+ 'diff_now' => 'ravnokar',
+ 'diff_yesterday' => 'včeraj',
+ 'diff_tomorrow' => 'jutri',
+ 'diff_before_yesterday' => 'predvčerajšnjim',
+ 'diff_after_tomorrow' => 'pojutrišnjem',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/sq.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/sq.php
new file mode 100644
index 0000000..6e138a0
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/sq.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count vit|:count vjet',
+ 'y' => ':count vit|:count vjet',
+ 'month' => ':count muaj|:count muaj',
+ 'm' => ':count muaj|:count muaj',
+ 'week' => ':count javë|:count javë',
+ 'w' => ':count javë|:count javë',
+ 'day' => ':count ditë|:count ditë',
+ 'd' => ':count ditë|:count ditë',
+ 'hour' => ':count orë|:count orë',
+ 'h' => ':count orë|:count orë',
+ 'minute' => ':count minutë|:count minuta',
+ 'min' => ':count minutë|:count minuta',
+ 'second' => ':count sekondë|:count sekonda',
+ 's' => ':count sekondë|:count sekonda',
+ 'ago' => ':time më parë',
+ 'from_now' => ':time nga tani',
+ 'after' => ':time pas',
+ 'before' => ':time para',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/sr.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/sr.php
new file mode 100644
index 0000000..5a10642
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/sr.php
@@ -0,0 +1,37 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count godina|:count godine|:count godina',
+ 'y' => ':count godina|:count godine|:count godina',
+ 'month' => ':count mesec|:count meseca|:count meseci',
+ 'm' => ':count mesec|:count meseca|:count meseci',
+ 'week' => ':count nedelja|:count nedelje|:count nedelja',
+ 'w' => ':count nedelja|:count nedelje|:count nedelja',
+ 'day' => ':count dan|:count dana|:count dana',
+ 'd' => ':count dan|:count dana|:count dana',
+ 'hour' => ':count sat|:count sata|:count sati',
+ 'h' => ':count sat|:count sata|:count sati',
+ 'minute' => ':count minut|:count minuta |:count minuta',
+ 'min' => ':count minut|:count minuta |:count minuta',
+ 'second' => ':count sekund|:count sekunde|:count sekunde',
+ 's' => ':count sekund|:count sekunde|:count sekunde',
+ 'ago' => 'pre :time',
+ 'from_now' => ':time od sada',
+ 'after' => 'nakon :time',
+ 'before' => 'pre :time',
+
+ 'year_from_now' => '{1,21,31,41,51} :count godinu|{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54} :count godine|[5,Inf[ :count godina',
+ 'year_ago' => '{1,21,31,41,51} :count godinu|{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54} :count godine|[5,Inf[ :count godina',
+
+ 'week_from_now' => '{1} :count nedelju|{2,3,4} :count nedelje|[5,Inf[ :count nedelja',
+ 'week_ago' => '{1} :count nedelju|{2,3,4} :count nedelje|[5,Inf[ :count nedelja',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/sr_Cyrl.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/sr_Cyrl.php
new file mode 100644
index 0000000..2db83ed
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/sr_Cyrl.php
@@ -0,0 +1,43 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54}:count године|[0,Inf[ :count година',
+ 'y' => ':count г.',
+ 'month' => '{1} :count месец|{2,3,4}:count месеца|[5,Inf[ :count месеци',
+ 'm' => ':count м.',
+ 'week' => '{1} :count недеља|{2,3,4}:count недеље|[5,Inf[ :count недеља',
+ 'w' => ':count нед.',
+ 'day' => '{1,21,31} :count дан|[2,Inf[ :count дана',
+ 'd' => ':count д.',
+ 'hour' => '{1,21} :count сат|{2,3,4,22,23,24}:count сата|[5,Inf[ :count сати',
+ 'h' => ':count ч.',
+ 'minute' => '{1,21,31,41,51} :count минут|[2,Inf[ :count минута',
+ 'min' => ':count мин.',
+ 'second' => '{1,21,31,41,51} :count секунд|{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54}:count секунде|[5,Inf[:count секунди',
+ 's' => ':count сек.',
+ 'ago' => 'пре :time',
+ 'from_now' => 'за :time',
+ 'after' => ':time након',
+ 'before' => ':time пре',
+
+ 'year_from_now' => '{1,21,31,41,51} :count годину|{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54} :count године|[5,Inf[ :count година',
+ 'year_ago' => '{1,21,31,41,51} :count годину|{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54} :count године|[5,Inf[ :count година',
+
+ 'week_from_now' => '{1} :count недељу|{2,3,4} :count недеље|[5,Inf[ :count недеља',
+ 'week_ago' => '{1} :count недељу|{2,3,4} :count недеље|[5,Inf[ :count недеља',
+
+ 'diff_now' => 'управо сада',
+ 'diff_yesterday' => 'јуче',
+ 'diff_tomorrow' => 'сутра',
+ 'diff_before_yesterday' => 'прекјуче',
+ 'diff_after_tomorrow' => 'прекосутра',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_ME.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_ME.php
new file mode 100644
index 0000000..18214c4
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_ME.php
@@ -0,0 +1,43 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54}:count године|[0,Inf[ :count година',
+ 'y' => ':count г.',
+ 'month' => '{1} :count мјесец|{2,3,4}:count мјесеца|[5,Inf[ :count мјесеци',
+ 'm' => ':count мј.',
+ 'week' => '{1} :count недјеља|{2,3,4}:count недјеље|[5,Inf[ :count недјеља',
+ 'w' => ':count нед.',
+ 'day' => '{1,21,31} :count дан|[2,Inf[ :count дана',
+ 'd' => ':count д.',
+ 'hour' => '{1,21} :count сат|{2,3,4,22,23,24}:count сата|[5,Inf[ :count сати',
+ 'h' => ':count ч.',
+ 'minute' => '{1,21,31,41,51} :count минут|[2,Inf[ :count минута',
+ 'min' => ':count мин.',
+ 'second' => '{1,21,31,41,51} :count секунд|{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54}:count секунде|[5,Inf[:count секунди',
+ 's' => ':count сек.',
+ 'ago' => 'прије :time',
+ 'from_now' => 'за :time',
+ 'after' => ':time након',
+ 'before' => ':time прије',
+
+ 'year_from_now' => '{1,21,31,41,51} :count годину|{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54} :count године|[5,Inf[ :count година',
+ 'year_ago' => '{1,21,31,41,51} :count годину|{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54} :count године|[5,Inf[ :count година',
+
+ 'week_from_now' => '{1} :count недјељу|{2,3,4} :count недјеље|[5,Inf[ :count недјеља',
+ 'week_ago' => '{1} :count недјељу|{2,3,4} :count недјеље|[5,Inf[ :count недјеља',
+
+ 'diff_now' => 'управо сада',
+ 'diff_yesterday' => 'јуче',
+ 'diff_tomorrow' => 'сутра',
+ 'diff_before_yesterday' => 'прекјуче',
+ 'diff_after_tomorrow' => 'прекосјутра',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/sr_Latn_ME.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/sr_Latn_ME.php
new file mode 100644
index 0000000..2d2e288
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/sr_Latn_ME.php
@@ -0,0 +1,43 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54}:count godine|[0,Inf[ :count godina',
+ 'y' => ':count g.',
+ 'month' => '{1} :count mjesec|{2,3,4}:count mjeseca|[5,Inf[ :count mjeseci',
+ 'm' => ':count mj.',
+ 'week' => '{1} :count nedjelja|{2,3,4}:count nedjelje|[5,Inf[ :count nedjelja',
+ 'w' => ':count ned.',
+ 'day' => '{1,21,31} :count dan|[2,Inf[ :count dana',
+ 'd' => ':count d.',
+ 'hour' => '{1,21} :count sat|{2,3,4,22,23,24}:count sata|[5,Inf[ :count sati',
+ 'h' => ':count č.',
+ 'minute' => '{1,21,31,41,51} :count minut|[2,Inf[ :count minuta',
+ 'min' => ':count min.',
+ 'second' => '{1,21,31,41,51} :count sekund|{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54}:count sekunde|[5,Inf[:count sekundi',
+ 's' => ':count sek.',
+ 'ago' => 'prije :time',
+ 'from_now' => 'za :time',
+ 'after' => ':time nakon',
+ 'before' => ':time prije',
+
+ 'year_from_now' => '{1,21,31,41,51} :count godinu|{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54} :count godine|[5,Inf[ :count godina',
+ 'year_ago' => '{1,21,31,41,51} :count godinu|{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54} :count godine|[5,Inf[ :count godina',
+
+ 'week_from_now' => '{1} :count nedjelju|{2,3,4} :count nedjelje|[5,Inf[ :count nedjelja',
+ 'week_ago' => '{1} :count nedjelju|{2,3,4} :count nedjelje|[5,Inf[ :count nedjelja',
+
+ 'diff_now' => 'upravo sada',
+ 'diff_yesterday' => 'juče',
+ 'diff_tomorrow' => 'sutra',
+ 'diff_before_yesterday' => 'prekjuče',
+ 'diff_after_tomorrow' => 'preksutra',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/sr_ME.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/sr_ME.php
new file mode 100644
index 0000000..7ebf6f0
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/sr_ME.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/sr_Latn_ME.php';
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/sv.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/sv.php
new file mode 100644
index 0000000..89a03b4
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/sv.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count år|:count år',
+ 'y' => ':count år|:count år',
+ 'month' => ':count månad|:count månader',
+ 'm' => ':count månad|:count månader',
+ 'week' => ':count vecka|:count veckor',
+ 'w' => ':count vecka|:count veckor',
+ 'day' => ':count dag|:count dagar',
+ 'd' => ':count dag|:count dagar',
+ 'hour' => ':count timme|:count timmar',
+ 'h' => ':count timme|:count timmar',
+ 'minute' => ':count minut|:count minuter',
+ 'min' => ':count minut|:count minuter',
+ 'second' => ':count sekund|:count sekunder',
+ 's' => ':count sekund|:count sekunder',
+ 'ago' => ':time sedan',
+ 'from_now' => 'om :time',
+ 'after' => ':time efter',
+ 'before' => ':time före',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/sw.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/sw.php
new file mode 100644
index 0000000..52f0342
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/sw.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => 'mwaka 1|miaka :count',
+ 'y' => 'mwaka 1|miaka :count',
+ 'month' => 'mwezi 1|miezi :count',
+ 'm' => 'mwezi 1|miezi :count',
+ 'week' => 'wiki 1|wiki :count',
+ 'w' => 'wiki 1|wiki :count',
+ 'day' => 'siku 1|siku :count',
+ 'd' => 'siku 1|siku :count',
+ 'hour' => 'saa 1|masaa :count',
+ 'h' => 'saa 1|masaa :count',
+ 'minute' => 'dakika 1|dakika :count',
+ 'min' => 'dakika 1|dakika :count',
+ 'second' => 'sekunde 1|sekunde :count',
+ 's' => 'sekunde 1|sekunde :count',
+ 'ago' => ':time ziliyopita',
+ 'from_now' => ':time kwanzia sasa',
+ 'after' => ':time baada',
+ 'before' => ':time kabla',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/th.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/th.php
new file mode 100644
index 0000000..88bb4ac
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/th.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count ปี',
+ 'y' => ':count ปี',
+ 'month' => ':count เดือน',
+ 'm' => ':count เดือน',
+ 'week' => ':count สัปดาห์',
+ 'w' => ':count สัปดาห์',
+ 'day' => ':count วัน',
+ 'd' => ':count วัน',
+ 'hour' => ':count ชั่วโมง',
+ 'h' => ':count ชั่วโมง',
+ 'minute' => ':count นาที',
+ 'min' => ':count นาที',
+ 'second' => ':count วินาที',
+ 's' => ':count วินาที',
+ 'ago' => ':timeที่แล้ว',
+ 'from_now' => ':timeต่อจากนี้',
+ 'after' => ':timeหลังจากนี้',
+ 'before' => ':timeก่อน',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/tr.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/tr.php
new file mode 100644
index 0000000..6a9dfed
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/tr.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count yıl',
+ 'y' => ':count yıl',
+ 'month' => ':count ay',
+ 'm' => ':count ay',
+ 'week' => ':count hafta',
+ 'w' => ':count hafta',
+ 'day' => ':count gün',
+ 'd' => ':count gün',
+ 'hour' => ':count saat',
+ 'h' => ':count saat',
+ 'minute' => ':count dakika',
+ 'min' => ':count dakika',
+ 'second' => ':count saniye',
+ 's' => ':count saniye',
+ 'ago' => ':time önce',
+ 'from_now' => ':time sonra',
+ 'after' => ':time sonra',
+ 'before' => ':time önce',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/uk.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/uk.php
new file mode 100644
index 0000000..8d08eaa
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/uk.php
@@ -0,0 +1,40 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count рік|:count роки|:count років',
+ 'y' => ':count рік|:count роки|:count років',
+ 'month' => ':count місяць|:count місяці|:count місяців',
+ 'm' => ':count місяць|:count місяці|:count місяців',
+ 'week' => ':count тиждень|:count тижні|:count тижнів',
+ 'w' => ':count тиждень|:count тижні|:count тижнів',
+ 'day' => ':count день|:count дні|:count днів',
+ 'd' => ':count день|:count дні|:count днів',
+ 'hour' => ':count година|:count години|:count годин',
+ 'h' => ':count година|:count години|:count годин',
+ 'minute' => ':count хвилину|:count хвилини|:count хвилин',
+ 'min' => ':count хвилину|:count хвилини|:count хвилин',
+ 'second' => ':count секунду|:count секунди|:count секунд',
+ 's' => ':count секунду|:count секунди|:count секунд',
+ 'ago' => ':time тому',
+ 'from_now' => 'через :time',
+ 'after' => ':time після',
+ 'before' => ':time до',
+ 'diff_now' => 'щойно',
+ 'diff_yesterday' => 'вчора',
+ 'diff_tomorrow' => 'завтра',
+ 'diff_before_yesterday' => 'позавчора',
+ 'diff_after_tomorrow' => 'післязавтра',
+ 'period_recurrences' => 'один раз|:count рази|:count разів',
+ 'period_interval' => 'кожні :interval',
+ 'period_start_date' => 'з :date',
+ 'period_end_date' => 'до :date',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/ur.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/ur.php
new file mode 100644
index 0000000..3c5f7ed
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/ur.php
@@ -0,0 +1,24 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count سال',
+ 'month' => ':count ماه',
+ 'week' => ':count ہفتے',
+ 'day' => ':count روز',
+ 'hour' => ':count گھنٹے',
+ 'minute' => ':count منٹ',
+ 'second' => ':count سیکنڈ',
+ 'ago' => ':time پہلے',
+ 'from_now' => ':time بعد',
+ 'after' => ':time بعد',
+ 'before' => ':time پہلے',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/uz.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/uz.php
new file mode 100644
index 0000000..1cb6f71
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/uz.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count yil',
+ 'y' => ':count yil',
+ 'month' => ':count oy',
+ 'm' => ':count oy',
+ 'week' => ':count hafta',
+ 'w' => ':count hafta',
+ 'day' => ':count kun',
+ 'd' => ':count kun',
+ 'hour' => ':count soat',
+ 'h' => ':count soat',
+ 'minute' => ':count daqiqa',
+ 'min' => ':count daq',
+ 'second' => ':count soniya',
+ 's' => ':count s',
+ 'ago' => ':time avval',
+ 'from_now' => ':time dan keyin',
+ 'after' => ':time keyin',
+ 'before' => ':time oldin',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/vi.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/vi.php
new file mode 100644
index 0000000..3f9838d
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/vi.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count năm',
+ 'y' => ':count năm',
+ 'month' => ':count tháng',
+ 'm' => ':count tháng',
+ 'week' => ':count tuần',
+ 'w' => ':count tuần',
+ 'day' => ':count ngày',
+ 'd' => ':count ngày',
+ 'hour' => ':count giờ',
+ 'h' => ':count giờ',
+ 'minute' => ':count phút',
+ 'min' => ':count phút',
+ 'second' => ':count giây',
+ 's' => ':count giây',
+ 'ago' => ':time trước',
+ 'from_now' => ':time từ bây giờ',
+ 'after' => ':time sau',
+ 'before' => ':time trước',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/zh.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/zh.php
new file mode 100644
index 0000000..9e1f6ca
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/zh.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count年',
+ 'y' => ':count年',
+ 'month' => ':count个月',
+ 'm' => ':count个月',
+ 'week' => ':count周',
+ 'w' => ':count周',
+ 'day' => ':count天',
+ 'd' => ':count天',
+ 'hour' => ':count小时',
+ 'h' => ':count小时',
+ 'minute' => ':count分钟',
+ 'min' => ':count分钟',
+ 'second' => ':count秒',
+ 's' => ':count秒',
+ 'ago' => ':time前',
+ 'from_now' => '距现在:time',
+ 'after' => ':time后',
+ 'before' => ':time前',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Lang/zh_TW.php b/_include/calendar/nesbot/carbon/src/Carbon/Lang/zh_TW.php
new file mode 100644
index 0000000..c848723
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Lang/zh_TW.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count年',
+ 'y' => ':count年',
+ 'month' => ':count月',
+ 'm' => ':count月',
+ 'week' => ':count週',
+ 'w' => ':count週',
+ 'day' => ':count天',
+ 'd' => ':count天',
+ 'hour' => ':count小時',
+ 'h' => ':count小時',
+ 'minute' => ':count分鐘',
+ 'min' => ':count分鐘',
+ 'second' => ':count秒',
+ 's' => ':count秒',
+ 'ago' => ':time前',
+ 'from_now' => '距現在:time',
+ 'after' => ':time後',
+ 'before' => ':time前',
+);
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Laravel/ServiceProvider.php b/_include/calendar/nesbot/carbon/src/Carbon/Laravel/ServiceProvider.php
new file mode 100644
index 0000000..4d83b0c
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Laravel/ServiceProvider.php
@@ -0,0 +1,37 @@
+app['events'];
+ if ($events instanceof EventDispatcher || $events instanceof Dispatcher) {
+ $events->listen(class_exists('Illuminate\Foundation\Events\LocaleUpdated') ? 'Illuminate\Foundation\Events\LocaleUpdated' : 'locale.changed', function () use ($service) {
+ $service->updateLocale();
+ });
+ $service->updateLocale();
+ }
+ }
+
+ public function updateLocale()
+ {
+ $translator = $this->app['translator'];
+ if ($translator instanceof Translator || $translator instanceof IlluminateTranslator) {
+ Carbon::setLocale($translator->getLocale());
+ }
+ }
+
+ public function register()
+ {
+ // Needed for Laravel < 5.3 compatibility
+ }
+}
diff --git a/_include/calendar/nesbot/carbon/src/Carbon/Translator.php b/_include/calendar/nesbot/carbon/src/Carbon/Translator.php
new file mode 100644
index 0000000..12115b0
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/Carbon/Translator.php
@@ -0,0 +1,143 @@
+addLoader('array', new Translation\Loader\ArrayLoader());
+ parent::__construct($locale, $formatter, $cacheDir, $debug);
+ }
+
+ /**
+ * Reset messages of a locale (all locale if no locale passed).
+ * Remove custom messages and reload initial messages from matching
+ * file in Lang directory.
+ *
+ * @param string|null $locale
+ *
+ * @return bool
+ */
+ public function resetMessages($locale = null)
+ {
+ if ($locale === null) {
+ static::$messages = array();
+
+ return true;
+ }
+
+ if (file_exists($filename = __DIR__.'/Lang/'.$locale.'.php')) {
+ static::$messages[$locale] = require $filename;
+ $this->addResource('array', static::$messages[$locale], $locale);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Init messages language from matching file in Lang directory.
+ *
+ * @param string $locale
+ *
+ * @return bool
+ */
+ protected function loadMessagesFromFile($locale)
+ {
+ if (isset(static::$messages[$locale])) {
+ return true;
+ }
+
+ return $this->resetMessages($locale);
+ }
+
+ /**
+ * Set messages of a locale and take file first if present.
+ *
+ * @param string $locale
+ * @param array $messages
+ *
+ * @return $this
+ */
+ public function setMessages($locale, $messages)
+ {
+ $this->loadMessagesFromFile($locale);
+ $this->addResource('array', $messages, $locale);
+ static::$messages[$locale] = array_merge(
+ isset(static::$messages[$locale]) ? static::$messages[$locale] : array(),
+ $messages
+ );
+
+ return $this;
+ }
+
+ /**
+ * Get messages of a locale, if none given, return all the
+ * languages.
+ *
+ * @param string|null $locale
+ *
+ * @return array
+ */
+ public function getMessages($locale = null)
+ {
+ return $locale === null ? static::$messages : static::$messages[$locale];
+ }
+
+ /**
+ * Set the current translator locale and indicate if the source locale file exists
+ *
+ * @param string $locale locale ex. en
+ *
+ * @return bool
+ */
+ public function setLocale($locale)
+ {
+ $locale = preg_replace_callback('/[-_]([a-z]{2,})/', function ($matches) {
+ // _2-letters is a region, _3+-letters is a variant
+ return '_'.call_user_func(strlen($matches[1]) > 2 ? 'ucfirst' : 'strtoupper', $matches[1]);
+ }, strtolower($locale));
+
+ if ($this->loadMessagesFromFile($locale)) {
+ parent::setLocale($locale);
+
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/_include/calendar/nesbot/carbon/src/JsonSerializable.php b/_include/calendar/nesbot/carbon/src/JsonSerializable.php
new file mode 100644
index 0000000..d34060b
--- /dev/null
+++ b/_include/calendar/nesbot/carbon/src/JsonSerializable.php
@@ -0,0 +1,18 @@
+json_encode,
+ * which is a value of any type other than a resource.
+ *
+ * @since 5.4.0
+ */
+ public function jsonSerialize();
+ }
+}
diff --git a/_include/calendar/symfony/polyfill-mbstring/LICENSE b/_include/calendar/symfony/polyfill-mbstring/LICENSE
new file mode 100644
index 0000000..24fa32c
--- /dev/null
+++ b/_include/calendar/symfony/polyfill-mbstring/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2015-2018 Fabien Potencier
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/_include/calendar/symfony/polyfill-mbstring/Mbstring.php b/_include/calendar/symfony/polyfill-mbstring/Mbstring.php
new file mode 100644
index 0000000..1f568b4
--- /dev/null
+++ b/_include/calendar/symfony/polyfill-mbstring/Mbstring.php
@@ -0,0 +1,789 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Polyfill\Mbstring;
+
+/**
+ * Partial mbstring implementation in PHP, iconv based, UTF-8 centric.
+ *
+ * Implemented:
+ * - mb_chr - Returns a specific character from its Unicode code point
+ * - mb_convert_encoding - Convert character encoding
+ * - mb_convert_variables - Convert character code in variable(s)
+ * - mb_decode_mimeheader - Decode string in MIME header field
+ * - mb_encode_mimeheader - Encode string for MIME header XXX NATIVE IMPLEMENTATION IS REALLY BUGGED
+ * - mb_decode_numericentity - Decode HTML numeric string reference to character
+ * - mb_encode_numericentity - Encode character to HTML numeric string reference
+ * - mb_convert_case - Perform case folding on a string
+ * - mb_detect_encoding - Detect character encoding
+ * - mb_get_info - Get internal settings of mbstring
+ * - mb_http_input - Detect HTTP input character encoding
+ * - mb_http_output - Set/Get HTTP output character encoding
+ * - mb_internal_encoding - Set/Get internal character encoding
+ * - mb_list_encodings - Returns an array of all supported encodings
+ * - mb_ord - Returns the Unicode code point of a character
+ * - mb_output_handler - Callback function converts character encoding in output buffer
+ * - mb_scrub - Replaces ill-formed byte sequences with substitute characters
+ * - mb_strlen - Get string length
+ * - mb_strpos - Find position of first occurrence of string in a string
+ * - mb_strrpos - Find position of last occurrence of a string in a string
+ * - mb_strtolower - Make a string lowercase
+ * - mb_strtoupper - Make a string uppercase
+ * - mb_substitute_character - Set/Get substitution character
+ * - mb_substr - Get part of string
+ * - mb_stripos - Finds position of first occurrence of a string within another, case insensitive
+ * - mb_stristr - Finds first occurrence of a string within another, case insensitive
+ * - mb_strrchr - Finds the last occurrence of a character in a string within another
+ * - mb_strrichr - Finds the last occurrence of a character in a string within another, case insensitive
+ * - mb_strripos - Finds position of last occurrence of a string within another, case insensitive
+ * - mb_strstr - Finds first occurrence of a string within another
+ * - mb_strwidth - Return width of string
+ * - mb_substr_count - Count the number of substring occurrences
+ *
+ * Not implemented:
+ * - mb_convert_kana - Convert "kana" one from another ("zen-kaku", "han-kaku" and more)
+ * - mb_ereg_* - Regular expression with multibyte support
+ * - mb_parse_str - Parse GET/POST/COOKIE data and set global variable
+ * - mb_preferred_mime_name - Get MIME charset string
+ * - mb_regex_encoding - Returns current encoding for multibyte regex as string
+ * - mb_regex_set_options - Set/Get the default options for mbregex functions
+ * - mb_send_mail - Send encoded mail
+ * - mb_split - Split multibyte string using regular expression
+ * - mb_strcut - Get part of string
+ * - mb_strimwidth - Get truncated string with specified width
+ *
+ * @author Nicolas Grekas
+ *
+ * @internal
+ */
+final class Mbstring
+{
+ const MB_CASE_FOLD = PHP_INT_MAX;
+
+ private static $encodingList = array('ASCII', 'UTF-8');
+ private static $language = 'neutral';
+ private static $internalEncoding = 'UTF-8';
+ private static $caseFold = array(
+ array('µ','ſ',"\xCD\x85",'ς',"\xCF\x90","\xCF\x91","\xCF\x95","\xCF\x96","\xCF\xB0","\xCF\xB1","\xCF\xB5","\xE1\xBA\x9B","\xE1\xBE\xBE"),
+ array('μ','s','ι', 'σ','β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', "\xE1\xB9\xA1",'ι'),
+ );
+
+ public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null)
+ {
+ if (\is_array($fromEncoding) || false !== strpos($fromEncoding, ',')) {
+ $fromEncoding = self::mb_detect_encoding($s, $fromEncoding);
+ } else {
+ $fromEncoding = self::getEncoding($fromEncoding);
+ }
+
+ $toEncoding = self::getEncoding($toEncoding);
+
+ if ('BASE64' === $fromEncoding) {
+ $s = base64_decode($s);
+ $fromEncoding = $toEncoding;
+ }
+
+ if ('BASE64' === $toEncoding) {
+ return base64_encode($s);
+ }
+
+ if ('HTML-ENTITIES' === $toEncoding || 'HTML' === $toEncoding) {
+ if ('HTML-ENTITIES' === $fromEncoding || 'HTML' === $fromEncoding) {
+ $fromEncoding = 'Windows-1252';
+ }
+ if ('UTF-8' !== $fromEncoding) {
+ $s = iconv($fromEncoding, 'UTF-8//IGNORE', $s);
+ }
+
+ return preg_replace_callback('/[\x80-\xFF]+/', array(__CLASS__, 'html_encoding_callback'), $s);
+ }
+
+ if ('HTML-ENTITIES' === $fromEncoding) {
+ $s = html_entity_decode($s, ENT_COMPAT, 'UTF-8');
+ $fromEncoding = 'UTF-8';
+ }
+
+ return iconv($fromEncoding, $toEncoding.'//IGNORE', $s);
+ }
+
+ public static function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null)
+ {
+ $vars = array(&$a, &$b, &$c, &$d, &$e, &$f);
+
+ $ok = true;
+ array_walk_recursive($vars, function (&$v) use (&$ok, $toEncoding, $fromEncoding) {
+ if (false === $v = Mbstring::mb_convert_encoding($v, $toEncoding, $fromEncoding)) {
+ $ok = false;
+ }
+ });
+
+ return $ok ? $fromEncoding : false;
+ }
+
+ public static function mb_decode_mimeheader($s)
+ {
+ return iconv_mime_decode($s, 2, self::$internalEncoding);
+ }
+
+ public static function mb_encode_mimeheader($s, $charset = null, $transferEncoding = null, $linefeed = null, $indent = null)
+ {
+ trigger_error('mb_encode_mimeheader() is bugged. Please use iconv_mime_encode() instead', E_USER_WARNING);
+ }
+
+ public static function mb_decode_numericentity($s, $convmap, $encoding = null)
+ {
+ if (null !== $s && !\is_scalar($s) && !(\is_object($s) && \method_exists($s, '__toString'))) {
+ trigger_error('mb_decode_numericentity() expects parameter 1 to be string, '.gettype($s).' given', E_USER_WARNING);
+ return null;
+ }
+
+ if (!\is_array($convmap) || !$convmap) {
+ return false;
+ }
+
+ if (null !== $encoding && !\is_scalar($encoding)) {
+ trigger_error('mb_decode_numericentity() expects parameter 3 to be string, '.gettype($s).' given', E_USER_WARNING);
+ return ''; // Instead of null (cf. mb_encode_numericentity).
+ }
+
+ $s = (string) $s;
+ if ('' === $s) {
+ return '';
+ }
+
+ $encoding = self::getEncoding($encoding);
+
+ if ('UTF-8' === $encoding) {
+ $encoding = null;
+ if (!preg_match('//u', $s)) {
+ $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s);
+ }
+ } else {
+ $s = iconv($encoding, 'UTF-8//IGNORE', $s);
+ }
+
+ $cnt = floor(\count($convmap) / 4) * 4;
+
+ for ($i = 0; $i < $cnt; $i += 4) {
+ // collector_decode_htmlnumericentity ignores $convmap[$i + 3]
+ $convmap[$i] += $convmap[$i + 2];
+ $convmap[$i + 1] += $convmap[$i + 2];
+ }
+
+ $s = preg_replace_callback('/(?:0*([0-9]+)|x0*([0-9a-fA-F]+))(?!&);?/', function (array $m) use ($cnt, $convmap) {
+ $c = isset($m[2]) ? (int) hexdec($m[2]) : $m[1];
+ for ($i = 0; $i < $cnt; $i += 4) {
+ if ($c >= $convmap[$i] && $c <= $convmap[$i + 1]) {
+ return Mbstring::mb_chr($c - $convmap[$i + 2]);
+ }
+ }
+ return $m[0];
+ }, $s);
+
+ if (null === $encoding) {
+ return $s;
+ }
+
+ return iconv('UTF-8', $encoding.'//IGNORE', $s);
+ }
+
+ public static function mb_encode_numericentity($s, $convmap, $encoding = null, $is_hex = false)
+ {
+ if (null !== $s && !\is_scalar($s) && !(\is_object($s) && \method_exists($s, '__toString'))) {
+ trigger_error('mb_encode_numericentity() expects parameter 1 to be string, '.gettype($s).' given', E_USER_WARNING);
+ return null;
+ }
+
+ if (!\is_array($convmap) || !$convmap) {
+ return false;
+ }
+
+ if (null !== $encoding && !\is_scalar($encoding)) {
+ trigger_error('mb_encode_numericentity() expects parameter 3 to be string, '.gettype($s).' given', E_USER_WARNING);
+ return null; // Instead of '' (cf. mb_decode_numericentity).
+ }
+
+ if (null !== $is_hex && !\is_scalar($is_hex)) {
+ trigger_error('mb_encode_numericentity() expects parameter 4 to be boolean, '.gettype($s).' given', E_USER_WARNING);
+ return null;
+ }
+
+ $s = (string) $s;
+ if ('' === $s) {
+ return '';
+ }
+
+ $encoding = self::getEncoding($encoding);
+
+ if ('UTF-8' === $encoding) {
+ $encoding = null;
+ if (!preg_match('//u', $s)) {
+ $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s);
+ }
+ } else {
+ $s = iconv($encoding, 'UTF-8//IGNORE', $s);
+ }
+
+ static $ulenMask = array("\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4);
+
+ $cnt = floor(\count($convmap) / 4) * 4;
+ $i = 0;
+ $len = \strlen($s);
+ $result = '';
+
+ while ($i < $len) {
+ $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"];
+ $uchr = substr($s, $i, $ulen);
+ $i += $ulen;
+ $c = self::mb_ord($uchr);
+
+ for ($j = 0; $j < $cnt; $j += 4) {
+ if ($c >= $convmap[$j] && $c <= $convmap[$j + 1]) {
+ $cOffset = ($c + $convmap[$j + 2]) & $convmap[$j + 3];
+ $result .= $is_hex ? sprintf('%X;', $cOffset) : ''.$cOffset.';';
+ continue 2;
+ }
+ }
+ $result .= $uchr;
+ }
+
+ if (null === $encoding) {
+ return $result;
+ }
+
+ return iconv('UTF-8', $encoding.'//IGNORE', $result);
+ }
+
+ public static function mb_convert_case($s, $mode, $encoding = null)
+ {
+ $s = (string) $s;
+ if ('' === $s) {
+ return '';
+ }
+
+ $encoding = self::getEncoding($encoding);
+
+ if ('UTF-8' === $encoding) {
+ $encoding = null;
+ if (!preg_match('//u', $s)) {
+ $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s);
+ }
+ } else {
+ $s = iconv($encoding, 'UTF-8//IGNORE', $s);
+ }
+
+ if (MB_CASE_TITLE == $mode) {
+ static $titleRegexp = null;
+ if (null === $titleRegexp) {
+ $titleRegexp = self::getData('titleCaseRegexp');
+ }
+ $s = preg_replace_callback($titleRegexp, array(__CLASS__, 'title_case'), $s);
+ } else {
+ if (MB_CASE_UPPER == $mode) {
+ static $upper = null;
+ if (null === $upper) {
+ $upper = self::getData('upperCase');
+ }
+ $map = $upper;
+ } else {
+ if (self::MB_CASE_FOLD === $mode) {
+ $s = str_replace(self::$caseFold[0], self::$caseFold[1], $s);
+ }
+
+ static $lower = null;
+ if (null === $lower) {
+ $lower = self::getData('lowerCase');
+ }
+ $map = $lower;
+ }
+
+ static $ulenMask = array("\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4);
+
+ $i = 0;
+ $len = \strlen($s);
+
+ while ($i < $len) {
+ $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"];
+ $uchr = substr($s, $i, $ulen);
+ $i += $ulen;
+
+ if (isset($map[$uchr])) {
+ $uchr = $map[$uchr];
+ $nlen = \strlen($uchr);
+
+ if ($nlen == $ulen) {
+ $nlen = $i;
+ do {
+ $s[--$nlen] = $uchr[--$ulen];
+ } while ($ulen);
+ } else {
+ $s = substr_replace($s, $uchr, $i - $ulen, $ulen);
+ $len += $nlen - $ulen;
+ $i += $nlen - $ulen;
+ }
+ }
+ }
+ }
+
+ if (null === $encoding) {
+ return $s;
+ }
+
+ return iconv('UTF-8', $encoding.'//IGNORE', $s);
+ }
+
+ public static function mb_internal_encoding($encoding = null)
+ {
+ if (null === $encoding) {
+ return self::$internalEncoding;
+ }
+
+ $encoding = self::getEncoding($encoding);
+
+ if ('UTF-8' === $encoding || false !== @iconv($encoding, $encoding, ' ')) {
+ self::$internalEncoding = $encoding;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ public static function mb_language($lang = null)
+ {
+ if (null === $lang) {
+ return self::$language;
+ }
+
+ switch ($lang = strtolower($lang)) {
+ case 'uni':
+ case 'neutral':
+ self::$language = $lang;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ public static function mb_list_encodings()
+ {
+ return array('UTF-8');
+ }
+
+ public static function mb_encoding_aliases($encoding)
+ {
+ switch (strtoupper($encoding)) {
+ case 'UTF8':
+ case 'UTF-8':
+ return array('utf8');
+ }
+
+ return false;
+ }
+
+ public static function mb_check_encoding($var = null, $encoding = null)
+ {
+ if (null === $encoding) {
+ if (null === $var) {
+ return false;
+ }
+ $encoding = self::$internalEncoding;
+ }
+
+ return self::mb_detect_encoding($var, array($encoding)) || false !== @iconv($encoding, $encoding, $var);
+ }
+
+ public static function mb_detect_encoding($str, $encodingList = null, $strict = false)
+ {
+ if (null === $encodingList) {
+ $encodingList = self::$encodingList;
+ } else {
+ if (!\is_array($encodingList)) {
+ $encodingList = array_map('trim', explode(',', $encodingList));
+ }
+ $encodingList = array_map('strtoupper', $encodingList);
+ }
+
+ foreach ($encodingList as $enc) {
+ switch ($enc) {
+ case 'ASCII':
+ if (!preg_match('/[\x80-\xFF]/', $str)) {
+ return $enc;
+ }
+ break;
+
+ case 'UTF8':
+ case 'UTF-8':
+ if (preg_match('//u', $str)) {
+ return 'UTF-8';
+ }
+ break;
+
+ default:
+ if (0 === strncmp($enc, 'ISO-8859-', 9)) {
+ return $enc;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public static function mb_detect_order($encodingList = null)
+ {
+ if (null === $encodingList) {
+ return self::$encodingList;
+ }
+
+ if (!\is_array($encodingList)) {
+ $encodingList = array_map('trim', explode(',', $encodingList));
+ }
+ $encodingList = array_map('strtoupper', $encodingList);
+
+ foreach ($encodingList as $enc) {
+ switch ($enc) {
+ default:
+ if (strncmp($enc, 'ISO-8859-', 9)) {
+ return false;
+ }
+ case 'ASCII':
+ case 'UTF8':
+ case 'UTF-8':
+ }
+ }
+
+ self::$encodingList = $encodingList;
+
+ return true;
+ }
+
+ public static function mb_strlen($s, $encoding = null)
+ {
+ $encoding = self::getEncoding($encoding);
+ if ('CP850' === $encoding || 'ASCII' === $encoding) {
+ return \strlen($s);
+ }
+
+ return @iconv_strlen($s, $encoding);
+ }
+
+ public static function mb_strpos($haystack, $needle, $offset = 0, $encoding = null)
+ {
+ $encoding = self::getEncoding($encoding);
+ if ('CP850' === $encoding || 'ASCII' === $encoding) {
+ return strpos($haystack, $needle, $offset);
+ }
+
+ $needle = (string) $needle;
+ if ('' === $needle) {
+ trigger_error(__METHOD__.': Empty delimiter', E_USER_WARNING);
+
+ return false;
+ }
+
+ return iconv_strpos($haystack, $needle, $offset, $encoding);
+ }
+
+ public static function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null)
+ {
+ $encoding = self::getEncoding($encoding);
+ if ('CP850' === $encoding || 'ASCII' === $encoding) {
+ return strrpos($haystack, $needle, $offset);
+ }
+
+ if ($offset != (int) $offset) {
+ $offset = 0;
+ } elseif ($offset = (int) $offset) {
+ if ($offset < 0) {
+ $haystack = self::mb_substr($haystack, 0, $offset, $encoding);
+ $offset = 0;
+ } else {
+ $haystack = self::mb_substr($haystack, $offset, 2147483647, $encoding);
+ }
+ }
+
+ $pos = iconv_strrpos($haystack, $needle, $encoding);
+
+ return false !== $pos ? $offset + $pos : false;
+ }
+
+ public static function mb_strtolower($s, $encoding = null)
+ {
+ return self::mb_convert_case($s, MB_CASE_LOWER, $encoding);
+ }
+
+ public static function mb_strtoupper($s, $encoding = null)
+ {
+ return self::mb_convert_case($s, MB_CASE_UPPER, $encoding);
+ }
+
+ public static function mb_substitute_character($c = null)
+ {
+ if (0 === strcasecmp($c, 'none')) {
+ return true;
+ }
+
+ return null !== $c ? false : 'none';
+ }
+
+ public static function mb_substr($s, $start, $length = null, $encoding = null)
+ {
+ $encoding = self::getEncoding($encoding);
+ if ('CP850' === $encoding || 'ASCII' === $encoding) {
+ return substr($s, $start, null === $length ? 2147483647 : $length);
+ }
+
+ if ($start < 0) {
+ $start = iconv_strlen($s, $encoding) + $start;
+ if ($start < 0) {
+ $start = 0;
+ }
+ }
+
+ if (null === $length) {
+ $length = 2147483647;
+ } elseif ($length < 0) {
+ $length = iconv_strlen($s, $encoding) + $length - $start;
+ if ($length < 0) {
+ return '';
+ }
+ }
+
+ return (string) iconv_substr($s, $start, $length, $encoding);
+ }
+
+ public static function mb_stripos($haystack, $needle, $offset = 0, $encoding = null)
+ {
+ $haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding);
+ $needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding);
+
+ return self::mb_strpos($haystack, $needle, $offset, $encoding);
+ }
+
+ public static function mb_stristr($haystack, $needle, $part = false, $encoding = null)
+ {
+ $pos = self::mb_stripos($haystack, $needle, 0, $encoding);
+
+ return self::getSubpart($pos, $part, $haystack, $encoding);
+ }
+
+ public static function mb_strrchr($haystack, $needle, $part = false, $encoding = null)
+ {
+ $encoding = self::getEncoding($encoding);
+ if ('CP850' === $encoding || 'ASCII' === $encoding) {
+ return strrchr($haystack, $needle, $part);
+ }
+ $needle = self::mb_substr($needle, 0, 1, $encoding);
+ $pos = iconv_strrpos($haystack, $needle, $encoding);
+
+ return self::getSubpart($pos, $part, $haystack, $encoding);
+ }
+
+ public static function mb_strrichr($haystack, $needle, $part = false, $encoding = null)
+ {
+ $needle = self::mb_substr($needle, 0, 1, $encoding);
+ $pos = self::mb_strripos($haystack, $needle, $encoding);
+
+ return self::getSubpart($pos, $part, $haystack, $encoding);
+ }
+
+ public static function mb_strripos($haystack, $needle, $offset = 0, $encoding = null)
+ {
+ $haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding);
+ $needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding);
+
+ return self::mb_strrpos($haystack, $needle, $offset, $encoding);
+ }
+
+ public static function mb_strstr($haystack, $needle, $part = false, $encoding = null)
+ {
+ $pos = strpos($haystack, $needle);
+ if (false === $pos) {
+ return false;
+ }
+ if ($part) {
+ return substr($haystack, 0, $pos);
+ }
+
+ return substr($haystack, $pos);
+ }
+
+ public static function mb_get_info($type = 'all')
+ {
+ $info = array(
+ 'internal_encoding' => self::$internalEncoding,
+ 'http_output' => 'pass',
+ 'http_output_conv_mimetypes' => '^(text/|application/xhtml\+xml)',
+ 'func_overload' => 0,
+ 'func_overload_list' => 'no overload',
+ 'mail_charset' => 'UTF-8',
+ 'mail_header_encoding' => 'BASE64',
+ 'mail_body_encoding' => 'BASE64',
+ 'illegal_chars' => 0,
+ 'encoding_translation' => 'Off',
+ 'language' => self::$language,
+ 'detect_order' => self::$encodingList,
+ 'substitute_character' => 'none',
+ 'strict_detection' => 'Off',
+ );
+
+ if ('all' === $type) {
+ return $info;
+ }
+ if (isset($info[$type])) {
+ return $info[$type];
+ }
+
+ return false;
+ }
+
+ public static function mb_http_input($type = '')
+ {
+ return false;
+ }
+
+ public static function mb_http_output($encoding = null)
+ {
+ return null !== $encoding ? 'pass' === $encoding : 'pass';
+ }
+
+ public static function mb_strwidth($s, $encoding = null)
+ {
+ $encoding = self::getEncoding($encoding);
+
+ if ('UTF-8' !== $encoding) {
+ $s = iconv($encoding, 'UTF-8//IGNORE', $s);
+ }
+
+ $s = preg_replace('/[\x{1100}-\x{115F}\x{2329}\x{232A}\x{2E80}-\x{303E}\x{3040}-\x{A4CF}\x{AC00}-\x{D7A3}\x{F900}-\x{FAFF}\x{FE10}-\x{FE19}\x{FE30}-\x{FE6F}\x{FF00}-\x{FF60}\x{FFE0}-\x{FFE6}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}]/u', '', $s, -1, $wide);
+
+ return ($wide << 1) + iconv_strlen($s, 'UTF-8');
+ }
+
+ public static function mb_substr_count($haystack, $needle, $encoding = null)
+ {
+ return substr_count($haystack, $needle);
+ }
+
+ public static function mb_output_handler($contents, $status)
+ {
+ return $contents;
+ }
+
+ public static function mb_chr($code, $encoding = null)
+ {
+ if (0x80 > $code %= 0x200000) {
+ $s = \chr($code);
+ } elseif (0x800 > $code) {
+ $s = \chr(0xC0 | $code >> 6).\chr(0x80 | $code & 0x3F);
+ } elseif (0x10000 > $code) {
+ $s = \chr(0xE0 | $code >> 12).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F);
+ } else {
+ $s = \chr(0xF0 | $code >> 18).\chr(0x80 | $code >> 12 & 0x3F).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F);
+ }
+
+ if ('UTF-8' !== $encoding = self::getEncoding($encoding)) {
+ $s = mb_convert_encoding($s, $encoding, 'UTF-8');
+ }
+
+ return $s;
+ }
+
+ public static function mb_ord($s, $encoding = null)
+ {
+ if ('UTF-8' !== $encoding = self::getEncoding($encoding)) {
+ $s = mb_convert_encoding($s, 'UTF-8', $encoding);
+ }
+
+ $code = ($s = unpack('C*', substr($s, 0, 4))) ? $s[1] : 0;
+ if (0xF0 <= $code) {
+ return (($code - 0xF0) << 18) + (($s[2] - 0x80) << 12) + (($s[3] - 0x80) << 6) + $s[4] - 0x80;
+ }
+ if (0xE0 <= $code) {
+ return (($code - 0xE0) << 12) + (($s[2] - 0x80) << 6) + $s[3] - 0x80;
+ }
+ if (0xC0 <= $code) {
+ return (($code - 0xC0) << 6) + $s[2] - 0x80;
+ }
+
+ return $code;
+ }
+
+ private static function getSubpart($pos, $part, $haystack, $encoding)
+ {
+ if (false === $pos) {
+ return false;
+ }
+ if ($part) {
+ return self::mb_substr($haystack, 0, $pos, $encoding);
+ }
+
+ return self::mb_substr($haystack, $pos, null, $encoding);
+ }
+
+ private static function html_encoding_callback(array $m)
+ {
+ $i = 1;
+ $entities = '';
+ $m = unpack('C*', htmlentities($m[0], ENT_COMPAT, 'UTF-8'));
+
+ while (isset($m[$i])) {
+ if (0x80 > $m[$i]) {
+ $entities .= \chr($m[$i++]);
+ continue;
+ }
+ if (0xF0 <= $m[$i]) {
+ $c = (($m[$i++] - 0xF0) << 18) + (($m[$i++] - 0x80) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80;
+ } elseif (0xE0 <= $m[$i]) {
+ $c = (($m[$i++] - 0xE0) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80;
+ } else {
+ $c = (($m[$i++] - 0xC0) << 6) + $m[$i++] - 0x80;
+ }
+
+ $entities .= ''.$c.';';
+ }
+
+ return $entities;
+ }
+
+ private static function title_case(array $s)
+ {
+ return self::mb_convert_case($s[1], MB_CASE_UPPER, 'UTF-8').self::mb_convert_case($s[2], MB_CASE_LOWER, 'UTF-8');
+ }
+
+ private static function getData($file)
+ {
+ if (file_exists($file = __DIR__.'/Resources/unidata/'.$file.'.php')) {
+ return require $file;
+ }
+
+ return false;
+ }
+
+ private static function getEncoding($encoding)
+ {
+ if (null === $encoding) {
+ return self::$internalEncoding;
+ }
+
+ $encoding = strtoupper($encoding);
+
+ if ('8BIT' === $encoding || 'BINARY' === $encoding) {
+ return 'CP850';
+ }
+ if ('UTF8' === $encoding) {
+ return 'UTF-8';
+ }
+
+ return $encoding;
+ }
+}
diff --git a/_include/calendar/symfony/polyfill-mbstring/README.md b/_include/calendar/symfony/polyfill-mbstring/README.md
new file mode 100644
index 0000000..342e828
--- /dev/null
+++ b/_include/calendar/symfony/polyfill-mbstring/README.md
@@ -0,0 +1,13 @@
+Symfony Polyfill / Mbstring
+===========================
+
+This component provides a partial, native PHP implementation for the
+[Mbstring](http://php.net/mbstring) extension.
+
+More information can be found in the
+[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md).
+
+License
+=======
+
+This library is released under the [MIT license](LICENSE).
diff --git a/_include/calendar/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php b/_include/calendar/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php
new file mode 100644
index 0000000..3ca1641
--- /dev/null
+++ b/_include/calendar/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php
@@ -0,0 +1,1101 @@
+ 'a',
+ 'B' => 'b',
+ 'C' => 'c',
+ 'D' => 'd',
+ 'E' => 'e',
+ 'F' => 'f',
+ 'G' => 'g',
+ 'H' => 'h',
+ 'I' => 'i',
+ 'J' => 'j',
+ 'K' => 'k',
+ 'L' => 'l',
+ 'M' => 'm',
+ 'N' => 'n',
+ 'O' => 'o',
+ 'P' => 'p',
+ 'Q' => 'q',
+ 'R' => 'r',
+ 'S' => 's',
+ 'T' => 't',
+ 'U' => 'u',
+ 'V' => 'v',
+ 'W' => 'w',
+ 'X' => 'x',
+ 'Y' => 'y',
+ 'Z' => 'z',
+ 'À' => 'à',
+ 'Á' => 'á',
+ 'Â' => 'â',
+ 'Ã' => 'ã',
+ 'Ä' => 'ä',
+ 'Å' => 'å',
+ 'Æ' => 'æ',
+ 'Ç' => 'ç',
+ 'È' => 'è',
+ 'É' => 'é',
+ 'Ê' => 'ê',
+ 'Ë' => 'ë',
+ 'Ì' => 'ì',
+ 'Í' => 'í',
+ 'Î' => 'î',
+ 'Ï' => 'ï',
+ 'Ð' => 'ð',
+ 'Ñ' => 'ñ',
+ 'Ò' => 'ò',
+ 'Ó' => 'ó',
+ 'Ô' => 'ô',
+ 'Õ' => 'õ',
+ 'Ö' => 'ö',
+ 'Ø' => 'ø',
+ 'Ù' => 'ù',
+ 'Ú' => 'ú',
+ 'Û' => 'û',
+ 'Ü' => 'ü',
+ 'Ý' => 'ý',
+ 'Þ' => 'þ',
+ 'Ā' => 'ā',
+ 'Ă' => 'ă',
+ 'Ą' => 'ą',
+ 'Ć' => 'ć',
+ 'Ĉ' => 'ĉ',
+ 'Ċ' => 'ċ',
+ 'Č' => 'č',
+ 'Ď' => 'ď',
+ 'Đ' => 'đ',
+ 'Ē' => 'ē',
+ 'Ĕ' => 'ĕ',
+ 'Ė' => 'ė',
+ 'Ę' => 'ę',
+ 'Ě' => 'ě',
+ 'Ĝ' => 'ĝ',
+ 'Ğ' => 'ğ',
+ 'Ġ' => 'ġ',
+ 'Ģ' => 'ģ',
+ 'Ĥ' => 'ĥ',
+ 'Ħ' => 'ħ',
+ 'Ĩ' => 'ĩ',
+ 'Ī' => 'ī',
+ 'Ĭ' => 'ĭ',
+ 'Į' => 'į',
+ 'İ' => 'i',
+ 'IJ' => 'ij',
+ 'Ĵ' => 'ĵ',
+ 'Ķ' => 'ķ',
+ 'Ĺ' => 'ĺ',
+ 'Ļ' => 'ļ',
+ 'Ľ' => 'ľ',
+ 'Ŀ' => 'ŀ',
+ 'Ł' => 'ł',
+ 'Ń' => 'ń',
+ 'Ņ' => 'ņ',
+ 'Ň' => 'ň',
+ 'Ŋ' => 'ŋ',
+ 'Ō' => 'ō',
+ 'Ŏ' => 'ŏ',
+ 'Ő' => 'ő',
+ 'Œ' => 'œ',
+ 'Ŕ' => 'ŕ',
+ 'Ŗ' => 'ŗ',
+ 'Ř' => 'ř',
+ 'Ś' => 'ś',
+ 'Ŝ' => 'ŝ',
+ 'Ş' => 'ş',
+ 'Š' => 'š',
+ 'Ţ' => 'ţ',
+ 'Ť' => 'ť',
+ 'Ŧ' => 'ŧ',
+ 'Ũ' => 'ũ',
+ 'Ū' => 'ū',
+ 'Ŭ' => 'ŭ',
+ 'Ů' => 'ů',
+ 'Ű' => 'ű',
+ 'Ų' => 'ų',
+ 'Ŵ' => 'ŵ',
+ 'Ŷ' => 'ŷ',
+ 'Ÿ' => 'ÿ',
+ 'Ź' => 'ź',
+ 'Ż' => 'ż',
+ 'Ž' => 'ž',
+ 'Ɓ' => 'ɓ',
+ 'Ƃ' => 'ƃ',
+ 'Ƅ' => 'ƅ',
+ 'Ɔ' => 'ɔ',
+ 'Ƈ' => 'ƈ',
+ 'Ɖ' => 'ɖ',
+ 'Ɗ' => 'ɗ',
+ 'Ƌ' => 'ƌ',
+ 'Ǝ' => 'ǝ',
+ 'Ə' => 'ə',
+ 'Ɛ' => 'ɛ',
+ 'Ƒ' => 'ƒ',
+ 'Ɠ' => 'ɠ',
+ 'Ɣ' => 'ɣ',
+ 'Ɩ' => 'ɩ',
+ 'Ɨ' => 'ɨ',
+ 'Ƙ' => 'ƙ',
+ 'Ɯ' => 'ɯ',
+ 'Ɲ' => 'ɲ',
+ 'Ɵ' => 'ɵ',
+ 'Ơ' => 'ơ',
+ 'Ƣ' => 'ƣ',
+ 'Ƥ' => 'ƥ',
+ 'Ʀ' => 'ʀ',
+ 'Ƨ' => 'ƨ',
+ 'Ʃ' => 'ʃ',
+ 'Ƭ' => 'ƭ',
+ 'Ʈ' => 'ʈ',
+ 'Ư' => 'ư',
+ 'Ʊ' => 'ʊ',
+ 'Ʋ' => 'ʋ',
+ 'Ƴ' => 'ƴ',
+ 'Ƶ' => 'ƶ',
+ 'Ʒ' => 'ʒ',
+ 'Ƹ' => 'ƹ',
+ 'Ƽ' => 'ƽ',
+ 'DŽ' => 'dž',
+ 'Dž' => 'dž',
+ 'LJ' => 'lj',
+ 'Lj' => 'lj',
+ 'NJ' => 'nj',
+ 'Nj' => 'nj',
+ 'Ǎ' => 'ǎ',
+ 'Ǐ' => 'ǐ',
+ 'Ǒ' => 'ǒ',
+ 'Ǔ' => 'ǔ',
+ 'Ǖ' => 'ǖ',
+ 'Ǘ' => 'ǘ',
+ 'Ǚ' => 'ǚ',
+ 'Ǜ' => 'ǜ',
+ 'Ǟ' => 'ǟ',
+ 'Ǡ' => 'ǡ',
+ 'Ǣ' => 'ǣ',
+ 'Ǥ' => 'ǥ',
+ 'Ǧ' => 'ǧ',
+ 'Ǩ' => 'ǩ',
+ 'Ǫ' => 'ǫ',
+ 'Ǭ' => 'ǭ',
+ 'Ǯ' => 'ǯ',
+ 'DZ' => 'dz',
+ 'Dz' => 'dz',
+ 'Ǵ' => 'ǵ',
+ 'Ƕ' => 'ƕ',
+ 'Ƿ' => 'ƿ',
+ 'Ǹ' => 'ǹ',
+ 'Ǻ' => 'ǻ',
+ 'Ǽ' => 'ǽ',
+ 'Ǿ' => 'ǿ',
+ 'Ȁ' => 'ȁ',
+ 'Ȃ' => 'ȃ',
+ 'Ȅ' => 'ȅ',
+ 'Ȇ' => 'ȇ',
+ 'Ȉ' => 'ȉ',
+ 'Ȋ' => 'ȋ',
+ 'Ȍ' => 'ȍ',
+ 'Ȏ' => 'ȏ',
+ 'Ȑ' => 'ȑ',
+ 'Ȓ' => 'ȓ',
+ 'Ȕ' => 'ȕ',
+ 'Ȗ' => 'ȗ',
+ 'Ș' => 'ș',
+ 'Ț' => 'ț',
+ 'Ȝ' => 'ȝ',
+ 'Ȟ' => 'ȟ',
+ 'Ƞ' => 'ƞ',
+ 'Ȣ' => 'ȣ',
+ 'Ȥ' => 'ȥ',
+ 'Ȧ' => 'ȧ',
+ 'Ȩ' => 'ȩ',
+ 'Ȫ' => 'ȫ',
+ 'Ȭ' => 'ȭ',
+ 'Ȯ' => 'ȯ',
+ 'Ȱ' => 'ȱ',
+ 'Ȳ' => 'ȳ',
+ 'Ⱥ' => 'ⱥ',
+ 'Ȼ' => 'ȼ',
+ 'Ƚ' => 'ƚ',
+ 'Ⱦ' => 'ⱦ',
+ 'Ɂ' => 'ɂ',
+ 'Ƀ' => 'ƀ',
+ 'Ʉ' => 'ʉ',
+ 'Ʌ' => 'ʌ',
+ 'Ɇ' => 'ɇ',
+ 'Ɉ' => 'ɉ',
+ 'Ɋ' => 'ɋ',
+ 'Ɍ' => 'ɍ',
+ 'Ɏ' => 'ɏ',
+ 'Ͱ' => 'ͱ',
+ 'Ͳ' => 'ͳ',
+ 'Ͷ' => 'ͷ',
+ 'Ϳ' => 'ϳ',
+ 'Ά' => 'ά',
+ 'Έ' => 'έ',
+ 'Ή' => 'ή',
+ 'Ί' => 'ί',
+ 'Ό' => 'ό',
+ 'Ύ' => 'ύ',
+ 'Ώ' => 'ώ',
+ 'Α' => 'α',
+ 'Β' => 'β',
+ 'Γ' => 'γ',
+ 'Δ' => 'δ',
+ 'Ε' => 'ε',
+ 'Ζ' => 'ζ',
+ 'Η' => 'η',
+ 'Θ' => 'θ',
+ 'Ι' => 'ι',
+ 'Κ' => 'κ',
+ 'Λ' => 'λ',
+ 'Μ' => 'μ',
+ 'Ν' => 'ν',
+ 'Ξ' => 'ξ',
+ 'Ο' => 'ο',
+ 'Π' => 'π',
+ 'Ρ' => 'ρ',
+ 'Σ' => 'σ',
+ 'Τ' => 'τ',
+ 'Υ' => 'υ',
+ 'Φ' => 'φ',
+ 'Χ' => 'χ',
+ 'Ψ' => 'ψ',
+ 'Ω' => 'ω',
+ 'Ϊ' => 'ϊ',
+ 'Ϋ' => 'ϋ',
+ 'Ϗ' => 'ϗ',
+ 'Ϙ' => 'ϙ',
+ 'Ϛ' => 'ϛ',
+ 'Ϝ' => 'ϝ',
+ 'Ϟ' => 'ϟ',
+ 'Ϡ' => 'ϡ',
+ 'Ϣ' => 'ϣ',
+ 'Ϥ' => 'ϥ',
+ 'Ϧ' => 'ϧ',
+ 'Ϩ' => 'ϩ',
+ 'Ϫ' => 'ϫ',
+ 'Ϭ' => 'ϭ',
+ 'Ϯ' => 'ϯ',
+ 'ϴ' => 'θ',
+ 'Ϸ' => 'ϸ',
+ 'Ϲ' => 'ϲ',
+ 'Ϻ' => 'ϻ',
+ 'Ͻ' => 'ͻ',
+ 'Ͼ' => 'ͼ',
+ 'Ͽ' => 'ͽ',
+ 'Ѐ' => 'ѐ',
+ 'Ё' => 'ё',
+ 'Ђ' => 'ђ',
+ 'Ѓ' => 'ѓ',
+ 'Є' => 'є',
+ 'Ѕ' => 'ѕ',
+ 'І' => 'і',
+ 'Ї' => 'ї',
+ 'Ј' => 'ј',
+ 'Љ' => 'љ',
+ 'Њ' => 'њ',
+ 'Ћ' => 'ћ',
+ 'Ќ' => 'ќ',
+ 'Ѝ' => 'ѝ',
+ 'Ў' => 'ў',
+ 'Џ' => 'џ',
+ 'А' => 'а',
+ 'Б' => 'б',
+ 'В' => 'в',
+ 'Г' => 'г',
+ 'Д' => 'д',
+ 'Е' => 'е',
+ 'Ж' => 'ж',
+ 'З' => 'з',
+ 'И' => 'и',
+ 'Й' => 'й',
+ 'К' => 'к',
+ 'Л' => 'л',
+ 'М' => 'м',
+ 'Н' => 'н',
+ 'О' => 'о',
+ 'П' => 'п',
+ 'Р' => 'р',
+ 'С' => 'с',
+ 'Т' => 'т',
+ 'У' => 'у',
+ 'Ф' => 'ф',
+ 'Х' => 'х',
+ 'Ц' => 'ц',
+ 'Ч' => 'ч',
+ 'Ш' => 'ш',
+ 'Щ' => 'щ',
+ 'Ъ' => 'ъ',
+ 'Ы' => 'ы',
+ 'Ь' => 'ь',
+ 'Э' => 'э',
+ 'Ю' => 'ю',
+ 'Я' => 'я',
+ 'Ѡ' => 'ѡ',
+ 'Ѣ' => 'ѣ',
+ 'Ѥ' => 'ѥ',
+ 'Ѧ' => 'ѧ',
+ 'Ѩ' => 'ѩ',
+ 'Ѫ' => 'ѫ',
+ 'Ѭ' => 'ѭ',
+ 'Ѯ' => 'ѯ',
+ 'Ѱ' => 'ѱ',
+ 'Ѳ' => 'ѳ',
+ 'Ѵ' => 'ѵ',
+ 'Ѷ' => 'ѷ',
+ 'Ѹ' => 'ѹ',
+ 'Ѻ' => 'ѻ',
+ 'Ѽ' => 'ѽ',
+ 'Ѿ' => 'ѿ',
+ 'Ҁ' => 'ҁ',
+ 'Ҋ' => 'ҋ',
+ 'Ҍ' => 'ҍ',
+ 'Ҏ' => 'ҏ',
+ 'Ґ' => 'ґ',
+ 'Ғ' => 'ғ',
+ 'Ҕ' => 'ҕ',
+ 'Җ' => 'җ',
+ 'Ҙ' => 'ҙ',
+ 'Қ' => 'қ',
+ 'Ҝ' => 'ҝ',
+ 'Ҟ' => 'ҟ',
+ 'Ҡ' => 'ҡ',
+ 'Ң' => 'ң',
+ 'Ҥ' => 'ҥ',
+ 'Ҧ' => 'ҧ',
+ 'Ҩ' => 'ҩ',
+ 'Ҫ' => 'ҫ',
+ 'Ҭ' => 'ҭ',
+ 'Ү' => 'ү',
+ 'Ұ' => 'ұ',
+ 'Ҳ' => 'ҳ',
+ 'Ҵ' => 'ҵ',
+ 'Ҷ' => 'ҷ',
+ 'Ҹ' => 'ҹ',
+ 'Һ' => 'һ',
+ 'Ҽ' => 'ҽ',
+ 'Ҿ' => 'ҿ',
+ 'Ӏ' => 'ӏ',
+ 'Ӂ' => 'ӂ',
+ 'Ӄ' => 'ӄ',
+ 'Ӆ' => 'ӆ',
+ 'Ӈ' => 'ӈ',
+ 'Ӊ' => 'ӊ',
+ 'Ӌ' => 'ӌ',
+ 'Ӎ' => 'ӎ',
+ 'Ӑ' => 'ӑ',
+ 'Ӓ' => 'ӓ',
+ 'Ӕ' => 'ӕ',
+ 'Ӗ' => 'ӗ',
+ 'Ә' => 'ә',
+ 'Ӛ' => 'ӛ',
+ 'Ӝ' => 'ӝ',
+ 'Ӟ' => 'ӟ',
+ 'Ӡ' => 'ӡ',
+ 'Ӣ' => 'ӣ',
+ 'Ӥ' => 'ӥ',
+ 'Ӧ' => 'ӧ',
+ 'Ө' => 'ө',
+ 'Ӫ' => 'ӫ',
+ 'Ӭ' => 'ӭ',
+ 'Ӯ' => 'ӯ',
+ 'Ӱ' => 'ӱ',
+ 'Ӳ' => 'ӳ',
+ 'Ӵ' => 'ӵ',
+ 'Ӷ' => 'ӷ',
+ 'Ӹ' => 'ӹ',
+ 'Ӻ' => 'ӻ',
+ 'Ӽ' => 'ӽ',
+ 'Ӿ' => 'ӿ',
+ 'Ԁ' => 'ԁ',
+ 'Ԃ' => 'ԃ',
+ 'Ԅ' => 'ԅ',
+ 'Ԇ' => 'ԇ',
+ 'Ԉ' => 'ԉ',
+ 'Ԋ' => 'ԋ',
+ 'Ԍ' => 'ԍ',
+ 'Ԏ' => 'ԏ',
+ 'Ԑ' => 'ԑ',
+ 'Ԓ' => 'ԓ',
+ 'Ԕ' => 'ԕ',
+ 'Ԗ' => 'ԗ',
+ 'Ԙ' => 'ԙ',
+ 'Ԛ' => 'ԛ',
+ 'Ԝ' => 'ԝ',
+ 'Ԟ' => 'ԟ',
+ 'Ԡ' => 'ԡ',
+ 'Ԣ' => 'ԣ',
+ 'Ԥ' => 'ԥ',
+ 'Ԧ' => 'ԧ',
+ 'Ԩ' => 'ԩ',
+ 'Ԫ' => 'ԫ',
+ 'Ԭ' => 'ԭ',
+ 'Ԯ' => 'ԯ',
+ 'Ա' => 'ա',
+ 'Բ' => 'բ',
+ 'Գ' => 'գ',
+ 'Դ' => 'դ',
+ 'Ե' => 'ե',
+ 'Զ' => 'զ',
+ 'Է' => 'է',
+ 'Ը' => 'ը',
+ 'Թ' => 'թ',
+ 'Ժ' => 'ժ',
+ 'Ի' => 'ի',
+ 'Լ' => 'լ',
+ 'Խ' => 'խ',
+ 'Ծ' => 'ծ',
+ 'Կ' => 'կ',
+ 'Հ' => 'հ',
+ 'Ձ' => 'ձ',
+ 'Ղ' => 'ղ',
+ 'Ճ' => 'ճ',
+ 'Մ' => 'մ',
+ 'Յ' => 'յ',
+ 'Ն' => 'ն',
+ 'Շ' => 'շ',
+ 'Ո' => 'ո',
+ 'Չ' => 'չ',
+ 'Պ' => 'պ',
+ 'Ջ' => 'ջ',
+ 'Ռ' => 'ռ',
+ 'Ս' => 'ս',
+ 'Վ' => 'վ',
+ 'Տ' => 'տ',
+ 'Ր' => 'ր',
+ 'Ց' => 'ց',
+ 'Ւ' => 'ւ',
+ 'Փ' => 'փ',
+ 'Ք' => 'ք',
+ 'Օ' => 'օ',
+ 'Ֆ' => 'ֆ',
+ 'Ⴀ' => 'ⴀ',
+ 'Ⴁ' => 'ⴁ',
+ 'Ⴂ' => 'ⴂ',
+ 'Ⴃ' => 'ⴃ',
+ 'Ⴄ' => 'ⴄ',
+ 'Ⴅ' => 'ⴅ',
+ 'Ⴆ' => 'ⴆ',
+ 'Ⴇ' => 'ⴇ',
+ 'Ⴈ' => 'ⴈ',
+ 'Ⴉ' => 'ⴉ',
+ 'Ⴊ' => 'ⴊ',
+ 'Ⴋ' => 'ⴋ',
+ 'Ⴌ' => 'ⴌ',
+ 'Ⴍ' => 'ⴍ',
+ 'Ⴎ' => 'ⴎ',
+ 'Ⴏ' => 'ⴏ',
+ 'Ⴐ' => 'ⴐ',
+ 'Ⴑ' => 'ⴑ',
+ 'Ⴒ' => 'ⴒ',
+ 'Ⴓ' => 'ⴓ',
+ 'Ⴔ' => 'ⴔ',
+ 'Ⴕ' => 'ⴕ',
+ 'Ⴖ' => 'ⴖ',
+ 'Ⴗ' => 'ⴗ',
+ 'Ⴘ' => 'ⴘ',
+ 'Ⴙ' => 'ⴙ',
+ 'Ⴚ' => 'ⴚ',
+ 'Ⴛ' => 'ⴛ',
+ 'Ⴜ' => 'ⴜ',
+ 'Ⴝ' => 'ⴝ',
+ 'Ⴞ' => 'ⴞ',
+ 'Ⴟ' => 'ⴟ',
+ 'Ⴠ' => 'ⴠ',
+ 'Ⴡ' => 'ⴡ',
+ 'Ⴢ' => 'ⴢ',
+ 'Ⴣ' => 'ⴣ',
+ 'Ⴤ' => 'ⴤ',
+ 'Ⴥ' => 'ⴥ',
+ 'Ⴧ' => 'ⴧ',
+ 'Ⴭ' => 'ⴭ',
+ 'Ḁ' => 'ḁ',
+ 'Ḃ' => 'ḃ',
+ 'Ḅ' => 'ḅ',
+ 'Ḇ' => 'ḇ',
+ 'Ḉ' => 'ḉ',
+ 'Ḋ' => 'ḋ',
+ 'Ḍ' => 'ḍ',
+ 'Ḏ' => 'ḏ',
+ 'Ḑ' => 'ḑ',
+ 'Ḓ' => 'ḓ',
+ 'Ḕ' => 'ḕ',
+ 'Ḗ' => 'ḗ',
+ 'Ḙ' => 'ḙ',
+ 'Ḛ' => 'ḛ',
+ 'Ḝ' => 'ḝ',
+ 'Ḟ' => 'ḟ',
+ 'Ḡ' => 'ḡ',
+ 'Ḣ' => 'ḣ',
+ 'Ḥ' => 'ḥ',
+ 'Ḧ' => 'ḧ',
+ 'Ḩ' => 'ḩ',
+ 'Ḫ' => 'ḫ',
+ 'Ḭ' => 'ḭ',
+ 'Ḯ' => 'ḯ',
+ 'Ḱ' => 'ḱ',
+ 'Ḳ' => 'ḳ',
+ 'Ḵ' => 'ḵ',
+ 'Ḷ' => 'ḷ',
+ 'Ḹ' => 'ḹ',
+ 'Ḻ' => 'ḻ',
+ 'Ḽ' => 'ḽ',
+ 'Ḿ' => 'ḿ',
+ 'Ṁ' => 'ṁ',
+ 'Ṃ' => 'ṃ',
+ 'Ṅ' => 'ṅ',
+ 'Ṇ' => 'ṇ',
+ 'Ṉ' => 'ṉ',
+ 'Ṋ' => 'ṋ',
+ 'Ṍ' => 'ṍ',
+ 'Ṏ' => 'ṏ',
+ 'Ṑ' => 'ṑ',
+ 'Ṓ' => 'ṓ',
+ 'Ṕ' => 'ṕ',
+ 'Ṗ' => 'ṗ',
+ 'Ṙ' => 'ṙ',
+ 'Ṛ' => 'ṛ',
+ 'Ṝ' => 'ṝ',
+ 'Ṟ' => 'ṟ',
+ 'Ṡ' => 'ṡ',
+ 'Ṣ' => 'ṣ',
+ 'Ṥ' => 'ṥ',
+ 'Ṧ' => 'ṧ',
+ 'Ṩ' => 'ṩ',
+ 'Ṫ' => 'ṫ',
+ 'Ṭ' => 'ṭ',
+ 'Ṯ' => 'ṯ',
+ 'Ṱ' => 'ṱ',
+ 'Ṳ' => 'ṳ',
+ 'Ṵ' => 'ṵ',
+ 'Ṷ' => 'ṷ',
+ 'Ṹ' => 'ṹ',
+ 'Ṻ' => 'ṻ',
+ 'Ṽ' => 'ṽ',
+ 'Ṿ' => 'ṿ',
+ 'Ẁ' => 'ẁ',
+ 'Ẃ' => 'ẃ',
+ 'Ẅ' => 'ẅ',
+ 'Ẇ' => 'ẇ',
+ 'Ẉ' => 'ẉ',
+ 'Ẋ' => 'ẋ',
+ 'Ẍ' => 'ẍ',
+ 'Ẏ' => 'ẏ',
+ 'Ẑ' => 'ẑ',
+ 'Ẓ' => 'ẓ',
+ 'Ẕ' => 'ẕ',
+ 'ẞ' => 'ß',
+ 'Ạ' => 'ạ',
+ 'Ả' => 'ả',
+ 'Ấ' => 'ấ',
+ 'Ầ' => 'ầ',
+ 'Ẩ' => 'ẩ',
+ 'Ẫ' => 'ẫ',
+ 'Ậ' => 'ậ',
+ 'Ắ' => 'ắ',
+ 'Ằ' => 'ằ',
+ 'Ẳ' => 'ẳ',
+ 'Ẵ' => 'ẵ',
+ 'Ặ' => 'ặ',
+ 'Ẹ' => 'ẹ',
+ 'Ẻ' => 'ẻ',
+ 'Ẽ' => 'ẽ',
+ 'Ế' => 'ế',
+ 'Ề' => 'ề',
+ 'Ể' => 'ể',
+ 'Ễ' => 'ễ',
+ 'Ệ' => 'ệ',
+ 'Ỉ' => 'ỉ',
+ 'Ị' => 'ị',
+ 'Ọ' => 'ọ',
+ 'Ỏ' => 'ỏ',
+ 'Ố' => 'ố',
+ 'Ồ' => 'ồ',
+ 'Ổ' => 'ổ',
+ 'Ỗ' => 'ỗ',
+ 'Ộ' => 'ộ',
+ 'Ớ' => 'ớ',
+ 'Ờ' => 'ờ',
+ 'Ở' => 'ở',
+ 'Ỡ' => 'ỡ',
+ 'Ợ' => 'ợ',
+ 'Ụ' => 'ụ',
+ 'Ủ' => 'ủ',
+ 'Ứ' => 'ứ',
+ 'Ừ' => 'ừ',
+ 'Ử' => 'ử',
+ 'Ữ' => 'ữ',
+ 'Ự' => 'ự',
+ 'Ỳ' => 'ỳ',
+ 'Ỵ' => 'ỵ',
+ 'Ỷ' => 'ỷ',
+ 'Ỹ' => 'ỹ',
+ 'Ỻ' => 'ỻ',
+ 'Ỽ' => 'ỽ',
+ 'Ỿ' => 'ỿ',
+ 'Ἀ' => 'ἀ',
+ 'Ἁ' => 'ἁ',
+ 'Ἂ' => 'ἂ',
+ 'Ἃ' => 'ἃ',
+ 'Ἄ' => 'ἄ',
+ 'Ἅ' => 'ἅ',
+ 'Ἆ' => 'ἆ',
+ 'Ἇ' => 'ἇ',
+ 'Ἐ' => 'ἐ',
+ 'Ἑ' => 'ἑ',
+ 'Ἒ' => 'ἒ',
+ 'Ἓ' => 'ἓ',
+ 'Ἔ' => 'ἔ',
+ 'Ἕ' => 'ἕ',
+ 'Ἠ' => 'ἠ',
+ 'Ἡ' => 'ἡ',
+ 'Ἢ' => 'ἢ',
+ 'Ἣ' => 'ἣ',
+ 'Ἤ' => 'ἤ',
+ 'Ἥ' => 'ἥ',
+ 'Ἦ' => 'ἦ',
+ 'Ἧ' => 'ἧ',
+ 'Ἰ' => 'ἰ',
+ 'Ἱ' => 'ἱ',
+ 'Ἲ' => 'ἲ',
+ 'Ἳ' => 'ἳ',
+ 'Ἴ' => 'ἴ',
+ 'Ἵ' => 'ἵ',
+ 'Ἶ' => 'ἶ',
+ 'Ἷ' => 'ἷ',
+ 'Ὀ' => 'ὀ',
+ 'Ὁ' => 'ὁ',
+ 'Ὂ' => 'ὂ',
+ 'Ὃ' => 'ὃ',
+ 'Ὄ' => 'ὄ',
+ 'Ὅ' => 'ὅ',
+ 'Ὑ' => 'ὑ',
+ 'Ὓ' => 'ὓ',
+ 'Ὕ' => 'ὕ',
+ 'Ὗ' => 'ὗ',
+ 'Ὠ' => 'ὠ',
+ 'Ὡ' => 'ὡ',
+ 'Ὢ' => 'ὢ',
+ 'Ὣ' => 'ὣ',
+ 'Ὤ' => 'ὤ',
+ 'Ὥ' => 'ὥ',
+ 'Ὦ' => 'ὦ',
+ 'Ὧ' => 'ὧ',
+ 'ᾈ' => 'ᾀ',
+ 'ᾉ' => 'ᾁ',
+ 'ᾊ' => 'ᾂ',
+ 'ᾋ' => 'ᾃ',
+ 'ᾌ' => 'ᾄ',
+ 'ᾍ' => 'ᾅ',
+ 'ᾎ' => 'ᾆ',
+ 'ᾏ' => 'ᾇ',
+ 'ᾘ' => 'ᾐ',
+ 'ᾙ' => 'ᾑ',
+ 'ᾚ' => 'ᾒ',
+ 'ᾛ' => 'ᾓ',
+ 'ᾜ' => 'ᾔ',
+ 'ᾝ' => 'ᾕ',
+ 'ᾞ' => 'ᾖ',
+ 'ᾟ' => 'ᾗ',
+ 'ᾨ' => 'ᾠ',
+ 'ᾩ' => 'ᾡ',
+ 'ᾪ' => 'ᾢ',
+ 'ᾫ' => 'ᾣ',
+ 'ᾬ' => 'ᾤ',
+ 'ᾭ' => 'ᾥ',
+ 'ᾮ' => 'ᾦ',
+ 'ᾯ' => 'ᾧ',
+ 'Ᾰ' => 'ᾰ',
+ 'Ᾱ' => 'ᾱ',
+ 'Ὰ' => 'ὰ',
+ 'Ά' => 'ά',
+ 'ᾼ' => 'ᾳ',
+ 'Ὲ' => 'ὲ',
+ 'Έ' => 'έ',
+ 'Ὴ' => 'ὴ',
+ 'Ή' => 'ή',
+ 'ῌ' => 'ῃ',
+ 'Ῐ' => 'ῐ',
+ 'Ῑ' => 'ῑ',
+ 'Ὶ' => 'ὶ',
+ 'Ί' => 'ί',
+ 'Ῠ' => 'ῠ',
+ 'Ῡ' => 'ῡ',
+ 'Ὺ' => 'ὺ',
+ 'Ύ' => 'ύ',
+ 'Ῥ' => 'ῥ',
+ 'Ὸ' => 'ὸ',
+ 'Ό' => 'ό',
+ 'Ὼ' => 'ὼ',
+ 'Ώ' => 'ώ',
+ 'ῼ' => 'ῳ',
+ 'Ω' => 'ω',
+ 'K' => 'k',
+ 'Å' => 'å',
+ 'Ⅎ' => 'ⅎ',
+ 'Ⅰ' => 'ⅰ',
+ 'Ⅱ' => 'ⅱ',
+ 'Ⅲ' => 'ⅲ',
+ 'Ⅳ' => 'ⅳ',
+ 'Ⅴ' => 'ⅴ',
+ 'Ⅵ' => 'ⅵ',
+ 'Ⅶ' => 'ⅶ',
+ 'Ⅷ' => 'ⅷ',
+ 'Ⅸ' => 'ⅸ',
+ 'Ⅹ' => 'ⅹ',
+ 'Ⅺ' => 'ⅺ',
+ 'Ⅻ' => 'ⅻ',
+ 'Ⅼ' => 'ⅼ',
+ 'Ⅽ' => 'ⅽ',
+ 'Ⅾ' => 'ⅾ',
+ 'Ⅿ' => 'ⅿ',
+ 'Ↄ' => 'ↄ',
+ 'Ⓐ' => 'ⓐ',
+ 'Ⓑ' => 'ⓑ',
+ 'Ⓒ' => 'ⓒ',
+ 'Ⓓ' => 'ⓓ',
+ 'Ⓔ' => 'ⓔ',
+ 'Ⓕ' => 'ⓕ',
+ 'Ⓖ' => 'ⓖ',
+ 'Ⓗ' => 'ⓗ',
+ 'Ⓘ' => 'ⓘ',
+ 'Ⓙ' => 'ⓙ',
+ 'Ⓚ' => 'ⓚ',
+ 'Ⓛ' => 'ⓛ',
+ 'Ⓜ' => 'ⓜ',
+ 'Ⓝ' => 'ⓝ',
+ 'Ⓞ' => 'ⓞ',
+ 'Ⓟ' => 'ⓟ',
+ 'Ⓠ' => 'ⓠ',
+ 'Ⓡ' => 'ⓡ',
+ 'Ⓢ' => 'ⓢ',
+ 'Ⓣ' => 'ⓣ',
+ 'Ⓤ' => 'ⓤ',
+ 'Ⓥ' => 'ⓥ',
+ 'Ⓦ' => 'ⓦ',
+ 'Ⓧ' => 'ⓧ',
+ 'Ⓨ' => 'ⓨ',
+ 'Ⓩ' => 'ⓩ',
+ 'Ⰰ' => 'ⰰ',
+ 'Ⰱ' => 'ⰱ',
+ 'Ⰲ' => 'ⰲ',
+ 'Ⰳ' => 'ⰳ',
+ 'Ⰴ' => 'ⰴ',
+ 'Ⰵ' => 'ⰵ',
+ 'Ⰶ' => 'ⰶ',
+ 'Ⰷ' => 'ⰷ',
+ 'Ⰸ' => 'ⰸ',
+ 'Ⰹ' => 'ⰹ',
+ 'Ⰺ' => 'ⰺ',
+ 'Ⰻ' => 'ⰻ',
+ 'Ⰼ' => 'ⰼ',
+ 'Ⰽ' => 'ⰽ',
+ 'Ⰾ' => 'ⰾ',
+ 'Ⰿ' => 'ⰿ',
+ 'Ⱀ' => 'ⱀ',
+ 'Ⱁ' => 'ⱁ',
+ 'Ⱂ' => 'ⱂ',
+ 'Ⱃ' => 'ⱃ',
+ 'Ⱄ' => 'ⱄ',
+ 'Ⱅ' => 'ⱅ',
+ 'Ⱆ' => 'ⱆ',
+ 'Ⱇ' => 'ⱇ',
+ 'Ⱈ' => 'ⱈ',
+ 'Ⱉ' => 'ⱉ',
+ 'Ⱊ' => 'ⱊ',
+ 'Ⱋ' => 'ⱋ',
+ 'Ⱌ' => 'ⱌ',
+ 'Ⱍ' => 'ⱍ',
+ 'Ⱎ' => 'ⱎ',
+ 'Ⱏ' => 'ⱏ',
+ 'Ⱐ' => 'ⱐ',
+ 'Ⱑ' => 'ⱑ',
+ 'Ⱒ' => 'ⱒ',
+ 'Ⱓ' => 'ⱓ',
+ 'Ⱔ' => 'ⱔ',
+ 'Ⱕ' => 'ⱕ',
+ 'Ⱖ' => 'ⱖ',
+ 'Ⱗ' => 'ⱗ',
+ 'Ⱘ' => 'ⱘ',
+ 'Ⱙ' => 'ⱙ',
+ 'Ⱚ' => 'ⱚ',
+ 'Ⱛ' => 'ⱛ',
+ 'Ⱜ' => 'ⱜ',
+ 'Ⱝ' => 'ⱝ',
+ 'Ⱞ' => 'ⱞ',
+ 'Ⱡ' => 'ⱡ',
+ 'Ɫ' => 'ɫ',
+ 'Ᵽ' => 'ᵽ',
+ 'Ɽ' => 'ɽ',
+ 'Ⱨ' => 'ⱨ',
+ 'Ⱪ' => 'ⱪ',
+ 'Ⱬ' => 'ⱬ',
+ 'Ɑ' => 'ɑ',
+ 'Ɱ' => 'ɱ',
+ 'Ɐ' => 'ɐ',
+ 'Ɒ' => 'ɒ',
+ 'Ⱳ' => 'ⱳ',
+ 'Ⱶ' => 'ⱶ',
+ 'Ȿ' => 'ȿ',
+ 'Ɀ' => 'ɀ',
+ 'Ⲁ' => 'ⲁ',
+ 'Ⲃ' => 'ⲃ',
+ 'Ⲅ' => 'ⲅ',
+ 'Ⲇ' => 'ⲇ',
+ 'Ⲉ' => 'ⲉ',
+ 'Ⲋ' => 'ⲋ',
+ 'Ⲍ' => 'ⲍ',
+ 'Ⲏ' => 'ⲏ',
+ 'Ⲑ' => 'ⲑ',
+ 'Ⲓ' => 'ⲓ',
+ 'Ⲕ' => 'ⲕ',
+ 'Ⲗ' => 'ⲗ',
+ 'Ⲙ' => 'ⲙ',
+ 'Ⲛ' => 'ⲛ',
+ 'Ⲝ' => 'ⲝ',
+ 'Ⲟ' => 'ⲟ',
+ 'Ⲡ' => 'ⲡ',
+ 'Ⲣ' => 'ⲣ',
+ 'Ⲥ' => 'ⲥ',
+ 'Ⲧ' => 'ⲧ',
+ 'Ⲩ' => 'ⲩ',
+ 'Ⲫ' => 'ⲫ',
+ 'Ⲭ' => 'ⲭ',
+ 'Ⲯ' => 'ⲯ',
+ 'Ⲱ' => 'ⲱ',
+ 'Ⲳ' => 'ⲳ',
+ 'Ⲵ' => 'ⲵ',
+ 'Ⲷ' => 'ⲷ',
+ 'Ⲹ' => 'ⲹ',
+ 'Ⲻ' => 'ⲻ',
+ 'Ⲽ' => 'ⲽ',
+ 'Ⲿ' => 'ⲿ',
+ 'Ⳁ' => 'ⳁ',
+ 'Ⳃ' => 'ⳃ',
+ 'Ⳅ' => 'ⳅ',
+ 'Ⳇ' => 'ⳇ',
+ 'Ⳉ' => 'ⳉ',
+ 'Ⳋ' => 'ⳋ',
+ 'Ⳍ' => 'ⳍ',
+ 'Ⳏ' => 'ⳏ',
+ 'Ⳑ' => 'ⳑ',
+ 'Ⳓ' => 'ⳓ',
+ 'Ⳕ' => 'ⳕ',
+ 'Ⳗ' => 'ⳗ',
+ 'Ⳙ' => 'ⳙ',
+ 'Ⳛ' => 'ⳛ',
+ 'Ⳝ' => 'ⳝ',
+ 'Ⳟ' => 'ⳟ',
+ 'Ⳡ' => 'ⳡ',
+ 'Ⳣ' => 'ⳣ',
+ 'Ⳬ' => 'ⳬ',
+ 'Ⳮ' => 'ⳮ',
+ 'Ⳳ' => 'ⳳ',
+ 'Ꙁ' => 'ꙁ',
+ 'Ꙃ' => 'ꙃ',
+ 'Ꙅ' => 'ꙅ',
+ 'Ꙇ' => 'ꙇ',
+ 'Ꙉ' => 'ꙉ',
+ 'Ꙋ' => 'ꙋ',
+ 'Ꙍ' => 'ꙍ',
+ 'Ꙏ' => 'ꙏ',
+ 'Ꙑ' => 'ꙑ',
+ 'Ꙓ' => 'ꙓ',
+ 'Ꙕ' => 'ꙕ',
+ 'Ꙗ' => 'ꙗ',
+ 'Ꙙ' => 'ꙙ',
+ 'Ꙛ' => 'ꙛ',
+ 'Ꙝ' => 'ꙝ',
+ 'Ꙟ' => 'ꙟ',
+ 'Ꙡ' => 'ꙡ',
+ 'Ꙣ' => 'ꙣ',
+ 'Ꙥ' => 'ꙥ',
+ 'Ꙧ' => 'ꙧ',
+ 'Ꙩ' => 'ꙩ',
+ 'Ꙫ' => 'ꙫ',
+ 'Ꙭ' => 'ꙭ',
+ 'Ꚁ' => 'ꚁ',
+ 'Ꚃ' => 'ꚃ',
+ 'Ꚅ' => 'ꚅ',
+ 'Ꚇ' => 'ꚇ',
+ 'Ꚉ' => 'ꚉ',
+ 'Ꚋ' => 'ꚋ',
+ 'Ꚍ' => 'ꚍ',
+ 'Ꚏ' => 'ꚏ',
+ 'Ꚑ' => 'ꚑ',
+ 'Ꚓ' => 'ꚓ',
+ 'Ꚕ' => 'ꚕ',
+ 'Ꚗ' => 'ꚗ',
+ 'Ꚙ' => 'ꚙ',
+ 'Ꚛ' => 'ꚛ',
+ 'Ꜣ' => 'ꜣ',
+ 'Ꜥ' => 'ꜥ',
+ 'Ꜧ' => 'ꜧ',
+ 'Ꜩ' => 'ꜩ',
+ 'Ꜫ' => 'ꜫ',
+ 'Ꜭ' => 'ꜭ',
+ 'Ꜯ' => 'ꜯ',
+ 'Ꜳ' => 'ꜳ',
+ 'Ꜵ' => 'ꜵ',
+ 'Ꜷ' => 'ꜷ',
+ 'Ꜹ' => 'ꜹ',
+ 'Ꜻ' => 'ꜻ',
+ 'Ꜽ' => 'ꜽ',
+ 'Ꜿ' => 'ꜿ',
+ 'Ꝁ' => 'ꝁ',
+ 'Ꝃ' => 'ꝃ',
+ 'Ꝅ' => 'ꝅ',
+ 'Ꝇ' => 'ꝇ',
+ 'Ꝉ' => 'ꝉ',
+ 'Ꝋ' => 'ꝋ',
+ 'Ꝍ' => 'ꝍ',
+ 'Ꝏ' => 'ꝏ',
+ 'Ꝑ' => 'ꝑ',
+ 'Ꝓ' => 'ꝓ',
+ 'Ꝕ' => 'ꝕ',
+ 'Ꝗ' => 'ꝗ',
+ 'Ꝙ' => 'ꝙ',
+ 'Ꝛ' => 'ꝛ',
+ 'Ꝝ' => 'ꝝ',
+ 'Ꝟ' => 'ꝟ',
+ 'Ꝡ' => 'ꝡ',
+ 'Ꝣ' => 'ꝣ',
+ 'Ꝥ' => 'ꝥ',
+ 'Ꝧ' => 'ꝧ',
+ 'Ꝩ' => 'ꝩ',
+ 'Ꝫ' => 'ꝫ',
+ 'Ꝭ' => 'ꝭ',
+ 'Ꝯ' => 'ꝯ',
+ 'Ꝺ' => 'ꝺ',
+ 'Ꝼ' => 'ꝼ',
+ 'Ᵹ' => 'ᵹ',
+ 'Ꝿ' => 'ꝿ',
+ 'Ꞁ' => 'ꞁ',
+ 'Ꞃ' => 'ꞃ',
+ 'Ꞅ' => 'ꞅ',
+ 'Ꞇ' => 'ꞇ',
+ 'Ꞌ' => 'ꞌ',
+ 'Ɥ' => 'ɥ',
+ 'Ꞑ' => 'ꞑ',
+ 'Ꞓ' => 'ꞓ',
+ 'Ꞗ' => 'ꞗ',
+ 'Ꞙ' => 'ꞙ',
+ 'Ꞛ' => 'ꞛ',
+ 'Ꞝ' => 'ꞝ',
+ 'Ꞟ' => 'ꞟ',
+ 'Ꞡ' => 'ꞡ',
+ 'Ꞣ' => 'ꞣ',
+ 'Ꞥ' => 'ꞥ',
+ 'Ꞧ' => 'ꞧ',
+ 'Ꞩ' => 'ꞩ',
+ 'Ɦ' => 'ɦ',
+ 'Ɜ' => 'ɜ',
+ 'Ɡ' => 'ɡ',
+ 'Ɬ' => 'ɬ',
+ 'Ʞ' => 'ʞ',
+ 'Ʇ' => 'ʇ',
+ 'A' => 'a',
+ 'B' => 'b',
+ 'C' => 'c',
+ 'D' => 'd',
+ 'E' => 'e',
+ 'F' => 'f',
+ 'G' => 'g',
+ 'H' => 'h',
+ 'I' => 'i',
+ 'J' => 'j',
+ 'K' => 'k',
+ 'L' => 'l',
+ 'M' => 'm',
+ 'N' => 'n',
+ 'O' => 'o',
+ 'P' => 'p',
+ 'Q' => 'q',
+ 'R' => 'r',
+ 'S' => 's',
+ 'T' => 't',
+ 'U' => 'u',
+ 'V' => 'v',
+ 'W' => 'w',
+ 'X' => 'x',
+ 'Y' => 'y',
+ 'Z' => 'z',
+ '𐐀' => '𐐨',
+ '𐐁' => '𐐩',
+ '𐐂' => '𐐪',
+ '𐐃' => '𐐫',
+ '𐐄' => '𐐬',
+ '𐐅' => '𐐭',
+ '𐐆' => '𐐮',
+ '𐐇' => '𐐯',
+ '𐐈' => '𐐰',
+ '𐐉' => '𐐱',
+ '𐐊' => '𐐲',
+ '𐐋' => '𐐳',
+ '𐐌' => '𐐴',
+ '𐐍' => '𐐵',
+ '𐐎' => '𐐶',
+ '𐐏' => '𐐷',
+ '𐐐' => '𐐸',
+ '𐐑' => '𐐹',
+ '𐐒' => '𐐺',
+ '𐐓' => '𐐻',
+ '𐐔' => '𐐼',
+ '𐐕' => '𐐽',
+ '𐐖' => '𐐾',
+ '𐐗' => '𐐿',
+ '𐐘' => '𐑀',
+ '𐐙' => '𐑁',
+ '𐐚' => '𐑂',
+ '𐐛' => '𐑃',
+ '𐐜' => '𐑄',
+ '𐐝' => '𐑅',
+ '𐐞' => '𐑆',
+ '𐐟' => '𐑇',
+ '𐐠' => '𐑈',
+ '𐐡' => '𐑉',
+ '𐐢' => '𐑊',
+ '𐐣' => '𐑋',
+ '𐐤' => '𐑌',
+ '𐐥' => '𐑍',
+ '𐐦' => '𐑎',
+ '𐐧' => '𐑏',
+ '𑢠' => '𑣀',
+ '𑢡' => '𑣁',
+ '𑢢' => '𑣂',
+ '𑢣' => '𑣃',
+ '𑢤' => '𑣄',
+ '𑢥' => '𑣅',
+ '𑢦' => '𑣆',
+ '𑢧' => '𑣇',
+ '𑢨' => '𑣈',
+ '𑢩' => '𑣉',
+ '𑢪' => '𑣊',
+ '𑢫' => '𑣋',
+ '𑢬' => '𑣌',
+ '𑢭' => '𑣍',
+ '𑢮' => '𑣎',
+ '𑢯' => '𑣏',
+ '𑢰' => '𑣐',
+ '𑢱' => '𑣑',
+ '𑢲' => '𑣒',
+ '𑢳' => '𑣓',
+ '𑢴' => '𑣔',
+ '𑢵' => '𑣕',
+ '𑢶' => '𑣖',
+ '𑢷' => '𑣗',
+ '𑢸' => '𑣘',
+ '𑢹' => '𑣙',
+ '𑢺' => '𑣚',
+ '𑢻' => '𑣛',
+ '𑢼' => '𑣜',
+ '𑢽' => '𑣝',
+ '𑢾' => '𑣞',
+ '𑢿' => '𑣟',
+);
+
+$result =& $data;
+unset($data);
+
+return $result;
diff --git a/_include/calendar/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php b/_include/calendar/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php
new file mode 100644
index 0000000..2a8f6e7
--- /dev/null
+++ b/_include/calendar/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php
@@ -0,0 +1,5 @@
+ 'A',
+ 'b' => 'B',
+ 'c' => 'C',
+ 'd' => 'D',
+ 'e' => 'E',
+ 'f' => 'F',
+ 'g' => 'G',
+ 'h' => 'H',
+ 'i' => 'I',
+ 'j' => 'J',
+ 'k' => 'K',
+ 'l' => 'L',
+ 'm' => 'M',
+ 'n' => 'N',
+ 'o' => 'O',
+ 'p' => 'P',
+ 'q' => 'Q',
+ 'r' => 'R',
+ 's' => 'S',
+ 't' => 'T',
+ 'u' => 'U',
+ 'v' => 'V',
+ 'w' => 'W',
+ 'x' => 'X',
+ 'y' => 'Y',
+ 'z' => 'Z',
+ 'µ' => 'Μ',
+ 'à' => 'À',
+ 'á' => 'Á',
+ 'â' => 'Â',
+ 'ã' => 'Ã',
+ 'ä' => 'Ä',
+ 'å' => 'Å',
+ 'æ' => 'Æ',
+ 'ç' => 'Ç',
+ 'è' => 'È',
+ 'é' => 'É',
+ 'ê' => 'Ê',
+ 'ë' => 'Ë',
+ 'ì' => 'Ì',
+ 'í' => 'Í',
+ 'î' => 'Î',
+ 'ï' => 'Ï',
+ 'ð' => 'Ð',
+ 'ñ' => 'Ñ',
+ 'ò' => 'Ò',
+ 'ó' => 'Ó',
+ 'ô' => 'Ô',
+ 'õ' => 'Õ',
+ 'ö' => 'Ö',
+ 'ø' => 'Ø',
+ 'ù' => 'Ù',
+ 'ú' => 'Ú',
+ 'û' => 'Û',
+ 'ü' => 'Ü',
+ 'ý' => 'Ý',
+ 'þ' => 'Þ',
+ 'ÿ' => 'Ÿ',
+ 'ā' => 'Ā',
+ 'ă' => 'Ă',
+ 'ą' => 'Ą',
+ 'ć' => 'Ć',
+ 'ĉ' => 'Ĉ',
+ 'ċ' => 'Ċ',
+ 'č' => 'Č',
+ 'ď' => 'Ď',
+ 'đ' => 'Đ',
+ 'ē' => 'Ē',
+ 'ĕ' => 'Ĕ',
+ 'ė' => 'Ė',
+ 'ę' => 'Ę',
+ 'ě' => 'Ě',
+ 'ĝ' => 'Ĝ',
+ 'ğ' => 'Ğ',
+ 'ġ' => 'Ġ',
+ 'ģ' => 'Ģ',
+ 'ĥ' => 'Ĥ',
+ 'ħ' => 'Ħ',
+ 'ĩ' => 'Ĩ',
+ 'ī' => 'Ī',
+ 'ĭ' => 'Ĭ',
+ 'į' => 'Į',
+ 'ı' => 'I',
+ 'ij' => 'IJ',
+ 'ĵ' => 'Ĵ',
+ 'ķ' => 'Ķ',
+ 'ĺ' => 'Ĺ',
+ 'ļ' => 'Ļ',
+ 'ľ' => 'Ľ',
+ 'ŀ' => 'Ŀ',
+ 'ł' => 'Ł',
+ 'ń' => 'Ń',
+ 'ņ' => 'Ņ',
+ 'ň' => 'Ň',
+ 'ŋ' => 'Ŋ',
+ 'ō' => 'Ō',
+ 'ŏ' => 'Ŏ',
+ 'ő' => 'Ő',
+ 'œ' => 'Œ',
+ 'ŕ' => 'Ŕ',
+ 'ŗ' => 'Ŗ',
+ 'ř' => 'Ř',
+ 'ś' => 'Ś',
+ 'ŝ' => 'Ŝ',
+ 'ş' => 'Ş',
+ 'š' => 'Š',
+ 'ţ' => 'Ţ',
+ 'ť' => 'Ť',
+ 'ŧ' => 'Ŧ',
+ 'ũ' => 'Ũ',
+ 'ū' => 'Ū',
+ 'ŭ' => 'Ŭ',
+ 'ů' => 'Ů',
+ 'ű' => 'Ű',
+ 'ų' => 'Ų',
+ 'ŵ' => 'Ŵ',
+ 'ŷ' => 'Ŷ',
+ 'ź' => 'Ź',
+ 'ż' => 'Ż',
+ 'ž' => 'Ž',
+ 'ſ' => 'S',
+ 'ƀ' => 'Ƀ',
+ 'ƃ' => 'Ƃ',
+ 'ƅ' => 'Ƅ',
+ 'ƈ' => 'Ƈ',
+ 'ƌ' => 'Ƌ',
+ 'ƒ' => 'Ƒ',
+ 'ƕ' => 'Ƕ',
+ 'ƙ' => 'Ƙ',
+ 'ƚ' => 'Ƚ',
+ 'ƞ' => 'Ƞ',
+ 'ơ' => 'Ơ',
+ 'ƣ' => 'Ƣ',
+ 'ƥ' => 'Ƥ',
+ 'ƨ' => 'Ƨ',
+ 'ƭ' => 'Ƭ',
+ 'ư' => 'Ư',
+ 'ƴ' => 'Ƴ',
+ 'ƶ' => 'Ƶ',
+ 'ƹ' => 'Ƹ',
+ 'ƽ' => 'Ƽ',
+ 'ƿ' => 'Ƿ',
+ 'Dž' => 'DŽ',
+ 'dž' => 'DŽ',
+ 'Lj' => 'LJ',
+ 'lj' => 'LJ',
+ 'Nj' => 'NJ',
+ 'nj' => 'NJ',
+ 'ǎ' => 'Ǎ',
+ 'ǐ' => 'Ǐ',
+ 'ǒ' => 'Ǒ',
+ 'ǔ' => 'Ǔ',
+ 'ǖ' => 'Ǖ',
+ 'ǘ' => 'Ǘ',
+ 'ǚ' => 'Ǚ',
+ 'ǜ' => 'Ǜ',
+ 'ǝ' => 'Ǝ',
+ 'ǟ' => 'Ǟ',
+ 'ǡ' => 'Ǡ',
+ 'ǣ' => 'Ǣ',
+ 'ǥ' => 'Ǥ',
+ 'ǧ' => 'Ǧ',
+ 'ǩ' => 'Ǩ',
+ 'ǫ' => 'Ǫ',
+ 'ǭ' => 'Ǭ',
+ 'ǯ' => 'Ǯ',
+ 'Dz' => 'DZ',
+ 'dz' => 'DZ',
+ 'ǵ' => 'Ǵ',
+ 'ǹ' => 'Ǹ',
+ 'ǻ' => 'Ǻ',
+ 'ǽ' => 'Ǽ',
+ 'ǿ' => 'Ǿ',
+ 'ȁ' => 'Ȁ',
+ 'ȃ' => 'Ȃ',
+ 'ȅ' => 'Ȅ',
+ 'ȇ' => 'Ȇ',
+ 'ȉ' => 'Ȉ',
+ 'ȋ' => 'Ȋ',
+ 'ȍ' => 'Ȍ',
+ 'ȏ' => 'Ȏ',
+ 'ȑ' => 'Ȑ',
+ 'ȓ' => 'Ȓ',
+ 'ȕ' => 'Ȕ',
+ 'ȗ' => 'Ȗ',
+ 'ș' => 'Ș',
+ 'ț' => 'Ț',
+ 'ȝ' => 'Ȝ',
+ 'ȟ' => 'Ȟ',
+ 'ȣ' => 'Ȣ',
+ 'ȥ' => 'Ȥ',
+ 'ȧ' => 'Ȧ',
+ 'ȩ' => 'Ȩ',
+ 'ȫ' => 'Ȫ',
+ 'ȭ' => 'Ȭ',
+ 'ȯ' => 'Ȯ',
+ 'ȱ' => 'Ȱ',
+ 'ȳ' => 'Ȳ',
+ 'ȼ' => 'Ȼ',
+ 'ȿ' => 'Ȿ',
+ 'ɀ' => 'Ɀ',
+ 'ɂ' => 'Ɂ',
+ 'ɇ' => 'Ɇ',
+ 'ɉ' => 'Ɉ',
+ 'ɋ' => 'Ɋ',
+ 'ɍ' => 'Ɍ',
+ 'ɏ' => 'Ɏ',
+ 'ɐ' => 'Ɐ',
+ 'ɑ' => 'Ɑ',
+ 'ɒ' => 'Ɒ',
+ 'ɓ' => 'Ɓ',
+ 'ɔ' => 'Ɔ',
+ 'ɖ' => 'Ɖ',
+ 'ɗ' => 'Ɗ',
+ 'ə' => 'Ə',
+ 'ɛ' => 'Ɛ',
+ 'ɜ' => 'Ɜ',
+ 'ɠ' => 'Ɠ',
+ 'ɡ' => 'Ɡ',
+ 'ɣ' => 'Ɣ',
+ 'ɥ' => 'Ɥ',
+ 'ɦ' => 'Ɦ',
+ 'ɨ' => 'Ɨ',
+ 'ɩ' => 'Ɩ',
+ 'ɫ' => 'Ɫ',
+ 'ɬ' => 'Ɬ',
+ 'ɯ' => 'Ɯ',
+ 'ɱ' => 'Ɱ',
+ 'ɲ' => 'Ɲ',
+ 'ɵ' => 'Ɵ',
+ 'ɽ' => 'Ɽ',
+ 'ʀ' => 'Ʀ',
+ 'ʃ' => 'Ʃ',
+ 'ʇ' => 'Ʇ',
+ 'ʈ' => 'Ʈ',
+ 'ʉ' => 'Ʉ',
+ 'ʊ' => 'Ʊ',
+ 'ʋ' => 'Ʋ',
+ 'ʌ' => 'Ʌ',
+ 'ʒ' => 'Ʒ',
+ 'ʞ' => 'Ʞ',
+ 'ͅ' => 'Ι',
+ 'ͱ' => 'Ͱ',
+ 'ͳ' => 'Ͳ',
+ 'ͷ' => 'Ͷ',
+ 'ͻ' => 'Ͻ',
+ 'ͼ' => 'Ͼ',
+ 'ͽ' => 'Ͽ',
+ 'ά' => 'Ά',
+ 'έ' => 'Έ',
+ 'ή' => 'Ή',
+ 'ί' => 'Ί',
+ 'α' => 'Α',
+ 'β' => 'Β',
+ 'γ' => 'Γ',
+ 'δ' => 'Δ',
+ 'ε' => 'Ε',
+ 'ζ' => 'Ζ',
+ 'η' => 'Η',
+ 'θ' => 'Θ',
+ 'ι' => 'Ι',
+ 'κ' => 'Κ',
+ 'λ' => 'Λ',
+ 'μ' => 'Μ',
+ 'ν' => 'Ν',
+ 'ξ' => 'Ξ',
+ 'ο' => 'Ο',
+ 'π' => 'Π',
+ 'ρ' => 'Ρ',
+ 'ς' => 'Σ',
+ 'σ' => 'Σ',
+ 'τ' => 'Τ',
+ 'υ' => 'Υ',
+ 'φ' => 'Φ',
+ 'χ' => 'Χ',
+ 'ψ' => 'Ψ',
+ 'ω' => 'Ω',
+ 'ϊ' => 'Ϊ',
+ 'ϋ' => 'Ϋ',
+ 'ό' => 'Ό',
+ 'ύ' => 'Ύ',
+ 'ώ' => 'Ώ',
+ 'ϐ' => 'Β',
+ 'ϑ' => 'Θ',
+ 'ϕ' => 'Φ',
+ 'ϖ' => 'Π',
+ 'ϗ' => 'Ϗ',
+ 'ϙ' => 'Ϙ',
+ 'ϛ' => 'Ϛ',
+ 'ϝ' => 'Ϝ',
+ 'ϟ' => 'Ϟ',
+ 'ϡ' => 'Ϡ',
+ 'ϣ' => 'Ϣ',
+ 'ϥ' => 'Ϥ',
+ 'ϧ' => 'Ϧ',
+ 'ϩ' => 'Ϩ',
+ 'ϫ' => 'Ϫ',
+ 'ϭ' => 'Ϭ',
+ 'ϯ' => 'Ϯ',
+ 'ϰ' => 'Κ',
+ 'ϱ' => 'Ρ',
+ 'ϲ' => 'Ϲ',
+ 'ϳ' => 'Ϳ',
+ 'ϵ' => 'Ε',
+ 'ϸ' => 'Ϸ',
+ 'ϻ' => 'Ϻ',
+ 'а' => 'А',
+ 'б' => 'Б',
+ 'в' => 'В',
+ 'г' => 'Г',
+ 'д' => 'Д',
+ 'е' => 'Е',
+ 'ж' => 'Ж',
+ 'з' => 'З',
+ 'и' => 'И',
+ 'й' => 'Й',
+ 'к' => 'К',
+ 'л' => 'Л',
+ 'м' => 'М',
+ 'н' => 'Н',
+ 'о' => 'О',
+ 'п' => 'П',
+ 'р' => 'Р',
+ 'с' => 'С',
+ 'т' => 'Т',
+ 'у' => 'У',
+ 'ф' => 'Ф',
+ 'х' => 'Х',
+ 'ц' => 'Ц',
+ 'ч' => 'Ч',
+ 'ш' => 'Ш',
+ 'щ' => 'Щ',
+ 'ъ' => 'Ъ',
+ 'ы' => 'Ы',
+ 'ь' => 'Ь',
+ 'э' => 'Э',
+ 'ю' => 'Ю',
+ 'я' => 'Я',
+ 'ѐ' => 'Ѐ',
+ 'ё' => 'Ё',
+ 'ђ' => 'Ђ',
+ 'ѓ' => 'Ѓ',
+ 'є' => 'Є',
+ 'ѕ' => 'Ѕ',
+ 'і' => 'І',
+ 'ї' => 'Ї',
+ 'ј' => 'Ј',
+ 'љ' => 'Љ',
+ 'њ' => 'Њ',
+ 'ћ' => 'Ћ',
+ 'ќ' => 'Ќ',
+ 'ѝ' => 'Ѝ',
+ 'ў' => 'Ў',
+ 'џ' => 'Џ',
+ 'ѡ' => 'Ѡ',
+ 'ѣ' => 'Ѣ',
+ 'ѥ' => 'Ѥ',
+ 'ѧ' => 'Ѧ',
+ 'ѩ' => 'Ѩ',
+ 'ѫ' => 'Ѫ',
+ 'ѭ' => 'Ѭ',
+ 'ѯ' => 'Ѯ',
+ 'ѱ' => 'Ѱ',
+ 'ѳ' => 'Ѳ',
+ 'ѵ' => 'Ѵ',
+ 'ѷ' => 'Ѷ',
+ 'ѹ' => 'Ѹ',
+ 'ѻ' => 'Ѻ',
+ 'ѽ' => 'Ѽ',
+ 'ѿ' => 'Ѿ',
+ 'ҁ' => 'Ҁ',
+ 'ҋ' => 'Ҋ',
+ 'ҍ' => 'Ҍ',
+ 'ҏ' => 'Ҏ',
+ 'ґ' => 'Ґ',
+ 'ғ' => 'Ғ',
+ 'ҕ' => 'Ҕ',
+ 'җ' => 'Җ',
+ 'ҙ' => 'Ҙ',
+ 'қ' => 'Қ',
+ 'ҝ' => 'Ҝ',
+ 'ҟ' => 'Ҟ',
+ 'ҡ' => 'Ҡ',
+ 'ң' => 'Ң',
+ 'ҥ' => 'Ҥ',
+ 'ҧ' => 'Ҧ',
+ 'ҩ' => 'Ҩ',
+ 'ҫ' => 'Ҫ',
+ 'ҭ' => 'Ҭ',
+ 'ү' => 'Ү',
+ 'ұ' => 'Ұ',
+ 'ҳ' => 'Ҳ',
+ 'ҵ' => 'Ҵ',
+ 'ҷ' => 'Ҷ',
+ 'ҹ' => 'Ҹ',
+ 'һ' => 'Һ',
+ 'ҽ' => 'Ҽ',
+ 'ҿ' => 'Ҿ',
+ 'ӂ' => 'Ӂ',
+ 'ӄ' => 'Ӄ',
+ 'ӆ' => 'Ӆ',
+ 'ӈ' => 'Ӈ',
+ 'ӊ' => 'Ӊ',
+ 'ӌ' => 'Ӌ',
+ 'ӎ' => 'Ӎ',
+ 'ӏ' => 'Ӏ',
+ 'ӑ' => 'Ӑ',
+ 'ӓ' => 'Ӓ',
+ 'ӕ' => 'Ӕ',
+ 'ӗ' => 'Ӗ',
+ 'ә' => 'Ә',
+ 'ӛ' => 'Ӛ',
+ 'ӝ' => 'Ӝ',
+ 'ӟ' => 'Ӟ',
+ 'ӡ' => 'Ӡ',
+ 'ӣ' => 'Ӣ',
+ 'ӥ' => 'Ӥ',
+ 'ӧ' => 'Ӧ',
+ 'ө' => 'Ө',
+ 'ӫ' => 'Ӫ',
+ 'ӭ' => 'Ӭ',
+ 'ӯ' => 'Ӯ',
+ 'ӱ' => 'Ӱ',
+ 'ӳ' => 'Ӳ',
+ 'ӵ' => 'Ӵ',
+ 'ӷ' => 'Ӷ',
+ 'ӹ' => 'Ӹ',
+ 'ӻ' => 'Ӻ',
+ 'ӽ' => 'Ӽ',
+ 'ӿ' => 'Ӿ',
+ 'ԁ' => 'Ԁ',
+ 'ԃ' => 'Ԃ',
+ 'ԅ' => 'Ԅ',
+ 'ԇ' => 'Ԇ',
+ 'ԉ' => 'Ԉ',
+ 'ԋ' => 'Ԋ',
+ 'ԍ' => 'Ԍ',
+ 'ԏ' => 'Ԏ',
+ 'ԑ' => 'Ԑ',
+ 'ԓ' => 'Ԓ',
+ 'ԕ' => 'Ԕ',
+ 'ԗ' => 'Ԗ',
+ 'ԙ' => 'Ԙ',
+ 'ԛ' => 'Ԛ',
+ 'ԝ' => 'Ԝ',
+ 'ԟ' => 'Ԟ',
+ 'ԡ' => 'Ԡ',
+ 'ԣ' => 'Ԣ',
+ 'ԥ' => 'Ԥ',
+ 'ԧ' => 'Ԧ',
+ 'ԩ' => 'Ԩ',
+ 'ԫ' => 'Ԫ',
+ 'ԭ' => 'Ԭ',
+ 'ԯ' => 'Ԯ',
+ 'ա' => 'Ա',
+ 'բ' => 'Բ',
+ 'գ' => 'Գ',
+ 'դ' => 'Դ',
+ 'ե' => 'Ե',
+ 'զ' => 'Զ',
+ 'է' => 'Է',
+ 'ը' => 'Ը',
+ 'թ' => 'Թ',
+ 'ժ' => 'Ժ',
+ 'ի' => 'Ի',
+ 'լ' => 'Լ',
+ 'խ' => 'Խ',
+ 'ծ' => 'Ծ',
+ 'կ' => 'Կ',
+ 'հ' => 'Հ',
+ 'ձ' => 'Ձ',
+ 'ղ' => 'Ղ',
+ 'ճ' => 'Ճ',
+ 'մ' => 'Մ',
+ 'յ' => 'Յ',
+ 'ն' => 'Ն',
+ 'շ' => 'Շ',
+ 'ո' => 'Ո',
+ 'չ' => 'Չ',
+ 'պ' => 'Պ',
+ 'ջ' => 'Ջ',
+ 'ռ' => 'Ռ',
+ 'ս' => 'Ս',
+ 'վ' => 'Վ',
+ 'տ' => 'Տ',
+ 'ր' => 'Ր',
+ 'ց' => 'Ց',
+ 'ւ' => 'Ւ',
+ 'փ' => 'Փ',
+ 'ք' => 'Ք',
+ 'օ' => 'Օ',
+ 'ֆ' => 'Ֆ',
+ 'ᵹ' => 'Ᵹ',
+ 'ᵽ' => 'Ᵽ',
+ 'ḁ' => 'Ḁ',
+ 'ḃ' => 'Ḃ',
+ 'ḅ' => 'Ḅ',
+ 'ḇ' => 'Ḇ',
+ 'ḉ' => 'Ḉ',
+ 'ḋ' => 'Ḋ',
+ 'ḍ' => 'Ḍ',
+ 'ḏ' => 'Ḏ',
+ 'ḑ' => 'Ḑ',
+ 'ḓ' => 'Ḓ',
+ 'ḕ' => 'Ḕ',
+ 'ḗ' => 'Ḗ',
+ 'ḙ' => 'Ḙ',
+ 'ḛ' => 'Ḛ',
+ 'ḝ' => 'Ḝ',
+ 'ḟ' => 'Ḟ',
+ 'ḡ' => 'Ḡ',
+ 'ḣ' => 'Ḣ',
+ 'ḥ' => 'Ḥ',
+ 'ḧ' => 'Ḧ',
+ 'ḩ' => 'Ḩ',
+ 'ḫ' => 'Ḫ',
+ 'ḭ' => 'Ḭ',
+ 'ḯ' => 'Ḯ',
+ 'ḱ' => 'Ḱ',
+ 'ḳ' => 'Ḳ',
+ 'ḵ' => 'Ḵ',
+ 'ḷ' => 'Ḷ',
+ 'ḹ' => 'Ḹ',
+ 'ḻ' => 'Ḻ',
+ 'ḽ' => 'Ḽ',
+ 'ḿ' => 'Ḿ',
+ 'ṁ' => 'Ṁ',
+ 'ṃ' => 'Ṃ',
+ 'ṅ' => 'Ṅ',
+ 'ṇ' => 'Ṇ',
+ 'ṉ' => 'Ṉ',
+ 'ṋ' => 'Ṋ',
+ 'ṍ' => 'Ṍ',
+ 'ṏ' => 'Ṏ',
+ 'ṑ' => 'Ṑ',
+ 'ṓ' => 'Ṓ',
+ 'ṕ' => 'Ṕ',
+ 'ṗ' => 'Ṗ',
+ 'ṙ' => 'Ṙ',
+ 'ṛ' => 'Ṛ',
+ 'ṝ' => 'Ṝ',
+ 'ṟ' => 'Ṟ',
+ 'ṡ' => 'Ṡ',
+ 'ṣ' => 'Ṣ',
+ 'ṥ' => 'Ṥ',
+ 'ṧ' => 'Ṧ',
+ 'ṩ' => 'Ṩ',
+ 'ṫ' => 'Ṫ',
+ 'ṭ' => 'Ṭ',
+ 'ṯ' => 'Ṯ',
+ 'ṱ' => 'Ṱ',
+ 'ṳ' => 'Ṳ',
+ 'ṵ' => 'Ṵ',
+ 'ṷ' => 'Ṷ',
+ 'ṹ' => 'Ṹ',
+ 'ṻ' => 'Ṻ',
+ 'ṽ' => 'Ṽ',
+ 'ṿ' => 'Ṿ',
+ 'ẁ' => 'Ẁ',
+ 'ẃ' => 'Ẃ',
+ 'ẅ' => 'Ẅ',
+ 'ẇ' => 'Ẇ',
+ 'ẉ' => 'Ẉ',
+ 'ẋ' => 'Ẋ',
+ 'ẍ' => 'Ẍ',
+ 'ẏ' => 'Ẏ',
+ 'ẑ' => 'Ẑ',
+ 'ẓ' => 'Ẓ',
+ 'ẕ' => 'Ẕ',
+ 'ẛ' => 'Ṡ',
+ 'ạ' => 'Ạ',
+ 'ả' => 'Ả',
+ 'ấ' => 'Ấ',
+ 'ầ' => 'Ầ',
+ 'ẩ' => 'Ẩ',
+ 'ẫ' => 'Ẫ',
+ 'ậ' => 'Ậ',
+ 'ắ' => 'Ắ',
+ 'ằ' => 'Ằ',
+ 'ẳ' => 'Ẳ',
+ 'ẵ' => 'Ẵ',
+ 'ặ' => 'Ặ',
+ 'ẹ' => 'Ẹ',
+ 'ẻ' => 'Ẻ',
+ 'ẽ' => 'Ẽ',
+ 'ế' => 'Ế',
+ 'ề' => 'Ề',
+ 'ể' => 'Ể',
+ 'ễ' => 'Ễ',
+ 'ệ' => 'Ệ',
+ 'ỉ' => 'Ỉ',
+ 'ị' => 'Ị',
+ 'ọ' => 'Ọ',
+ 'ỏ' => 'Ỏ',
+ 'ố' => 'Ố',
+ 'ồ' => 'Ồ',
+ 'ổ' => 'Ổ',
+ 'ỗ' => 'Ỗ',
+ 'ộ' => 'Ộ',
+ 'ớ' => 'Ớ',
+ 'ờ' => 'Ờ',
+ 'ở' => 'Ở',
+ 'ỡ' => 'Ỡ',
+ 'ợ' => 'Ợ',
+ 'ụ' => 'Ụ',
+ 'ủ' => 'Ủ',
+ 'ứ' => 'Ứ',
+ 'ừ' => 'Ừ',
+ 'ử' => 'Ử',
+ 'ữ' => 'Ữ',
+ 'ự' => 'Ự',
+ 'ỳ' => 'Ỳ',
+ 'ỵ' => 'Ỵ',
+ 'ỷ' => 'Ỷ',
+ 'ỹ' => 'Ỹ',
+ 'ỻ' => 'Ỻ',
+ 'ỽ' => 'Ỽ',
+ 'ỿ' => 'Ỿ',
+ 'ἀ' => 'Ἀ',
+ 'ἁ' => 'Ἁ',
+ 'ἂ' => 'Ἂ',
+ 'ἃ' => 'Ἃ',
+ 'ἄ' => 'Ἄ',
+ 'ἅ' => 'Ἅ',
+ 'ἆ' => 'Ἆ',
+ 'ἇ' => 'Ἇ',
+ 'ἐ' => 'Ἐ',
+ 'ἑ' => 'Ἑ',
+ 'ἒ' => 'Ἒ',
+ 'ἓ' => 'Ἓ',
+ 'ἔ' => 'Ἔ',
+ 'ἕ' => 'Ἕ',
+ 'ἠ' => 'Ἠ',
+ 'ἡ' => 'Ἡ',
+ 'ἢ' => 'Ἢ',
+ 'ἣ' => 'Ἣ',
+ 'ἤ' => 'Ἤ',
+ 'ἥ' => 'Ἥ',
+ 'ἦ' => 'Ἦ',
+ 'ἧ' => 'Ἧ',
+ 'ἰ' => 'Ἰ',
+ 'ἱ' => 'Ἱ',
+ 'ἲ' => 'Ἲ',
+ 'ἳ' => 'Ἳ',
+ 'ἴ' => 'Ἴ',
+ 'ἵ' => 'Ἵ',
+ 'ἶ' => 'Ἶ',
+ 'ἷ' => 'Ἷ',
+ 'ὀ' => 'Ὀ',
+ 'ὁ' => 'Ὁ',
+ 'ὂ' => 'Ὂ',
+ 'ὃ' => 'Ὃ',
+ 'ὄ' => 'Ὄ',
+ 'ὅ' => 'Ὅ',
+ 'ὑ' => 'Ὑ',
+ 'ὓ' => 'Ὓ',
+ 'ὕ' => 'Ὕ',
+ 'ὗ' => 'Ὗ',
+ 'ὠ' => 'Ὠ',
+ 'ὡ' => 'Ὡ',
+ 'ὢ' => 'Ὢ',
+ 'ὣ' => 'Ὣ',
+ 'ὤ' => 'Ὤ',
+ 'ὥ' => 'Ὥ',
+ 'ὦ' => 'Ὦ',
+ 'ὧ' => 'Ὧ',
+ 'ὰ' => 'Ὰ',
+ 'ά' => 'Ά',
+ 'ὲ' => 'Ὲ',
+ 'έ' => 'Έ',
+ 'ὴ' => 'Ὴ',
+ 'ή' => 'Ή',
+ 'ὶ' => 'Ὶ',
+ 'ί' => 'Ί',
+ 'ὸ' => 'Ὸ',
+ 'ό' => 'Ό',
+ 'ὺ' => 'Ὺ',
+ 'ύ' => 'Ύ',
+ 'ὼ' => 'Ὼ',
+ 'ώ' => 'Ώ',
+ 'ᾀ' => 'ᾈ',
+ 'ᾁ' => 'ᾉ',
+ 'ᾂ' => 'ᾊ',
+ 'ᾃ' => 'ᾋ',
+ 'ᾄ' => 'ᾌ',
+ 'ᾅ' => 'ᾍ',
+ 'ᾆ' => 'ᾎ',
+ 'ᾇ' => 'ᾏ',
+ 'ᾐ' => 'ᾘ',
+ 'ᾑ' => 'ᾙ',
+ 'ᾒ' => 'ᾚ',
+ 'ᾓ' => 'ᾛ',
+ 'ᾔ' => 'ᾜ',
+ 'ᾕ' => 'ᾝ',
+ 'ᾖ' => 'ᾞ',
+ 'ᾗ' => 'ᾟ',
+ 'ᾠ' => 'ᾨ',
+ 'ᾡ' => 'ᾩ',
+ 'ᾢ' => 'ᾪ',
+ 'ᾣ' => 'ᾫ',
+ 'ᾤ' => 'ᾬ',
+ 'ᾥ' => 'ᾭ',
+ 'ᾦ' => 'ᾮ',
+ 'ᾧ' => 'ᾯ',
+ 'ᾰ' => 'Ᾰ',
+ 'ᾱ' => 'Ᾱ',
+ 'ᾳ' => 'ᾼ',
+ 'ι' => 'Ι',
+ 'ῃ' => 'ῌ',
+ 'ῐ' => 'Ῐ',
+ 'ῑ' => 'Ῑ',
+ 'ῠ' => 'Ῠ',
+ 'ῡ' => 'Ῡ',
+ 'ῥ' => 'Ῥ',
+ 'ῳ' => 'ῼ',
+ 'ⅎ' => 'Ⅎ',
+ 'ⅰ' => 'Ⅰ',
+ 'ⅱ' => 'Ⅱ',
+ 'ⅲ' => 'Ⅲ',
+ 'ⅳ' => 'Ⅳ',
+ 'ⅴ' => 'Ⅴ',
+ 'ⅵ' => 'Ⅵ',
+ 'ⅶ' => 'Ⅶ',
+ 'ⅷ' => 'Ⅷ',
+ 'ⅸ' => 'Ⅸ',
+ 'ⅹ' => 'Ⅹ',
+ 'ⅺ' => 'Ⅺ',
+ 'ⅻ' => 'Ⅻ',
+ 'ⅼ' => 'Ⅼ',
+ 'ⅽ' => 'Ⅽ',
+ 'ⅾ' => 'Ⅾ',
+ 'ⅿ' => 'Ⅿ',
+ 'ↄ' => 'Ↄ',
+ 'ⓐ' => 'Ⓐ',
+ 'ⓑ' => 'Ⓑ',
+ 'ⓒ' => 'Ⓒ',
+ 'ⓓ' => 'Ⓓ',
+ 'ⓔ' => 'Ⓔ',
+ 'ⓕ' => 'Ⓕ',
+ 'ⓖ' => 'Ⓖ',
+ 'ⓗ' => 'Ⓗ',
+ 'ⓘ' => 'Ⓘ',
+ 'ⓙ' => 'Ⓙ',
+ 'ⓚ' => 'Ⓚ',
+ 'ⓛ' => 'Ⓛ',
+ 'ⓜ' => 'Ⓜ',
+ 'ⓝ' => 'Ⓝ',
+ 'ⓞ' => 'Ⓞ',
+ 'ⓟ' => 'Ⓟ',
+ 'ⓠ' => 'Ⓠ',
+ 'ⓡ' => 'Ⓡ',
+ 'ⓢ' => 'Ⓢ',
+ 'ⓣ' => 'Ⓣ',
+ 'ⓤ' => 'Ⓤ',
+ 'ⓥ' => 'Ⓥ',
+ 'ⓦ' => 'Ⓦ',
+ 'ⓧ' => 'Ⓧ',
+ 'ⓨ' => 'Ⓨ',
+ 'ⓩ' => 'Ⓩ',
+ 'ⰰ' => 'Ⰰ',
+ 'ⰱ' => 'Ⰱ',
+ 'ⰲ' => 'Ⰲ',
+ 'ⰳ' => 'Ⰳ',
+ 'ⰴ' => 'Ⰴ',
+ 'ⰵ' => 'Ⰵ',
+ 'ⰶ' => 'Ⰶ',
+ 'ⰷ' => 'Ⰷ',
+ 'ⰸ' => 'Ⰸ',
+ 'ⰹ' => 'Ⰹ',
+ 'ⰺ' => 'Ⰺ',
+ 'ⰻ' => 'Ⰻ',
+ 'ⰼ' => 'Ⰼ',
+ 'ⰽ' => 'Ⰽ',
+ 'ⰾ' => 'Ⰾ',
+ 'ⰿ' => 'Ⰿ',
+ 'ⱀ' => 'Ⱀ',
+ 'ⱁ' => 'Ⱁ',
+ 'ⱂ' => 'Ⱂ',
+ 'ⱃ' => 'Ⱃ',
+ 'ⱄ' => 'Ⱄ',
+ 'ⱅ' => 'Ⱅ',
+ 'ⱆ' => 'Ⱆ',
+ 'ⱇ' => 'Ⱇ',
+ 'ⱈ' => 'Ⱈ',
+ 'ⱉ' => 'Ⱉ',
+ 'ⱊ' => 'Ⱊ',
+ 'ⱋ' => 'Ⱋ',
+ 'ⱌ' => 'Ⱌ',
+ 'ⱍ' => 'Ⱍ',
+ 'ⱎ' => 'Ⱎ',
+ 'ⱏ' => 'Ⱏ',
+ 'ⱐ' => 'Ⱐ',
+ 'ⱑ' => 'Ⱑ',
+ 'ⱒ' => 'Ⱒ',
+ 'ⱓ' => 'Ⱓ',
+ 'ⱔ' => 'Ⱔ',
+ 'ⱕ' => 'Ⱕ',
+ 'ⱖ' => 'Ⱖ',
+ 'ⱗ' => 'Ⱗ',
+ 'ⱘ' => 'Ⱘ',
+ 'ⱙ' => 'Ⱙ',
+ 'ⱚ' => 'Ⱚ',
+ 'ⱛ' => 'Ⱛ',
+ 'ⱜ' => 'Ⱜ',
+ 'ⱝ' => 'Ⱝ',
+ 'ⱞ' => 'Ⱞ',
+ 'ⱡ' => 'Ⱡ',
+ 'ⱥ' => 'Ⱥ',
+ 'ⱦ' => 'Ⱦ',
+ 'ⱨ' => 'Ⱨ',
+ 'ⱪ' => 'Ⱪ',
+ 'ⱬ' => 'Ⱬ',
+ 'ⱳ' => 'Ⱳ',
+ 'ⱶ' => 'Ⱶ',
+ 'ⲁ' => 'Ⲁ',
+ 'ⲃ' => 'Ⲃ',
+ 'ⲅ' => 'Ⲅ',
+ 'ⲇ' => 'Ⲇ',
+ 'ⲉ' => 'Ⲉ',
+ 'ⲋ' => 'Ⲋ',
+ 'ⲍ' => 'Ⲍ',
+ 'ⲏ' => 'Ⲏ',
+ 'ⲑ' => 'Ⲑ',
+ 'ⲓ' => 'Ⲓ',
+ 'ⲕ' => 'Ⲕ',
+ 'ⲗ' => 'Ⲗ',
+ 'ⲙ' => 'Ⲙ',
+ 'ⲛ' => 'Ⲛ',
+ 'ⲝ' => 'Ⲝ',
+ 'ⲟ' => 'Ⲟ',
+ 'ⲡ' => 'Ⲡ',
+ 'ⲣ' => 'Ⲣ',
+ 'ⲥ' => 'Ⲥ',
+ 'ⲧ' => 'Ⲧ',
+ 'ⲩ' => 'Ⲩ',
+ 'ⲫ' => 'Ⲫ',
+ 'ⲭ' => 'Ⲭ',
+ 'ⲯ' => 'Ⲯ',
+ 'ⲱ' => 'Ⲱ',
+ 'ⲳ' => 'Ⲳ',
+ 'ⲵ' => 'Ⲵ',
+ 'ⲷ' => 'Ⲷ',
+ 'ⲹ' => 'Ⲹ',
+ 'ⲻ' => 'Ⲻ',
+ 'ⲽ' => 'Ⲽ',
+ 'ⲿ' => 'Ⲿ',
+ 'ⳁ' => 'Ⳁ',
+ 'ⳃ' => 'Ⳃ',
+ 'ⳅ' => 'Ⳅ',
+ 'ⳇ' => 'Ⳇ',
+ 'ⳉ' => 'Ⳉ',
+ 'ⳋ' => 'Ⳋ',
+ 'ⳍ' => 'Ⳍ',
+ 'ⳏ' => 'Ⳏ',
+ 'ⳑ' => 'Ⳑ',
+ 'ⳓ' => 'Ⳓ',
+ 'ⳕ' => 'Ⳕ',
+ 'ⳗ' => 'Ⳗ',
+ 'ⳙ' => 'Ⳙ',
+ 'ⳛ' => 'Ⳛ',
+ 'ⳝ' => 'Ⳝ',
+ 'ⳟ' => 'Ⳟ',
+ 'ⳡ' => 'Ⳡ',
+ 'ⳣ' => 'Ⳣ',
+ 'ⳬ' => 'Ⳬ',
+ 'ⳮ' => 'Ⳮ',
+ 'ⳳ' => 'Ⳳ',
+ 'ⴀ' => 'Ⴀ',
+ 'ⴁ' => 'Ⴁ',
+ 'ⴂ' => 'Ⴂ',
+ 'ⴃ' => 'Ⴃ',
+ 'ⴄ' => 'Ⴄ',
+ 'ⴅ' => 'Ⴅ',
+ 'ⴆ' => 'Ⴆ',
+ 'ⴇ' => 'Ⴇ',
+ 'ⴈ' => 'Ⴈ',
+ 'ⴉ' => 'Ⴉ',
+ 'ⴊ' => 'Ⴊ',
+ 'ⴋ' => 'Ⴋ',
+ 'ⴌ' => 'Ⴌ',
+ 'ⴍ' => 'Ⴍ',
+ 'ⴎ' => 'Ⴎ',
+ 'ⴏ' => 'Ⴏ',
+ 'ⴐ' => 'Ⴐ',
+ 'ⴑ' => 'Ⴑ',
+ 'ⴒ' => 'Ⴒ',
+ 'ⴓ' => 'Ⴓ',
+ 'ⴔ' => 'Ⴔ',
+ 'ⴕ' => 'Ⴕ',
+ 'ⴖ' => 'Ⴖ',
+ 'ⴗ' => 'Ⴗ',
+ 'ⴘ' => 'Ⴘ',
+ 'ⴙ' => 'Ⴙ',
+ 'ⴚ' => 'Ⴚ',
+ 'ⴛ' => 'Ⴛ',
+ 'ⴜ' => 'Ⴜ',
+ 'ⴝ' => 'Ⴝ',
+ 'ⴞ' => 'Ⴞ',
+ 'ⴟ' => 'Ⴟ',
+ 'ⴠ' => 'Ⴠ',
+ 'ⴡ' => 'Ⴡ',
+ 'ⴢ' => 'Ⴢ',
+ 'ⴣ' => 'Ⴣ',
+ 'ⴤ' => 'Ⴤ',
+ 'ⴥ' => 'Ⴥ',
+ 'ⴧ' => 'Ⴧ',
+ 'ⴭ' => 'Ⴭ',
+ 'ꙁ' => 'Ꙁ',
+ 'ꙃ' => 'Ꙃ',
+ 'ꙅ' => 'Ꙅ',
+ 'ꙇ' => 'Ꙇ',
+ 'ꙉ' => 'Ꙉ',
+ 'ꙋ' => 'Ꙋ',
+ 'ꙍ' => 'Ꙍ',
+ 'ꙏ' => 'Ꙏ',
+ 'ꙑ' => 'Ꙑ',
+ 'ꙓ' => 'Ꙓ',
+ 'ꙕ' => 'Ꙕ',
+ 'ꙗ' => 'Ꙗ',
+ 'ꙙ' => 'Ꙙ',
+ 'ꙛ' => 'Ꙛ',
+ 'ꙝ' => 'Ꙝ',
+ 'ꙟ' => 'Ꙟ',
+ 'ꙡ' => 'Ꙡ',
+ 'ꙣ' => 'Ꙣ',
+ 'ꙥ' => 'Ꙥ',
+ 'ꙧ' => 'Ꙧ',
+ 'ꙩ' => 'Ꙩ',
+ 'ꙫ' => 'Ꙫ',
+ 'ꙭ' => 'Ꙭ',
+ 'ꚁ' => 'Ꚁ',
+ 'ꚃ' => 'Ꚃ',
+ 'ꚅ' => 'Ꚅ',
+ 'ꚇ' => 'Ꚇ',
+ 'ꚉ' => 'Ꚉ',
+ 'ꚋ' => 'Ꚋ',
+ 'ꚍ' => 'Ꚍ',
+ 'ꚏ' => 'Ꚏ',
+ 'ꚑ' => 'Ꚑ',
+ 'ꚓ' => 'Ꚓ',
+ 'ꚕ' => 'Ꚕ',
+ 'ꚗ' => 'Ꚗ',
+ 'ꚙ' => 'Ꚙ',
+ 'ꚛ' => 'Ꚛ',
+ 'ꜣ' => 'Ꜣ',
+ 'ꜥ' => 'Ꜥ',
+ 'ꜧ' => 'Ꜧ',
+ 'ꜩ' => 'Ꜩ',
+ 'ꜫ' => 'Ꜫ',
+ 'ꜭ' => 'Ꜭ',
+ 'ꜯ' => 'Ꜯ',
+ 'ꜳ' => 'Ꜳ',
+ 'ꜵ' => 'Ꜵ',
+ 'ꜷ' => 'Ꜷ',
+ 'ꜹ' => 'Ꜹ',
+ 'ꜻ' => 'Ꜻ',
+ 'ꜽ' => 'Ꜽ',
+ 'ꜿ' => 'Ꜿ',
+ 'ꝁ' => 'Ꝁ',
+ 'ꝃ' => 'Ꝃ',
+ 'ꝅ' => 'Ꝅ',
+ 'ꝇ' => 'Ꝇ',
+ 'ꝉ' => 'Ꝉ',
+ 'ꝋ' => 'Ꝋ',
+ 'ꝍ' => 'Ꝍ',
+ 'ꝏ' => 'Ꝏ',
+ 'ꝑ' => 'Ꝑ',
+ 'ꝓ' => 'Ꝓ',
+ 'ꝕ' => 'Ꝕ',
+ 'ꝗ' => 'Ꝗ',
+ 'ꝙ' => 'Ꝙ',
+ 'ꝛ' => 'Ꝛ',
+ 'ꝝ' => 'Ꝝ',
+ 'ꝟ' => 'Ꝟ',
+ 'ꝡ' => 'Ꝡ',
+ 'ꝣ' => 'Ꝣ',
+ 'ꝥ' => 'Ꝥ',
+ 'ꝧ' => 'Ꝧ',
+ 'ꝩ' => 'Ꝩ',
+ 'ꝫ' => 'Ꝫ',
+ 'ꝭ' => 'Ꝭ',
+ 'ꝯ' => 'Ꝯ',
+ 'ꝺ' => 'Ꝺ',
+ 'ꝼ' => 'Ꝼ',
+ 'ꝿ' => 'Ꝿ',
+ 'ꞁ' => 'Ꞁ',
+ 'ꞃ' => 'Ꞃ',
+ 'ꞅ' => 'Ꞅ',
+ 'ꞇ' => 'Ꞇ',
+ 'ꞌ' => 'Ꞌ',
+ 'ꞑ' => 'Ꞑ',
+ 'ꞓ' => 'Ꞓ',
+ 'ꞗ' => 'Ꞗ',
+ 'ꞙ' => 'Ꞙ',
+ 'ꞛ' => 'Ꞛ',
+ 'ꞝ' => 'Ꞝ',
+ 'ꞟ' => 'Ꞟ',
+ 'ꞡ' => 'Ꞡ',
+ 'ꞣ' => 'Ꞣ',
+ 'ꞥ' => 'Ꞥ',
+ 'ꞧ' => 'Ꞧ',
+ 'ꞩ' => 'Ꞩ',
+ 'a' => 'A',
+ 'b' => 'B',
+ 'c' => 'C',
+ 'd' => 'D',
+ 'e' => 'E',
+ 'f' => 'F',
+ 'g' => 'G',
+ 'h' => 'H',
+ 'i' => 'I',
+ 'j' => 'J',
+ 'k' => 'K',
+ 'l' => 'L',
+ 'm' => 'M',
+ 'n' => 'N',
+ 'o' => 'O',
+ 'p' => 'P',
+ 'q' => 'Q',
+ 'r' => 'R',
+ 's' => 'S',
+ 't' => 'T',
+ 'u' => 'U',
+ 'v' => 'V',
+ 'w' => 'W',
+ 'x' => 'X',
+ 'y' => 'Y',
+ 'z' => 'Z',
+ '𐐨' => '𐐀',
+ '𐐩' => '𐐁',
+ '𐐪' => '𐐂',
+ '𐐫' => '𐐃',
+ '𐐬' => '𐐄',
+ '𐐭' => '𐐅',
+ '𐐮' => '𐐆',
+ '𐐯' => '𐐇',
+ '𐐰' => '𐐈',
+ '𐐱' => '𐐉',
+ '𐐲' => '𐐊',
+ '𐐳' => '𐐋',
+ '𐐴' => '𐐌',
+ '𐐵' => '𐐍',
+ '𐐶' => '𐐎',
+ '𐐷' => '𐐏',
+ '𐐸' => '𐐐',
+ '𐐹' => '𐐑',
+ '𐐺' => '𐐒',
+ '𐐻' => '𐐓',
+ '𐐼' => '𐐔',
+ '𐐽' => '𐐕',
+ '𐐾' => '𐐖',
+ '𐐿' => '𐐗',
+ '𐑀' => '𐐘',
+ '𐑁' => '𐐙',
+ '𐑂' => '𐐚',
+ '𐑃' => '𐐛',
+ '𐑄' => '𐐜',
+ '𐑅' => '𐐝',
+ '𐑆' => '𐐞',
+ '𐑇' => '𐐟',
+ '𐑈' => '𐐠',
+ '𐑉' => '𐐡',
+ '𐑊' => '𐐢',
+ '𐑋' => '𐐣',
+ '𐑌' => '𐐤',
+ '𐑍' => '𐐥',
+ '𐑎' => '𐐦',
+ '𐑏' => '𐐧',
+ '𑣀' => '𑢠',
+ '𑣁' => '𑢡',
+ '𑣂' => '𑢢',
+ '𑣃' => '𑢣',
+ '𑣄' => '𑢤',
+ '𑣅' => '𑢥',
+ '𑣆' => '𑢦',
+ '𑣇' => '𑢧',
+ '𑣈' => '𑢨',
+ '𑣉' => '𑢩',
+ '𑣊' => '𑢪',
+ '𑣋' => '𑢫',
+ '𑣌' => '𑢬',
+ '𑣍' => '𑢭',
+ '𑣎' => '𑢮',
+ '𑣏' => '𑢯',
+ '𑣐' => '𑢰',
+ '𑣑' => '𑢱',
+ '𑣒' => '𑢲',
+ '𑣓' => '𑢳',
+ '𑣔' => '𑢴',
+ '𑣕' => '𑢵',
+ '𑣖' => '𑢶',
+ '𑣗' => '𑢷',
+ '𑣘' => '𑢸',
+ '𑣙' => '𑢹',
+ '𑣚' => '𑢺',
+ '𑣛' => '𑢻',
+ '𑣜' => '𑢼',
+ '𑣝' => '𑢽',
+ '𑣞' => '𑢾',
+ '𑣟' => '𑢿',
+);
+
+$result =& $data;
+unset($data);
+
+return $result;
diff --git a/_include/calendar/symfony/polyfill-mbstring/bootstrap.php b/_include/calendar/symfony/polyfill-mbstring/bootstrap.php
new file mode 100644
index 0000000..2fdcc5a
--- /dev/null
+++ b/_include/calendar/symfony/polyfill-mbstring/bootstrap.php
@@ -0,0 +1,58 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Symfony\Polyfill\Mbstring as p;
+
+if (!function_exists('mb_strlen')) {
+ define('MB_CASE_UPPER', 0);
+ define('MB_CASE_LOWER', 1);
+ define('MB_CASE_TITLE', 2);
+
+ function mb_convert_encoding($s, $to, $from = null) { return p\Mbstring::mb_convert_encoding($s, $to, $from); }
+ function mb_decode_mimeheader($s) { return p\Mbstring::mb_decode_mimeheader($s); }
+ function mb_encode_mimeheader($s, $charset = null, $transferEnc = null, $lf = null, $indent = null) { return p\Mbstring::mb_encode_mimeheader($s, $charset, $transferEnc, $lf, $indent); }
+ function mb_decode_numericentity($s, $convmap, $enc = null) { return p\Mbstring::mb_decode_numericentity($s, $convmap, $enc); }
+ function mb_encode_numericentity($s, $convmap, $enc = null, $is_hex = false) { return p\Mbstring::mb_encode_numericentity($s, $convmap, $enc, $is_hex); }
+ function mb_convert_case($s, $mode, $enc = null) { return p\Mbstring::mb_convert_case($s, $mode, $enc); }
+ function mb_internal_encoding($enc = null) { return p\Mbstring::mb_internal_encoding($enc); }
+ function mb_language($lang = null) { return p\Mbstring::mb_language($lang); }
+ function mb_list_encodings() { return p\Mbstring::mb_list_encodings(); }
+ function mb_encoding_aliases($encoding) { return p\Mbstring::mb_encoding_aliases($encoding); }
+ function mb_check_encoding($var = null, $encoding = null) { return p\Mbstring::mb_check_encoding($var, $encoding); }
+ function mb_detect_encoding($str, $encodingList = null, $strict = false) { return p\Mbstring::mb_detect_encoding($str, $encodingList, $strict); }
+ function mb_detect_order($encodingList = null) { return p\Mbstring::mb_detect_order($encodingList); }
+ function mb_parse_str($s, &$result = array()) { parse_str($s, $result); }
+ function mb_strlen($s, $enc = null) { return p\Mbstring::mb_strlen($s, $enc); }
+ function mb_strpos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strpos($s, $needle, $offset, $enc); }
+ function mb_strtolower($s, $enc = null) { return p\Mbstring::mb_strtolower($s, $enc); }
+ function mb_strtoupper($s, $enc = null) { return p\Mbstring::mb_strtoupper($s, $enc); }
+ function mb_substitute_character($char = null) { return p\Mbstring::mb_substitute_character($char); }
+ function mb_substr($s, $start, $length = 2147483647, $enc = null) { return p\Mbstring::mb_substr($s, $start, $length, $enc); }
+ function mb_stripos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_stripos($s, $needle, $offset, $enc); }
+ function mb_stristr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_stristr($s, $needle, $part, $enc); }
+ function mb_strrchr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strrchr($s, $needle, $part, $enc); }
+ function mb_strrichr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strrichr($s, $needle, $part, $enc); }
+ function mb_strripos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strripos($s, $needle, $offset, $enc); }
+ function mb_strrpos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strrpos($s, $needle, $offset, $enc); }
+ function mb_strstr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strstr($s, $needle, $part, $enc); }
+ function mb_get_info($type = 'all') { return p\Mbstring::mb_get_info($type); }
+ function mb_http_output($enc = null) { return p\Mbstring::mb_http_output($enc); }
+ function mb_strwidth($s, $enc = null) { return p\Mbstring::mb_strwidth($s, $enc); }
+ function mb_substr_count($haystack, $needle, $enc = null) { return p\Mbstring::mb_substr_count($haystack, $needle, $enc); }
+ function mb_output_handler($contents, $status) { return p\Mbstring::mb_output_handler($contents, $status); }
+ function mb_http_input($type = '') { return p\Mbstring::mb_http_input($type); }
+ function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null) { return p\Mbstring::mb_convert_variables($toEncoding, $fromEncoding, $a, $b, $c, $d, $e, $f); }
+}
+if (!function_exists('mb_chr')) {
+ function mb_ord($s, $enc = null) { return p\Mbstring::mb_ord($s, $enc); }
+ function mb_chr($code, $enc = null) { return p\Mbstring::mb_chr($code, $enc); }
+ function mb_scrub($s, $enc = null) { $enc = null === $enc ? mb_internal_encoding() : $enc; return mb_convert_encoding($s, $enc, $enc); }
+}
diff --git a/_include/calendar/symfony/polyfill-mbstring/composer.json b/_include/calendar/symfony/polyfill-mbstring/composer.json
new file mode 100644
index 0000000..50ea12f
--- /dev/null
+++ b/_include/calendar/symfony/polyfill-mbstring/composer.json
@@ -0,0 +1,34 @@
+{
+ "name": "symfony/polyfill-mbstring",
+ "type": "library",
+ "description": "Symfony polyfill for the Mbstring extension",
+ "keywords": ["polyfill", "shim", "compatibility", "portable", "mbstring"],
+ "homepage": "https://symfony.com",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "autoload": {
+ "psr-4": { "Symfony\\Polyfill\\Mbstring\\": "" },
+ "files": [ "bootstrap.php" ]
+ },
+ "suggest": {
+ "ext-mbstring": "For best performance"
+ },
+ "minimum-stability": "dev",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.9-dev"
+ }
+ }
+}
diff --git a/_include/calendar/symfony/translation/.gitignore b/_include/calendar/symfony/translation/.gitignore
new file mode 100644
index 0000000..c49a5d8
--- /dev/null
+++ b/_include/calendar/symfony/translation/.gitignore
@@ -0,0 +1,3 @@
+vendor/
+composer.lock
+phpunit.xml
diff --git a/_include/calendar/symfony/translation/CHANGELOG.md b/_include/calendar/symfony/translation/CHANGELOG.md
new file mode 100644
index 0000000..527b804
--- /dev/null
+++ b/_include/calendar/symfony/translation/CHANGELOG.md
@@ -0,0 +1,108 @@
+CHANGELOG
+=========
+
+4.1.0
+-----
+
+ * The `FileDumper::setBackup()` method is deprecated.
+ * The `TranslationWriter::disableBackup()` method is deprecated.
+ * The `XliffFileDumper` will write "name" on the "unit" node when dumping XLIFF 2.0.
+
+4.0.0
+-----
+
+ * removed the backup feature of the `FileDumper` class
+ * removed `TranslationWriter::writeTranslations()` method
+ * removed support for passing `MessageSelector` instances to the constructor of the `Translator` class
+
+3.4.0
+-----
+
+ * Added `TranslationDumperPass`
+ * Added `TranslationExtractorPass`
+ * Added `TranslatorPass`
+ * Added `TranslationReader` and `TranslationReaderInterface`
+ * Added `` section to the Xliff 2.0 dumper.
+ * Improved Xliff 2.0 loader to load `` section.
+ * Added `TranslationWriterInterface`
+ * Deprecated `TranslationWriter::writeTranslations` in favor of `TranslationWriter::write`
+ * added support for adding custom message formatter and decoupling the default one.
+ * Added `PhpExtractor`
+ * Added `PhpStringTokenParser`
+
+3.2.0
+-----
+
+ * Added support for escaping `|` in plural translations with double pipe.
+
+3.1.0
+-----
+
+ * Deprecated the backup feature of the file dumper classes.
+
+3.0.0
+-----
+
+ * removed `FileDumper::format()` method.
+ * Changed the visibility of the locale property in `Translator` from protected to private.
+
+2.8.0
+-----
+
+ * deprecated FileDumper::format(), overwrite FileDumper::formatCatalogue() instead.
+ * deprecated Translator::getMessages(), rely on TranslatorBagInterface::getCatalogue() instead.
+ * added `FileDumper::formatCatalogue` which allows format the catalogue without dumping it into file.
+ * added option `json_encoding` to JsonFileDumper
+ * added options `as_tree`, `inline` to YamlFileDumper
+ * added support for XLIFF 2.0.
+ * added support for XLIFF target and tool attributes.
+ * added message parameters to DataCollectorTranslator.
+ * [DEPRECATION] The `DiffOperation` class has been deprecated and
+ will be removed in Symfony 3.0, since its operation has nothing to do with 'diff',
+ so the class name is misleading. The `TargetOperation` class should be used for
+ this use-case instead.
+
+2.7.0
+-----
+
+ * added DataCollectorTranslator for collecting the translated messages.
+
+2.6.0
+-----
+
+ * added possibility to cache catalogues
+ * added TranslatorBagInterface
+ * added LoggingTranslator
+ * added Translator::getMessages() for retrieving the message catalogue as an array
+
+2.5.0
+-----
+
+ * added relative file path template to the file dumpers
+ * added optional backup to the file dumpers
+ * changed IcuResFileDumper to extend FileDumper
+
+2.3.0
+-----
+
+ * added classes to make operations on catalogues (like making a diff or a merge on 2 catalogues)
+ * added Translator::getFallbackLocales()
+ * deprecated Translator::setFallbackLocale() in favor of the new Translator::setFallbackLocales() method
+
+2.2.0
+-----
+
+ * QtTranslationsLoader class renamed to QtFileLoader. QtTranslationsLoader is deprecated and will be removed in 2.3.
+ * [BC BREAK] uniformized the exception thrown by the load() method when an error occurs. The load() method now
+ throws Symfony\Component\Translation\Exception\NotFoundResourceException when a resource cannot be found
+ and Symfony\Component\Translation\Exception\InvalidResourceException when a resource is invalid.
+ * changed the exception class thrown by some load() methods from \RuntimeException to \InvalidArgumentException
+ (IcuDatFileLoader, IcuResFileLoader and QtFileLoader)
+
+2.1.0
+-----
+
+ * added support for more than one fallback locale
+ * added support for extracting translation messages from templates (Twig and PHP)
+ * added dumpers for translation catalogs
+ * added support for QT, gettext, and ResourceBundles
diff --git a/_include/calendar/symfony/translation/Catalogue/AbstractOperation.php b/_include/calendar/symfony/translation/Catalogue/AbstractOperation.php
new file mode 100644
index 0000000..a0765dd
--- /dev/null
+++ b/_include/calendar/symfony/translation/Catalogue/AbstractOperation.php
@@ -0,0 +1,157 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Catalogue;
+
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+use Symfony\Component\Translation\Exception\LogicException;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\MessageCatalogueInterface;
+
+/**
+ * Base catalogues binary operation class.
+ *
+ * A catalogue binary operation performs operation on
+ * source (the left argument) and target (the right argument) catalogues.
+ *
+ * @author Jean-François Simon
+ */
+abstract class AbstractOperation implements OperationInterface
+{
+ protected $source;
+ protected $target;
+ protected $result;
+
+ /**
+ * @var array|null The domains affected by this operation
+ */
+ private $domains;
+
+ /**
+ * This array stores 'all', 'new' and 'obsolete' messages for all valid domains.
+ *
+ * The data structure of this array is as follows:
+ *
+ * array(
+ * 'domain 1' => array(
+ * 'all' => array(...),
+ * 'new' => array(...),
+ * 'obsolete' => array(...)
+ * ),
+ * 'domain 2' => array(
+ * 'all' => array(...),
+ * 'new' => array(...),
+ * 'obsolete' => array(...)
+ * ),
+ * ...
+ * )
+ *
+ * @var array The array that stores 'all', 'new' and 'obsolete' messages
+ */
+ protected $messages;
+
+ /**
+ * @throws LogicException
+ */
+ public function __construct(MessageCatalogueInterface $source, MessageCatalogueInterface $target)
+ {
+ if ($source->getLocale() !== $target->getLocale()) {
+ throw new LogicException('Operated catalogues must belong to the same locale.');
+ }
+
+ $this->source = $source;
+ $this->target = $target;
+ $this->result = new MessageCatalogue($source->getLocale());
+ $this->messages = array();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getDomains()
+ {
+ if (null === $this->domains) {
+ $this->domains = array_values(array_unique(array_merge($this->source->getDomains(), $this->target->getDomains())));
+ }
+
+ return $this->domains;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getMessages($domain)
+ {
+ if (!\in_array($domain, $this->getDomains())) {
+ throw new InvalidArgumentException(sprintf('Invalid domain: %s.', $domain));
+ }
+
+ if (!isset($this->messages[$domain]['all'])) {
+ $this->processDomain($domain);
+ }
+
+ return $this->messages[$domain]['all'];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getNewMessages($domain)
+ {
+ if (!\in_array($domain, $this->getDomains())) {
+ throw new InvalidArgumentException(sprintf('Invalid domain: %s.', $domain));
+ }
+
+ if (!isset($this->messages[$domain]['new'])) {
+ $this->processDomain($domain);
+ }
+
+ return $this->messages[$domain]['new'];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getObsoleteMessages($domain)
+ {
+ if (!\in_array($domain, $this->getDomains())) {
+ throw new InvalidArgumentException(sprintf('Invalid domain: %s.', $domain));
+ }
+
+ if (!isset($this->messages[$domain]['obsolete'])) {
+ $this->processDomain($domain);
+ }
+
+ return $this->messages[$domain]['obsolete'];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getResult()
+ {
+ foreach ($this->getDomains() as $domain) {
+ if (!isset($this->messages[$domain])) {
+ $this->processDomain($domain);
+ }
+ }
+
+ return $this->result;
+ }
+
+ /**
+ * Performs operation on source and target catalogues for the given domain and
+ * stores the results.
+ *
+ * @param string $domain The domain which the operation will be performed for
+ */
+ abstract protected function processDomain($domain);
+}
diff --git a/_include/calendar/symfony/translation/Catalogue/MergeOperation.php b/_include/calendar/symfony/translation/Catalogue/MergeOperation.php
new file mode 100644
index 0000000..6db3f80
--- /dev/null
+++ b/_include/calendar/symfony/translation/Catalogue/MergeOperation.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Catalogue;
+
+/**
+ * Merge operation between two catalogues as follows:
+ * all = source ∪ target = {x: x ∈ source ∨ x ∈ target}
+ * new = all ∖ source = {x: x ∈ target ∧ x ∉ source}
+ * obsolete = source ∖ all = {x: x ∈ source ∧ x ∉ source ∧ x ∉ target} = ∅
+ * Basically, the result contains messages from both catalogues.
+ *
+ * @author Jean-François Simon
+ */
+class MergeOperation extends AbstractOperation
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function processDomain($domain)
+ {
+ $this->messages[$domain] = array(
+ 'all' => array(),
+ 'new' => array(),
+ 'obsolete' => array(),
+ );
+
+ foreach ($this->source->all($domain) as $id => $message) {
+ $this->messages[$domain]['all'][$id] = $message;
+ $this->result->add(array($id => $message), $domain);
+ if (null !== $keyMetadata = $this->source->getMetadata($id, $domain)) {
+ $this->result->setMetadata($id, $keyMetadata, $domain);
+ }
+ }
+
+ foreach ($this->target->all($domain) as $id => $message) {
+ if (!$this->source->has($id, $domain)) {
+ $this->messages[$domain]['all'][$id] = $message;
+ $this->messages[$domain]['new'][$id] = $message;
+ $this->result->add(array($id => $message), $domain);
+ if (null !== $keyMetadata = $this->target->getMetadata($id, $domain)) {
+ $this->result->setMetadata($id, $keyMetadata, $domain);
+ }
+ }
+ }
+ }
+}
diff --git a/_include/calendar/symfony/translation/Catalogue/OperationInterface.php b/_include/calendar/symfony/translation/Catalogue/OperationInterface.php
new file mode 100644
index 0000000..87d888e
--- /dev/null
+++ b/_include/calendar/symfony/translation/Catalogue/OperationInterface.php
@@ -0,0 +1,77 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Catalogue;
+
+use Symfony\Component\Translation\MessageCatalogueInterface;
+
+/**
+ * Represents an operation on catalogue(s).
+ *
+ * An instance of this interface performs an operation on one or more catalogues and
+ * stores intermediate and final results of the operation.
+ *
+ * The first catalogue in its argument(s) is called the 'source catalogue' or 'source' and
+ * the following results are stored:
+ *
+ * Messages: also called 'all', are valid messages for the given domain after the operation is performed.
+ *
+ * New Messages: also called 'new' (new = all ∖ source = {x: x ∈ all ∧ x ∉ source}).
+ *
+ * Obsolete Messages: also called 'obsolete' (obsolete = source ∖ all = {x: x ∈ source ∧ x ∉ all}).
+ *
+ * Result: also called 'result', is the resulting catalogue for the given domain that holds the same messages as 'all'.
+ *
+ * @author Jean-François Simon
+ */
+interface OperationInterface
+{
+ /**
+ * Returns domains affected by operation.
+ *
+ * @return array
+ */
+ public function getDomains();
+
+ /**
+ * Returns all valid messages ('all') after operation.
+ *
+ * @param string $domain
+ *
+ * @return array
+ */
+ public function getMessages($domain);
+
+ /**
+ * Returns new messages ('new') after operation.
+ *
+ * @param string $domain
+ *
+ * @return array
+ */
+ public function getNewMessages($domain);
+
+ /**
+ * Returns obsolete messages ('obsolete') after operation.
+ *
+ * @param string $domain
+ *
+ * @return array
+ */
+ public function getObsoleteMessages($domain);
+
+ /**
+ * Returns resulting catalogue ('result').
+ *
+ * @return MessageCatalogueInterface
+ */
+ public function getResult();
+}
diff --git a/_include/calendar/symfony/translation/Catalogue/TargetOperation.php b/_include/calendar/symfony/translation/Catalogue/TargetOperation.php
new file mode 100644
index 0000000..f3b0a29
--- /dev/null
+++ b/_include/calendar/symfony/translation/Catalogue/TargetOperation.php
@@ -0,0 +1,69 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Catalogue;
+
+/**
+ * Target operation between two catalogues:
+ * intersection = source ∩ target = {x: x ∈ source ∧ x ∈ target}
+ * all = intersection ∪ (target ∖ intersection) = target
+ * new = all ∖ source = {x: x ∈ target ∧ x ∉ source}
+ * obsolete = source ∖ all = source ∖ target = {x: x ∈ source ∧ x ∉ target}
+ * Basically, the result contains messages from the target catalogue.
+ *
+ * @author Michael Lee
+ */
+class TargetOperation extends AbstractOperation
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function processDomain($domain)
+ {
+ $this->messages[$domain] = array(
+ 'all' => array(),
+ 'new' => array(),
+ 'obsolete' => array(),
+ );
+
+ // For 'all' messages, the code can't be simplified as ``$this->messages[$domain]['all'] = $target->all($domain);``,
+ // because doing so will drop messages like {x: x ∈ source ∧ x ∉ target.all ∧ x ∈ target.fallback}
+ //
+ // For 'new' messages, the code can't be simplied as ``array_diff_assoc($this->target->all($domain), $this->source->all($domain));``
+ // because doing so will not exclude messages like {x: x ∈ target ∧ x ∉ source.all ∧ x ∈ source.fallback}
+ //
+ // For 'obsolete' messages, the code can't be simplifed as ``array_diff_assoc($this->source->all($domain), $this->target->all($domain))``
+ // because doing so will not exclude messages like {x: x ∈ source ∧ x ∉ target.all ∧ x ∈ target.fallback}
+
+ foreach ($this->source->all($domain) as $id => $message) {
+ if ($this->target->has($id, $domain)) {
+ $this->messages[$domain]['all'][$id] = $message;
+ $this->result->add(array($id => $message), $domain);
+ if (null !== $keyMetadata = $this->source->getMetadata($id, $domain)) {
+ $this->result->setMetadata($id, $keyMetadata, $domain);
+ }
+ } else {
+ $this->messages[$domain]['obsolete'][$id] = $message;
+ }
+ }
+
+ foreach ($this->target->all($domain) as $id => $message) {
+ if (!$this->source->has($id, $domain)) {
+ $this->messages[$domain]['all'][$id] = $message;
+ $this->messages[$domain]['new'][$id] = $message;
+ $this->result->add(array($id => $message), $domain);
+ if (null !== $keyMetadata = $this->target->getMetadata($id, $domain)) {
+ $this->result->setMetadata($id, $keyMetadata, $domain);
+ }
+ }
+ }
+ }
+}
diff --git a/_include/calendar/symfony/translation/Command/XliffLintCommand.php b/_include/calendar/symfony/translation/Command/XliffLintCommand.php
new file mode 100644
index 0000000..378788f
--- /dev/null
+++ b/_include/calendar/symfony/translation/Command/XliffLintCommand.php
@@ -0,0 +1,270 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Command;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Exception\RuntimeException;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Style\SymfonyStyle;
+
+/**
+ * Validates XLIFF files syntax and outputs encountered errors.
+ *
+ * @author Grégoire Pineau
+ * @author Robin Chalas
+ * @author Javier Eguiluz
+ */
+class XliffLintCommand extends Command
+{
+ protected static $defaultName = 'lint:xliff';
+
+ private $format;
+ private $displayCorrectFiles;
+ private $directoryIteratorProvider;
+ private $isReadableProvider;
+
+ public function __construct(string $name = null, callable $directoryIteratorProvider = null, callable $isReadableProvider = null)
+ {
+ parent::__construct($name);
+
+ $this->directoryIteratorProvider = $directoryIteratorProvider;
+ $this->isReadableProvider = $isReadableProvider;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function configure()
+ {
+ $this
+ ->setDescription('Lints a XLIFF file and outputs encountered errors')
+ ->addArgument('filename', null, 'A file or a directory or STDIN')
+ ->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format', 'txt')
+ ->setHelp(<<%command.name% command lints a XLIFF file and outputs to STDOUT
+the first encountered syntax error.
+
+You can validates XLIFF contents passed from STDIN:
+
+ cat filename | php %command.full_name%
+
+You can also validate the syntax of a file:
+
+ php %command.full_name% filename
+
+Or of a whole directory:
+
+ php %command.full_name% dirname
+ php %command.full_name% dirname --format=json
+
+EOF
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $io = new SymfonyStyle($input, $output);
+ $filename = $input->getArgument('filename');
+ $this->format = $input->getOption('format');
+ $this->displayCorrectFiles = $output->isVerbose();
+
+ if (!$filename) {
+ if (!$stdin = $this->getStdin()) {
+ throw new RuntimeException('Please provide a filename or pipe file content to STDIN.');
+ }
+
+ return $this->display($io, array($this->validate($stdin)));
+ }
+
+ if (!$this->isReadable($filename)) {
+ throw new RuntimeException(sprintf('File or directory "%s" is not readable.', $filename));
+ }
+
+ $filesInfo = array();
+ foreach ($this->getFiles($filename) as $file) {
+ $filesInfo[] = $this->validate(file_get_contents($file), $file);
+ }
+
+ return $this->display($io, $filesInfo);
+ }
+
+ private function validate($content, $file = null)
+ {
+ $errors = array();
+
+ // Avoid: Warning DOMDocument::loadXML(): Empty string supplied as input
+ if ('' === trim($content)) {
+ return array('file' => $file, 'valid' => true);
+ }
+
+ libxml_use_internal_errors(true);
+
+ $document = new \DOMDocument();
+ $document->loadXML($content);
+
+ if (null !== $targetLanguage = $this->getTargetLanguageFromFile($document)) {
+ $expectedFileExtension = sprintf('%s.xlf', str_replace('-', '_', $targetLanguage));
+ $realFileExtension = explode('.', basename($file), 2)[1] ?? '';
+
+ if ($expectedFileExtension !== $realFileExtension) {
+ $errors[] = array(
+ 'line' => -1,
+ 'column' => -1,
+ 'message' => sprintf('There is a mismatch between the file extension ("%s") and the "%s" value used in the "target-language" attribute of the file.', $realFileExtension, $targetLanguage),
+ );
+ }
+ }
+
+ $document->schemaValidate(__DIR__.'/../Resources/schemas/xliff-core-1.2-strict.xsd');
+ foreach (libxml_get_errors() as $xmlError) {
+ $errors[] = array(
+ 'line' => $xmlError->line,
+ 'column' => $xmlError->column,
+ 'message' => trim($xmlError->message),
+ );
+ }
+
+ libxml_clear_errors();
+ libxml_use_internal_errors(false);
+
+ return array('file' => $file, 'valid' => 0 === \count($errors), 'messages' => $errors);
+ }
+
+ private function display(SymfonyStyle $io, array $files)
+ {
+ switch ($this->format) {
+ case 'txt':
+ return $this->displayTxt($io, $files);
+ case 'json':
+ return $this->displayJson($io, $files);
+ default:
+ throw new InvalidArgumentException(sprintf('The format "%s" is not supported.', $this->format));
+ }
+ }
+
+ private function displayTxt(SymfonyStyle $io, array $filesInfo)
+ {
+ $countFiles = \count($filesInfo);
+ $erroredFiles = 0;
+
+ foreach ($filesInfo as $info) {
+ if ($info['valid'] && $this->displayCorrectFiles) {
+ $io->comment('OK '.($info['file'] ? sprintf(' in %s', $info['file']) : ''));
+ } elseif (!$info['valid']) {
+ ++$erroredFiles;
+ $io->text(' ERROR '.($info['file'] ? sprintf(' in %s', $info['file']) : ''));
+ $io->listing(array_map(function ($error) {
+ // general document errors have a '-1' line number
+ return -1 === $error['line'] ? $error['message'] : sprintf('Line %d, Column %d: %s', $error['line'], $error['column'], $error['message']);
+ }, $info['messages']));
+ }
+ }
+
+ if (0 === $erroredFiles) {
+ $io->success(sprintf('All %d XLIFF files contain valid syntax.', $countFiles));
+ } else {
+ $io->warning(sprintf('%d XLIFF files have valid syntax and %d contain errors.', $countFiles - $erroredFiles, $erroredFiles));
+ }
+
+ return min($erroredFiles, 1);
+ }
+
+ private function displayJson(SymfonyStyle $io, array $filesInfo)
+ {
+ $errors = 0;
+
+ array_walk($filesInfo, function (&$v) use (&$errors) {
+ $v['file'] = (string) $v['file'];
+ if (!$v['valid']) {
+ ++$errors;
+ }
+ });
+
+ $io->writeln(json_encode($filesInfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
+
+ return min($errors, 1);
+ }
+
+ private function getFiles($fileOrDirectory)
+ {
+ if (is_file($fileOrDirectory)) {
+ yield new \SplFileInfo($fileOrDirectory);
+
+ return;
+ }
+
+ foreach ($this->getDirectoryIterator($fileOrDirectory) as $file) {
+ if (!\in_array($file->getExtension(), array('xlf', 'xliff'))) {
+ continue;
+ }
+
+ yield $file;
+ }
+ }
+
+ private function getStdin()
+ {
+ if (0 !== ftell(STDIN)) {
+ return;
+ }
+
+ $inputs = '';
+ while (!feof(STDIN)) {
+ $inputs .= fread(STDIN, 1024);
+ }
+
+ return $inputs;
+ }
+
+ private function getDirectoryIterator($directory)
+ {
+ $default = function ($directory) {
+ return new \RecursiveIteratorIterator(
+ new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS),
+ \RecursiveIteratorIterator::LEAVES_ONLY
+ );
+ };
+
+ if (null !== $this->directoryIteratorProvider) {
+ return \call_user_func($this->directoryIteratorProvider, $directory, $default);
+ }
+
+ return $default($directory);
+ }
+
+ private function isReadable($fileOrDirectory)
+ {
+ $default = function ($fileOrDirectory) {
+ return is_readable($fileOrDirectory);
+ };
+
+ if (null !== $this->isReadableProvider) {
+ return \call_user_func($this->isReadableProvider, $fileOrDirectory, $default);
+ }
+
+ return $default($fileOrDirectory);
+ }
+
+ private function getTargetLanguageFromFile(\DOMDocument $xliffContents): ?string
+ {
+ foreach ($xliffContents->getElementsByTagName('file')[0]->attributes ?? array() as $attribute) {
+ if ('target-language' === $attribute->nodeName) {
+ return $attribute->nodeValue;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/_include/calendar/symfony/translation/DataCollector/TranslationDataCollector.php b/_include/calendar/symfony/translation/DataCollector/TranslationDataCollector.php
new file mode 100644
index 0000000..edd712d
--- /dev/null
+++ b/_include/calendar/symfony/translation/DataCollector/TranslationDataCollector.php
@@ -0,0 +1,167 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\DataCollector;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\DataCollector\DataCollector;
+use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface;
+use Symfony\Component\Translation\DataCollectorTranslator;
+
+/**
+ * @author Abdellatif Ait boudad
+ */
+class TranslationDataCollector extends DataCollector implements LateDataCollectorInterface
+{
+ private $translator;
+
+ public function __construct(DataCollectorTranslator $translator)
+ {
+ $this->translator = $translator;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function lateCollect()
+ {
+ $messages = $this->sanitizeCollectedMessages($this->translator->getCollectedMessages());
+
+ $this->data = $this->computeCount($messages);
+ $this->data['messages'] = $messages;
+
+ $this->data['locale'] = $this->translator->getLocale();
+ $this->data['fallback_locales'] = $this->translator->getFallbackLocales();
+
+ $this->data = $this->cloneVar($this->data);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function collect(Request $request, Response $response, \Exception $exception = null)
+ {
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function reset()
+ {
+ $this->data = array();
+ }
+
+ /**
+ * @return array
+ */
+ public function getMessages()
+ {
+ return isset($this->data['messages']) ? $this->data['messages'] : array();
+ }
+
+ /**
+ * @return int
+ */
+ public function getCountMissings()
+ {
+ return isset($this->data[DataCollectorTranslator::MESSAGE_MISSING]) ? $this->data[DataCollectorTranslator::MESSAGE_MISSING] : 0;
+ }
+
+ /**
+ * @return int
+ */
+ public function getCountFallbacks()
+ {
+ return isset($this->data[DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK]) ? $this->data[DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK] : 0;
+ }
+
+ /**
+ * @return int
+ */
+ public function getCountDefines()
+ {
+ return isset($this->data[DataCollectorTranslator::MESSAGE_DEFINED]) ? $this->data[DataCollectorTranslator::MESSAGE_DEFINED] : 0;
+ }
+
+ public function getLocale()
+ {
+ return !empty($this->data['locale']) ? $this->data['locale'] : null;
+ }
+
+ public function getFallbackLocales()
+ {
+ return (isset($this->data['fallback_locales']) && \count($this->data['fallback_locales']) > 0) ? $this->data['fallback_locales'] : array();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName()
+ {
+ return 'translation';
+ }
+
+ private function sanitizeCollectedMessages($messages)
+ {
+ $result = array();
+ foreach ($messages as $key => $message) {
+ $messageId = $message['locale'].$message['domain'].$message['id'];
+
+ if (!isset($result[$messageId])) {
+ $message['count'] = 1;
+ $message['parameters'] = !empty($message['parameters']) ? array($message['parameters']) : array();
+ $messages[$key]['translation'] = $this->sanitizeString($message['translation']);
+ $result[$messageId] = $message;
+ } else {
+ if (!empty($message['parameters'])) {
+ $result[$messageId]['parameters'][] = $message['parameters'];
+ }
+
+ ++$result[$messageId]['count'];
+ }
+
+ unset($messages[$key]);
+ }
+
+ return $result;
+ }
+
+ private function computeCount($messages)
+ {
+ $count = array(
+ DataCollectorTranslator::MESSAGE_DEFINED => 0,
+ DataCollectorTranslator::MESSAGE_MISSING => 0,
+ DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK => 0,
+ );
+
+ foreach ($messages as $message) {
+ ++$count[$message['state']];
+ }
+
+ return $count;
+ }
+
+ private function sanitizeString($string, $length = 80)
+ {
+ $string = trim(preg_replace('/\s+/', ' ', $string));
+
+ if (false !== $encoding = mb_detect_encoding($string, null, true)) {
+ if (mb_strlen($string, $encoding) > $length) {
+ return mb_substr($string, 0, $length - 3, $encoding).'...';
+ }
+ } elseif (\strlen($string) > $length) {
+ return substr($string, 0, $length - 3).'...';
+ }
+
+ return $string;
+ }
+}
diff --git a/_include/calendar/symfony/translation/DataCollectorTranslator.php b/_include/calendar/symfony/translation/DataCollectorTranslator.php
new file mode 100644
index 0000000..5d4d819
--- /dev/null
+++ b/_include/calendar/symfony/translation/DataCollectorTranslator.php
@@ -0,0 +1,165 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation;
+
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+
+/**
+ * @author Abdellatif Ait boudad
+ */
+class DataCollectorTranslator implements TranslatorInterface, TranslatorBagInterface
+{
+ const MESSAGE_DEFINED = 0;
+ const MESSAGE_MISSING = 1;
+ const MESSAGE_EQUALS_FALLBACK = 2;
+
+ /**
+ * @var TranslatorInterface|TranslatorBagInterface
+ */
+ private $translator;
+
+ private $messages = array();
+
+ /**
+ * @param TranslatorInterface $translator The translator must implement TranslatorBagInterface
+ */
+ public function __construct(TranslatorInterface $translator)
+ {
+ if (!$translator instanceof TranslatorBagInterface) {
+ throw new InvalidArgumentException(sprintf('The Translator "%s" must implement TranslatorInterface and TranslatorBagInterface.', \get_class($translator)));
+ }
+
+ $this->translator = $translator;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function trans($id, array $parameters = array(), $domain = null, $locale = null)
+ {
+ $trans = $this->translator->trans($id, $parameters, $domain, $locale);
+ $this->collectMessage($locale, $domain, $id, $trans, $parameters);
+
+ return $trans;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null)
+ {
+ $trans = $this->translator->transChoice($id, $number, $parameters, $domain, $locale);
+ $this->collectMessage($locale, $domain, $id, $trans, $parameters, $number);
+
+ return $trans;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setLocale($locale)
+ {
+ $this->translator->setLocale($locale);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getLocale()
+ {
+ return $this->translator->getLocale();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getCatalogue($locale = null)
+ {
+ return $this->translator->getCatalogue($locale);
+ }
+
+ /**
+ * Gets the fallback locales.
+ *
+ * @return array $locales The fallback locales
+ */
+ public function getFallbackLocales()
+ {
+ if ($this->translator instanceof Translator || method_exists($this->translator, 'getFallbackLocales')) {
+ return $this->translator->getFallbackLocales();
+ }
+
+ return array();
+ }
+
+ /**
+ * Passes through all unknown calls onto the translator object.
+ */
+ public function __call($method, $args)
+ {
+ return \call_user_func_array(array($this->translator, $method), $args);
+ }
+
+ /**
+ * @return array
+ */
+ public function getCollectedMessages()
+ {
+ return $this->messages;
+ }
+
+ /**
+ * @param string|null $locale
+ * @param string|null $domain
+ * @param string $id
+ * @param string $translation
+ * @param array|null $parameters
+ * @param int|null $number
+ */
+ private function collectMessage($locale, $domain, $id, $translation, $parameters = array(), $number = null)
+ {
+ if (null === $domain) {
+ $domain = 'messages';
+ }
+
+ $id = (string) $id;
+ $catalogue = $this->translator->getCatalogue($locale);
+ $locale = $catalogue->getLocale();
+ if ($catalogue->defines($id, $domain)) {
+ $state = self::MESSAGE_DEFINED;
+ } elseif ($catalogue->has($id, $domain)) {
+ $state = self::MESSAGE_EQUALS_FALLBACK;
+
+ $fallbackCatalogue = $catalogue->getFallbackCatalogue();
+ while ($fallbackCatalogue) {
+ if ($fallbackCatalogue->defines($id, $domain)) {
+ $locale = $fallbackCatalogue->getLocale();
+ break;
+ }
+
+ $fallbackCatalogue = $fallbackCatalogue->getFallbackCatalogue();
+ }
+ } else {
+ $state = self::MESSAGE_MISSING;
+ }
+
+ $this->messages[] = array(
+ 'locale' => $locale,
+ 'domain' => $domain,
+ 'id' => $id,
+ 'translation' => $translation,
+ 'parameters' => $parameters,
+ 'transChoiceNumber' => $number,
+ 'state' => $state,
+ );
+ }
+}
diff --git a/_include/calendar/symfony/translation/DependencyInjection/TranslationDumperPass.php b/_include/calendar/symfony/translation/DependencyInjection/TranslationDumperPass.php
new file mode 100644
index 0000000..31d791d
--- /dev/null
+++ b/_include/calendar/symfony/translation/DependencyInjection/TranslationDumperPass.php
@@ -0,0 +1,44 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\DependencyInjection;
+
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+
+/**
+ * Adds tagged translation.formatter services to translation writer.
+ */
+class TranslationDumperPass implements CompilerPassInterface
+{
+ private $writerServiceId;
+ private $dumperTag;
+
+ public function __construct(string $writerServiceId = 'translation.writer', string $dumperTag = 'translation.dumper')
+ {
+ $this->writerServiceId = $writerServiceId;
+ $this->dumperTag = $dumperTag;
+ }
+
+ public function process(ContainerBuilder $container)
+ {
+ if (!$container->hasDefinition($this->writerServiceId)) {
+ return;
+ }
+
+ $definition = $container->getDefinition($this->writerServiceId);
+
+ foreach ($container->findTaggedServiceIds($this->dumperTag, true) as $id => $attributes) {
+ $definition->addMethodCall('addDumper', array($attributes[0]['alias'], new Reference($id)));
+ }
+ }
+}
diff --git a/_include/calendar/symfony/translation/DependencyInjection/TranslationExtractorPass.php b/_include/calendar/symfony/translation/DependencyInjection/TranslationExtractorPass.php
new file mode 100644
index 0000000..b7e6f73
--- /dev/null
+++ b/_include/calendar/symfony/translation/DependencyInjection/TranslationExtractorPass.php
@@ -0,0 +1,49 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\DependencyInjection;
+
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Exception\RuntimeException;
+use Symfony\Component\DependencyInjection\Reference;
+
+/**
+ * Adds tagged translation.extractor services to translation extractor.
+ */
+class TranslationExtractorPass implements CompilerPassInterface
+{
+ private $extractorServiceId;
+ private $extractorTag;
+
+ public function __construct(string $extractorServiceId = 'translation.extractor', string $extractorTag = 'translation.extractor')
+ {
+ $this->extractorServiceId = $extractorServiceId;
+ $this->extractorTag = $extractorTag;
+ }
+
+ public function process(ContainerBuilder $container)
+ {
+ if (!$container->hasDefinition($this->extractorServiceId)) {
+ return;
+ }
+
+ $definition = $container->getDefinition($this->extractorServiceId);
+
+ foreach ($container->findTaggedServiceIds($this->extractorTag, true) as $id => $attributes) {
+ if (!isset($attributes[0]['alias'])) {
+ throw new RuntimeException(sprintf('The alias for the tag "translation.extractor" of service "%s" must be set.', $id));
+ }
+
+ $definition->addMethodCall('addExtractor', array($attributes[0]['alias'], new Reference($id)));
+ }
+ }
+}
diff --git a/_include/calendar/symfony/translation/DependencyInjection/TranslatorPass.php b/_include/calendar/symfony/translation/DependencyInjection/TranslatorPass.php
new file mode 100644
index 0000000..cbaa43d
--- /dev/null
+++ b/_include/calendar/symfony/translation/DependencyInjection/TranslatorPass.php
@@ -0,0 +1,79 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\DependencyInjection;
+
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+
+class TranslatorPass implements CompilerPassInterface
+{
+ private $translatorServiceId;
+ private $readerServiceId;
+ private $loaderTag;
+ private $debugCommandServiceId;
+ private $updateCommandServiceId;
+
+ public function __construct(string $translatorServiceId = 'translator.default', string $readerServiceId = 'translation.reader', string $loaderTag = 'translation.loader', string $debugCommandServiceId = 'console.command.translation_debug', string $updateCommandServiceId = 'console.command.translation_update')
+ {
+ $this->translatorServiceId = $translatorServiceId;
+ $this->readerServiceId = $readerServiceId;
+ $this->loaderTag = $loaderTag;
+ $this->debugCommandServiceId = $debugCommandServiceId;
+ $this->updateCommandServiceId = $updateCommandServiceId;
+ }
+
+ public function process(ContainerBuilder $container)
+ {
+ if (!$container->hasDefinition($this->translatorServiceId)) {
+ return;
+ }
+
+ $loaders = array();
+ $loaderRefs = array();
+ foreach ($container->findTaggedServiceIds($this->loaderTag, true) as $id => $attributes) {
+ $loaderRefs[$id] = new Reference($id);
+ $loaders[$id][] = $attributes[0]['alias'];
+ if (isset($attributes[0]['legacy-alias'])) {
+ $loaders[$id][] = $attributes[0]['legacy-alias'];
+ }
+ }
+
+ if ($container->hasDefinition($this->readerServiceId)) {
+ $definition = $container->getDefinition($this->readerServiceId);
+ foreach ($loaders as $id => $formats) {
+ foreach ($formats as $format) {
+ $definition->addMethodCall('addLoader', array($format, $loaderRefs[$id]));
+ }
+ }
+ }
+
+ $container
+ ->findDefinition($this->translatorServiceId)
+ ->replaceArgument(0, ServiceLocatorTagPass::register($container, $loaderRefs))
+ ->replaceArgument(3, $loaders)
+ ;
+
+ if (!$container->hasParameter('twig.default_path')) {
+ return;
+ }
+
+ if ($container->hasDefinition($this->debugCommandServiceId)) {
+ $container->getDefinition($this->debugCommandServiceId)->replaceArgument(4, $container->getParameter('twig.default_path'));
+ }
+
+ if ($container->hasDefinition($this->updateCommandServiceId)) {
+ $container->getDefinition($this->updateCommandServiceId)->replaceArgument(5, $container->getParameter('twig.default_path'));
+ }
+ }
+}
diff --git a/_include/calendar/symfony/translation/Dumper/CsvFileDumper.php b/_include/calendar/symfony/translation/Dumper/CsvFileDumper.php
new file mode 100644
index 0000000..0880528
--- /dev/null
+++ b/_include/calendar/symfony/translation/Dumper/CsvFileDumper.php
@@ -0,0 +1,63 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * CsvFileDumper generates a csv formatted string representation of a message catalogue.
+ *
+ * @author Stealth35
+ */
+class CsvFileDumper extends FileDumper
+{
+ private $delimiter = ';';
+ private $enclosure = '"';
+
+ /**
+ * {@inheritdoc}
+ */
+ public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
+ {
+ $handle = fopen('php://memory', 'r+b');
+
+ foreach ($messages->all($domain) as $source => $target) {
+ fputcsv($handle, array($source, $target), $this->delimiter, $this->enclosure);
+ }
+
+ rewind($handle);
+ $output = stream_get_contents($handle);
+ fclose($handle);
+
+ return $output;
+ }
+
+ /**
+ * Sets the delimiter and escape character for CSV.
+ *
+ * @param string $delimiter Delimiter character
+ * @param string $enclosure Enclosure character
+ */
+ public function setCsvControl($delimiter = ';', $enclosure = '"')
+ {
+ $this->delimiter = $delimiter;
+ $this->enclosure = $enclosure;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExtension()
+ {
+ return 'csv';
+ }
+}
diff --git a/_include/calendar/symfony/translation/Dumper/DumperInterface.php b/_include/calendar/symfony/translation/Dumper/DumperInterface.php
new file mode 100644
index 0000000..cebc65e
--- /dev/null
+++ b/_include/calendar/symfony/translation/Dumper/DumperInterface.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * DumperInterface is the interface implemented by all translation dumpers.
+ * There is no common option.
+ *
+ * @author Michel Salib
+ */
+interface DumperInterface
+{
+ /**
+ * Dumps the message catalogue.
+ *
+ * @param MessageCatalogue $messages The message catalogue
+ * @param array $options Options that are used by the dumper
+ */
+ public function dump(MessageCatalogue $messages, $options = array());
+}
diff --git a/_include/calendar/symfony/translation/Dumper/FileDumper.php b/_include/calendar/symfony/translation/Dumper/FileDumper.php
new file mode 100644
index 0000000..5b10e45
--- /dev/null
+++ b/_include/calendar/symfony/translation/Dumper/FileDumper.php
@@ -0,0 +1,113 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+use Symfony\Component\Translation\Exception\RuntimeException;
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * FileDumper is an implementation of DumperInterface that dump a message catalogue to file(s).
+ *
+ * Options:
+ * - path (mandatory): the directory where the files should be saved
+ *
+ * @author Michel Salib
+ */
+abstract class FileDumper implements DumperInterface
+{
+ /**
+ * A template for the relative paths to files.
+ *
+ * @var string
+ */
+ protected $relativePathTemplate = '%domain%.%locale%.%extension%';
+
+ /**
+ * Sets the template for the relative paths to files.
+ *
+ * @param string $relativePathTemplate A template for the relative paths to files
+ */
+ public function setRelativePathTemplate($relativePathTemplate)
+ {
+ $this->relativePathTemplate = $relativePathTemplate;
+ }
+
+ /**
+ * Sets backup flag.
+ *
+ * @param bool
+ *
+ * @deprecated since Symfony 4.1
+ */
+ public function setBackup($backup)
+ {
+ @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1.', __METHOD__), E_USER_DEPRECATED);
+
+ if (false !== $backup) {
+ throw new \LogicException('The backup feature is no longer supported.');
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function dump(MessageCatalogue $messages, $options = array())
+ {
+ if (!array_key_exists('path', $options)) {
+ throw new InvalidArgumentException('The file dumper needs a path option.');
+ }
+
+ // save a file for each domain
+ foreach ($messages->getDomains() as $domain) {
+ $fullpath = $options['path'].'/'.$this->getRelativePath($domain, $messages->getLocale());
+ if (!file_exists($fullpath)) {
+ $directory = \dirname($fullpath);
+ if (!file_exists($directory) && !@mkdir($directory, 0777, true)) {
+ throw new RuntimeException(sprintf('Unable to create directory "%s".', $directory));
+ }
+ }
+ // save file
+ file_put_contents($fullpath, $this->formatCatalogue($messages, $domain, $options));
+ }
+ }
+
+ /**
+ * Transforms a domain of a message catalogue to its string representation.
+ *
+ * @param MessageCatalogue $messages
+ * @param string $domain
+ * @param array $options
+ *
+ * @return string representation
+ */
+ abstract public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array());
+
+ /**
+ * Gets the file extension of the dumper.
+ *
+ * @return string file extension
+ */
+ abstract protected function getExtension();
+
+ /**
+ * Gets the relative file path using the template.
+ */
+ private function getRelativePath(string $domain, string $locale): string
+ {
+ return strtr($this->relativePathTemplate, array(
+ '%domain%' => $domain,
+ '%locale%' => $locale,
+ '%extension%' => $this->getExtension(),
+ ));
+ }
+}
diff --git a/_include/calendar/symfony/translation/Dumper/IcuResFileDumper.php b/_include/calendar/symfony/translation/Dumper/IcuResFileDumper.php
new file mode 100644
index 0000000..efa9d7f
--- /dev/null
+++ b/_include/calendar/symfony/translation/Dumper/IcuResFileDumper.php
@@ -0,0 +1,106 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * IcuResDumper generates an ICU ResourceBundle formatted string representation of a message catalogue.
+ *
+ * @author Stealth35
+ */
+class IcuResFileDumper extends FileDumper
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected $relativePathTemplate = '%domain%/%locale%.%extension%';
+
+ /**
+ * {@inheritdoc}
+ */
+ public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
+ {
+ $data = $indexes = $resources = '';
+
+ foreach ($messages->all($domain) as $source => $target) {
+ $indexes .= pack('v', \strlen($data) + 28);
+ $data .= $source."\0";
+ }
+
+ $data .= $this->writePadding($data);
+
+ $keyTop = $this->getPosition($data);
+
+ foreach ($messages->all($domain) as $source => $target) {
+ $resources .= pack('V', $this->getPosition($data));
+
+ $data .= pack('V', \strlen($target))
+ .mb_convert_encoding($target."\0", 'UTF-16LE', 'UTF-8')
+ .$this->writePadding($data)
+ ;
+ }
+
+ $resOffset = $this->getPosition($data);
+
+ $data .= pack('v', \count($messages->all($domain)))
+ .$indexes
+ .$this->writePadding($data)
+ .$resources
+ ;
+
+ $bundleTop = $this->getPosition($data);
+
+ $root = pack('V7',
+ $resOffset + (2 << 28), // Resource Offset + Resource Type
+ 6, // Index length
+ $keyTop, // Index keys top
+ $bundleTop, // Index resources top
+ $bundleTop, // Index bundle top
+ \count($messages->all($domain)), // Index max table length
+ 0 // Index attributes
+ );
+
+ $header = pack('vC2v4C12@32',
+ 32, // Header size
+ 0xDA, 0x27, // Magic number 1 and 2
+ 20, 0, 0, 2, // Rest of the header, ..., Size of a char
+ 0x52, 0x65, 0x73, 0x42, // Data format identifier
+ 1, 2, 0, 0, // Data version
+ 1, 4, 0, 0 // Unicode version
+ );
+
+ return $header.$root.$data;
+ }
+
+ private function writePadding($data)
+ {
+ $padding = \strlen($data) % 4;
+
+ if ($padding) {
+ return str_repeat("\xAA", 4 - $padding);
+ }
+ }
+
+ private function getPosition($data)
+ {
+ return (\strlen($data) + 28) / 4;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExtension()
+ {
+ return 'res';
+ }
+}
diff --git a/_include/calendar/symfony/translation/Dumper/IniFileDumper.php b/_include/calendar/symfony/translation/Dumper/IniFileDumper.php
new file mode 100644
index 0000000..9ed3754
--- /dev/null
+++ b/_include/calendar/symfony/translation/Dumper/IniFileDumper.php
@@ -0,0 +1,45 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * IniFileDumper generates an ini formatted string representation of a message catalogue.
+ *
+ * @author Stealth35
+ */
+class IniFileDumper extends FileDumper
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
+ {
+ $output = '';
+
+ foreach ($messages->all($domain) as $source => $target) {
+ $escapeTarget = str_replace('"', '\"', $target);
+ $output .= $source.'="'.$escapeTarget."\"\n";
+ }
+
+ return $output;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExtension()
+ {
+ return 'ini';
+ }
+}
diff --git a/_include/calendar/symfony/translation/Dumper/JsonFileDumper.php b/_include/calendar/symfony/translation/Dumper/JsonFileDumper.php
new file mode 100644
index 0000000..32bdaf5
--- /dev/null
+++ b/_include/calendar/symfony/translation/Dumper/JsonFileDumper.php
@@ -0,0 +1,44 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * JsonFileDumper generates an json formatted string representation of a message catalogue.
+ *
+ * @author singles
+ */
+class JsonFileDumper extends FileDumper
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
+ {
+ if (isset($options['json_encoding'])) {
+ $flags = $options['json_encoding'];
+ } else {
+ $flags = JSON_PRETTY_PRINT;
+ }
+
+ return json_encode($messages->all($domain), $flags);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExtension()
+ {
+ return 'json';
+ }
+}
diff --git a/_include/calendar/symfony/translation/Dumper/MoFileDumper.php b/_include/calendar/symfony/translation/Dumper/MoFileDumper.php
new file mode 100644
index 0000000..32ed0ac
--- /dev/null
+++ b/_include/calendar/symfony/translation/Dumper/MoFileDumper.php
@@ -0,0 +1,82 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\Loader\MoFileLoader;
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * MoFileDumper generates a gettext formatted string representation of a message catalogue.
+ *
+ * @author Stealth35
+ */
+class MoFileDumper extends FileDumper
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
+ {
+ $sources = $targets = $sourceOffsets = $targetOffsets = '';
+ $offsets = array();
+ $size = 0;
+
+ foreach ($messages->all($domain) as $source => $target) {
+ $offsets[] = array_map('strlen', array($sources, $source, $targets, $target));
+ $sources .= "\0".$source;
+ $targets .= "\0".$target;
+ ++$size;
+ }
+
+ $header = array(
+ 'magicNumber' => MoFileLoader::MO_LITTLE_ENDIAN_MAGIC,
+ 'formatRevision' => 0,
+ 'count' => $size,
+ 'offsetId' => MoFileLoader::MO_HEADER_SIZE,
+ 'offsetTranslated' => MoFileLoader::MO_HEADER_SIZE + (8 * $size),
+ 'sizeHashes' => 0,
+ 'offsetHashes' => MoFileLoader::MO_HEADER_SIZE + (16 * $size),
+ );
+
+ $sourcesSize = \strlen($sources);
+ $sourcesStart = $header['offsetHashes'] + 1;
+
+ foreach ($offsets as $offset) {
+ $sourceOffsets .= $this->writeLong($offset[1])
+ .$this->writeLong($offset[0] + $sourcesStart);
+ $targetOffsets .= $this->writeLong($offset[3])
+ .$this->writeLong($offset[2] + $sourcesStart + $sourcesSize);
+ }
+
+ $output = implode('', array_map(array($this, 'writeLong'), $header))
+ .$sourceOffsets
+ .$targetOffsets
+ .$sources
+ .$targets
+ ;
+
+ return $output;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExtension()
+ {
+ return 'mo';
+ }
+
+ private function writeLong($str)
+ {
+ return pack('V*', $str);
+ }
+}
diff --git a/_include/calendar/symfony/translation/Dumper/PhpFileDumper.php b/_include/calendar/symfony/translation/Dumper/PhpFileDumper.php
new file mode 100644
index 0000000..c7c37aa
--- /dev/null
+++ b/_include/calendar/symfony/translation/Dumper/PhpFileDumper.php
@@ -0,0 +1,38 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * PhpFileDumper generates PHP files from a message catalogue.
+ *
+ * @author Michel Salib
+ */
+class PhpFileDumper extends FileDumper
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
+ {
+ return "all($domain), true).";\n";
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExtension()
+ {
+ return 'php';
+ }
+}
diff --git a/_include/calendar/symfony/translation/Dumper/PoFileDumper.php b/_include/calendar/symfony/translation/Dumper/PoFileDumper.php
new file mode 100644
index 0000000..ed4418b
--- /dev/null
+++ b/_include/calendar/symfony/translation/Dumper/PoFileDumper.php
@@ -0,0 +1,61 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * PoFileDumper generates a gettext formatted string representation of a message catalogue.
+ *
+ * @author Stealth35
+ */
+class PoFileDumper extends FileDumper
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
+ {
+ $output = 'msgid ""'."\n";
+ $output .= 'msgstr ""'."\n";
+ $output .= '"Content-Type: text/plain; charset=UTF-8\n"'."\n";
+ $output .= '"Content-Transfer-Encoding: 8bit\n"'."\n";
+ $output .= '"Language: '.$messages->getLocale().'\n"'."\n";
+ $output .= "\n";
+
+ $newLine = false;
+ foreach ($messages->all($domain) as $source => $target) {
+ if ($newLine) {
+ $output .= "\n";
+ } else {
+ $newLine = true;
+ }
+ $output .= sprintf('msgid "%s"'."\n", $this->escape($source));
+ $output .= sprintf('msgstr "%s"', $this->escape($target));
+ }
+
+ return $output;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExtension()
+ {
+ return 'po';
+ }
+
+ private function escape($str)
+ {
+ return addcslashes($str, "\0..\37\42\134");
+ }
+}
diff --git a/_include/calendar/symfony/translation/Dumper/QtFileDumper.php b/_include/calendar/symfony/translation/Dumper/QtFileDumper.php
new file mode 100644
index 0000000..a9073f2
--- /dev/null
+++ b/_include/calendar/symfony/translation/Dumper/QtFileDumper.php
@@ -0,0 +1,50 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * QtFileDumper generates ts files from a message catalogue.
+ *
+ * @author Benjamin Eberlei
+ */
+class QtFileDumper extends FileDumper
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
+ {
+ $dom = new \DOMDocument('1.0', 'utf-8');
+ $dom->formatOutput = true;
+ $ts = $dom->appendChild($dom->createElement('TS'));
+ $context = $ts->appendChild($dom->createElement('context'));
+ $context->appendChild($dom->createElement('name', $domain));
+
+ foreach ($messages->all($domain) as $source => $target) {
+ $message = $context->appendChild($dom->createElement('message'));
+ $message->appendChild($dom->createElement('source', $source));
+ $message->appendChild($dom->createElement('translation', $target));
+ }
+
+ return $dom->saveXML();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExtension()
+ {
+ return 'ts';
+ }
+}
diff --git a/_include/calendar/symfony/translation/Dumper/XliffFileDumper.php b/_include/calendar/symfony/translation/Dumper/XliffFileDumper.php
new file mode 100644
index 0000000..c12454c
--- /dev/null
+++ b/_include/calendar/symfony/translation/Dumper/XliffFileDumper.php
@@ -0,0 +1,205 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * XliffFileDumper generates xliff files from a message catalogue.
+ *
+ * @author Michel Salib
+ */
+class XliffFileDumper extends FileDumper
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
+ {
+ $xliffVersion = '1.2';
+ if (array_key_exists('xliff_version', $options)) {
+ $xliffVersion = $options['xliff_version'];
+ }
+
+ if (array_key_exists('default_locale', $options)) {
+ $defaultLocale = $options['default_locale'];
+ } else {
+ $defaultLocale = \Locale::getDefault();
+ }
+
+ if ('1.2' === $xliffVersion) {
+ return $this->dumpXliff1($defaultLocale, $messages, $domain, $options);
+ }
+ if ('2.0' === $xliffVersion) {
+ return $this->dumpXliff2($defaultLocale, $messages, $domain, $options);
+ }
+
+ throw new InvalidArgumentException(sprintf('No support implemented for dumping XLIFF version "%s".', $xliffVersion));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExtension()
+ {
+ return 'xlf';
+ }
+
+ private function dumpXliff1($defaultLocale, MessageCatalogue $messages, $domain, array $options = array())
+ {
+ $toolInfo = array('tool-id' => 'symfony', 'tool-name' => 'Symfony');
+ if (array_key_exists('tool_info', $options)) {
+ $toolInfo = array_merge($toolInfo, $options['tool_info']);
+ }
+
+ $dom = new \DOMDocument('1.0', 'utf-8');
+ $dom->formatOutput = true;
+
+ $xliff = $dom->appendChild($dom->createElement('xliff'));
+ $xliff->setAttribute('version', '1.2');
+ $xliff->setAttribute('xmlns', 'urn:oasis:names:tc:xliff:document:1.2');
+
+ $xliffFile = $xliff->appendChild($dom->createElement('file'));
+ $xliffFile->setAttribute('source-language', str_replace('_', '-', $defaultLocale));
+ $xliffFile->setAttribute('target-language', str_replace('_', '-', $messages->getLocale()));
+ $xliffFile->setAttribute('datatype', 'plaintext');
+ $xliffFile->setAttribute('original', 'file.ext');
+
+ $xliffHead = $xliffFile->appendChild($dom->createElement('header'));
+ $xliffTool = $xliffHead->appendChild($dom->createElement('tool'));
+ foreach ($toolInfo as $id => $value) {
+ $xliffTool->setAttribute($id, $value);
+ }
+
+ $xliffBody = $xliffFile->appendChild($dom->createElement('body'));
+ foreach ($messages->all($domain) as $source => $target) {
+ $translation = $dom->createElement('trans-unit');
+
+ $translation->setAttribute('id', strtr(substr(base64_encode(hash('sha256', $source, true)), 0, 7), '/+', '._'));
+ $translation->setAttribute('resname', $source);
+
+ $s = $translation->appendChild($dom->createElement('source'));
+ $s->appendChild($dom->createTextNode($source));
+
+ // Does the target contain characters requiring a CDATA section?
+ $text = 1 === preg_match('/[&<>]/', $target) ? $dom->createCDATASection($target) : $dom->createTextNode($target);
+
+ $targetElement = $dom->createElement('target');
+ $metadata = $messages->getMetadata($source, $domain);
+ if ($this->hasMetadataArrayInfo('target-attributes', $metadata)) {
+ foreach ($metadata['target-attributes'] as $name => $value) {
+ $targetElement->setAttribute($name, $value);
+ }
+ }
+ $t = $translation->appendChild($targetElement);
+ $t->appendChild($text);
+
+ if ($this->hasMetadataArrayInfo('notes', $metadata)) {
+ foreach ($metadata['notes'] as $note) {
+ if (!isset($note['content'])) {
+ continue;
+ }
+
+ $n = $translation->appendChild($dom->createElement('note'));
+ $n->appendChild($dom->createTextNode($note['content']));
+
+ if (isset($note['priority'])) {
+ $n->setAttribute('priority', $note['priority']);
+ }
+
+ if (isset($note['from'])) {
+ $n->setAttribute('from', $note['from']);
+ }
+ }
+ }
+
+ $xliffBody->appendChild($translation);
+ }
+
+ return $dom->saveXML();
+ }
+
+ private function dumpXliff2($defaultLocale, MessageCatalogue $messages, $domain, array $options = array())
+ {
+ $dom = new \DOMDocument('1.0', 'utf-8');
+ $dom->formatOutput = true;
+
+ $xliff = $dom->appendChild($dom->createElement('xliff'));
+ $xliff->setAttribute('xmlns', 'urn:oasis:names:tc:xliff:document:2.0');
+ $xliff->setAttribute('version', '2.0');
+ $xliff->setAttribute('srcLang', str_replace('_', '-', $defaultLocale));
+ $xliff->setAttribute('trgLang', str_replace('_', '-', $messages->getLocale()));
+
+ $xliffFile = $xliff->appendChild($dom->createElement('file'));
+ $xliffFile->setAttribute('id', $domain.'.'.$messages->getLocale());
+
+ foreach ($messages->all($domain) as $source => $target) {
+ $translation = $dom->createElement('unit');
+ $translation->setAttribute('id', strtr(substr(base64_encode(hash('sha256', $source, true)), 0, 7), '/+', '._'));
+ $name = $source;
+ if (\strlen($source) > 80) {
+ $name = substr(md5($source), -7);
+ }
+ $translation->setAttribute('name', $name);
+ $metadata = $messages->getMetadata($source, $domain);
+
+ // Add notes section
+ if ($this->hasMetadataArrayInfo('notes', $metadata)) {
+ $notesElement = $dom->createElement('notes');
+ foreach ($metadata['notes'] as $note) {
+ $n = $dom->createElement('note');
+ $n->appendChild($dom->createTextNode(isset($note['content']) ? $note['content'] : ''));
+ unset($note['content']);
+
+ foreach ($note as $name => $value) {
+ $n->setAttribute($name, $value);
+ }
+ $notesElement->appendChild($n);
+ }
+ $translation->appendChild($notesElement);
+ }
+
+ $segment = $translation->appendChild($dom->createElement('segment'));
+
+ $s = $segment->appendChild($dom->createElement('source'));
+ $s->appendChild($dom->createTextNode($source));
+
+ // Does the target contain characters requiring a CDATA section?
+ $text = 1 === preg_match('/[&<>]/', $target) ? $dom->createCDATASection($target) : $dom->createTextNode($target);
+
+ $targetElement = $dom->createElement('target');
+ if ($this->hasMetadataArrayInfo('target-attributes', $metadata)) {
+ foreach ($metadata['target-attributes'] as $name => $value) {
+ $targetElement->setAttribute($name, $value);
+ }
+ }
+ $t = $segment->appendChild($targetElement);
+ $t->appendChild($text);
+
+ $xliffFile->appendChild($translation);
+ }
+
+ return $dom->saveXML();
+ }
+
+ /**
+ * @param string $key
+ * @param array|null $metadata
+ *
+ * @return bool
+ */
+ private function hasMetadataArrayInfo($key, $metadata = null)
+ {
+ return null !== $metadata && array_key_exists($key, $metadata) && ($metadata[$key] instanceof \Traversable || \is_array($metadata[$key]));
+ }
+}
diff --git a/_include/calendar/symfony/translation/Dumper/YamlFileDumper.php b/_include/calendar/symfony/translation/Dumper/YamlFileDumper.php
new file mode 100644
index 0000000..b19b9ae
--- /dev/null
+++ b/_include/calendar/symfony/translation/Dumper/YamlFileDumper.php
@@ -0,0 +1,62 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\Exception\LogicException;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Util\ArrayConverter;
+use Symfony\Component\Yaml\Yaml;
+
+/**
+ * YamlFileDumper generates yaml files from a message catalogue.
+ *
+ * @author Michel Salib
+ */
+class YamlFileDumper extends FileDumper
+{
+ private $extension;
+
+ public function __construct(string $extension = 'yml')
+ {
+ $this->extension = $extension;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
+ {
+ if (!class_exists('Symfony\Component\Yaml\Yaml')) {
+ throw new LogicException('Dumping translations in the YAML format requires the Symfony Yaml component.');
+ }
+
+ $data = $messages->all($domain);
+
+ if (isset($options['as_tree']) && $options['as_tree']) {
+ $data = ArrayConverter::expandToTree($data);
+ }
+
+ if (isset($options['inline']) && ($inline = (int) $options['inline']) > 0) {
+ return Yaml::dump($data, $inline);
+ }
+
+ return Yaml::dump($data);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExtension()
+ {
+ return $this->extension;
+ }
+}
diff --git a/_include/calendar/symfony/translation/Exception/ExceptionInterface.php b/_include/calendar/symfony/translation/Exception/ExceptionInterface.php
new file mode 100644
index 0000000..c85fb93
--- /dev/null
+++ b/_include/calendar/symfony/translation/Exception/ExceptionInterface.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Exception;
+
+/**
+ * Exception interface for all exceptions thrown by the component.
+ *
+ * @author Fabien Potencier
+ */
+interface ExceptionInterface
+{
+}
diff --git a/_include/calendar/symfony/translation/Exception/InvalidArgumentException.php b/_include/calendar/symfony/translation/Exception/InvalidArgumentException.php
new file mode 100644
index 0000000..90d0669
--- /dev/null
+++ b/_include/calendar/symfony/translation/Exception/InvalidArgumentException.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Exception;
+
+/**
+ * Base InvalidArgumentException for the Translation component.
+ *
+ * @author Abdellatif Ait boudad
+ */
+class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
+{
+}
diff --git a/_include/calendar/symfony/translation/Exception/InvalidResourceException.php b/_include/calendar/symfony/translation/Exception/InvalidResourceException.php
new file mode 100644
index 0000000..cf07943
--- /dev/null
+++ b/_include/calendar/symfony/translation/Exception/InvalidResourceException.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Exception;
+
+/**
+ * Thrown when a resource cannot be loaded.
+ *
+ * @author Fabien Potencier
+ */
+class InvalidResourceException extends \InvalidArgumentException implements ExceptionInterface
+{
+}
diff --git a/_include/calendar/symfony/translation/Exception/LogicException.php b/_include/calendar/symfony/translation/Exception/LogicException.php
new file mode 100644
index 0000000..9019c7e
--- /dev/null
+++ b/_include/calendar/symfony/translation/Exception/LogicException.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Exception;
+
+/**
+ * Base LogicException for Translation component.
+ *
+ * @author Abdellatif Ait boudad
+ */
+class LogicException extends \LogicException implements ExceptionInterface
+{
+}
diff --git a/_include/calendar/symfony/translation/Exception/NotFoundResourceException.php b/_include/calendar/symfony/translation/Exception/NotFoundResourceException.php
new file mode 100644
index 0000000..cff73ae
--- /dev/null
+++ b/_include/calendar/symfony/translation/Exception/NotFoundResourceException.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Exception;
+
+/**
+ * Thrown when a resource does not exist.
+ *
+ * @author Fabien Potencier
+ */
+class NotFoundResourceException extends \InvalidArgumentException implements ExceptionInterface
+{
+}
diff --git a/_include/calendar/symfony/translation/Exception/RuntimeException.php b/_include/calendar/symfony/translation/Exception/RuntimeException.php
new file mode 100644
index 0000000..dcd7940
--- /dev/null
+++ b/_include/calendar/symfony/translation/Exception/RuntimeException.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Exception;
+
+/**
+ * Base RuntimeException for the Translation component.
+ *
+ * @author Abdellatif Ait boudad
+ */
+class RuntimeException extends \RuntimeException implements ExceptionInterface
+{
+}
diff --git a/_include/calendar/symfony/translation/Extractor/AbstractFileExtractor.php b/_include/calendar/symfony/translation/Extractor/AbstractFileExtractor.php
new file mode 100644
index 0000000..bebd6d9
--- /dev/null
+++ b/_include/calendar/symfony/translation/Extractor/AbstractFileExtractor.php
@@ -0,0 +1,80 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Extractor;
+
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+
+/**
+ * Base class used by classes that extract translation messages from files.
+ *
+ * @author Marcos D. Sánchez
+ */
+abstract class AbstractFileExtractor
+{
+ /**
+ * @param string|array $resource Files, a file or a directory
+ *
+ * @return array
+ */
+ protected function extractFiles($resource)
+ {
+ if (\is_array($resource) || $resource instanceof \Traversable) {
+ $files = array();
+ foreach ($resource as $file) {
+ if ($this->canBeExtracted($file)) {
+ $files[] = $this->toSplFileInfo($file);
+ }
+ }
+ } elseif (is_file($resource)) {
+ $files = $this->canBeExtracted($resource) ? array($this->toSplFileInfo($resource)) : array();
+ } else {
+ $files = $this->extractFromDirectory($resource);
+ }
+
+ return $files;
+ }
+
+ private function toSplFileInfo(string $file): \SplFileInfo
+ {
+ return new \SplFileInfo($file);
+ }
+
+ /**
+ * @param string $file
+ *
+ * @return bool
+ *
+ * @throws InvalidArgumentException
+ */
+ protected function isFile($file)
+ {
+ if (!is_file($file)) {
+ throw new InvalidArgumentException(sprintf('The "%s" file does not exist.', $file));
+ }
+
+ return true;
+ }
+
+ /**
+ * @param string $file
+ *
+ * @return bool
+ */
+ abstract protected function canBeExtracted($file);
+
+ /**
+ * @param string|array $resource Files, a file or a directory
+ *
+ * @return array files to be extracted
+ */
+ abstract protected function extractFromDirectory($resource);
+}
diff --git a/_include/calendar/symfony/translation/Extractor/ChainExtractor.php b/_include/calendar/symfony/translation/Extractor/ChainExtractor.php
new file mode 100644
index 0000000..50e3c84
--- /dev/null
+++ b/_include/calendar/symfony/translation/Extractor/ChainExtractor.php
@@ -0,0 +1,60 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Extractor;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * ChainExtractor extracts translation messages from template files.
+ *
+ * @author Michel Salib
+ */
+class ChainExtractor implements ExtractorInterface
+{
+ /**
+ * The extractors.
+ *
+ * @var ExtractorInterface[]
+ */
+ private $extractors = array();
+
+ /**
+ * Adds a loader to the translation extractor.
+ *
+ * @param string $format The format of the loader
+ * @param ExtractorInterface $extractor The loader
+ */
+ public function addExtractor($format, ExtractorInterface $extractor)
+ {
+ $this->extractors[$format] = $extractor;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setPrefix($prefix)
+ {
+ foreach ($this->extractors as $extractor) {
+ $extractor->setPrefix($prefix);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function extract($directory, MessageCatalogue $catalogue)
+ {
+ foreach ($this->extractors as $extractor) {
+ $extractor->extract($directory, $catalogue);
+ }
+ }
+}
diff --git a/_include/calendar/symfony/translation/Extractor/ExtractorInterface.php b/_include/calendar/symfony/translation/Extractor/ExtractorInterface.php
new file mode 100644
index 0000000..b4534fa
--- /dev/null
+++ b/_include/calendar/symfony/translation/Extractor/ExtractorInterface.php
@@ -0,0 +1,38 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Extractor;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * Extracts translation messages from a directory or files to the catalogue.
+ * New found messages are injected to the catalogue using the prefix.
+ *
+ * @author Michel Salib
+ */
+interface ExtractorInterface
+{
+ /**
+ * Extracts translation messages from files, a file or a directory to the catalogue.
+ *
+ * @param string|array $resource Files, a file or a directory
+ * @param MessageCatalogue $catalogue The catalogue
+ */
+ public function extract($resource, MessageCatalogue $catalogue);
+
+ /**
+ * Sets the prefix that should be used for new found messages.
+ *
+ * @param string $prefix The prefix
+ */
+ public function setPrefix($prefix);
+}
diff --git a/_include/calendar/symfony/translation/Extractor/PhpExtractor.php b/_include/calendar/symfony/translation/Extractor/PhpExtractor.php
new file mode 100644
index 0000000..b4b3236
--- /dev/null
+++ b/_include/calendar/symfony/translation/Extractor/PhpExtractor.php
@@ -0,0 +1,256 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Extractor;
+
+use Symfony\Component\Finder\Finder;
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * PhpExtractor extracts translation messages from a PHP template.
+ *
+ * @author Michel Salib
+ */
+class PhpExtractor extends AbstractFileExtractor implements ExtractorInterface
+{
+ const MESSAGE_TOKEN = 300;
+ const METHOD_ARGUMENTS_TOKEN = 1000;
+ const DOMAIN_TOKEN = 1001;
+
+ /**
+ * Prefix for new found message.
+ *
+ * @var string
+ */
+ private $prefix = '';
+
+ /**
+ * The sequence that captures translation messages.
+ *
+ * @var array
+ */
+ protected $sequences = array(
+ array(
+ '->',
+ 'trans',
+ '(',
+ self::MESSAGE_TOKEN,
+ ',',
+ self::METHOD_ARGUMENTS_TOKEN,
+ ',',
+ self::DOMAIN_TOKEN,
+ ),
+ array(
+ '->',
+ 'transChoice',
+ '(',
+ self::MESSAGE_TOKEN,
+ ',',
+ self::METHOD_ARGUMENTS_TOKEN,
+ ',',
+ self::METHOD_ARGUMENTS_TOKEN,
+ ',',
+ self::DOMAIN_TOKEN,
+ ),
+ array(
+ '->',
+ 'trans',
+ '(',
+ self::MESSAGE_TOKEN,
+ ),
+ array(
+ '->',
+ 'transChoice',
+ '(',
+ self::MESSAGE_TOKEN,
+ ),
+ );
+
+ /**
+ * {@inheritdoc}
+ */
+ public function extract($resource, MessageCatalogue $catalog)
+ {
+ $files = $this->extractFiles($resource);
+ foreach ($files as $file) {
+ $this->parseTokens(token_get_all(file_get_contents($file)), $catalog);
+
+ // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098
+ gc_mem_caches();
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setPrefix($prefix)
+ {
+ $this->prefix = $prefix;
+ }
+
+ /**
+ * Normalizes a token.
+ *
+ * @param mixed $token
+ *
+ * @return string
+ */
+ protected function normalizeToken($token)
+ {
+ if (isset($token[1]) && 'b"' !== $token) {
+ return $token[1];
+ }
+
+ return $token;
+ }
+
+ /**
+ * Seeks to a non-whitespace token.
+ */
+ private function seekToNextRelevantToken(\Iterator $tokenIterator)
+ {
+ for (; $tokenIterator->valid(); $tokenIterator->next()) {
+ $t = $tokenIterator->current();
+ if (T_WHITESPACE !== $t[0]) {
+ break;
+ }
+ }
+ }
+
+ private function skipMethodArgument(\Iterator $tokenIterator)
+ {
+ $openBraces = 0;
+
+ for (; $tokenIterator->valid(); $tokenIterator->next()) {
+ $t = $tokenIterator->current();
+
+ if ('[' === $t[0] || '(' === $t[0]) {
+ ++$openBraces;
+ }
+
+ if (']' === $t[0] || ')' === $t[0]) {
+ --$openBraces;
+ }
+
+ if ((0 === $openBraces && ',' === $t[0]) || (-1 === $openBraces && ')' === $t[0])) {
+ break;
+ }
+ }
+ }
+
+ /**
+ * Extracts the message from the iterator while the tokens
+ * match allowed message tokens.
+ */
+ private function getValue(\Iterator $tokenIterator)
+ {
+ $message = '';
+ $docToken = '';
+
+ for (; $tokenIterator->valid(); $tokenIterator->next()) {
+ $t = $tokenIterator->current();
+ if (!isset($t[1])) {
+ break;
+ }
+
+ switch ($t[0]) {
+ case T_START_HEREDOC:
+ $docToken = $t[1];
+ break;
+ case T_ENCAPSED_AND_WHITESPACE:
+ case T_CONSTANT_ENCAPSED_STRING:
+ $message .= $t[1];
+ break;
+ case T_END_HEREDOC:
+ return PhpStringTokenParser::parseDocString($docToken, $message);
+ default:
+ break 2;
+ }
+ }
+
+ if ($message) {
+ $message = PhpStringTokenParser::parse($message);
+ }
+
+ return $message;
+ }
+
+ /**
+ * Extracts trans message from PHP tokens.
+ *
+ * @param array $tokens
+ * @param MessageCatalogue $catalog
+ */
+ protected function parseTokens($tokens, MessageCatalogue $catalog)
+ {
+ $tokenIterator = new \ArrayIterator($tokens);
+
+ for ($key = 0; $key < $tokenIterator->count(); ++$key) {
+ foreach ($this->sequences as $sequence) {
+ $message = '';
+ $domain = 'messages';
+ $tokenIterator->seek($key);
+
+ foreach ($sequence as $sequenceKey => $item) {
+ $this->seekToNextRelevantToken($tokenIterator);
+
+ if ($this->normalizeToken($tokenIterator->current()) === $item) {
+ $tokenIterator->next();
+ continue;
+ } elseif (self::MESSAGE_TOKEN === $item) {
+ $message = $this->getValue($tokenIterator);
+
+ if (\count($sequence) === ($sequenceKey + 1)) {
+ break;
+ }
+ } elseif (self::METHOD_ARGUMENTS_TOKEN === $item) {
+ $this->skipMethodArgument($tokenIterator);
+ } elseif (self::DOMAIN_TOKEN === $item) {
+ $domain = $this->getValue($tokenIterator);
+
+ break;
+ } else {
+ break;
+ }
+ }
+
+ if ($message) {
+ $catalog->set($message, $this->prefix.$message, $domain);
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * @param string $file
+ *
+ * @return bool
+ *
+ * @throws \InvalidArgumentException
+ */
+ protected function canBeExtracted($file)
+ {
+ return $this->isFile($file) && 'php' === pathinfo($file, PATHINFO_EXTENSION);
+ }
+
+ /**
+ * @param string|array $directory
+ *
+ * @return array
+ */
+ protected function extractFromDirectory($directory)
+ {
+ $finder = new Finder();
+
+ return $finder->files()->name('*.php')->in($directory);
+ }
+}
diff --git a/_include/calendar/symfony/translation/Extractor/PhpStringTokenParser.php b/_include/calendar/symfony/translation/Extractor/PhpStringTokenParser.php
new file mode 100644
index 0000000..e8094e6
--- /dev/null
+++ b/_include/calendar/symfony/translation/Extractor/PhpStringTokenParser.php
@@ -0,0 +1,142 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Extractor;
+
+/*
+ * The following is derived from code at http://github.com/nikic/PHP-Parser
+ *
+ * Copyright (c) 2011 by Nikita Popov
+ *
+ * Some 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.
+ *
+ * * The names of the contributors may not 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 THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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.
+ */
+
+class PhpStringTokenParser
+{
+ protected static $replacements = array(
+ '\\' => '\\',
+ '$' => '$',
+ 'n' => "\n",
+ 'r' => "\r",
+ 't' => "\t",
+ 'f' => "\f",
+ 'v' => "\v",
+ 'e' => "\x1B",
+ );
+
+ /**
+ * Parses a string token.
+ *
+ * @param string $str String token content
+ *
+ * @return string The parsed string
+ */
+ public static function parse($str)
+ {
+ $bLength = 0;
+ if ('b' === $str[0]) {
+ $bLength = 1;
+ }
+
+ if ('\'' === $str[$bLength]) {
+ return str_replace(
+ array('\\\\', '\\\''),
+ array('\\', '\''),
+ substr($str, $bLength + 1, -1)
+ );
+ } else {
+ return self::parseEscapeSequences(substr($str, $bLength + 1, -1), '"');
+ }
+ }
+
+ /**
+ * Parses escape sequences in strings (all string types apart from single quoted).
+ *
+ * @param string $str String without quotes
+ * @param string|null $quote Quote type
+ *
+ * @return string String with escape sequences parsed
+ */
+ public static function parseEscapeSequences($str, $quote)
+ {
+ if (null !== $quote) {
+ $str = str_replace('\\'.$quote, $quote, $str);
+ }
+
+ return preg_replace_callback(
+ '~\\\\([\\\\$nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3})~',
+ array(__CLASS__, 'parseCallback'),
+ $str
+ );
+ }
+
+ private static function parseCallback($matches)
+ {
+ $str = $matches[1];
+
+ if (isset(self::$replacements[$str])) {
+ return self::$replacements[$str];
+ } elseif ('x' === $str[0] || 'X' === $str[0]) {
+ return \chr(hexdec($str));
+ } else {
+ return \chr(octdec($str));
+ }
+ }
+
+ /**
+ * Parses a constant doc string.
+ *
+ * @param string $startToken Doc string start token content (<<
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Formatter;
+
+/**
+ * @author Abdellatif Ait boudad
+ */
+interface ChoiceMessageFormatterInterface
+{
+ /**
+ * Formats a localized message pattern with given arguments.
+ *
+ * @param string $message The message (may also be an object that can be cast to string)
+ * @param int $number The number to use to find the indice of the message
+ * @param string $locale The message locale
+ * @param array $parameters An array of parameters for the message
+ *
+ * @return string
+ */
+ public function choiceFormat($message, $number, $locale, array $parameters = array());
+}
diff --git a/_include/calendar/symfony/translation/Formatter/MessageFormatter.php b/_include/calendar/symfony/translation/Formatter/MessageFormatter.php
new file mode 100644
index 0000000..e174be3
--- /dev/null
+++ b/_include/calendar/symfony/translation/Formatter/MessageFormatter.php
@@ -0,0 +1,48 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Formatter;
+
+use Symfony\Component\Translation\MessageSelector;
+
+/**
+ * @author Abdellatif Ait boudad
+ */
+class MessageFormatter implements MessageFormatterInterface, ChoiceMessageFormatterInterface
+{
+ private $selector;
+
+ /**
+ * @param MessageSelector|null $selector The message selector for pluralization
+ */
+ public function __construct(MessageSelector $selector = null)
+ {
+ $this->selector = $selector ?: new MessageSelector();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function format($message, $locale, array $parameters = array())
+ {
+ return strtr($message, $parameters);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function choiceFormat($message, $number, $locale, array $parameters = array())
+ {
+ $parameters = array_merge(array('%count%' => $number), $parameters);
+
+ return $this->format($this->selector->choose($message, (int) $number, $locale), $locale, $parameters);
+ }
+}
diff --git a/_include/calendar/symfony/translation/Formatter/MessageFormatterInterface.php b/_include/calendar/symfony/translation/Formatter/MessageFormatterInterface.php
new file mode 100644
index 0000000..86937fb
--- /dev/null
+++ b/_include/calendar/symfony/translation/Formatter/MessageFormatterInterface.php
@@ -0,0 +1,30 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Formatter;
+
+/**
+ * @author Guilherme Blanco
+ * @author Abdellatif Ait boudad
+ */
+interface MessageFormatterInterface
+{
+ /**
+ * Formats a localized message pattern with given arguments.
+ *
+ * @param string $message The message (may also be an object that can be cast to string)
+ * @param string $locale The message locale
+ * @param array $parameters An array of parameters for the message
+ *
+ * @return string
+ */
+ public function format($message, $locale, array $parameters = array());
+}
diff --git a/_include/calendar/symfony/translation/IdentityTranslator.php b/_include/calendar/symfony/translation/IdentityTranslator.php
new file mode 100644
index 0000000..82b2470
--- /dev/null
+++ b/_include/calendar/symfony/translation/IdentityTranslator.php
@@ -0,0 +1,63 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation;
+
+/**
+ * IdentityTranslator does not translate anything.
+ *
+ * @author Fabien Potencier
+ */
+class IdentityTranslator implements TranslatorInterface
+{
+ private $selector;
+ private $locale;
+
+ /**
+ * @param MessageSelector|null $selector The message selector for pluralization
+ */
+ public function __construct(MessageSelector $selector = null)
+ {
+ $this->selector = $selector ?: new MessageSelector();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setLocale($locale)
+ {
+ $this->locale = $locale;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getLocale()
+ {
+ return $this->locale ?: \Locale::getDefault();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function trans($id, array $parameters = array(), $domain = null, $locale = null)
+ {
+ return strtr((string) $id, $parameters);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null)
+ {
+ return strtr($this->selector->choose((string) $id, (int) $number, $locale ?: $this->getLocale()), $parameters);
+ }
+}
diff --git a/_include/calendar/symfony/translation/Interval.php b/_include/calendar/symfony/translation/Interval.php
new file mode 100644
index 0000000..9e2cae6
--- /dev/null
+++ b/_include/calendar/symfony/translation/Interval.php
@@ -0,0 +1,109 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation;
+
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+
+/**
+ * Tests if a given number belongs to a given math interval.
+ *
+ * An interval can represent a finite set of numbers:
+ *
+ * {1,2,3,4}
+ *
+ * An interval can represent numbers between two numbers:
+ *
+ * [1, +Inf]
+ * ]-1,2[
+ *
+ * The left delimiter can be [ (inclusive) or ] (exclusive).
+ * The right delimiter can be [ (exclusive) or ] (inclusive).
+ * Beside numbers, you can use -Inf and +Inf for the infinite.
+ *
+ * @author Fabien Potencier
+ *
+ * @see http://en.wikipedia.org/wiki/Interval_%28mathematics%29#The_ISO_notation
+ */
+class Interval
+{
+ /**
+ * Tests if the given number is in the math interval.
+ *
+ * @param int $number A number
+ * @param string $interval An interval
+ *
+ * @return bool
+ *
+ * @throws InvalidArgumentException
+ */
+ public static function test($number, $interval)
+ {
+ $interval = trim($interval);
+
+ if (!preg_match('/^'.self::getIntervalRegexp().'$/x', $interval, $matches)) {
+ throw new InvalidArgumentException(sprintf('"%s" is not a valid interval.', $interval));
+ }
+
+ if ($matches[1]) {
+ foreach (explode(',', $matches[2]) as $n) {
+ if ($number == $n) {
+ return true;
+ }
+ }
+ } else {
+ $leftNumber = self::convertNumber($matches['left']);
+ $rightNumber = self::convertNumber($matches['right']);
+
+ return
+ ('[' === $matches['left_delimiter'] ? $number >= $leftNumber : $number > $leftNumber)
+ && (']' === $matches['right_delimiter'] ? $number <= $rightNumber : $number < $rightNumber)
+ ;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns a Regexp that matches valid intervals.
+ *
+ * @return string A Regexp (without the delimiters)
+ */
+ public static function getIntervalRegexp()
+ {
+ return <<[\[\]])
+ \s*
+ (?P-Inf|\-?\d+(\.\d+)?)
+ \s*,\s*
+ (?P\+?Inf|\-?\d+(\.\d+)?)
+ \s*
+ (?P[\[\]])
+EOF;
+ }
+
+ private static function convertNumber($number)
+ {
+ if ('-Inf' === $number) {
+ return log(0);
+ } elseif ('+Inf' === $number || 'Inf' === $number) {
+ return -log(0);
+ }
+
+ return (float) $number;
+ }
+}
diff --git a/_include/calendar/symfony/translation/LICENSE b/_include/calendar/symfony/translation/LICENSE
new file mode 100644
index 0000000..21d7fb9
--- /dev/null
+++ b/_include/calendar/symfony/translation/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2004-2018 Fabien Potencier
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/_include/calendar/symfony/translation/Loader/ArrayLoader.php b/_include/calendar/symfony/translation/Loader/ArrayLoader.php
new file mode 100644
index 0000000..7bbfca6
--- /dev/null
+++ b/_include/calendar/symfony/translation/Loader/ArrayLoader.php
@@ -0,0 +1,66 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * ArrayLoader loads translations from a PHP array.
+ *
+ * @author Fabien Potencier
+ */
+class ArrayLoader implements LoaderInterface
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function load($resource, $locale, $domain = 'messages')
+ {
+ $this->flatten($resource);
+ $catalogue = new MessageCatalogue($locale);
+ $catalogue->add($resource, $domain);
+
+ return $catalogue;
+ }
+
+ /**
+ * Flattens an nested array of translations.
+ *
+ * The scheme used is:
+ * 'key' => array('key2' => array('key3' => 'value'))
+ * Becomes:
+ * 'key.key2.key3' => 'value'
+ *
+ * This function takes an array by reference and will modify it
+ *
+ * @param array &$messages The array that will be flattened
+ * @param array $subnode Current subnode being parsed, used internally for recursive calls
+ * @param string $path Current path being parsed, used internally for recursive calls
+ */
+ private function flatten(array &$messages, array $subnode = null, $path = null)
+ {
+ if (null === $subnode) {
+ $subnode = &$messages;
+ }
+ foreach ($subnode as $key => $value) {
+ if (\is_array($value)) {
+ $nodePath = $path ? $path.'.'.$key : $key;
+ $this->flatten($messages, $value, $nodePath);
+ if (null === $path) {
+ unset($messages[$key]);
+ }
+ } elseif (null !== $path) {
+ $messages[$path.'.'.$key] = $value;
+ }
+ }
+ }
+}
diff --git a/_include/calendar/symfony/translation/Loader/CsvFileLoader.php b/_include/calendar/symfony/translation/Loader/CsvFileLoader.php
new file mode 100644
index 0000000..8a763e7
--- /dev/null
+++ b/_include/calendar/symfony/translation/Loader/CsvFileLoader.php
@@ -0,0 +1,65 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+
+/**
+ * CsvFileLoader loads translations from CSV files.
+ *
+ * @author Saša Stamenković
+ */
+class CsvFileLoader extends FileLoader
+{
+ private $delimiter = ';';
+ private $enclosure = '"';
+ private $escape = '\\';
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function loadResource($resource)
+ {
+ $messages = array();
+
+ try {
+ $file = new \SplFileObject($resource, 'rb');
+ } catch (\RuntimeException $e) {
+ throw new NotFoundResourceException(sprintf('Error opening file "%s".', $resource), 0, $e);
+ }
+
+ $file->setFlags(\SplFileObject::READ_CSV | \SplFileObject::SKIP_EMPTY);
+ $file->setCsvControl($this->delimiter, $this->enclosure, $this->escape);
+
+ foreach ($file as $data) {
+ if ('#' !== substr($data[0], 0, 1) && isset($data[1]) && 2 === \count($data)) {
+ $messages[$data[0]] = $data[1];
+ }
+ }
+
+ return $messages;
+ }
+
+ /**
+ * Sets the delimiter, enclosure, and escape character for CSV.
+ *
+ * @param string $delimiter Delimiter character
+ * @param string $enclosure Enclosure character
+ * @param string $escape Escape character
+ */
+ public function setCsvControl($delimiter = ';', $enclosure = '"', $escape = '\\')
+ {
+ $this->delimiter = $delimiter;
+ $this->enclosure = $enclosure;
+ $this->escape = $escape;
+ }
+}
diff --git a/_include/calendar/symfony/translation/Loader/FileLoader.php b/_include/calendar/symfony/translation/Loader/FileLoader.php
new file mode 100644
index 0000000..9a02f52
--- /dev/null
+++ b/_include/calendar/symfony/translation/Loader/FileLoader.php
@@ -0,0 +1,65 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+
+/**
+ * @author Abdellatif Ait boudad
+ */
+abstract class FileLoader extends ArrayLoader
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function load($resource, $locale, $domain = 'messages')
+ {
+ if (!stream_is_local($resource)) {
+ throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
+ }
+
+ if (!file_exists($resource)) {
+ throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
+ }
+
+ $messages = $this->loadResource($resource);
+
+ // empty resource
+ if (null === $messages) {
+ $messages = array();
+ }
+
+ // not an array
+ if (!\is_array($messages)) {
+ throw new InvalidResourceException(sprintf('Unable to load file "%s".', $resource));
+ }
+
+ $catalogue = parent::load($messages, $locale, $domain);
+
+ if (class_exists('Symfony\Component\Config\Resource\FileResource')) {
+ $catalogue->addResource(new FileResource($resource));
+ }
+
+ return $catalogue;
+ }
+
+ /**
+ * @param string $resource
+ *
+ * @return array
+ *
+ * @throws InvalidResourceException if stream content has an invalid format
+ */
+ abstract protected function loadResource($resource);
+}
diff --git a/_include/calendar/symfony/translation/Loader/IcuDatFileLoader.php b/_include/calendar/symfony/translation/Loader/IcuDatFileLoader.php
new file mode 100644
index 0000000..7bbf4c2
--- /dev/null
+++ b/_include/calendar/symfony/translation/Loader/IcuDatFileLoader.php
@@ -0,0 +1,61 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * IcuResFileLoader loads translations from a resource bundle.
+ *
+ * @author stealth35
+ */
+class IcuDatFileLoader extends IcuResFileLoader
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function load($resource, $locale, $domain = 'messages')
+ {
+ if (!stream_is_local($resource.'.dat')) {
+ throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
+ }
+
+ if (!file_exists($resource.'.dat')) {
+ throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
+ }
+
+ try {
+ $rb = new \ResourceBundle($locale, $resource);
+ } catch (\Exception $e) {
+ $rb = null;
+ }
+
+ if (!$rb) {
+ throw new InvalidResourceException(sprintf('Cannot load resource "%s"', $resource));
+ } elseif (intl_is_failure($rb->getErrorCode())) {
+ throw new InvalidResourceException($rb->getErrorMessage(), $rb->getErrorCode());
+ }
+
+ $messages = $this->flatten($rb);
+ $catalogue = new MessageCatalogue($locale);
+ $catalogue->add($messages, $domain);
+
+ if (class_exists('Symfony\Component\Config\Resource\FileResource')) {
+ $catalogue->addResource(new FileResource($resource.'.dat'));
+ }
+
+ return $catalogue;
+ }
+}
diff --git a/_include/calendar/symfony/translation/Loader/IcuResFileLoader.php b/_include/calendar/symfony/translation/Loader/IcuResFileLoader.php
new file mode 100644
index 0000000..28ace29
--- /dev/null
+++ b/_include/calendar/symfony/translation/Loader/IcuResFileLoader.php
@@ -0,0 +1,91 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Config\Resource\DirectoryResource;
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * IcuResFileLoader loads translations from a resource bundle.
+ *
+ * @author stealth35
+ */
+class IcuResFileLoader implements LoaderInterface
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function load($resource, $locale, $domain = 'messages')
+ {
+ if (!stream_is_local($resource)) {
+ throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
+ }
+
+ if (!is_dir($resource)) {
+ throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
+ }
+
+ try {
+ $rb = new \ResourceBundle($locale, $resource);
+ } catch (\Exception $e) {
+ $rb = null;
+ }
+
+ if (!$rb) {
+ throw new InvalidResourceException(sprintf('Cannot load resource "%s"', $resource));
+ } elseif (intl_is_failure($rb->getErrorCode())) {
+ throw new InvalidResourceException($rb->getErrorMessage(), $rb->getErrorCode());
+ }
+
+ $messages = $this->flatten($rb);
+ $catalogue = new MessageCatalogue($locale);
+ $catalogue->add($messages, $domain);
+
+ if (class_exists('Symfony\Component\Config\Resource\DirectoryResource')) {
+ $catalogue->addResource(new DirectoryResource($resource));
+ }
+
+ return $catalogue;
+ }
+
+ /**
+ * Flattens an ResourceBundle.
+ *
+ * The scheme used is:
+ * key { key2 { key3 { "value" } } }
+ * Becomes:
+ * 'key.key2.key3' => 'value'
+ *
+ * This function takes an array by reference and will modify it
+ *
+ * @param \ResourceBundle $rb The ResourceBundle that will be flattened
+ * @param array $messages Used internally for recursive calls
+ * @param string $path Current path being parsed, used internally for recursive calls
+ *
+ * @return array the flattened ResourceBundle
+ */
+ protected function flatten(\ResourceBundle $rb, array &$messages = array(), $path = null)
+ {
+ foreach ($rb as $key => $value) {
+ $nodePath = $path ? $path.'.'.$key : $key;
+ if ($value instanceof \ResourceBundle) {
+ $this->flatten($value, $messages, $nodePath);
+ } else {
+ $messages[$nodePath] = $value;
+ }
+ }
+
+ return $messages;
+ }
+}
diff --git a/_include/calendar/symfony/translation/Loader/IniFileLoader.php b/_include/calendar/symfony/translation/Loader/IniFileLoader.php
new file mode 100644
index 0000000..11d9b27
--- /dev/null
+++ b/_include/calendar/symfony/translation/Loader/IniFileLoader.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+/**
+ * IniFileLoader loads translations from an ini file.
+ *
+ * @author stealth35
+ */
+class IniFileLoader extends FileLoader
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function loadResource($resource)
+ {
+ return parse_ini_file($resource, true);
+ }
+}
diff --git a/_include/calendar/symfony/translation/Loader/JsonFileLoader.php b/_include/calendar/symfony/translation/Loader/JsonFileLoader.php
new file mode 100644
index 0000000..ce4e91f
--- /dev/null
+++ b/_include/calendar/symfony/translation/Loader/JsonFileLoader.php
@@ -0,0 +1,64 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+
+/**
+ * JsonFileLoader loads translations from an json file.
+ *
+ * @author singles
+ */
+class JsonFileLoader extends FileLoader
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function loadResource($resource)
+ {
+ $messages = array();
+ if ($data = file_get_contents($resource)) {
+ $messages = json_decode($data, true);
+
+ if (0 < $errorCode = json_last_error()) {
+ throw new InvalidResourceException(sprintf('Error parsing JSON - %s', $this->getJSONErrorMessage($errorCode)));
+ }
+ }
+
+ return $messages;
+ }
+
+ /**
+ * Translates JSON_ERROR_* constant into meaningful message.
+ *
+ * @param int $errorCode Error code returned by json_last_error() call
+ *
+ * @return string Message string
+ */
+ private function getJSONErrorMessage($errorCode)
+ {
+ switch ($errorCode) {
+ case JSON_ERROR_DEPTH:
+ return 'Maximum stack depth exceeded';
+ case JSON_ERROR_STATE_MISMATCH:
+ return 'Underflow or the modes mismatch';
+ case JSON_ERROR_CTRL_CHAR:
+ return 'Unexpected control character found';
+ case JSON_ERROR_SYNTAX:
+ return 'Syntax error, malformed JSON';
+ case JSON_ERROR_UTF8:
+ return 'Malformed UTF-8 characters, possibly incorrectly encoded';
+ default:
+ return 'Unknown error';
+ }
+ }
+}
diff --git a/_include/calendar/symfony/translation/Loader/LoaderInterface.php b/_include/calendar/symfony/translation/Loader/LoaderInterface.php
new file mode 100644
index 0000000..1785402
--- /dev/null
+++ b/_include/calendar/symfony/translation/Loader/LoaderInterface.php
@@ -0,0 +1,38 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * LoaderInterface is the interface implemented by all translation loaders.
+ *
+ * @author Fabien Potencier
+ */
+interface LoaderInterface
+{
+ /**
+ * Loads a locale.
+ *
+ * @param mixed $resource A resource
+ * @param string $locale A locale
+ * @param string $domain The domain
+ *
+ * @return MessageCatalogue A MessageCatalogue instance
+ *
+ * @throws NotFoundResourceException when the resource cannot be found
+ * @throws InvalidResourceException when the resource cannot be loaded
+ */
+ public function load($resource, $locale, $domain = 'messages');
+}
diff --git a/_include/calendar/symfony/translation/Loader/MoFileLoader.php b/_include/calendar/symfony/translation/Loader/MoFileLoader.php
new file mode 100644
index 0000000..a1d7c4a
--- /dev/null
+++ b/_include/calendar/symfony/translation/Loader/MoFileLoader.php
@@ -0,0 +1,145 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+
+/**
+ * @copyright Copyright (c) 2010, Union of RAD http://union-of-rad.org (http://lithify.me/)
+ */
+class MoFileLoader extends FileLoader
+{
+ /**
+ * Magic used for validating the format of a MO file as well as
+ * detecting if the machine used to create that file was little endian.
+ */
+ const MO_LITTLE_ENDIAN_MAGIC = 0x950412de;
+
+ /**
+ * Magic used for validating the format of a MO file as well as
+ * detecting if the machine used to create that file was big endian.
+ */
+ const MO_BIG_ENDIAN_MAGIC = 0xde120495;
+
+ /**
+ * The size of the header of a MO file in bytes.
+ */
+ const MO_HEADER_SIZE = 28;
+
+ /**
+ * Parses machine object (MO) format, independent of the machine's endian it
+ * was created on. Both 32bit and 64bit systems are supported.
+ *
+ * {@inheritdoc}
+ */
+ protected function loadResource($resource)
+ {
+ $stream = fopen($resource, 'r');
+
+ $stat = fstat($stream);
+
+ if ($stat['size'] < self::MO_HEADER_SIZE) {
+ throw new InvalidResourceException('MO stream content has an invalid format.');
+ }
+ $magic = unpack('V1', fread($stream, 4));
+ $magic = hexdec(substr(dechex(current($magic)), -8));
+
+ if (self::MO_LITTLE_ENDIAN_MAGIC == $magic) {
+ $isBigEndian = false;
+ } elseif (self::MO_BIG_ENDIAN_MAGIC == $magic) {
+ $isBigEndian = true;
+ } else {
+ throw new InvalidResourceException('MO stream content has an invalid format.');
+ }
+
+ // formatRevision
+ $this->readLong($stream, $isBigEndian);
+ $count = $this->readLong($stream, $isBigEndian);
+ $offsetId = $this->readLong($stream, $isBigEndian);
+ $offsetTranslated = $this->readLong($stream, $isBigEndian);
+ // sizeHashes
+ $this->readLong($stream, $isBigEndian);
+ // offsetHashes
+ $this->readLong($stream, $isBigEndian);
+
+ $messages = array();
+
+ for ($i = 0; $i < $count; ++$i) {
+ $pluralId = null;
+ $translated = null;
+
+ fseek($stream, $offsetId + $i * 8);
+
+ $length = $this->readLong($stream, $isBigEndian);
+ $offset = $this->readLong($stream, $isBigEndian);
+
+ if ($length < 1) {
+ continue;
+ }
+
+ fseek($stream, $offset);
+ $singularId = fread($stream, $length);
+
+ if (false !== strpos($singularId, "\000")) {
+ list($singularId, $pluralId) = explode("\000", $singularId);
+ }
+
+ fseek($stream, $offsetTranslated + $i * 8);
+ $length = $this->readLong($stream, $isBigEndian);
+ $offset = $this->readLong($stream, $isBigEndian);
+
+ if ($length < 1) {
+ continue;
+ }
+
+ fseek($stream, $offset);
+ $translated = fread($stream, $length);
+
+ if (false !== strpos($translated, "\000")) {
+ $translated = explode("\000", $translated);
+ }
+
+ $ids = array('singular' => $singularId, 'plural' => $pluralId);
+ $item = compact('ids', 'translated');
+
+ if (\is_array($item['translated'])) {
+ $messages[$item['ids']['singular']] = stripcslashes($item['translated'][0]);
+ if (isset($item['ids']['plural'])) {
+ $plurals = array();
+ foreach ($item['translated'] as $plural => $translated) {
+ $plurals[] = sprintf('{%d} %s', $plural, $translated);
+ }
+ $messages[$item['ids']['plural']] = stripcslashes(implode('|', $plurals));
+ }
+ } elseif (!empty($item['ids']['singular'])) {
+ $messages[$item['ids']['singular']] = stripcslashes($item['translated']);
+ }
+ }
+
+ fclose($stream);
+
+ return array_filter($messages);
+ }
+
+ /**
+ * Reads an unsigned long from stream respecting endianness.
+ *
+ * @param resource $stream
+ */
+ private function readLong($stream, bool $isBigEndian): int
+ {
+ $result = unpack($isBigEndian ? 'N1' : 'V1', fread($stream, 4));
+ $result = current($result);
+
+ return (int) substr($result, -8);
+ }
+}
diff --git a/_include/calendar/symfony/translation/Loader/PhpFileLoader.php b/_include/calendar/symfony/translation/Loader/PhpFileLoader.php
new file mode 100644
index 0000000..a0050e8
--- /dev/null
+++ b/_include/calendar/symfony/translation/Loader/PhpFileLoader.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+/**
+ * PhpFileLoader loads translations from PHP files returning an array of translations.
+ *
+ * @author Fabien Potencier
+ */
+class PhpFileLoader extends FileLoader
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function loadResource($resource)
+ {
+ return require $resource;
+ }
+}
diff --git a/_include/calendar/symfony/translation/Loader/PoFileLoader.php b/_include/calendar/symfony/translation/Loader/PoFileLoader.php
new file mode 100644
index 0000000..066168d
--- /dev/null
+++ b/_include/calendar/symfony/translation/Loader/PoFileLoader.php
@@ -0,0 +1,148 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+/**
+ * @copyright Copyright (c) 2010, Union of RAD http://union-of-rad.org (http://lithify.me/)
+ * @copyright Copyright (c) 2012, Clemens Tolboom
+ */
+class PoFileLoader extends FileLoader
+{
+ /**
+ * Parses portable object (PO) format.
+ *
+ * From http://www.gnu.org/software/gettext/manual/gettext.html#PO-Files
+ * we should be able to parse files having:
+ *
+ * white-space
+ * # translator-comments
+ * #. extracted-comments
+ * #: reference...
+ * #, flag...
+ * #| msgid previous-untranslated-string
+ * msgid untranslated-string
+ * msgstr translated-string
+ *
+ * extra or different lines are:
+ *
+ * #| msgctxt previous-context
+ * #| msgid previous-untranslated-string
+ * msgctxt context
+ *
+ * #| msgid previous-untranslated-string-singular
+ * #| msgid_plural previous-untranslated-string-plural
+ * msgid untranslated-string-singular
+ * msgid_plural untranslated-string-plural
+ * msgstr[0] translated-string-case-0
+ * ...
+ * msgstr[N] translated-string-case-n
+ *
+ * The definition states:
+ * - white-space and comments are optional.
+ * - msgid "" that an empty singleline defines a header.
+ *
+ * This parser sacrifices some features of the reference implementation the
+ * differences to that implementation are as follows.
+ * - No support for comments spanning multiple lines.
+ * - Translator and extracted comments are treated as being the same type.
+ * - Message IDs are allowed to have other encodings as just US-ASCII.
+ *
+ * Items with an empty id are ignored.
+ *
+ * {@inheritdoc}
+ */
+ protected function loadResource($resource)
+ {
+ $stream = fopen($resource, 'r');
+
+ $defaults = array(
+ 'ids' => array(),
+ 'translated' => null,
+ );
+
+ $messages = array();
+ $item = $defaults;
+ $flags = array();
+
+ while ($line = fgets($stream)) {
+ $line = trim($line);
+
+ if ('' === $line) {
+ // Whitespace indicated current item is done
+ if (!\in_array('fuzzy', $flags)) {
+ $this->addMessage($messages, $item);
+ }
+ $item = $defaults;
+ $flags = array();
+ } elseif ('#,' === substr($line, 0, 2)) {
+ $flags = array_map('trim', explode(',', substr($line, 2)));
+ } elseif ('msgid "' === substr($line, 0, 7)) {
+ // We start a new msg so save previous
+ // TODO: this fails when comments or contexts are added
+ $this->addMessage($messages, $item);
+ $item = $defaults;
+ $item['ids']['singular'] = substr($line, 7, -1);
+ } elseif ('msgstr "' === substr($line, 0, 8)) {
+ $item['translated'] = substr($line, 8, -1);
+ } elseif ('"' === $line[0]) {
+ $continues = isset($item['translated']) ? 'translated' : 'ids';
+
+ if (\is_array($item[$continues])) {
+ end($item[$continues]);
+ $item[$continues][key($item[$continues])] .= substr($line, 1, -1);
+ } else {
+ $item[$continues] .= substr($line, 1, -1);
+ }
+ } elseif ('msgid_plural "' === substr($line, 0, 14)) {
+ $item['ids']['plural'] = substr($line, 14, -1);
+ } elseif ('msgstr[' === substr($line, 0, 7)) {
+ $size = strpos($line, ']');
+ $item['translated'][(int) substr($line, 7, 1)] = substr($line, $size + 3, -1);
+ }
+ }
+ // save last item
+ if (!\in_array('fuzzy', $flags)) {
+ $this->addMessage($messages, $item);
+ }
+ fclose($stream);
+
+ return $messages;
+ }
+
+ /**
+ * Save a translation item to the messages.
+ *
+ * A .po file could contain by error missing plural indexes. We need to
+ * fix these before saving them.
+ */
+ private function addMessage(array &$messages, array $item)
+ {
+ if (\is_array($item['translated'])) {
+ $messages[stripcslashes($item['ids']['singular'])] = stripcslashes($item['translated'][0]);
+ if (isset($item['ids']['plural'])) {
+ $plurals = $item['translated'];
+ // PO are by definition indexed so sort by index.
+ ksort($plurals);
+ // Make sure every index is filled.
+ end($plurals);
+ $count = key($plurals);
+ // Fill missing spots with '-'.
+ $empties = array_fill(0, $count + 1, '-');
+ $plurals += $empties;
+ ksort($plurals);
+ $messages[stripcslashes($item['ids']['plural'])] = stripcslashes(implode('|', $plurals));
+ }
+ } elseif (!empty($item['ids']['singular'])) {
+ $messages[stripcslashes($item['ids']['singular'])] = stripcslashes($item['translated']);
+ }
+ }
+}
diff --git a/_include/calendar/symfony/translation/Loader/QtFileLoader.php b/_include/calendar/symfony/translation/Loader/QtFileLoader.php
new file mode 100644
index 0000000..2d4a4c0
--- /dev/null
+++ b/_include/calendar/symfony/translation/Loader/QtFileLoader.php
@@ -0,0 +1,77 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Config\Util\XmlUtils;
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * QtFileLoader loads translations from QT Translations XML files.
+ *
+ * @author Benjamin Eberlei
+ */
+class QtFileLoader implements LoaderInterface
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function load($resource, $locale, $domain = 'messages')
+ {
+ if (!stream_is_local($resource)) {
+ throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
+ }
+
+ if (!file_exists($resource)) {
+ throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
+ }
+
+ try {
+ $dom = XmlUtils::loadFile($resource);
+ } catch (\InvalidArgumentException $e) {
+ throw new InvalidResourceException(sprintf('Unable to load "%s".', $resource), $e->getCode(), $e);
+ }
+
+ $internalErrors = libxml_use_internal_errors(true);
+ libxml_clear_errors();
+
+ $xpath = new \DOMXPath($dom);
+ $nodes = $xpath->evaluate('//TS/context/name[text()="'.$domain.'"]');
+
+ $catalogue = new MessageCatalogue($locale);
+ if (1 == $nodes->length) {
+ $translations = $nodes->item(0)->nextSibling->parentNode->parentNode->getElementsByTagName('message');
+ foreach ($translations as $translation) {
+ $translationValue = (string) $translation->getElementsByTagName('translation')->item(0)->nodeValue;
+
+ if (!empty($translationValue)) {
+ $catalogue->set(
+ (string) $translation->getElementsByTagName('source')->item(0)->nodeValue,
+ $translationValue,
+ $domain
+ );
+ }
+ $translation = $translation->nextSibling;
+ }
+
+ if (class_exists('Symfony\Component\Config\Resource\FileResource')) {
+ $catalogue->addResource(new FileResource($resource));
+ }
+ }
+
+ libxml_use_internal_errors($internalErrors);
+
+ return $catalogue;
+ }
+}
diff --git a/_include/calendar/symfony/translation/Loader/XliffFileLoader.php b/_include/calendar/symfony/translation/Loader/XliffFileLoader.php
new file mode 100644
index 0000000..9c6b8c9
--- /dev/null
+++ b/_include/calendar/symfony/translation/Loader/XliffFileLoader.php
@@ -0,0 +1,314 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Config\Util\XmlUtils;
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * XliffFileLoader loads translations from XLIFF files.
+ *
+ * @author Fabien Potencier
+ */
+class XliffFileLoader implements LoaderInterface
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function load($resource, $locale, $domain = 'messages')
+ {
+ if (!stream_is_local($resource)) {
+ throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
+ }
+
+ if (!file_exists($resource)) {
+ throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
+ }
+
+ $catalogue = new MessageCatalogue($locale);
+ $this->extract($resource, $catalogue, $domain);
+
+ if (class_exists('Symfony\Component\Config\Resource\FileResource')) {
+ $catalogue->addResource(new FileResource($resource));
+ }
+
+ return $catalogue;
+ }
+
+ private function extract($resource, MessageCatalogue $catalogue, $domain)
+ {
+ try {
+ $dom = XmlUtils::loadFile($resource);
+ } catch (\InvalidArgumentException $e) {
+ throw new InvalidResourceException(sprintf('Unable to load "%s": %s', $resource, $e->getMessage()), $e->getCode(), $e);
+ }
+
+ $xliffVersion = $this->getVersionNumber($dom);
+ $this->validateSchema($xliffVersion, $dom, $this->getSchema($xliffVersion));
+
+ if ('1.2' === $xliffVersion) {
+ $this->extractXliff1($dom, $catalogue, $domain);
+ }
+
+ if ('2.0' === $xliffVersion) {
+ $this->extractXliff2($dom, $catalogue, $domain);
+ }
+ }
+
+ /**
+ * Extract messages and metadata from DOMDocument into a MessageCatalogue.
+ *
+ * @param \DOMDocument $dom Source to extract messages and metadata
+ * @param MessageCatalogue $catalogue Catalogue where we'll collect messages and metadata
+ * @param string $domain The domain
+ */
+ private function extractXliff1(\DOMDocument $dom, MessageCatalogue $catalogue, string $domain)
+ {
+ $xml = simplexml_import_dom($dom);
+ $encoding = strtoupper($dom->encoding);
+
+ $xml->registerXPathNamespace('xliff', 'urn:oasis:names:tc:xliff:document:1.2');
+ foreach ($xml->xpath('//xliff:trans-unit') as $translation) {
+ $attributes = $translation->attributes();
+
+ if (!(isset($attributes['resname']) || isset($translation->source))) {
+ continue;
+ }
+
+ $source = isset($attributes['resname']) && $attributes['resname'] ? $attributes['resname'] : $translation->source;
+ // If the xlf file has another encoding specified, try to convert it because
+ // simple_xml will always return utf-8 encoded values
+ $target = $this->utf8ToCharset((string) (isset($translation->target) ? $translation->target : $source), $encoding);
+
+ $catalogue->set((string) $source, $target, $domain);
+
+ $metadata = array();
+ if ($notes = $this->parseNotesMetadata($translation->note, $encoding)) {
+ $metadata['notes'] = $notes;
+ }
+
+ if (isset($translation->target) && $translation->target->attributes()) {
+ $metadata['target-attributes'] = array();
+ foreach ($translation->target->attributes() as $key => $value) {
+ $metadata['target-attributes'][$key] = (string) $value;
+ }
+ }
+
+ if (isset($attributes['id'])) {
+ $metadata['id'] = (string) $attributes['id'];
+ }
+
+ $catalogue->setMetadata((string) $source, $metadata, $domain);
+ }
+ }
+
+ private function extractXliff2(\DOMDocument $dom, MessageCatalogue $catalogue, string $domain)
+ {
+ $xml = simplexml_import_dom($dom);
+ $encoding = strtoupper($dom->encoding);
+
+ $xml->registerXPathNamespace('xliff', 'urn:oasis:names:tc:xliff:document:2.0');
+
+ foreach ($xml->xpath('//xliff:unit') as $unit) {
+ foreach ($unit->segment as $segment) {
+ $source = $segment->source;
+
+ // If the xlf file has another encoding specified, try to convert it because
+ // simple_xml will always return utf-8 encoded values
+ $target = $this->utf8ToCharset((string) (isset($segment->target) ? $segment->target : $source), $encoding);
+
+ $catalogue->set((string) $source, $target, $domain);
+
+ $metadata = array();
+ if (isset($segment->target) && $segment->target->attributes()) {
+ $metadata['target-attributes'] = array();
+ foreach ($segment->target->attributes() as $key => $value) {
+ $metadata['target-attributes'][$key] = (string) $value;
+ }
+ }
+
+ if (isset($unit->notes)) {
+ $metadata['notes'] = array();
+ foreach ($unit->notes->note as $noteNode) {
+ $note = array();
+ foreach ($noteNode->attributes() as $key => $value) {
+ $note[$key] = (string) $value;
+ }
+ $note['content'] = (string) $noteNode;
+ $metadata['notes'][] = $note;
+ }
+ }
+
+ $catalogue->setMetadata((string) $source, $metadata, $domain);
+ }
+ }
+ }
+
+ /**
+ * Convert a UTF8 string to the specified encoding.
+ */
+ private function utf8ToCharset(string $content, string $encoding = null): string
+ {
+ if ('UTF-8' !== $encoding && !empty($encoding)) {
+ return mb_convert_encoding($content, $encoding, 'UTF-8');
+ }
+
+ return $content;
+ }
+
+ /**
+ * Validates and parses the given file into a DOMDocument.
+ *
+ * @throws InvalidResourceException
+ */
+ private function validateSchema(string $file, \DOMDocument $dom, string $schema)
+ {
+ $internalErrors = libxml_use_internal_errors(true);
+
+ $disableEntities = libxml_disable_entity_loader(false);
+
+ if (!@$dom->schemaValidateSource($schema)) {
+ libxml_disable_entity_loader($disableEntities);
+
+ throw new InvalidResourceException(sprintf('Invalid resource provided: "%s"; Errors: %s', $file, implode("\n", $this->getXmlErrors($internalErrors))));
+ }
+
+ libxml_disable_entity_loader($disableEntities);
+
+ $dom->normalizeDocument();
+
+ libxml_clear_errors();
+ libxml_use_internal_errors($internalErrors);
+ }
+
+ private function getSchema($xliffVersion)
+ {
+ if ('1.2' === $xliffVersion) {
+ $schemaSource = file_get_contents(__DIR__.'/schema/dic/xliff-core/xliff-core-1.2-strict.xsd');
+ $xmlUri = 'http://www.w3.org/2001/xml.xsd';
+ } elseif ('2.0' === $xliffVersion) {
+ $schemaSource = file_get_contents(__DIR__.'/schema/dic/xliff-core/xliff-core-2.0.xsd');
+ $xmlUri = 'informativeCopiesOf3rdPartySchemas/w3c/xml.xsd';
+ } else {
+ throw new InvalidArgumentException(sprintf('No support implemented for loading XLIFF version "%s".', $xliffVersion));
+ }
+
+ return $this->fixXmlLocation($schemaSource, $xmlUri);
+ }
+
+ /**
+ * Internally changes the URI of a dependent xsd to be loaded locally.
+ */
+ private function fixXmlLocation(string $schemaSource, string $xmlUri): string
+ {
+ $newPath = str_replace('\\', '/', __DIR__).'/schema/dic/xliff-core/xml.xsd';
+ $parts = explode('/', $newPath);
+ $locationstart = 'file:///';
+ if (0 === stripos($newPath, 'phar://')) {
+ $tmpfile = tempnam(sys_get_temp_dir(), 'symfony');
+ if ($tmpfile) {
+ copy($newPath, $tmpfile);
+ $parts = explode('/', str_replace('\\', '/', $tmpfile));
+ } else {
+ array_shift($parts);
+ $locationstart = 'phar:///';
+ }
+ }
+
+ $drive = '\\' === \DIRECTORY_SEPARATOR ? array_shift($parts).'/' : '';
+ $newPath = $locationstart.$drive.implode('/', array_map('rawurlencode', $parts));
+
+ return str_replace($xmlUri, $newPath, $schemaSource);
+ }
+
+ /**
+ * Returns the XML errors of the internal XML parser.
+ */
+ private function getXmlErrors(bool $internalErrors): array
+ {
+ $errors = array();
+ foreach (libxml_get_errors() as $error) {
+ $errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)',
+ LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR',
+ $error->code,
+ trim($error->message),
+ $error->file ?: 'n/a',
+ $error->line,
+ $error->column
+ );
+ }
+
+ libxml_clear_errors();
+ libxml_use_internal_errors($internalErrors);
+
+ return $errors;
+ }
+
+ /**
+ * Gets xliff file version based on the root "version" attribute.
+ * Defaults to 1.2 for backwards compatibility.
+ *
+ * @throws InvalidArgumentException
+ */
+ private function getVersionNumber(\DOMDocument $dom): string
+ {
+ /** @var \DOMNode $xliff */
+ foreach ($dom->getElementsByTagName('xliff') as $xliff) {
+ $version = $xliff->attributes->getNamedItem('version');
+ if ($version) {
+ return $version->nodeValue;
+ }
+
+ $namespace = $xliff->attributes->getNamedItem('xmlns');
+ if ($namespace) {
+ if (0 !== substr_compare('urn:oasis:names:tc:xliff:document:', $namespace->nodeValue, 0, 34)) {
+ throw new InvalidArgumentException(sprintf('Not a valid XLIFF namespace "%s"', $namespace));
+ }
+
+ return substr($namespace, 34);
+ }
+ }
+
+ // Falls back to v1.2
+ return '1.2';
+ }
+
+ private function parseNotesMetadata(\SimpleXMLElement $noteElement = null, string $encoding = null): array
+ {
+ $notes = array();
+
+ if (null === $noteElement) {
+ return $notes;
+ }
+
+ /** @var \SimpleXMLElement $xmlNote */
+ foreach ($noteElement as $xmlNote) {
+ $noteAttributes = $xmlNote->attributes();
+ $note = array('content' => $this->utf8ToCharset((string) $xmlNote, $encoding));
+ if (isset($noteAttributes['priority'])) {
+ $note['priority'] = (int) $noteAttributes['priority'];
+ }
+
+ if (isset($noteAttributes['from'])) {
+ $note['from'] = (string) $noteAttributes['from'];
+ }
+
+ $notes[] = $note;
+ }
+
+ return $notes;
+ }
+}
diff --git a/_include/calendar/symfony/translation/Loader/YamlFileLoader.php b/_include/calendar/symfony/translation/Loader/YamlFileLoader.php
new file mode 100644
index 0000000..584a055
--- /dev/null
+++ b/_include/calendar/symfony/translation/Loader/YamlFileLoader.php
@@ -0,0 +1,50 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\LogicException;
+use Symfony\Component\Yaml\Exception\ParseException;
+use Symfony\Component\Yaml\Parser as YamlParser;
+use Symfony\Component\Yaml\Yaml;
+
+/**
+ * YamlFileLoader loads translations from Yaml files.
+ *
+ * @author Fabien Potencier
+ */
+class YamlFileLoader extends FileLoader
+{
+ private $yamlParser;
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function loadResource($resource)
+ {
+ if (null === $this->yamlParser) {
+ if (!class_exists('Symfony\Component\Yaml\Parser')) {
+ throw new LogicException('Loading translations from the YAML format requires the Symfony Yaml component.');
+ }
+
+ $this->yamlParser = new YamlParser();
+ }
+
+ try {
+ $messages = $this->yamlParser->parseFile($resource, Yaml::PARSE_CONSTANT);
+ } catch (ParseException $e) {
+ throw new InvalidResourceException(sprintf('Error parsing YAML, invalid file "%s"', $resource), 0, $e);
+ }
+
+ return $messages;
+ }
+}
diff --git a/_include/calendar/symfony/translation/Loader/schema/dic/xliff-core/xliff-core-1.2-strict.xsd b/_include/calendar/symfony/translation/Loader/schema/dic/xliff-core/xliff-core-1.2-strict.xsd
new file mode 100644
index 0000000..3ce2a8e
--- /dev/null
+++ b/_include/calendar/symfony/translation/Loader/schema/dic/xliff-core/xliff-core-1.2-strict.xsd
@@ -0,0 +1,2223 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Values for the attribute 'context-type'.
+
+
+
+
+ Indicates a database content.
+
+
+
+
+ Indicates the content of an element within an XML document.
+
+
+
+
+ Indicates the name of an element within an XML document.
+
+
+
+
+ Indicates the line number from the sourcefile (see context-type="sourcefile") where the <source> is found.
+
+
+
+
+ Indicates a the number of parameters contained within the <source>.
+
+
+
+
+ Indicates notes pertaining to the parameters in the <source>.
+
+
+
+
+ Indicates the content of a record within a database.
+
+
+
+
+ Indicates the name of a record within a database.
+
+
+
+
+ Indicates the original source file in the case that multiple files are merged to form the original file from which the XLIFF file is created. This differs from the original <file> attribute in that this sourcefile is one of many that make up that file.
+
+
+
+
+
+
+ Values for the attribute 'count-type'.
+
+
+
+
+ Indicates the count units are items that are used X times in a certain context; example: this is a reusable text unit which is used 42 times in other texts.
+
+
+
+
+ Indicates the count units are translation units existing already in the same document.
+
+
+
+
+ Indicates a total count.
+
+
+
+
+
+
+ Values for the attribute 'ctype' when used other elements than <ph> or <x>.
+
+
+
+
+ Indicates a run of bolded text.
+
+
+
+
+ Indicates a run of text in italics.
+
+
+
+
+ Indicates a run of underlined text.
+
+
+
+
+ Indicates a run of hyper-text.
+
+
+
+
+
+
+ Values for the attribute 'ctype' when used with <ph> or <x>.
+
+
+
+
+ Indicates a inline image.
+
+
+
+
+ Indicates a page break.
+
+
+
+
+ Indicates a line break.
+
+
+
+
+
+
+
+
+
+
+
+ Values for the attribute 'datatype'.
+
+
+
+
+ Indicates Active Server Page data.
+
+
+
+
+ Indicates C source file data.
+
+
+
+
+ Indicates Channel Definition Format (CDF) data.
+
+
+
+
+ Indicates ColdFusion data.
+
+
+
+
+ Indicates C++ source file data.
+
+
+
+
+ Indicates C-Sharp data.
+
+
+
+
+ Indicates strings from C, ASM, and driver files data.
+
+
+
+
+ Indicates comma-separated values data.
+
+
+
+
+ Indicates database data.
+
+
+
+
+ Indicates portions of document that follows data and contains metadata.
+
+
+
+
+ Indicates portions of document that precedes data and contains metadata.
+
+
+
+
+ Indicates data from standard UI file operations dialogs (e.g., Open, Save, Save As, Export, Import).
+
+
+
+
+ Indicates standard user input screen data.
+
+
+
+
+ Indicates HyperText Markup Language (HTML) data - document instance.
+
+
+
+
+ Indicates content within an HTML document’s <body> element.
+
+
+
+
+ Indicates Windows INI file data.
+
+
+
+
+ Indicates Interleaf data.
+
+
+
+
+ Indicates Java source file data (extension '.java').
+
+
+
+
+ Indicates Java property resource bundle data.
+
+
+
+
+ Indicates Java list resource bundle data.
+
+
+
+
+ Indicates JavaScript source file data.
+
+
+
+
+ Indicates JScript source file data.
+
+
+
+
+ Indicates information relating to formatting.
+
+
+
+
+ Indicates LISP source file data.
+
+
+
+
+ Indicates information relating to margin formats.
+
+
+
+
+ Indicates a file containing menu.
+
+
+
+
+ Indicates numerically identified string table.
+
+
+
+
+ Indicates Maker Interchange Format (MIF) data.
+
+
+
+
+ Indicates that the datatype attribute value is a MIME Type value and is defined in the mime-type attribute.
+
+
+
+
+ Indicates GNU Machine Object data.
+
+
+
+
+ Indicates Message Librarian strings created by Novell's Message Librarian Tool.
+
+
+
+
+ Indicates information to be displayed at the bottom of each page of a document.
+
+
+
+
+ Indicates information to be displayed at the top of each page of a document.
+
+
+
+
+ Indicates a list of property values (e.g., settings within INI files or preferences dialog).
+
+
+
+
+ Indicates Pascal source file data.
+
+
+
+
+ Indicates Hypertext Preprocessor data.
+
+
+
+
+ Indicates plain text file (no formatting other than, possibly, wrapping).
+
+
+
+
+ Indicates GNU Portable Object file.
+
+
+
+
+ Indicates dynamically generated user defined document. e.g. Oracle Report, Crystal Report, etc.
+
+
+
+
+ Indicates Windows .NET binary resources.
+
+
+
+
+ Indicates Windows .NET Resources.
+
+
+
+
+ Indicates Rich Text Format (RTF) data.
+
+
+
+
+ Indicates Standard Generalized Markup Language (SGML) data - document instance.
+
+
+
+
+ Indicates Standard Generalized Markup Language (SGML) data - Document Type Definition (DTD).
+
+
+
+
+ Indicates Scalable Vector Graphic (SVG) data.
+
+
+
+
+ Indicates VisualBasic Script source file.
+
+
+
+
+ Indicates warning message.
+
+
+
+
+ Indicates Windows (Win32) resources (i.e. resources extracted from an RC script, a message file, or a compiled file).
+
+
+
+
+ Indicates Extensible HyperText Markup Language (XHTML) data - document instance.
+
+
+
+
+ Indicates Extensible Markup Language (XML) data - document instance.
+
+
+
+
+ Indicates Extensible Markup Language (XML) data - Document Type Definition (DTD).
+
+
+
+
+ Indicates Extensible Stylesheet Language (XSL) data.
+
+
+
+
+ Indicates XUL elements.
+
+
+
+
+
+
+ Values for the attribute 'mtype'.
+
+
+
+
+ Indicates the marked text is an abbreviation.
+
+
+
+
+ ISO-12620 2.1.8: A term resulting from the omission of any part of the full term while designating the same concept.
+
+
+
+
+ ISO-12620 2.1.8.1: An abbreviated form of a simple term resulting from the omission of some of its letters (e.g. 'adj.' for 'adjective').
+
+
+
+
+ ISO-12620 2.1.8.4: An abbreviated form of a term made up of letters from the full form of a multiword term strung together into a sequence pronounced only syllabically (e.g. 'radar' for 'radio detecting and ranging').
+
+
+
+
+ ISO-12620: A proper-name term, such as the name of an agency or other proper entity.
+
+
+
+
+ ISO-12620 2.1.18.1: A recurrent word combination characterized by cohesion in that the components of the collocation must co-occur within an utterance or series of utterances, even though they do not necessarily have to maintain immediate proximity to one another.
+
+
+
+
+ ISO-12620 2.1.5: A synonym for an international scientific term that is used in general discourse in a given language.
+
+
+
+
+ Indicates the marked text is a date and/or time.
+
+
+
+
+ ISO-12620 2.1.15: An expression used to represent a concept based on a statement that two mathematical expressions are, for instance, equal as identified by the equal sign (=), or assigned to one another by a similar sign.
+
+
+
+
+ ISO-12620 2.1.7: The complete representation of a term for which there is an abbreviated form.
+
+
+
+
+ ISO-12620 2.1.14: Figures, symbols or the like used to express a concept briefly, such as a mathematical or chemical formula.
+
+
+
+
+ ISO-12620 2.1.1: The concept designation that has been chosen to head a terminological record.
+
+
+
+
+ ISO-12620 2.1.8.3: An abbreviated form of a term consisting of some of the initial letters of the words making up a multiword term or the term elements making up a compound term when these letters are pronounced individually (e.g. 'BSE' for 'bovine spongiform encephalopathy').
+
+
+
+
+ ISO-12620 2.1.4: A term that is part of an international scientific nomenclature as adopted by an appropriate scientific body.
+
+
+
+
+ ISO-12620 2.1.6: A term that has the same or nearly identical orthographic or phonemic form in many languages.
+
+
+
+
+ ISO-12620 2.1.16: An expression used to represent a concept based on mathematical or logical relations, such as statements of inequality, set relationships, Boolean operations, and the like.
+
+
+
+
+ ISO-12620 2.1.17: A unit to track object.
+
+
+
+
+ Indicates the marked text is a name.
+
+
+
+
+ ISO-12620 2.1.3: A term that represents the same or a very similar concept as another term in the same language, but for which interchangeability is limited to some contexts and inapplicable in others.
+
+
+
+
+ ISO-12620 2.1.17.2: A unique alphanumeric designation assigned to an object in a manufacturing system.
+
+
+
+
+ Indicates the marked text is a phrase.
+
+
+
+
+ ISO-12620 2.1.18: Any group of two or more words that form a unit, the meaning of which frequently cannot be deduced based on the combined sense of the words making up the phrase.
+
+
+
+
+ Indicates the marked text should not be translated.
+
+
+
+
+ ISO-12620 2.1.12: A form of a term resulting from an operation whereby non-Latin writing systems are converted to the Latin alphabet.
+
+
+
+
+ Indicates that the marked text represents a segment.
+
+
+
+
+ ISO-12620 2.1.18.2: A fixed, lexicalized phrase.
+
+
+
+
+ ISO-12620 2.1.8.2: A variant of a multiword term that includes fewer words than the full form of the term (e.g. 'Group of Twenty-four' for 'Intergovernmental Group of Twenty-four on International Monetary Affairs').
+
+
+
+
+ ISO-12620 2.1.17.1: Stock keeping unit, an inventory item identified by a unique alphanumeric designation assigned to an object in an inventory control system.
+
+
+
+
+ ISO-12620 2.1.19: A fixed chunk of recurring text.
+
+
+
+
+ ISO-12620 2.1.13: A designation of a concept by letters, numerals, pictograms or any combination thereof.
+
+
+
+
+ ISO-12620 2.1.2: Any term that represents the same or a very similar concept as the main entry term in a term entry.
+
+
+
+
+ ISO-12620 2.1.18.3: Phraseological unit in a language that expresses the same semantic content as another phrase in that same language.
+
+
+
+
+ Indicates the marked text is a term.
+
+
+
+
+ ISO-12620 2.1.11: A form of a term resulting from an operation whereby the characters of one writing system are represented by characters from another writing system, taking into account the pronunciation of the characters converted.
+
+
+
+
+ ISO-12620 2.1.10: A form of a term resulting from an operation whereby the characters of an alphabetic writing system are represented by characters from another alphabetic writing system.
+
+
+
+
+ ISO-12620 2.1.8.5: An abbreviated form of a term resulting from the omission of one or more term elements or syllables (e.g. 'flu' for 'influenza').
+
+
+
+
+ ISO-12620 2.1.9: One of the alternate forms of a term.
+
+
+
+
+
+
+ Values for the attribute 'restype'.
+
+
+
+
+ Indicates a Windows RC AUTO3STATE control.
+
+
+
+
+ Indicates a Windows RC AUTOCHECKBOX control.
+
+
+
+
+ Indicates a Windows RC AUTORADIOBUTTON control.
+
+
+
+
+ Indicates a Windows RC BEDIT control.
+
+
+
+
+ Indicates a bitmap, for example a BITMAP resource in Windows.
+
+
+
+
+ Indicates a button object, for example a BUTTON control Windows.
+
+
+
+
+ Indicates a caption, such as the caption of a dialog box.
+
+
+
+
+ Indicates the cell in a table, for example the content of the <td> element in HTML.
+
+
+
+
+ Indicates check box object, for example a CHECKBOX control in Windows.
+
+
+
+
+ Indicates a menu item with an associated checkbox.
+
+
+
+
+ Indicates a list box, but with a check-box for each item.
+
+
+
+
+ Indicates a color selection dialog.
+
+
+
+
+ Indicates a combination of edit box and listbox object, for example a COMBOBOX control in Windows.
+
+
+
+
+ Indicates an initialization entry of an extended combobox DLGINIT resource block. (code 0x1234).
+
+
+
+
+ Indicates an initialization entry of a combobox DLGINIT resource block (code 0x0403).
+
+
+
+
+ Indicates a UI base class element that cannot be represented by any other element.
+
+
+
+
+ Indicates a context menu.
+
+
+
+
+ Indicates a Windows RC CTEXT control.
+
+
+
+
+ Indicates a cursor, for example a CURSOR resource in Windows.
+
+
+
+
+ Indicates a date/time picker.
+
+
+
+
+ Indicates a Windows RC DEFPUSHBUTTON control.
+
+
+
+
+ Indicates a dialog box.
+
+
+
+
+ Indicates a Windows RC DLGINIT resource block.
+
+
+
+
+ Indicates an edit box object, for example an EDIT control in Windows.
+
+
+
+
+ Indicates a filename.
+
+
+
+
+ Indicates a file dialog.
+
+
+
+
+ Indicates a footnote.
+
+
+
+
+ Indicates a font name.
+
+
+
+
+ Indicates a footer.
+
+
+
+
+ Indicates a frame object.
+
+
+
+
+ Indicates a XUL grid element.
+
+
+
+
+ Indicates a groupbox object, for example a GROUPBOX control in Windows.
+
+
+
+
+ Indicates a header item.
+
+
+
+
+ Indicates a heading, such has the content of <h1>, <h2>, etc. in HTML.
+
+
+
+
+ Indicates a Windows RC HEDIT control.
+
+
+
+
+ Indicates a horizontal scrollbar.
+
+
+
+
+ Indicates an icon, for example an ICON resource in Windows.
+
+
+
+
+ Indicates a Windows RC IEDIT control.
+
+
+
+
+ Indicates keyword list, such as the content of the Keywords meta-data in HTML, or a K footnote in WinHelp RTF.
+
+
+
+
+ Indicates a label object.
+
+
+
+
+ Indicates a label that is also a HTML link (not necessarily a URL).
+
+
+
+
+ Indicates a list (a group of list-items, for example an <ol> or <ul> element in HTML).
+
+
+
+
+ Indicates a listbox object, for example an LISTBOX control in Windows.
+
+
+
+
+ Indicates an list item (an entry in a list).
+
+
+
+
+ Indicates a Windows RC LTEXT control.
+
+
+
+
+ Indicates a menu (a group of menu-items).
+
+
+
+
+ Indicates a toolbar containing one or more tope level menus.
+
+
+
+
+ Indicates a menu item (an entry in a menu).
+
+
+
+
+ Indicates a XUL menuseparator element.
+
+
+
+
+ Indicates a message, for example an entry in a MESSAGETABLE resource in Windows.
+
+
+
+
+ Indicates a calendar control.
+
+
+
+
+ Indicates an edit box beside a spin control.
+
+
+
+
+ Indicates a catch all for rectangular areas.
+
+
+
+
+ Indicates a standalone menu not necessarily associated with a menubar.
+
+
+
+
+ Indicates a pushbox object, for example a PUSHBOX control in Windows.
+
+
+
+
+ Indicates a Windows RC PUSHBUTTON control.
+
+
+
+
+ Indicates a radio button object.
+
+
+
+
+ Indicates a menuitem with associated radio button.
+
+
+
+
+ Indicates raw data resources for an application.
+
+
+
+
+ Indicates a row in a table.
+
+
+
+
+ Indicates a Windows RC RTEXT control.
+
+
+
+
+ Indicates a user navigable container used to show a portion of a document.
+
+
+
+
+ Indicates a generic divider object (e.g. menu group separator).
+
+
+
+
+ Windows accelerators, shortcuts in resource or property files.
+
+
+
+
+ Indicates a UI control to indicate process activity but not progress.
+
+
+
+
+ Indicates a splitter bar.
+
+
+
+
+ Indicates a Windows RC STATE3 control.
+
+
+
+
+ Indicates a window for providing feedback to the users, like 'read-only', etc.
+
+
+
+
+ Indicates a string, for example an entry in a STRINGTABLE resource in Windows.
+
+
+
+
+ Indicates a layers of controls with a tab to select layers.
+
+
+
+
+ Indicates a display and edits regular two-dimensional tables of cells.
+
+
+
+
+ Indicates a XUL textbox element.
+
+
+
+
+ Indicates a UI button that can be toggled to on or off state.
+
+
+
+
+ Indicates an array of controls, usually buttons.
+
+
+
+
+ Indicates a pop up tool tip text.
+
+
+
+
+ Indicates a bar with a pointer indicating a position within a certain range.
+
+
+
+
+ Indicates a control that displays a set of hierarchical data.
+
+
+
+
+ Indicates a URI (URN or URL).
+
+
+
+
+ Indicates a Windows RC USERBUTTON control.
+
+
+
+
+ Indicates a user-defined control like CONTROL control in Windows.
+
+
+
+
+ Indicates the text of a variable.
+
+
+
+
+ Indicates version information about a resource like VERSIONINFO in Windows.
+
+
+
+
+ Indicates a vertical scrollbar.
+
+
+
+
+ Indicates a graphical window.
+
+
+
+
+
+
+ Values for the attribute 'size-unit'.
+
+
+
+
+ Indicates a size in 8-bit bytes.
+
+
+
+
+ Indicates a size in Unicode characters.
+
+
+
+
+ Indicates a size in columns. Used for HTML text area.
+
+
+
+
+ Indicates a size in centimeters.
+
+
+
+
+ Indicates a size in dialog units, as defined in Windows resources.
+
+
+
+
+ Indicates a size in 'font-size' units (as defined in CSS).
+
+
+
+
+ Indicates a size in 'x-height' units (as defined in CSS).
+
+
+
+
+ Indicates a size in glyphs. A glyph is considered to be one or more combined Unicode characters that represent a single displayable text character. Sometimes referred to as a 'grapheme cluster'
+
+
+
+
+ Indicates a size in inches.
+
+
+
+
+ Indicates a size in millimeters.
+
+
+
+
+ Indicates a size in percentage.
+
+
+
+
+ Indicates a size in pixels.
+
+
+
+
+ Indicates a size in point.
+
+
+
+
+ Indicates a size in rows. Used for HTML text area.
+
+
+
+
+
+
+ Values for the attribute 'state'.
+
+
+
+
+ Indicates the terminating state.
+
+
+
+
+ Indicates only non-textual information needs adaptation.
+
+
+
+
+ Indicates both text and non-textual information needs adaptation.
+
+
+
+
+ Indicates only non-textual information needs review.
+
+
+
+
+ Indicates both text and non-textual information needs review.
+
+
+
+
+ Indicates that only the text of the item needs to be reviewed.
+
+
+
+
+ Indicates that the item needs to be translated.
+
+
+
+
+ Indicates that the item is new. For example, translation units that were not in a previous version of the document.
+
+
+
+
+ Indicates that changes are reviewed and approved.
+
+
+
+
+ Indicates that the item has been translated.
+
+
+
+
+
+
+ Values for the attribute 'state-qualifier'.
+
+
+
+
+ Indicates an exact match. An exact match occurs when a source text of a segment is exactly the same as the source text of a segment that was translated previously.
+
+
+
+
+ Indicates a fuzzy match. A fuzzy match occurs when a source text of a segment is very similar to the source text of a segment that was translated previously (e.g. when the difference is casing, a few changed words, white-space discripancy, etc.).
+
+
+
+
+ Indicates a match based on matching IDs (in addition to matching text).
+
+
+
+
+ Indicates a translation derived from a glossary.
+
+
+
+
+ Indicates a translation derived from existing translation.
+
+
+
+
+ Indicates a translation derived from machine translation.
+
+
+
+
+ Indicates a translation derived from a translation repository.
+
+
+
+
+ Indicates a translation derived from a translation memory.
+
+
+
+
+ Indicates the translation is suggested by machine translation.
+
+
+
+
+ Indicates that the item has been rejected because of incorrect grammar.
+
+
+
+
+ Indicates that the item has been rejected because it is incorrect.
+
+
+
+
+ Indicates that the item has been rejected because it is too long or too short.
+
+
+
+
+ Indicates that the item has been rejected because of incorrect spelling.
+
+
+
+
+ Indicates the translation is suggested by translation memory.
+
+
+
+
+
+
+ Values for the attribute 'unit'.
+
+
+
+
+ Refers to words.
+
+
+
+
+ Refers to pages.
+
+
+
+
+ Refers to <trans-unit> elements.
+
+
+
+
+ Refers to <bin-unit> elements.
+
+
+
+
+ Refers to glyphs.
+
+
+
+
+ Refers to <trans-unit> and/or <bin-unit> elements.
+
+
+
+
+ Refers to the occurrences of instances defined by the count-type value.
+
+
+
+
+ Refers to characters.
+
+
+
+
+ Refers to lines.
+
+
+
+
+ Refers to sentences.
+
+
+
+
+ Refers to paragraphs.
+
+
+
+
+ Refers to segments.
+
+
+
+
+ Refers to placeables (inline elements).
+
+
+
+
+
+
+ Values for the attribute 'priority'.
+
+
+
+
+ Highest priority.
+
+
+
+
+ High priority.
+
+
+
+
+ High priority, but not as important as 2.
+
+
+
+
+ High priority, but not as important as 3.
+
+
+
+
+ Medium priority, but more important than 6.
+
+
+
+
+ Medium priority, but less important than 5.
+
+
+
+
+ Low priority, but more important than 8.
+
+
+
+
+ Low priority, but more important than 9.
+
+
+
+
+ Low priority.
+
+
+
+
+ Lowest priority.
+
+
+
+
+
+
+
+
+ This value indicates that all properties can be reformatted. This value must be used alone.
+
+
+
+
+ This value indicates that no properties should be reformatted. This value must be used alone.
+
+
+
+
+
+
+
+
+
+
+
+
+ This value indicates that all information in the coord attribute can be modified.
+
+
+
+
+ This value indicates that the x information in the coord attribute can be modified.
+
+
+
+
+ This value indicates that the y information in the coord attribute can be modified.
+
+
+
+
+ This value indicates that the cx information in the coord attribute can be modified.
+
+
+
+
+ This value indicates that the cy information in the coord attribute can be modified.
+
+
+
+
+ This value indicates that all the information in the font attribute can be modified.
+
+
+
+
+ This value indicates that the name information in the font attribute can be modified.
+
+
+
+
+ This value indicates that the size information in the font attribute can be modified.
+
+
+
+
+ This value indicates that the weight information in the font attribute can be modified.
+
+
+
+
+ This value indicates that the information in the css-style attribute can be modified.
+
+
+
+
+ This value indicates that the information in the style attribute can be modified.
+
+
+
+
+ This value indicates that the information in the exstyle attribute can be modified.
+
+
+
+
+
+
+
+
+
+
+
+
+ Indicates that the context is informational in nature, specifying for example, how a term should be translated. Thus, should be displayed to anyone editing the XLIFF document.
+
+
+
+
+ Indicates that the context-group is used to specify where the term was found in the translatable source. Thus, it is not displayed.
+
+
+
+
+ Indicates that the context information should be used during translation memory lookups. Thus, it is not displayed.
+
+
+
+
+
+
+
+
+ Represents a translation proposal from a translation memory or other resource.
+
+
+
+
+ Represents a previous version of the target element.
+
+
+
+
+ Represents a rejected version of the target element.
+
+
+
+
+ Represents a translation to be used for reference purposes only, for example from a related product or a different language.
+
+
+
+
+ Represents a proposed translation that was used for the translation of the trans-unit, possibly modified.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Values for the attribute 'coord'.
+
+
+
+
+
+
+
+ Version values: 1.0 and 1.1 are allowed for backward compatibility.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/_include/calendar/symfony/translation/Loader/schema/dic/xliff-core/xliff-core-2.0.xsd b/_include/calendar/symfony/translation/Loader/schema/dic/xliff-core/xliff-core-2.0.xsd
new file mode 100644
index 0000000..963232f
--- /dev/null
+++ b/_include/calendar/symfony/translation/Loader/schema/dic/xliff-core/xliff-core-2.0.xsd
@@ -0,0 +1,411 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/_include/calendar/symfony/translation/Loader/schema/dic/xliff-core/xml.xsd b/_include/calendar/symfony/translation/Loader/schema/dic/xliff-core/xml.xsd
new file mode 100644
index 0000000..a46162a
--- /dev/null
+++ b/_include/calendar/symfony/translation/Loader/schema/dic/xliff-core/xml.xsd
@@ -0,0 +1,309 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
lang (as an attribute name)
+
+
+ denotes an attribute whose value
+ is a language code for the natural language of the content of
+ any element; its value is inherited. This name is reserved
+ by virtue of its definition in the XML specification.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
space (as an attribute name)
+
+ denotes an attribute whose
+ value is a keyword indicating what whitespace processing
+ discipline is intended for the content of the element; its
+ value is inherited. This name is reserved by virtue of its
+ definition in the XML specification.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
base (as an attribute name)
+
+ denotes an attribute whose value
+ provides a URI to be used as the base for interpreting any
+ relative URIs in the scope of the element on which it
+ appears; its value is inherited. This name is reserved
+ by virtue of its definition in the XML Base specification.
+
+
+ See http://www.w3.org/TR/xmlbase/
+ for information about this attribute.
+
+
+
+
+
+
+
+
+
+
+
+
+
id (as an attribute name)
+
+
+ denotes an attribute whose value
+ should be interpreted as if declared to be of type ID.
+ This name is reserved by virtue of its definition in the
+ xml:id specification.
+
+
+ See http://www.w3.org/TR/xml-id/
+ for information about this attribute.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Father (in any context at all)
+
+
+
+ denotes Jon Bosak, the chair of
+ the original XML Working Group. This name is reserved by
+ the following decision of the W3C XML Plenary and
+ XML Coordination groups:
+
+
+
+
+ In appreciation for his vision, leadership and
+ dedication the W3C XML Plenary on this 10th day of
+ February, 2000, reserves for Jon Bosak in perpetuity
+ the XML name "xml:Father".
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This schema defines attributes and an attribute group suitable
+ for use by schemas wishing to allow xml:base
,
+ xml:lang
, xml:space
or
+ xml:id
attributes on elements they define.
+
+
+
+ To enable this, such a schema must import this schema for
+ the XML namespace, e.g. as follows:
+
+
+ <schema.. .>
+ .. .
+ <import namespace="http://www.w3.org/XML/1998/namespace"
+ schemaLocation="http://www.w3.org/2001/xml.xsd"/>
+
+
+ or
+
+
+
+ <import namespace="http://www.w3.org/XML/1998/namespace"
+ schemaLocation="http://www.w3.org/2009/01/xml.xsd"/>
+
+
+ Subsequently, qualified reference to any of the attributes or the
+ group defined below will have the desired effect, e.g.
+
+
+ <type.. .>
+ .. .
+ <attributeGroup ref="xml:specialAttrs"/>
+
+
+ will define a type which will schema-validate an instance element
+ with any of those attributes.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ In keeping with the XML Schema WG's standard versioning
+ policy, this schema document will persist at
+
+ http://www.w3.org/2009/01/xml.xsd .
+
+
+ At the date of issue it can also be found at
+
+ http://www.w3.org/2001/xml.xsd .
+
+
+
+ The schema document at that URI may however change in the future,
+ in order to remain compatible with the latest version of XML
+ Schema itself, or with the XML namespace itself. In other words,
+ if the XML Schema or XML namespaces change, the version of this
+ document at
+ http://www.w3.org/2001/xml.xsd
+
+ will change accordingly; the version at
+
+ http://www.w3.org/2009/01/xml.xsd
+
+ will not change.
+
+
+
+ Previous dated (and unchanging) versions of this schema
+ document are at:
+
+
+
+
+
+
+
+
diff --git a/_include/calendar/symfony/translation/LoggingTranslator.php b/_include/calendar/symfony/translation/LoggingTranslator.php
new file mode 100644
index 0000000..0145670
--- /dev/null
+++ b/_include/calendar/symfony/translation/LoggingTranslator.php
@@ -0,0 +1,136 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation;
+
+use Psr\Log\LoggerInterface;
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+
+/**
+ * @author Abdellatif Ait boudad
+ */
+class LoggingTranslator implements TranslatorInterface, TranslatorBagInterface
+{
+ /**
+ * @var TranslatorInterface|TranslatorBagInterface
+ */
+ private $translator;
+
+ private $logger;
+
+ /**
+ * @param TranslatorInterface $translator The translator must implement TranslatorBagInterface
+ * @param LoggerInterface $logger
+ */
+ public function __construct(TranslatorInterface $translator, LoggerInterface $logger)
+ {
+ if (!$translator instanceof TranslatorBagInterface) {
+ throw new InvalidArgumentException(sprintf('The Translator "%s" must implement TranslatorInterface and TranslatorBagInterface.', \get_class($translator)));
+ }
+
+ $this->translator = $translator;
+ $this->logger = $logger;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function trans($id, array $parameters = array(), $domain = null, $locale = null)
+ {
+ $trans = $this->translator->trans($id, $parameters, $domain, $locale);
+ $this->log($id, $domain, $locale);
+
+ return $trans;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null)
+ {
+ $trans = $this->translator->transChoice($id, $number, $parameters, $domain, $locale);
+ $this->log($id, $domain, $locale);
+
+ return $trans;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setLocale($locale)
+ {
+ $this->translator->setLocale($locale);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getLocale()
+ {
+ return $this->translator->getLocale();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getCatalogue($locale = null)
+ {
+ return $this->translator->getCatalogue($locale);
+ }
+
+ /**
+ * Gets the fallback locales.
+ *
+ * @return array $locales The fallback locales
+ */
+ public function getFallbackLocales()
+ {
+ if ($this->translator instanceof Translator || method_exists($this->translator, 'getFallbackLocales')) {
+ return $this->translator->getFallbackLocales();
+ }
+
+ return array();
+ }
+
+ /**
+ * Passes through all unknown calls onto the translator object.
+ */
+ public function __call($method, $args)
+ {
+ return \call_user_func_array(array($this->translator, $method), $args);
+ }
+
+ /**
+ * Logs for missing translations.
+ *
+ * @param string $id
+ * @param string|null $domain
+ * @param string|null $locale
+ */
+ private function log($id, $domain, $locale)
+ {
+ if (null === $domain) {
+ $domain = 'messages';
+ }
+
+ $id = (string) $id;
+ $catalogue = $this->translator->getCatalogue($locale);
+ if ($catalogue->defines($id, $domain)) {
+ return;
+ }
+
+ if ($catalogue->has($id, $domain)) {
+ $this->logger->debug('Translation use fallback catalogue.', array('id' => $id, 'domain' => $domain, 'locale' => $catalogue->getLocale()));
+ } else {
+ $this->logger->warning('Translation not found.', array('id' => $id, 'domain' => $domain, 'locale' => $catalogue->getLocale()));
+ }
+ }
+}
diff --git a/_include/calendar/symfony/translation/MessageCatalogue.php b/_include/calendar/symfony/translation/MessageCatalogue.php
new file mode 100644
index 0000000..c36ea30
--- /dev/null
+++ b/_include/calendar/symfony/translation/MessageCatalogue.php
@@ -0,0 +1,271 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation;
+
+use Symfony\Component\Config\Resource\ResourceInterface;
+use Symfony\Component\Translation\Exception\LogicException;
+
+/**
+ * @author Fabien Potencier
+ */
+class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterface
+{
+ private $messages = array();
+ private $metadata = array();
+ private $resources = array();
+ private $locale;
+ private $fallbackCatalogue;
+ private $parent;
+
+ /**
+ * @param string $locale The locale
+ * @param array $messages An array of messages classified by domain
+ */
+ public function __construct(?string $locale, array $messages = array())
+ {
+ $this->locale = $locale;
+ $this->messages = $messages;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getLocale()
+ {
+ return $this->locale;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getDomains()
+ {
+ return array_keys($this->messages);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function all($domain = null)
+ {
+ if (null === $domain) {
+ return $this->messages;
+ }
+
+ return isset($this->messages[$domain]) ? $this->messages[$domain] : array();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set($id, $translation, $domain = 'messages')
+ {
+ $this->add(array($id => $translation), $domain);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function has($id, $domain = 'messages')
+ {
+ if (isset($this->messages[$domain][$id])) {
+ return true;
+ }
+
+ if (null !== $this->fallbackCatalogue) {
+ return $this->fallbackCatalogue->has($id, $domain);
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function defines($id, $domain = 'messages')
+ {
+ return isset($this->messages[$domain][$id]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get($id, $domain = 'messages')
+ {
+ if (isset($this->messages[$domain][$id])) {
+ return $this->messages[$domain][$id];
+ }
+
+ if (null !== $this->fallbackCatalogue) {
+ return $this->fallbackCatalogue->get($id, $domain);
+ }
+
+ return $id;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function replace($messages, $domain = 'messages')
+ {
+ $this->messages[$domain] = array();
+
+ $this->add($messages, $domain);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function add($messages, $domain = 'messages')
+ {
+ if (!isset($this->messages[$domain])) {
+ $this->messages[$domain] = $messages;
+ } else {
+ $this->messages[$domain] = array_replace($this->messages[$domain], $messages);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addCatalogue(MessageCatalogueInterface $catalogue)
+ {
+ if ($catalogue->getLocale() !== $this->locale) {
+ throw new LogicException(sprintf('Cannot add a catalogue for locale "%s" as the current locale for this catalogue is "%s"', $catalogue->getLocale(), $this->locale));
+ }
+
+ foreach ($catalogue->all() as $domain => $messages) {
+ $this->add($messages, $domain);
+ }
+
+ foreach ($catalogue->getResources() as $resource) {
+ $this->addResource($resource);
+ }
+
+ if ($catalogue instanceof MetadataAwareInterface) {
+ $metadata = $catalogue->getMetadata('', '');
+ $this->addMetadata($metadata);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addFallbackCatalogue(MessageCatalogueInterface $catalogue)
+ {
+ // detect circular references
+ $c = $catalogue;
+ while ($c = $c->getFallbackCatalogue()) {
+ if ($c->getLocale() === $this->getLocale()) {
+ throw new LogicException(sprintf('Circular reference detected when adding a fallback catalogue for locale "%s".', $catalogue->getLocale()));
+ }
+ }
+
+ $c = $this;
+ do {
+ if ($c->getLocale() === $catalogue->getLocale()) {
+ throw new LogicException(sprintf('Circular reference detected when adding a fallback catalogue for locale "%s".', $catalogue->getLocale()));
+ }
+
+ foreach ($catalogue->getResources() as $resource) {
+ $c->addResource($resource);
+ }
+ } while ($c = $c->parent);
+
+ $catalogue->parent = $this;
+ $this->fallbackCatalogue = $catalogue;
+
+ foreach ($catalogue->getResources() as $resource) {
+ $this->addResource($resource);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getFallbackCatalogue()
+ {
+ return $this->fallbackCatalogue;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getResources()
+ {
+ return array_values($this->resources);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addResource(ResourceInterface $resource)
+ {
+ $this->resources[$resource->__toString()] = $resource;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getMetadata($key = '', $domain = 'messages')
+ {
+ if ('' == $domain) {
+ return $this->metadata;
+ }
+
+ if (isset($this->metadata[$domain])) {
+ if ('' == $key) {
+ return $this->metadata[$domain];
+ }
+
+ if (isset($this->metadata[$domain][$key])) {
+ return $this->metadata[$domain][$key];
+ }
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setMetadata($key, $value, $domain = 'messages')
+ {
+ $this->metadata[$domain][$key] = $value;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function deleteMetadata($key = '', $domain = 'messages')
+ {
+ if ('' == $domain) {
+ $this->metadata = array();
+ } elseif ('' == $key) {
+ unset($this->metadata[$domain]);
+ } else {
+ unset($this->metadata[$domain][$key]);
+ }
+ }
+
+ /**
+ * Adds current values with the new values.
+ *
+ * @param array $values Values to add
+ */
+ private function addMetadata(array $values)
+ {
+ foreach ($values as $domain => $keys) {
+ foreach ($keys as $key => $value) {
+ $this->setMetadata($key, $value, $domain);
+ }
+ }
+ }
+}
diff --git a/_include/calendar/symfony/translation/MessageCatalogueInterface.php b/_include/calendar/symfony/translation/MessageCatalogueInterface.php
new file mode 100644
index 0000000..e0dbb2b
--- /dev/null
+++ b/_include/calendar/symfony/translation/MessageCatalogueInterface.php
@@ -0,0 +1,136 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation;
+
+use Symfony\Component\Config\Resource\ResourceInterface;
+
+/**
+ * MessageCatalogueInterface.
+ *
+ * @author Fabien Potencier
+ */
+interface MessageCatalogueInterface
+{
+ /**
+ * Gets the catalogue locale.
+ *
+ * @return string The locale
+ */
+ public function getLocale();
+
+ /**
+ * Gets the domains.
+ *
+ * @return array An array of domains
+ */
+ public function getDomains();
+
+ /**
+ * Gets the messages within a given domain.
+ *
+ * If $domain is null, it returns all messages.
+ *
+ * @param string $domain The domain name
+ *
+ * @return array An array of messages
+ */
+ public function all($domain = null);
+
+ /**
+ * Sets a message translation.
+ *
+ * @param string $id The message id
+ * @param string $translation The messages translation
+ * @param string $domain The domain name
+ */
+ public function set($id, $translation, $domain = 'messages');
+
+ /**
+ * Checks if a message has a translation.
+ *
+ * @param string $id The message id
+ * @param string $domain The domain name
+ *
+ * @return bool true if the message has a translation, false otherwise
+ */
+ public function has($id, $domain = 'messages');
+
+ /**
+ * Checks if a message has a translation (it does not take into account the fallback mechanism).
+ *
+ * @param string $id The message id
+ * @param string $domain The domain name
+ *
+ * @return bool true if the message has a translation, false otherwise
+ */
+ public function defines($id, $domain = 'messages');
+
+ /**
+ * Gets a message translation.
+ *
+ * @param string $id The message id
+ * @param string $domain The domain name
+ *
+ * @return string The message translation
+ */
+ public function get($id, $domain = 'messages');
+
+ /**
+ * Sets translations for a given domain.
+ *
+ * @param array $messages An array of translations
+ * @param string $domain The domain name
+ */
+ public function replace($messages, $domain = 'messages');
+
+ /**
+ * Adds translations for a given domain.
+ *
+ * @param array $messages An array of translations
+ * @param string $domain The domain name
+ */
+ public function add($messages, $domain = 'messages');
+
+ /**
+ * Merges translations from the given Catalogue into the current one.
+ *
+ * The two catalogues must have the same locale.
+ */
+ public function addCatalogue(self $catalogue);
+
+ /**
+ * Merges translations from the given Catalogue into the current one
+ * only when the translation does not exist.
+ *
+ * This is used to provide default translations when they do not exist for the current locale.
+ */
+ public function addFallbackCatalogue(self $catalogue);
+
+ /**
+ * Gets the fallback catalogue.
+ *
+ * @return self|null A MessageCatalogueInterface instance or null when no fallback has been set
+ */
+ public function getFallbackCatalogue();
+
+ /**
+ * Returns an array of resources loaded to build this collection.
+ *
+ * @return ResourceInterface[] An array of resources
+ */
+ public function getResources();
+
+ /**
+ * Adds a resource for this collection.
+ */
+ public function addResource(ResourceInterface $resource);
+}
diff --git a/_include/calendar/symfony/translation/MessageSelector.php b/_include/calendar/symfony/translation/MessageSelector.php
new file mode 100644
index 0000000..5de171c
--- /dev/null
+++ b/_include/calendar/symfony/translation/MessageSelector.php
@@ -0,0 +1,94 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation;
+
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+
+/**
+ * MessageSelector.
+ *
+ * @author Fabien Potencier
+ * @author Bernhard Schussek
+ */
+class MessageSelector
+{
+ /**
+ * Given a message with different plural translations separated by a
+ * pipe (|), this method returns the correct portion of the message based
+ * on the given number, locale and the pluralization rules in the message
+ * itself.
+ *
+ * The message supports two different types of pluralization rules:
+ *
+ * interval: {0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples
+ * indexed: There is one apple|There are %count% apples
+ *
+ * The indexed solution can also contain labels (e.g. one: There is one apple).
+ * This is purely for making the translations more clear - it does not
+ * affect the functionality.
+ *
+ * The two methods can also be mixed:
+ * {0} There are no apples|one: There is one apple|more: There are %count% apples
+ *
+ * @param string $message The message being translated
+ * @param int $number The number of items represented for the message
+ * @param string $locale The locale to use for choosing
+ *
+ * @return string
+ *
+ * @throws InvalidArgumentException
+ */
+ public function choose($message, $number, $locale)
+ {
+ $parts = array();
+ if (preg_match('/^\|++$/', $message)) {
+ $parts = explode('|', $message);
+ } elseif (preg_match_all('/(?:\|\||[^\|])++/', $message, $matches)) {
+ $parts = $matches[0];
+ }
+
+ $explicitRules = array();
+ $standardRules = array();
+ foreach ($parts as $part) {
+ $part = trim(str_replace('||', '|', $part));
+
+ if (preg_match('/^(?P'.Interval::getIntervalRegexp().')\s*(?P.*?)$/xs', $part, $matches)) {
+ $explicitRules[$matches['interval']] = $matches['message'];
+ } elseif (preg_match('/^\w+\:\s*(.*?)$/', $part, $matches)) {
+ $standardRules[] = $matches[1];
+ } else {
+ $standardRules[] = $part;
+ }
+ }
+
+ // try to match an explicit rule, then fallback to the standard ones
+ foreach ($explicitRules as $interval => $m) {
+ if (Interval::test($number, $interval)) {
+ return $m;
+ }
+ }
+
+ $position = PluralizationRules::get($number, $locale);
+
+ if (!isset($standardRules[$position])) {
+ // when there's exactly one rule given, and that rule is a standard
+ // rule, use this rule
+ if (1 === \count($parts) && isset($standardRules[0])) {
+ return $standardRules[0];
+ }
+
+ throw new InvalidArgumentException(sprintf('Unable to choose a translation for "%s" with locale "%s" for value "%d". Double check that this translation has the correct plural options (e.g. "There is one apple|There are %%count%% apples").', $message, $locale, $number));
+ }
+
+ return $standardRules[$position];
+ }
+}
diff --git a/_include/calendar/symfony/translation/MetadataAwareInterface.php b/_include/calendar/symfony/translation/MetadataAwareInterface.php
new file mode 100644
index 0000000..e93c6fb
--- /dev/null
+++ b/_include/calendar/symfony/translation/MetadataAwareInterface.php
@@ -0,0 +1,54 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation;
+
+/**
+ * MetadataAwareInterface.
+ *
+ * @author Fabien Potencier
+ */
+interface MetadataAwareInterface
+{
+ /**
+ * Gets metadata for the given domain and key.
+ *
+ * Passing an empty domain will return an array with all metadata indexed by
+ * domain and then by key. Passing an empty key will return an array with all
+ * metadata for the given domain.
+ *
+ * @param string $key The key
+ * @param string $domain The domain name
+ *
+ * @return mixed The value that was set or an array with the domains/keys or null
+ */
+ public function getMetadata($key = '', $domain = 'messages');
+
+ /**
+ * Adds metadata to a message domain.
+ *
+ * @param string $key The key
+ * @param mixed $value The value
+ * @param string $domain The domain name
+ */
+ public function setMetadata($key, $value, $domain = 'messages');
+
+ /**
+ * Deletes metadata for the given key and domain.
+ *
+ * Passing an empty domain will delete all metadata. Passing an empty key will
+ * delete all metadata for the given domain.
+ *
+ * @param string $key The key
+ * @param string $domain The domain name
+ */
+ public function deleteMetadata($key = '', $domain = 'messages');
+}
diff --git a/_include/calendar/symfony/translation/PluralizationRules.php b/_include/calendar/symfony/translation/PluralizationRules.php
new file mode 100644
index 0000000..3aca0ba
--- /dev/null
+++ b/_include/calendar/symfony/translation/PluralizationRules.php
@@ -0,0 +1,210 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation;
+
+/**
+ * Returns the plural rules for a given locale.
+ *
+ * @author Fabien Potencier
+ */
+class PluralizationRules
+{
+ private static $rules = array();
+
+ /**
+ * Returns the plural position to use for the given locale and number.
+ *
+ * @param int $number The number
+ * @param string $locale The locale
+ *
+ * @return int The plural position
+ */
+ public static function get($number, $locale)
+ {
+ if ('pt_BR' === $locale) {
+ // temporary set a locale for brazilian
+ $locale = 'xbr';
+ }
+
+ if (\strlen($locale) > 3) {
+ $locale = substr($locale, 0, -\strlen(strrchr($locale, '_')));
+ }
+
+ if (isset(self::$rules[$locale])) {
+ $return = \call_user_func(self::$rules[$locale], $number);
+
+ if (!\is_int($return) || $return < 0) {
+ return 0;
+ }
+
+ return $return;
+ }
+
+ /*
+ * The plural rules are derived from code of the Zend Framework (2010-09-25),
+ * which is subject to the new BSD license (http://framework.zend.com/license/new-bsd).
+ * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ */
+ switch ($locale) {
+ case 'az':
+ case 'bo':
+ case 'dz':
+ case 'id':
+ case 'ja':
+ case 'jv':
+ case 'ka':
+ case 'km':
+ case 'kn':
+ case 'ko':
+ case 'ms':
+ case 'th':
+ case 'tr':
+ case 'vi':
+ case 'zh':
+ return 0;
+
+ case 'af':
+ case 'bn':
+ case 'bg':
+ case 'ca':
+ case 'da':
+ case 'de':
+ case 'el':
+ case 'en':
+ case 'eo':
+ case 'es':
+ case 'et':
+ case 'eu':
+ case 'fa':
+ case 'fi':
+ case 'fo':
+ case 'fur':
+ case 'fy':
+ case 'gl':
+ case 'gu':
+ case 'ha':
+ case 'he':
+ case 'hu':
+ case 'is':
+ case 'it':
+ case 'ku':
+ case 'lb':
+ case 'ml':
+ case 'mn':
+ case 'mr':
+ case 'nah':
+ case 'nb':
+ case 'ne':
+ case 'nl':
+ case 'nn':
+ case 'no':
+ case 'oc':
+ case 'om':
+ case 'or':
+ case 'pa':
+ case 'pap':
+ case 'ps':
+ case 'pt':
+ case 'so':
+ case 'sq':
+ case 'sv':
+ case 'sw':
+ case 'ta':
+ case 'te':
+ case 'tk':
+ case 'ur':
+ case 'zu':
+ return (1 == $number) ? 0 : 1;
+
+ case 'am':
+ case 'bh':
+ case 'fil':
+ case 'fr':
+ case 'gun':
+ case 'hi':
+ case 'hy':
+ case 'ln':
+ case 'mg':
+ case 'nso':
+ case 'xbr':
+ case 'ti':
+ case 'wa':
+ return ((0 == $number) || (1 == $number)) ? 0 : 1;
+
+ case 'be':
+ case 'bs':
+ case 'hr':
+ case 'ru':
+ case 'sh':
+ case 'sr':
+ case 'uk':
+ return ((1 == $number % 10) && (11 != $number % 100)) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2);
+
+ case 'cs':
+ case 'sk':
+ return (1 == $number) ? 0 : ((($number >= 2) && ($number <= 4)) ? 1 : 2);
+
+ case 'ga':
+ return (1 == $number) ? 0 : ((2 == $number) ? 1 : 2);
+
+ case 'lt':
+ return ((1 == $number % 10) && (11 != $number % 100)) ? 0 : ((($number % 10 >= 2) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2);
+
+ case 'sl':
+ return (1 == $number % 100) ? 0 : ((2 == $number % 100) ? 1 : (((3 == $number % 100) || (4 == $number % 100)) ? 2 : 3));
+
+ case 'mk':
+ return (1 == $number % 10) ? 0 : 1;
+
+ case 'mt':
+ return (1 == $number) ? 0 : (((0 == $number) || (($number % 100 > 1) && ($number % 100 < 11))) ? 1 : ((($number % 100 > 10) && ($number % 100 < 20)) ? 2 : 3));
+
+ case 'lv':
+ return (0 == $number) ? 0 : (((1 == $number % 10) && (11 != $number % 100)) ? 1 : 2);
+
+ case 'pl':
+ return (1 == $number) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 12) || ($number % 100 > 14))) ? 1 : 2);
+
+ case 'cy':
+ return (1 == $number) ? 0 : ((2 == $number) ? 1 : (((8 == $number) || (11 == $number)) ? 2 : 3));
+
+ case 'ro':
+ return (1 == $number) ? 0 : (((0 == $number) || (($number % 100 > 0) && ($number % 100 < 20))) ? 1 : 2);
+
+ case 'ar':
+ return (0 == $number) ? 0 : ((1 == $number) ? 1 : ((2 == $number) ? 2 : ((($number % 100 >= 3) && ($number % 100 <= 10)) ? 3 : ((($number % 100 >= 11) && ($number % 100 <= 99)) ? 4 : 5))));
+
+ default:
+ return 0;
+ }
+ }
+
+ /**
+ * Overrides the default plural rule for a given locale.
+ *
+ * @param callable $rule A PHP callable
+ * @param string $locale The locale
+ */
+ public static function set(callable $rule, $locale)
+ {
+ if ('pt_BR' === $locale) {
+ // temporary set a locale for brazilian
+ $locale = 'xbr';
+ }
+
+ if (\strlen($locale) > 3) {
+ $locale = substr($locale, 0, -\strlen(strrchr($locale, '_')));
+ }
+
+ self::$rules[$locale] = $rule;
+ }
+}
diff --git a/_include/calendar/symfony/translation/README.md b/_include/calendar/symfony/translation/README.md
new file mode 100644
index 0000000..46f3d1f
--- /dev/null
+++ b/_include/calendar/symfony/translation/README.md
@@ -0,0 +1,13 @@
+Translation Component
+=====================
+
+The Translation component provides tools to internationalize your application.
+
+Resources
+---------
+
+ * [Documentation](https://symfony.com/doc/current/components/translation/index.html)
+ * [Contributing](https://symfony.com/doc/current/contributing/index.html)
+ * [Report issues](https://github.com/symfony/symfony/issues) and
+ [send Pull Requests](https://github.com/symfony/symfony/pulls)
+ in the [main Symfony repository](https://github.com/symfony/symfony)
diff --git a/_include/calendar/symfony/translation/Reader/TranslationReader.php b/_include/calendar/symfony/translation/Reader/TranslationReader.php
new file mode 100644
index 0000000..948edec
--- /dev/null
+++ b/_include/calendar/symfony/translation/Reader/TranslationReader.php
@@ -0,0 +1,63 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Reader;
+
+use Symfony\Component\Finder\Finder;
+use Symfony\Component\Translation\Loader\LoaderInterface;
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * TranslationReader reads translation messages from translation files.
+ *
+ * @author Michel Salib
+ */
+class TranslationReader implements TranslationReaderInterface
+{
+ /**
+ * Loaders used for import.
+ *
+ * @var array
+ */
+ private $loaders = array();
+
+ /**
+ * Adds a loader to the translation extractor.
+ *
+ * @param string $format The format of the loader
+ * @param LoaderInterface $loader
+ */
+ public function addLoader($format, LoaderInterface $loader)
+ {
+ $this->loaders[$format] = $loader;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function read($directory, MessageCatalogue $catalogue)
+ {
+ if (!is_dir($directory)) {
+ return;
+ }
+
+ foreach ($this->loaders as $format => $loader) {
+ // load any existing translation files
+ $finder = new Finder();
+ $extension = $catalogue->getLocale().'.'.$format;
+ $files = $finder->files()->name('*.'.$extension)->in($directory);
+ foreach ($files as $file) {
+ $domain = substr($file->getFilename(), 0, -1 * \strlen($extension) - 1);
+ $catalogue->addCatalogue($loader->load($file->getPathname(), $catalogue->getLocale(), $domain));
+ }
+ }
+ }
+}
diff --git a/_include/calendar/symfony/translation/Reader/TranslationReaderInterface.php b/_include/calendar/symfony/translation/Reader/TranslationReaderInterface.php
new file mode 100644
index 0000000..0aa55c6
--- /dev/null
+++ b/_include/calendar/symfony/translation/Reader/TranslationReaderInterface.php
@@ -0,0 +1,30 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Reader;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * TranslationReader reads translation messages from translation files.
+ *
+ * @author Tobias Nyholm
+ */
+interface TranslationReaderInterface
+{
+ /**
+ * Reads translation messages from a directory to the catalogue.
+ *
+ * @param string $directory
+ * @param MessageCatalogue $catalogue
+ */
+ public function read($directory, MessageCatalogue $catalogue);
+}
diff --git a/_include/calendar/symfony/translation/Resources/schemas/xliff-core-1.2-strict.xsd b/_include/calendar/symfony/translation/Resources/schemas/xliff-core-1.2-strict.xsd
new file mode 100644
index 0000000..4102d64
--- /dev/null
+++ b/_include/calendar/symfony/translation/Resources/schemas/xliff-core-1.2-strict.xsd
@@ -0,0 +1,2223 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Values for the attribute 'context-type'.
+
+
+
+
+ Indicates a database content.
+
+
+
+
+ Indicates the content of an element within an XML document.
+
+
+
+
+ Indicates the name of an element within an XML document.
+
+
+
+
+ Indicates the line number from the sourcefile (see context-type="sourcefile") where the <source> is found.
+
+
+
+
+ Indicates a the number of parameters contained within the <source>.
+
+
+
+
+ Indicates notes pertaining to the parameters in the <source>.
+
+
+
+
+ Indicates the content of a record within a database.
+
+
+
+
+ Indicates the name of a record within a database.
+
+
+
+
+ Indicates the original source file in the case that multiple files are merged to form the original file from which the XLIFF file is created. This differs from the original <file> attribute in that this sourcefile is one of many that make up that file.
+
+
+
+
+
+
+ Values for the attribute 'count-type'.
+
+
+
+
+ Indicates the count units are items that are used X times in a certain context; example: this is a reusable text unit which is used 42 times in other texts.
+
+
+
+
+ Indicates the count units are translation units existing already in the same document.
+
+
+
+
+ Indicates a total count.
+
+
+
+
+
+
+ Values for the attribute 'ctype' when used other elements than <ph> or <x>.
+
+
+
+
+ Indicates a run of bolded text.
+
+
+
+
+ Indicates a run of text in italics.
+
+
+
+
+ Indicates a run of underlined text.
+
+
+
+
+ Indicates a run of hyper-text.
+
+
+
+
+
+
+ Values for the attribute 'ctype' when used with <ph> or <x>.
+
+
+
+
+ Indicates a inline image.
+
+
+
+
+ Indicates a page break.
+
+
+
+
+ Indicates a line break.
+
+
+
+
+
+
+
+
+
+
+
+ Values for the attribute 'datatype'.
+
+
+
+
+ Indicates Active Server Page data.
+
+
+
+
+ Indicates C source file data.
+
+
+
+
+ Indicates Channel Definition Format (CDF) data.
+
+
+
+
+ Indicates ColdFusion data.
+
+
+
+
+ Indicates C++ source file data.
+
+
+
+
+ Indicates C-Sharp data.
+
+
+
+
+ Indicates strings from C, ASM, and driver files data.
+
+
+
+
+ Indicates comma-separated values data.
+
+
+
+
+ Indicates database data.
+
+
+
+
+ Indicates portions of document that follows data and contains metadata.
+
+
+
+
+ Indicates portions of document that precedes data and contains metadata.
+
+
+
+
+ Indicates data from standard UI file operations dialogs (e.g., Open, Save, Save As, Export, Import).
+
+
+
+
+ Indicates standard user input screen data.
+
+
+
+
+ Indicates HyperText Markup Language (HTML) data - document instance.
+
+
+
+
+ Indicates content within an HTML document’s <body> element.
+
+
+
+
+ Indicates Windows INI file data.
+
+
+
+
+ Indicates Interleaf data.
+
+
+
+
+ Indicates Java source file data (extension '.java').
+
+
+
+
+ Indicates Java property resource bundle data.
+
+
+
+
+ Indicates Java list resource bundle data.
+
+
+
+
+ Indicates JavaScript source file data.
+
+
+
+
+ Indicates JScript source file data.
+
+
+
+
+ Indicates information relating to formatting.
+
+
+
+
+ Indicates LISP source file data.
+
+
+
+
+ Indicates information relating to margin formats.
+
+
+
+
+ Indicates a file containing menu.
+
+
+
+
+ Indicates numerically identified string table.
+
+
+
+
+ Indicates Maker Interchange Format (MIF) data.
+
+
+
+
+ Indicates that the datatype attribute value is a MIME Type value and is defined in the mime-type attribute.
+
+
+
+
+ Indicates GNU Machine Object data.
+
+
+
+
+ Indicates Message Librarian strings created by Novell's Message Librarian Tool.
+
+
+
+
+ Indicates information to be displayed at the bottom of each page of a document.
+
+
+
+
+ Indicates information to be displayed at the top of each page of a document.
+
+
+
+
+ Indicates a list of property values (e.g., settings within INI files or preferences dialog).
+
+
+
+
+ Indicates Pascal source file data.
+
+
+
+
+ Indicates Hypertext Preprocessor data.
+
+
+
+
+ Indicates plain text file (no formatting other than, possibly, wrapping).
+
+
+
+
+ Indicates GNU Portable Object file.
+
+
+
+
+ Indicates dynamically generated user defined document. e.g. Oracle Report, Crystal Report, etc.
+
+
+
+
+ Indicates Windows .NET binary resources.
+
+
+
+
+ Indicates Windows .NET Resources.
+
+
+
+
+ Indicates Rich Text Format (RTF) data.
+
+
+
+
+ Indicates Standard Generalized Markup Language (SGML) data - document instance.
+
+
+
+
+ Indicates Standard Generalized Markup Language (SGML) data - Document Type Definition (DTD).
+
+
+
+
+ Indicates Scalable Vector Graphic (SVG) data.
+
+
+
+
+ Indicates VisualBasic Script source file.
+
+
+
+
+ Indicates warning message.
+
+
+
+
+ Indicates Windows (Win32) resources (i.e. resources extracted from an RC script, a message file, or a compiled file).
+
+
+
+
+ Indicates Extensible HyperText Markup Language (XHTML) data - document instance.
+
+
+
+
+ Indicates Extensible Markup Language (XML) data - document instance.
+
+
+
+
+ Indicates Extensible Markup Language (XML) data - Document Type Definition (DTD).
+
+
+
+
+ Indicates Extensible Stylesheet Language (XSL) data.
+
+
+
+
+ Indicates XUL elements.
+
+
+
+
+
+
+ Values for the attribute 'mtype'.
+
+
+
+
+ Indicates the marked text is an abbreviation.
+
+
+
+
+ ISO-12620 2.1.8: A term resulting from the omission of any part of the full term while designating the same concept.
+
+
+
+
+ ISO-12620 2.1.8.1: An abbreviated form of a simple term resulting from the omission of some of its letters (e.g. 'adj.' for 'adjective').
+
+
+
+
+ ISO-12620 2.1.8.4: An abbreviated form of a term made up of letters from the full form of a multiword term strung together into a sequence pronounced only syllabically (e.g. 'radar' for 'radio detecting and ranging').
+
+
+
+
+ ISO-12620: A proper-name term, such as the name of an agency or other proper entity.
+
+
+
+
+ ISO-12620 2.1.18.1: A recurrent word combination characterized by cohesion in that the components of the collocation must co-occur within an utterance or series of utterances, even though they do not necessarily have to maintain immediate proximity to one another.
+
+
+
+
+ ISO-12620 2.1.5: A synonym for an international scientific term that is used in general discourse in a given language.
+
+
+
+
+ Indicates the marked text is a date and/or time.
+
+
+
+
+ ISO-12620 2.1.15: An expression used to represent a concept based on a statement that two mathematical expressions are, for instance, equal as identified by the equal sign (=), or assigned to one another by a similar sign.
+
+
+
+
+ ISO-12620 2.1.7: The complete representation of a term for which there is an abbreviated form.
+
+
+
+
+ ISO-12620 2.1.14: Figures, symbols or the like used to express a concept briefly, such as a mathematical or chemical formula.
+
+
+
+
+ ISO-12620 2.1.1: The concept designation that has been chosen to head a terminological record.
+
+
+
+
+ ISO-12620 2.1.8.3: An abbreviated form of a term consisting of some of the initial letters of the words making up a multiword term or the term elements making up a compound term when these letters are pronounced individually (e.g. 'BSE' for 'bovine spongiform encephalopathy').
+
+
+
+
+ ISO-12620 2.1.4: A term that is part of an international scientific nomenclature as adopted by an appropriate scientific body.
+
+
+
+
+ ISO-12620 2.1.6: A term that has the same or nearly identical orthographic or phonemic form in many languages.
+
+
+
+
+ ISO-12620 2.1.16: An expression used to represent a concept based on mathematical or logical relations, such as statements of inequality, set relationships, Boolean operations, and the like.
+
+
+
+
+ ISO-12620 2.1.17: A unit to track object.
+
+
+
+
+ Indicates the marked text is a name.
+
+
+
+
+ ISO-12620 2.1.3: A term that represents the same or a very similar concept as another term in the same language, but for which interchangeability is limited to some contexts and inapplicable in others.
+
+
+
+
+ ISO-12620 2.1.17.2: A unique alphanumeric designation assigned to an object in a manufacturing system.
+
+
+
+
+ Indicates the marked text is a phrase.
+
+
+
+
+ ISO-12620 2.1.18: Any group of two or more words that form a unit, the meaning of which frequently cannot be deduced based on the combined sense of the words making up the phrase.
+
+
+
+
+ Indicates the marked text should not be translated.
+
+
+
+
+ ISO-12620 2.1.12: A form of a term resulting from an operation whereby non-Latin writing systems are converted to the Latin alphabet.
+
+
+
+
+ Indicates that the marked text represents a segment.
+
+
+
+
+ ISO-12620 2.1.18.2: A fixed, lexicalized phrase.
+
+
+
+
+ ISO-12620 2.1.8.2: A variant of a multiword term that includes fewer words than the full form of the term (e.g. 'Group of Twenty-four' for 'Intergovernmental Group of Twenty-four on International Monetary Affairs').
+
+
+
+
+ ISO-12620 2.1.17.1: Stock keeping unit, an inventory item identified by a unique alphanumeric designation assigned to an object in an inventory control system.
+
+
+
+
+ ISO-12620 2.1.19: A fixed chunk of recurring text.
+
+
+
+
+ ISO-12620 2.1.13: A designation of a concept by letters, numerals, pictograms or any combination thereof.
+
+
+
+
+ ISO-12620 2.1.2: Any term that represents the same or a very similar concept as the main entry term in a term entry.
+
+
+
+
+ ISO-12620 2.1.18.3: Phraseological unit in a language that expresses the same semantic content as another phrase in that same language.
+
+
+
+
+ Indicates the marked text is a term.
+
+
+
+
+ ISO-12620 2.1.11: A form of a term resulting from an operation whereby the characters of one writing system are represented by characters from another writing system, taking into account the pronunciation of the characters converted.
+
+
+
+
+ ISO-12620 2.1.10: A form of a term resulting from an operation whereby the characters of an alphabetic writing system are represented by characters from another alphabetic writing system.
+
+
+
+
+ ISO-12620 2.1.8.5: An abbreviated form of a term resulting from the omission of one or more term elements or syllables (e.g. 'flu' for 'influenza').
+
+
+
+
+ ISO-12620 2.1.9: One of the alternate forms of a term.
+
+
+
+
+
+
+ Values for the attribute 'restype'.
+
+
+
+
+ Indicates a Windows RC AUTO3STATE control.
+
+
+
+
+ Indicates a Windows RC AUTOCHECKBOX control.
+
+
+
+
+ Indicates a Windows RC AUTORADIOBUTTON control.
+
+
+
+
+ Indicates a Windows RC BEDIT control.
+
+
+
+
+ Indicates a bitmap, for example a BITMAP resource in Windows.
+
+
+
+
+ Indicates a button object, for example a BUTTON control Windows.
+
+
+
+
+ Indicates a caption, such as the caption of a dialog box.
+
+
+
+
+ Indicates the cell in a table, for example the content of the <td> element in HTML.
+
+
+
+
+ Indicates check box object, for example a CHECKBOX control in Windows.
+
+
+
+
+ Indicates a menu item with an associated checkbox.
+
+
+
+
+ Indicates a list box, but with a check-box for each item.
+
+
+
+
+ Indicates a color selection dialog.
+
+
+
+
+ Indicates a combination of edit box and listbox object, for example a COMBOBOX control in Windows.
+
+
+
+
+ Indicates an initialization entry of an extended combobox DLGINIT resource block. (code 0x1234).
+
+
+
+
+ Indicates an initialization entry of a combobox DLGINIT resource block (code 0x0403).
+
+
+
+
+ Indicates a UI base class element that cannot be represented by any other element.
+
+
+
+
+ Indicates a context menu.
+
+
+
+
+ Indicates a Windows RC CTEXT control.
+
+
+
+
+ Indicates a cursor, for example a CURSOR resource in Windows.
+
+
+
+
+ Indicates a date/time picker.
+
+
+
+
+ Indicates a Windows RC DEFPUSHBUTTON control.
+
+
+
+
+ Indicates a dialog box.
+
+
+
+
+ Indicates a Windows RC DLGINIT resource block.
+
+
+
+
+ Indicates an edit box object, for example an EDIT control in Windows.
+
+
+
+
+ Indicates a filename.
+
+
+
+
+ Indicates a file dialog.
+
+
+
+
+ Indicates a footnote.
+
+
+
+
+ Indicates a font name.
+
+
+
+
+ Indicates a footer.
+
+
+
+
+ Indicates a frame object.
+
+
+
+
+ Indicates a XUL grid element.
+
+
+
+
+ Indicates a groupbox object, for example a GROUPBOX control in Windows.
+
+
+
+
+ Indicates a header item.
+
+
+
+
+ Indicates a heading, such has the content of <h1>, <h2>, etc. in HTML.
+
+
+
+
+ Indicates a Windows RC HEDIT control.
+
+
+
+
+ Indicates a horizontal scrollbar.
+
+
+
+
+ Indicates an icon, for example an ICON resource in Windows.
+
+
+
+
+ Indicates a Windows RC IEDIT control.
+
+
+
+
+ Indicates keyword list, such as the content of the Keywords meta-data in HTML, or a K footnote in WinHelp RTF.
+
+
+
+
+ Indicates a label object.
+
+
+
+
+ Indicates a label that is also a HTML link (not necessarily a URL).
+
+
+
+
+ Indicates a list (a group of list-items, for example an <ol> or <ul> element in HTML).
+
+
+
+
+ Indicates a listbox object, for example an LISTBOX control in Windows.
+
+
+
+
+ Indicates an list item (an entry in a list).
+
+
+
+
+ Indicates a Windows RC LTEXT control.
+
+
+
+
+ Indicates a menu (a group of menu-items).
+
+
+
+
+ Indicates a toolbar containing one or more tope level menus.
+
+
+
+
+ Indicates a menu item (an entry in a menu).
+
+
+
+
+ Indicates a XUL menuseparator element.
+
+
+
+
+ Indicates a message, for example an entry in a MESSAGETABLE resource in Windows.
+
+
+
+
+ Indicates a calendar control.
+
+
+
+
+ Indicates an edit box beside a spin control.
+
+
+
+
+ Indicates a catch all for rectangular areas.
+
+
+
+
+ Indicates a standalone menu not necessarily associated with a menubar.
+
+
+
+
+ Indicates a pushbox object, for example a PUSHBOX control in Windows.
+
+
+
+
+ Indicates a Windows RC PUSHBUTTON control.
+
+
+
+
+ Indicates a radio button object.
+
+
+
+
+ Indicates a menuitem with associated radio button.
+
+
+
+
+ Indicates raw data resources for an application.
+
+
+
+
+ Indicates a row in a table.
+
+
+
+
+ Indicates a Windows RC RTEXT control.
+
+
+
+
+ Indicates a user navigable container used to show a portion of a document.
+
+
+
+
+ Indicates a generic divider object (e.g. menu group separator).
+
+
+
+
+ Windows accelerators, shortcuts in resource or property files.
+
+
+
+
+ Indicates a UI control to indicate process activity but not progress.
+
+
+
+
+ Indicates a splitter bar.
+
+
+
+
+ Indicates a Windows RC STATE3 control.
+
+
+
+
+ Indicates a window for providing feedback to the users, like 'read-only', etc.
+
+
+
+
+ Indicates a string, for example an entry in a STRINGTABLE resource in Windows.
+
+
+
+
+ Indicates a layers of controls with a tab to select layers.
+
+
+
+
+ Indicates a display and edits regular two-dimensional tables of cells.
+
+
+
+
+ Indicates a XUL textbox element.
+
+
+
+
+ Indicates a UI button that can be toggled to on or off state.
+
+
+
+
+ Indicates an array of controls, usually buttons.
+
+
+
+
+ Indicates a pop up tool tip text.
+
+
+
+
+ Indicates a bar with a pointer indicating a position within a certain range.
+
+
+
+
+ Indicates a control that displays a set of hierarchical data.
+
+
+
+
+ Indicates a URI (URN or URL).
+
+
+
+
+ Indicates a Windows RC USERBUTTON control.
+
+
+
+
+ Indicates a user-defined control like CONTROL control in Windows.
+
+
+
+
+ Indicates the text of a variable.
+
+
+
+
+ Indicates version information about a resource like VERSIONINFO in Windows.
+
+
+
+
+ Indicates a vertical scrollbar.
+
+
+
+
+ Indicates a graphical window.
+
+
+
+
+
+
+ Values for the attribute 'size-unit'.
+
+
+
+
+ Indicates a size in 8-bit bytes.
+
+
+
+
+ Indicates a size in Unicode characters.
+
+
+
+
+ Indicates a size in columns. Used for HTML text area.
+
+
+
+
+ Indicates a size in centimeters.
+
+
+
+
+ Indicates a size in dialog units, as defined in Windows resources.
+
+
+
+
+ Indicates a size in 'font-size' units (as defined in CSS).
+
+
+
+
+ Indicates a size in 'x-height' units (as defined in CSS).
+
+
+
+
+ Indicates a size in glyphs. A glyph is considered to be one or more combined Unicode characters that represent a single displayable text character. Sometimes referred to as a 'grapheme cluster'
+
+
+
+
+ Indicates a size in inches.
+
+
+
+
+ Indicates a size in millimeters.
+
+
+
+
+ Indicates a size in percentage.
+
+
+
+
+ Indicates a size in pixels.
+
+
+
+
+ Indicates a size in point.
+
+
+
+
+ Indicates a size in rows. Used for HTML text area.
+
+
+
+
+
+
+ Values for the attribute 'state'.
+
+
+
+
+ Indicates the terminating state.
+
+
+
+
+ Indicates only non-textual information needs adaptation.
+
+
+
+
+ Indicates both text and non-textual information needs adaptation.
+
+
+
+
+ Indicates only non-textual information needs review.
+
+
+
+
+ Indicates both text and non-textual information needs review.
+
+
+
+
+ Indicates that only the text of the item needs to be reviewed.
+
+
+
+
+ Indicates that the item needs to be translated.
+
+
+
+
+ Indicates that the item is new. For example, translation units that were not in a previous version of the document.
+
+
+
+
+ Indicates that changes are reviewed and approved.
+
+
+
+
+ Indicates that the item has been translated.
+
+
+
+
+
+
+ Values for the attribute 'state-qualifier'.
+
+
+
+
+ Indicates an exact match. An exact match occurs when a source text of a segment is exactly the same as the source text of a segment that was translated previously.
+
+
+
+
+ Indicates a fuzzy match. A fuzzy match occurs when a source text of a segment is very similar to the source text of a segment that was translated previously (e.g. when the difference is casing, a few changed words, white-space discripancy, etc.).
+
+
+
+
+ Indicates a match based on matching IDs (in addition to matching text).
+
+
+
+
+ Indicates a translation derived from a glossary.
+
+
+
+
+ Indicates a translation derived from existing translation.
+
+
+
+
+ Indicates a translation derived from machine translation.
+
+
+
+
+ Indicates a translation derived from a translation repository.
+
+
+
+
+ Indicates a translation derived from a translation memory.
+
+
+
+
+ Indicates the translation is suggested by machine translation.
+
+
+
+
+ Indicates that the item has been rejected because of incorrect grammar.
+
+
+
+
+ Indicates that the item has been rejected because it is incorrect.
+
+
+
+
+ Indicates that the item has been rejected because it is too long or too short.
+
+
+
+
+ Indicates that the item has been rejected because of incorrect spelling.
+
+
+
+
+ Indicates the translation is suggested by translation memory.
+
+
+
+
+
+
+ Values for the attribute 'unit'.
+
+
+
+
+ Refers to words.
+
+
+
+
+ Refers to pages.
+
+
+
+
+ Refers to <trans-unit> elements.
+
+
+
+
+ Refers to <bin-unit> elements.
+
+
+
+
+ Refers to glyphs.
+
+
+
+
+ Refers to <trans-unit> and/or <bin-unit> elements.
+
+
+
+
+ Refers to the occurrences of instances defined by the count-type value.
+
+
+
+
+ Refers to characters.
+
+
+
+
+ Refers to lines.
+
+
+
+
+ Refers to sentences.
+
+
+
+
+ Refers to paragraphs.
+
+
+
+
+ Refers to segments.
+
+
+
+
+ Refers to placeables (inline elements).
+
+
+
+
+
+
+ Values for the attribute 'priority'.
+
+
+
+
+ Highest priority.
+
+
+
+
+ High priority.
+
+
+
+
+ High priority, but not as important as 2.
+
+
+
+
+ High priority, but not as important as 3.
+
+
+
+
+ Medium priority, but more important than 6.
+
+
+
+
+ Medium priority, but less important than 5.
+
+
+
+
+ Low priority, but more important than 8.
+
+
+
+
+ Low priority, but more important than 9.
+
+
+
+
+ Low priority.
+
+
+
+
+ Lowest priority.
+
+
+
+
+
+
+
+
+ This value indicates that all properties can be reformatted. This value must be used alone.
+
+
+
+
+ This value indicates that no properties should be reformatted. This value must be used alone.
+
+
+
+
+
+
+
+
+
+
+
+
+ This value indicates that all information in the coord attribute can be modified.
+
+
+
+
+ This value indicates that the x information in the coord attribute can be modified.
+
+
+
+
+ This value indicates that the y information in the coord attribute can be modified.
+
+
+
+
+ This value indicates that the cx information in the coord attribute can be modified.
+
+
+
+
+ This value indicates that the cy information in the coord attribute can be modified.
+
+
+
+
+ This value indicates that all the information in the font attribute can be modified.
+
+
+
+
+ This value indicates that the name information in the font attribute can be modified.
+
+
+
+
+ This value indicates that the size information in the font attribute can be modified.
+
+
+
+
+ This value indicates that the weight information in the font attribute can be modified.
+
+
+
+
+ This value indicates that the information in the css-style attribute can be modified.
+
+
+
+
+ This value indicates that the information in the style attribute can be modified.
+
+
+
+
+ This value indicates that the information in the exstyle attribute can be modified.
+
+
+
+
+
+
+
+
+
+
+
+
+ Indicates that the context is informational in nature, specifying for example, how a term should be translated. Thus, should be displayed to anyone editing the XLIFF document.
+
+
+
+
+ Indicates that the context-group is used to specify where the term was found in the translatable source. Thus, it is not displayed.
+
+
+
+
+ Indicates that the context information should be used during translation memory lookups. Thus, it is not displayed.
+
+
+
+
+
+
+
+
+ Represents a translation proposal from a translation memory or other resource.
+
+
+
+
+ Represents a previous version of the target element.
+
+
+
+
+ Represents a rejected version of the target element.
+
+
+
+
+ Represents a translation to be used for reference purposes only, for example from a related product or a different language.
+
+
+
+
+ Represents a proposed translation that was used for the translation of the trans-unit, possibly modified.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Values for the attribute 'coord'.
+
+
+
+
+
+
+
+ Version values: 1.0 and 1.1 are allowed for backward compatibility.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/_include/calendar/symfony/translation/Tests/Catalogue/AbstractOperationTest.php b/_include/calendar/symfony/translation/Tests/Catalogue/AbstractOperationTest.php
new file mode 100644
index 0000000..90cf4a5
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/Catalogue/AbstractOperationTest.php
@@ -0,0 +1,74 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Catalogue;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\MessageCatalogueInterface;
+
+abstract class AbstractOperationTest extends TestCase
+{
+ public function testGetEmptyDomains()
+ {
+ $this->assertEquals(
+ array(),
+ $this->createOperation(
+ new MessageCatalogue('en'),
+ new MessageCatalogue('en')
+ )->getDomains()
+ );
+ }
+
+ public function testGetMergedDomains()
+ {
+ $this->assertEquals(
+ array('a', 'b', 'c'),
+ $this->createOperation(
+ new MessageCatalogue('en', array('a' => array(), 'b' => array())),
+ new MessageCatalogue('en', array('b' => array(), 'c' => array()))
+ )->getDomains()
+ );
+ }
+
+ public function testGetMessagesFromUnknownDomain()
+ {
+ $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('InvalidArgumentException');
+ $this->createOperation(
+ new MessageCatalogue('en'),
+ new MessageCatalogue('en')
+ )->getMessages('domain');
+ }
+
+ public function testGetEmptyMessages()
+ {
+ $this->assertEquals(
+ array(),
+ $this->createOperation(
+ new MessageCatalogue('en', array('a' => array())),
+ new MessageCatalogue('en')
+ )->getMessages('a')
+ );
+ }
+
+ public function testGetEmptyResult()
+ {
+ $this->assertEquals(
+ new MessageCatalogue('en'),
+ $this->createOperation(
+ new MessageCatalogue('en'),
+ new MessageCatalogue('en')
+ )->getResult()
+ );
+ }
+
+ abstract protected function createOperation(MessageCatalogueInterface $source, MessageCatalogueInterface $target);
+}
diff --git a/_include/calendar/symfony/translation/Tests/Catalogue/MergeOperationTest.php b/_include/calendar/symfony/translation/Tests/Catalogue/MergeOperationTest.php
new file mode 100644
index 0000000..8b51c15
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/Catalogue/MergeOperationTest.php
@@ -0,0 +1,83 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Catalogue;
+
+use Symfony\Component\Translation\Catalogue\MergeOperation;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\MessageCatalogueInterface;
+
+class MergeOperationTest extends AbstractOperationTest
+{
+ public function testGetMessagesFromSingleDomain()
+ {
+ $operation = $this->createOperation(
+ new MessageCatalogue('en', array('messages' => array('a' => 'old_a', 'b' => 'old_b'))),
+ new MessageCatalogue('en', array('messages' => array('a' => 'new_a', 'c' => 'new_c')))
+ );
+
+ $this->assertEquals(
+ array('a' => 'old_a', 'b' => 'old_b', 'c' => 'new_c'),
+ $operation->getMessages('messages')
+ );
+
+ $this->assertEquals(
+ array('c' => 'new_c'),
+ $operation->getNewMessages('messages')
+ );
+
+ $this->assertEquals(
+ array(),
+ $operation->getObsoleteMessages('messages')
+ );
+ }
+
+ public function testGetResultFromSingleDomain()
+ {
+ $this->assertEquals(
+ new MessageCatalogue('en', array(
+ 'messages' => array('a' => 'old_a', 'b' => 'old_b', 'c' => 'new_c'),
+ )),
+ $this->createOperation(
+ new MessageCatalogue('en', array('messages' => array('a' => 'old_a', 'b' => 'old_b'))),
+ new MessageCatalogue('en', array('messages' => array('a' => 'new_a', 'c' => 'new_c')))
+ )->getResult()
+ );
+ }
+
+ public function testGetResultWithMetadata()
+ {
+ $leftCatalogue = new MessageCatalogue('en', array('messages' => array('a' => 'old_a', 'b' => 'old_b')));
+ $leftCatalogue->setMetadata('a', 'foo', 'messages');
+ $leftCatalogue->setMetadata('b', 'bar', 'messages');
+ $rightCatalogue = new MessageCatalogue('en', array('messages' => array('b' => 'new_b', 'c' => 'new_c')));
+ $rightCatalogue->setMetadata('b', 'baz', 'messages');
+ $rightCatalogue->setMetadata('c', 'qux', 'messages');
+
+ $mergedCatalogue = new MessageCatalogue('en', array('messages' => array('a' => 'old_a', 'b' => 'old_b', 'c' => 'new_c')));
+ $mergedCatalogue->setMetadata('a', 'foo', 'messages');
+ $mergedCatalogue->setMetadata('b', 'bar', 'messages');
+ $mergedCatalogue->setMetadata('c', 'qux', 'messages');
+
+ $this->assertEquals(
+ $mergedCatalogue,
+ $this->createOperation(
+ $leftCatalogue,
+ $rightCatalogue
+ )->getResult()
+ );
+ }
+
+ protected function createOperation(MessageCatalogueInterface $source, MessageCatalogueInterface $target)
+ {
+ return new MergeOperation($source, $target);
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/Catalogue/TargetOperationTest.php b/_include/calendar/symfony/translation/Tests/Catalogue/TargetOperationTest.php
new file mode 100644
index 0000000..271d17f
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/Catalogue/TargetOperationTest.php
@@ -0,0 +1,82 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Catalogue;
+
+use Symfony\Component\Translation\Catalogue\TargetOperation;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\MessageCatalogueInterface;
+
+class TargetOperationTest extends AbstractOperationTest
+{
+ public function testGetMessagesFromSingleDomain()
+ {
+ $operation = $this->createOperation(
+ new MessageCatalogue('en', array('messages' => array('a' => 'old_a', 'b' => 'old_b'))),
+ new MessageCatalogue('en', array('messages' => array('a' => 'new_a', 'c' => 'new_c')))
+ );
+
+ $this->assertEquals(
+ array('a' => 'old_a', 'c' => 'new_c'),
+ $operation->getMessages('messages')
+ );
+
+ $this->assertEquals(
+ array('c' => 'new_c'),
+ $operation->getNewMessages('messages')
+ );
+
+ $this->assertEquals(
+ array('b' => 'old_b'),
+ $operation->getObsoleteMessages('messages')
+ );
+ }
+
+ public function testGetResultFromSingleDomain()
+ {
+ $this->assertEquals(
+ new MessageCatalogue('en', array(
+ 'messages' => array('a' => 'old_a', 'c' => 'new_c'),
+ )),
+ $this->createOperation(
+ new MessageCatalogue('en', array('messages' => array('a' => 'old_a', 'b' => 'old_b'))),
+ new MessageCatalogue('en', array('messages' => array('a' => 'new_a', 'c' => 'new_c')))
+ )->getResult()
+ );
+ }
+
+ public function testGetResultWithMetadata()
+ {
+ $leftCatalogue = new MessageCatalogue('en', array('messages' => array('a' => 'old_a', 'b' => 'old_b')));
+ $leftCatalogue->setMetadata('a', 'foo', 'messages');
+ $leftCatalogue->setMetadata('b', 'bar', 'messages');
+ $rightCatalogue = new MessageCatalogue('en', array('messages' => array('b' => 'new_b', 'c' => 'new_c')));
+ $rightCatalogue->setMetadata('b', 'baz', 'messages');
+ $rightCatalogue->setMetadata('c', 'qux', 'messages');
+
+ $diffCatalogue = new MessageCatalogue('en', array('messages' => array('b' => 'old_b', 'c' => 'new_c')));
+ $diffCatalogue->setMetadata('b', 'bar', 'messages');
+ $diffCatalogue->setMetadata('c', 'qux', 'messages');
+
+ $this->assertEquals(
+ $diffCatalogue,
+ $this->createOperation(
+ $leftCatalogue,
+ $rightCatalogue
+ )->getResult()
+ );
+ }
+
+ protected function createOperation(MessageCatalogueInterface $source, MessageCatalogueInterface $target)
+ {
+ return new TargetOperation($source, $target);
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/Command/XliffLintCommandTest.php b/_include/calendar/symfony/translation/Tests/Command/XliffLintCommandTest.php
new file mode 100644
index 0000000..e37600c
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/Command/XliffLintCommandTest.php
@@ -0,0 +1,163 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Command;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Console\Application;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Tester\CommandTester;
+use Symfony\Component\Translation\Command\XliffLintCommand;
+
+/**
+ * Tests the XliffLintCommand.
+ *
+ * @author Javier Eguiluz
+ */
+class XliffLintCommandTest extends TestCase
+{
+ private $files;
+
+ public function testLintCorrectFile()
+ {
+ $tester = $this->createCommandTester();
+ $filename = $this->createFile();
+
+ $tester->execute(
+ array('filename' => $filename),
+ array('verbosity' => OutputInterface::VERBOSITY_VERBOSE, 'decorated' => false)
+ );
+
+ $this->assertEquals(0, $tester->getStatusCode(), 'Returns 0 in case of success');
+ $this->assertContains('OK', trim($tester->getDisplay()));
+ }
+
+ public function testLintIncorrectXmlSyntax()
+ {
+ $tester = $this->createCommandTester();
+ $filename = $this->createFile('note ');
+
+ $tester->execute(array('filename' => $filename), array('decorated' => false));
+
+ $this->assertEquals(1, $tester->getStatusCode(), 'Returns 1 in case of error');
+ $this->assertContains('Opening and ending tag mismatch: target line 6 and source', trim($tester->getDisplay()));
+ }
+
+ public function testLintIncorrectTargetLanguage()
+ {
+ $tester = $this->createCommandTester();
+ $filename = $this->createFile('note', 'es');
+
+ $tester->execute(array('filename' => $filename), array('decorated' => false));
+
+ $this->assertEquals(1, $tester->getStatusCode(), 'Returns 1 in case of error');
+ $this->assertContains('There is a mismatch between the file extension ("en.xlf") and the "es" value used in the "target-language" attribute of the file.', trim($tester->getDisplay()));
+ }
+
+ /**
+ * @expectedException \RuntimeException
+ */
+ public function testLintFileNotReadable()
+ {
+ $tester = $this->createCommandTester();
+ $filename = $this->createFile();
+ unlink($filename);
+
+ $tester->execute(array('filename' => $filename), array('decorated' => false));
+ }
+
+ public function testGetHelp()
+ {
+ $command = new XliffLintCommand();
+ $expected = <<%command.name% command lints a XLIFF file and outputs to STDOUT
+the first encountered syntax error.
+
+You can validates XLIFF contents passed from STDIN:
+
+ cat filename | php %command.full_name%
+
+You can also validate the syntax of a file:
+
+ php %command.full_name% filename
+
+Or of a whole directory:
+
+ php %command.full_name% dirname
+ php %command.full_name% dirname --format=json
+
+EOF;
+
+ $this->assertEquals($expected, $command->getHelp());
+ }
+
+ /**
+ * @return string Path to the new file
+ */
+ private function createFile($sourceContent = 'note', $targetLanguage = 'en')
+ {
+ $xliffContent = <<
+
+
+
+
+ $sourceContent
+ NOTE
+
+
+
+
+XLIFF;
+
+ $filename = sprintf('%s/translation-xliff-lint-test/messages.en.xlf', sys_get_temp_dir());
+ file_put_contents($filename, $xliffContent);
+
+ $this->files[] = $filename;
+
+ return $filename;
+ }
+
+ /**
+ * @return CommandTester
+ */
+ private function createCommandTester($application = null)
+ {
+ if (!$application) {
+ $application = new Application();
+ $application->add(new XliffLintCommand());
+ }
+
+ $command = $application->find('lint:xliff');
+
+ if ($application) {
+ $command->setApplication($application);
+ }
+
+ return new CommandTester($command);
+ }
+
+ protected function setUp()
+ {
+ $this->files = array();
+ @mkdir(sys_get_temp_dir().'/translation-xliff-lint-test');
+ }
+
+ protected function tearDown()
+ {
+ foreach ($this->files as $file) {
+ if (file_exists($file)) {
+ unlink($file);
+ }
+ }
+ rmdir(sys_get_temp_dir().'/translation-xliff-lint-test');
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/DataCollector/TranslationDataCollectorTest.php b/_include/calendar/symfony/translation/Tests/DataCollector/TranslationDataCollectorTest.php
new file mode 100644
index 0000000..6665eca
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/DataCollector/TranslationDataCollectorTest.php
@@ -0,0 +1,150 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\DataCollector;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\DataCollector\TranslationDataCollector;
+use Symfony\Component\Translation\DataCollectorTranslator;
+
+class TranslationDataCollectorTest extends TestCase
+{
+ protected function setUp()
+ {
+ if (!class_exists('Symfony\Component\HttpKernel\DataCollector\DataCollector')) {
+ $this->markTestSkipped('The "DataCollector" is not available');
+ }
+ }
+
+ public function testCollectEmptyMessages()
+ {
+ $translator = $this->getTranslator();
+ $translator->expects($this->any())->method('getCollectedMessages')->will($this->returnValue(array()));
+
+ $dataCollector = new TranslationDataCollector($translator);
+ $dataCollector->lateCollect();
+
+ $this->assertEquals(0, $dataCollector->getCountMissings());
+ $this->assertEquals(0, $dataCollector->getCountFallbacks());
+ $this->assertEquals(0, $dataCollector->getCountDefines());
+ $this->assertEquals(array(), $dataCollector->getMessages()->getValue());
+ }
+
+ public function testCollect()
+ {
+ $collectedMessages = array(
+ array(
+ 'id' => 'foo',
+ 'translation' => 'foo (en)',
+ 'locale' => 'en',
+ 'domain' => 'messages',
+ 'state' => DataCollectorTranslator::MESSAGE_DEFINED,
+ 'parameters' => array(),
+ 'transChoiceNumber' => null,
+ ),
+ array(
+ 'id' => 'bar',
+ 'translation' => 'bar (fr)',
+ 'locale' => 'fr',
+ 'domain' => 'messages',
+ 'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK,
+ 'parameters' => array(),
+ 'transChoiceNumber' => null,
+ ),
+ array(
+ 'id' => 'choice',
+ 'translation' => 'choice',
+ 'locale' => 'en',
+ 'domain' => 'messages',
+ 'state' => DataCollectorTranslator::MESSAGE_MISSING,
+ 'parameters' => array('%count%' => 3),
+ 'transChoiceNumber' => 3,
+ ),
+ array(
+ 'id' => 'choice',
+ 'translation' => 'choice',
+ 'locale' => 'en',
+ 'domain' => 'messages',
+ 'state' => DataCollectorTranslator::MESSAGE_MISSING,
+ 'parameters' => array('%count%' => 3),
+ 'transChoiceNumber' => 3,
+ ),
+ array(
+ 'id' => 'choice',
+ 'translation' => 'choice',
+ 'locale' => 'en',
+ 'domain' => 'messages',
+ 'state' => DataCollectorTranslator::MESSAGE_MISSING,
+ 'parameters' => array('%count%' => 4, '%foo%' => 'bar'),
+ 'transChoiceNumber' => 4,
+ ),
+ );
+ $expectedMessages = array(
+ array(
+ 'id' => 'foo',
+ 'translation' => 'foo (en)',
+ 'locale' => 'en',
+ 'domain' => 'messages',
+ 'state' => DataCollectorTranslator::MESSAGE_DEFINED,
+ 'count' => 1,
+ 'parameters' => array(),
+ 'transChoiceNumber' => null,
+ ),
+ array(
+ 'id' => 'bar',
+ 'translation' => 'bar (fr)',
+ 'locale' => 'fr',
+ 'domain' => 'messages',
+ 'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK,
+ 'count' => 1,
+ 'parameters' => array(),
+ 'transChoiceNumber' => null,
+ ),
+ array(
+ 'id' => 'choice',
+ 'translation' => 'choice',
+ 'locale' => 'en',
+ 'domain' => 'messages',
+ 'state' => DataCollectorTranslator::MESSAGE_MISSING,
+ 'count' => 3,
+ 'parameters' => array(
+ array('%count%' => 3),
+ array('%count%' => 3),
+ array('%count%' => 4, '%foo%' => 'bar'),
+ ),
+ 'transChoiceNumber' => 3,
+ ),
+ );
+
+ $translator = $this->getTranslator();
+ $translator->expects($this->any())->method('getCollectedMessages')->will($this->returnValue($collectedMessages));
+
+ $dataCollector = new TranslationDataCollector($translator);
+ $dataCollector->lateCollect();
+
+ $this->assertEquals(1, $dataCollector->getCountMissings());
+ $this->assertEquals(1, $dataCollector->getCountFallbacks());
+ $this->assertEquals(1, $dataCollector->getCountDefines());
+
+ $this->assertEquals($expectedMessages, array_values($dataCollector->getMessages()->getValue(true)));
+ }
+
+ private function getTranslator()
+ {
+ $translator = $this
+ ->getMockBuilder('Symfony\Component\Translation\DataCollectorTranslator')
+ ->disableOriginalConstructor()
+ ->getMock()
+ ;
+
+ return $translator;
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/DataCollectorTranslatorTest.php b/_include/calendar/symfony/translation/Tests/DataCollectorTranslatorTest.php
new file mode 100644
index 0000000..1cdd33b
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/DataCollectorTranslatorTest.php
@@ -0,0 +1,92 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\DataCollectorTranslator;
+use Symfony\Component\Translation\Loader\ArrayLoader;
+use Symfony\Component\Translation\Translator;
+
+class DataCollectorTranslatorTest extends TestCase
+{
+ public function testCollectMessages()
+ {
+ $collector = $this->createCollector();
+ $collector->setFallbackLocales(array('fr', 'ru'));
+
+ $collector->trans('foo');
+ $collector->trans('bar');
+ $collector->transChoice('choice', 0);
+ $collector->trans('bar_ru');
+ $collector->trans('bar_ru', array('foo' => 'bar'));
+
+ $expectedMessages = array();
+ $expectedMessages[] = array(
+ 'id' => 'foo',
+ 'translation' => 'foo (en)',
+ 'locale' => 'en',
+ 'domain' => 'messages',
+ 'state' => DataCollectorTranslator::MESSAGE_DEFINED,
+ 'parameters' => array(),
+ 'transChoiceNumber' => null,
+ );
+ $expectedMessages[] = array(
+ 'id' => 'bar',
+ 'translation' => 'bar (fr)',
+ 'locale' => 'fr',
+ 'domain' => 'messages',
+ 'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK,
+ 'parameters' => array(),
+ 'transChoiceNumber' => null,
+ );
+ $expectedMessages[] = array(
+ 'id' => 'choice',
+ 'translation' => 'choice',
+ 'locale' => 'en',
+ 'domain' => 'messages',
+ 'state' => DataCollectorTranslator::MESSAGE_MISSING,
+ 'parameters' => array(),
+ 'transChoiceNumber' => 0,
+ );
+ $expectedMessages[] = array(
+ 'id' => 'bar_ru',
+ 'translation' => 'bar (ru)',
+ 'locale' => 'ru',
+ 'domain' => 'messages',
+ 'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK,
+ 'parameters' => array(),
+ 'transChoiceNumber' => null,
+ );
+ $expectedMessages[] = array(
+ 'id' => 'bar_ru',
+ 'translation' => 'bar (ru)',
+ 'locale' => 'ru',
+ 'domain' => 'messages',
+ 'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK,
+ 'parameters' => array('foo' => 'bar'),
+ 'transChoiceNumber' => null,
+ );
+
+ $this->assertEquals($expectedMessages, $collector->getCollectedMessages());
+ }
+
+ private function createCollector()
+ {
+ $translator = new Translator('en');
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('foo' => 'foo (en)'), 'en');
+ $translator->addResource('array', array('bar' => 'bar (fr)'), 'fr');
+ $translator->addResource('array', array('bar_ru' => 'bar (ru)'), 'ru');
+
+ return new DataCollectorTranslator($translator);
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/DependencyInjection/TranslationDumperPassTest.php b/_include/calendar/symfony/translation/Tests/DependencyInjection/TranslationDumperPassTest.php
new file mode 100644
index 0000000..759bb0e
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/DependencyInjection/TranslationDumperPassTest.php
@@ -0,0 +1,48 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\DependencyInjection;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\Translation\DependencyInjection\TranslationDumperPass;
+
+class TranslationDumperPassTest extends TestCase
+{
+ public function testProcess()
+ {
+ $container = new ContainerBuilder();
+ $writerDefinition = $container->register('translation.writer');
+ $container->register('foo.id')
+ ->addTag('translation.dumper', array('alias' => 'bar.alias'));
+
+ $translationDumperPass = new TranslationDumperPass();
+ $translationDumperPass->process($container);
+
+ $this->assertEquals(array(array('addDumper', array('bar.alias', new Reference('foo.id')))), $writerDefinition->getMethodCalls());
+ }
+
+ public function testProcessNoDefinitionFound()
+ {
+ $container = new ContainerBuilder();
+
+ $definitionsBefore = \count($container->getDefinitions());
+ $aliasesBefore = \count($container->getAliases());
+
+ $translationDumperPass = new TranslationDumperPass();
+ $translationDumperPass->process($container);
+
+ // the container is untouched (i.e. no new definitions or aliases)
+ $this->assertCount($definitionsBefore, $container->getDefinitions());
+ $this->assertCount($aliasesBefore, $container->getAliases());
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/DependencyInjection/TranslationExtractorPassTest.php b/_include/calendar/symfony/translation/Tests/DependencyInjection/TranslationExtractorPassTest.php
new file mode 100644
index 0000000..14d164d
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/DependencyInjection/TranslationExtractorPassTest.php
@@ -0,0 +1,66 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\DependencyInjection;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\Translation\DependencyInjection\TranslationExtractorPass;
+
+class TranslationExtractorPassTest extends TestCase
+{
+ public function testProcess()
+ {
+ $container = new ContainerBuilder();
+ $extractorDefinition = $container->register('translation.extractor');
+ $container->register('foo.id')
+ ->addTag('translation.extractor', array('alias' => 'bar.alias'));
+
+ $translationDumperPass = new TranslationExtractorPass();
+ $translationDumperPass->process($container);
+
+ $this->assertEquals(array(array('addExtractor', array('bar.alias', new Reference('foo.id')))), $extractorDefinition->getMethodCalls());
+ }
+
+ public function testProcessNoDefinitionFound()
+ {
+ $container = new ContainerBuilder();
+
+ $definitionsBefore = \count($container->getDefinitions());
+ $aliasesBefore = \count($container->getAliases());
+
+ $translationDumperPass = new TranslationExtractorPass();
+ $translationDumperPass->process($container);
+
+ // the container is untouched (i.e. no new definitions or aliases)
+ $this->assertCount($definitionsBefore, $container->getDefinitions());
+ $this->assertCount($aliasesBefore, $container->getAliases());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
+ * @expectedExceptionMessage The alias for the tag "translation.extractor" of service "foo.id" must be set.
+ */
+ public function testProcessMissingAlias()
+ {
+ $definition = $this->getMockBuilder('Symfony\Component\DependencyInjection\Definition')->disableOriginalConstructor()->getMock();
+ $container = new ContainerBuilder();
+ $container->register('translation.extractor');
+ $container->register('foo.id')
+ ->addTag('translation.extractor', array());
+
+ $definition->expects($this->never())->method('addMethodCall');
+
+ $translationDumperPass = new TranslationExtractorPass();
+ $translationDumperPass->process($container);
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/DependencyInjection/TranslationPassTest.php b/_include/calendar/symfony/translation/Tests/DependencyInjection/TranslationPassTest.php
new file mode 100644
index 0000000..4082d16
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/DependencyInjection/TranslationPassTest.php
@@ -0,0 +1,57 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\DependencyInjection;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\Translation\DependencyInjection\TranslatorPass;
+
+class TranslationPassTest extends TestCase
+{
+ public function testValidCollector()
+ {
+ $loader = (new Definition())
+ ->addTag('translation.loader', array('alias' => 'xliff', 'legacy-alias' => 'xlf'));
+
+ $reader = new Definition();
+
+ $translator = (new Definition())
+ ->setArguments(array(null, null, null, null));
+
+ $container = new ContainerBuilder();
+ $container->setDefinition('translator.default', $translator);
+ $container->setDefinition('translation.reader', $reader);
+ $container->setDefinition('translation.xliff_loader', $loader);
+
+ $pass = new TranslatorPass('translator.default', 'translation.reader');
+ $pass->process($container);
+
+ $expectedReader = (new Definition())
+ ->addMethodCall('addLoader', array('xliff', new Reference('translation.xliff_loader')))
+ ->addMethodCall('addLoader', array('xlf', new Reference('translation.xliff_loader')))
+ ;
+ $this->assertEquals($expectedReader, $reader);
+
+ $expectedLoader = (new Definition())
+ ->addTag('translation.loader', array('alias' => 'xliff', 'legacy-alias' => 'xlf'))
+ ;
+ $this->assertEquals($expectedLoader, $loader);
+
+ $this->assertSame(array('translation.xliff_loader' => array('xliff', 'xlf')), $translator->getArgument(3));
+
+ $expected = array('translation.xliff_loader' => new ServiceClosureArgument(new Reference('translation.xliff_loader')));
+ $this->assertEquals($expected, $container->getDefinition((string) $translator->getArgument(0))->getArgument(0));
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/Dumper/CsvFileDumperTest.php b/_include/calendar/symfony/translation/Tests/Dumper/CsvFileDumperTest.php
new file mode 100644
index 0000000..9a7a059
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/Dumper/CsvFileDumperTest.php
@@ -0,0 +1,30 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Dumper\CsvFileDumper;
+use Symfony\Component\Translation\MessageCatalogue;
+
+class CsvFileDumperTest extends TestCase
+{
+ public function testFormatCatalogue()
+ {
+ $catalogue = new MessageCatalogue('en');
+ $catalogue->add(array('foo' => 'bar', 'bar' => 'foo
+foo', 'foo;foo' => 'bar'));
+
+ $dumper = new CsvFileDumper();
+
+ $this->assertStringEqualsFile(__DIR__.'/../fixtures/valid.csv', $dumper->formatCatalogue($catalogue, 'messages'));
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/Dumper/FileDumperTest.php b/_include/calendar/symfony/translation/Tests/Dumper/FileDumperTest.php
new file mode 100644
index 0000000..d1524d6
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/Dumper/FileDumperTest.php
@@ -0,0 +1,66 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Dumper\FileDumper;
+use Symfony\Component\Translation\MessageCatalogue;
+
+class FileDumperTest extends TestCase
+{
+ public function testDump()
+ {
+ $tempDir = sys_get_temp_dir();
+
+ $catalogue = new MessageCatalogue('en');
+ $catalogue->add(array('foo' => 'bar'));
+
+ $dumper = new ConcreteFileDumper();
+ $dumper->dump($catalogue, array('path' => $tempDir));
+
+ $this->assertFileExists($tempDir.'/messages.en.concrete');
+
+ @unlink($tempDir.'/messages.en.concrete');
+ }
+
+ public function testDumpCreatesNestedDirectoriesAndFile()
+ {
+ $tempDir = sys_get_temp_dir();
+ $translationsDir = $tempDir.'/test/translations';
+ $file = $translationsDir.'/messages.en.concrete';
+
+ $catalogue = new MessageCatalogue('en');
+ $catalogue->add(array('foo' => 'bar'));
+
+ $dumper = new ConcreteFileDumper();
+ $dumper->setRelativePathTemplate('test/translations/%domain%.%locale%.%extension%');
+ $dumper->dump($catalogue, array('path' => $tempDir));
+
+ $this->assertFileExists($file);
+
+ @unlink($file);
+ @rmdir($translationsDir);
+ }
+}
+
+class ConcreteFileDumper extends FileDumper
+{
+ public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
+ {
+ return '';
+ }
+
+ protected function getExtension()
+ {
+ return 'concrete';
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/Dumper/IcuResFileDumperTest.php b/_include/calendar/symfony/translation/Tests/Dumper/IcuResFileDumperTest.php
new file mode 100644
index 0000000..78d0abb
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/Dumper/IcuResFileDumperTest.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Dumper\IcuResFileDumper;
+use Symfony\Component\Translation\MessageCatalogue;
+
+class IcuResFileDumperTest extends TestCase
+{
+ public function testFormatCatalogue()
+ {
+ $catalogue = new MessageCatalogue('en');
+ $catalogue->add(array('foo' => 'bar'));
+
+ $dumper = new IcuResFileDumper();
+
+ $this->assertStringEqualsFile(__DIR__.'/../fixtures/resourcebundle/res/en.res', $dumper->formatCatalogue($catalogue, 'messages'));
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/Dumper/IniFileDumperTest.php b/_include/calendar/symfony/translation/Tests/Dumper/IniFileDumperTest.php
new file mode 100644
index 0000000..5f3c123
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/Dumper/IniFileDumperTest.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Dumper\IniFileDumper;
+use Symfony\Component\Translation\MessageCatalogue;
+
+class IniFileDumperTest extends TestCase
+{
+ public function testFormatCatalogue()
+ {
+ $catalogue = new MessageCatalogue('en');
+ $catalogue->add(array('foo' => 'bar'));
+
+ $dumper = new IniFileDumper();
+
+ $this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.ini', $dumper->formatCatalogue($catalogue, 'messages'));
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/Dumper/JsonFileDumperTest.php b/_include/calendar/symfony/translation/Tests/Dumper/JsonFileDumperTest.php
new file mode 100644
index 0000000..b134ce4
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/Dumper/JsonFileDumperTest.php
@@ -0,0 +1,39 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Dumper\JsonFileDumper;
+use Symfony\Component\Translation\MessageCatalogue;
+
+class JsonFileDumperTest extends TestCase
+{
+ public function testFormatCatalogue()
+ {
+ $catalogue = new MessageCatalogue('en');
+ $catalogue->add(array('foo' => 'bar'));
+
+ $dumper = new JsonFileDumper();
+
+ $this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.json', $dumper->formatCatalogue($catalogue, 'messages'));
+ }
+
+ public function testDumpWithCustomEncoding()
+ {
+ $catalogue = new MessageCatalogue('en');
+ $catalogue->add(array('foo' => '"bar"'));
+
+ $dumper = new JsonFileDumper();
+
+ $this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.dump.json', $dumper->formatCatalogue($catalogue, 'messages', array('json_encoding' => JSON_HEX_QUOT)));
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/Dumper/MoFileDumperTest.php b/_include/calendar/symfony/translation/Tests/Dumper/MoFileDumperTest.php
new file mode 100644
index 0000000..d051967
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/Dumper/MoFileDumperTest.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Dumper\MoFileDumper;
+use Symfony\Component\Translation\MessageCatalogue;
+
+class MoFileDumperTest extends TestCase
+{
+ public function testFormatCatalogue()
+ {
+ $catalogue = new MessageCatalogue('en');
+ $catalogue->add(array('foo' => 'bar'));
+
+ $dumper = new MoFileDumper();
+
+ $this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.mo', $dumper->formatCatalogue($catalogue, 'messages'));
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/Dumper/PhpFileDumperTest.php b/_include/calendar/symfony/translation/Tests/Dumper/PhpFileDumperTest.php
new file mode 100644
index 0000000..22a39dd
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/Dumper/PhpFileDumperTest.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Dumper\PhpFileDumper;
+use Symfony\Component\Translation\MessageCatalogue;
+
+class PhpFileDumperTest extends TestCase
+{
+ public function testFormatCatalogue()
+ {
+ $catalogue = new MessageCatalogue('en');
+ $catalogue->add(array('foo' => 'bar'));
+
+ $dumper = new PhpFileDumper();
+
+ $this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.php', $dumper->formatCatalogue($catalogue, 'messages'));
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/Dumper/PoFileDumperTest.php b/_include/calendar/symfony/translation/Tests/Dumper/PoFileDumperTest.php
new file mode 100644
index 0000000..5d75247
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/Dumper/PoFileDumperTest.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Dumper\PoFileDumper;
+use Symfony\Component\Translation\MessageCatalogue;
+
+class PoFileDumperTest extends TestCase
+{
+ public function testFormatCatalogue()
+ {
+ $catalogue = new MessageCatalogue('en');
+ $catalogue->add(array('foo' => 'bar'));
+
+ $dumper = new PoFileDumper();
+
+ $this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.po', $dumper->formatCatalogue($catalogue, 'messages'));
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/Dumper/QtFileDumperTest.php b/_include/calendar/symfony/translation/Tests/Dumper/QtFileDumperTest.php
new file mode 100644
index 0000000..8e4a6f4
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/Dumper/QtFileDumperTest.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Dumper\QtFileDumper;
+use Symfony\Component\Translation\MessageCatalogue;
+
+class QtFileDumperTest extends TestCase
+{
+ public function testFormatCatalogue()
+ {
+ $catalogue = new MessageCatalogue('en');
+ $catalogue->add(array('foo' => 'bar'), 'resources');
+
+ $dumper = new QtFileDumper();
+
+ $this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.ts', $dumper->formatCatalogue($catalogue, 'resources'));
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/Dumper/XliffFileDumperTest.php b/_include/calendar/symfony/translation/Tests/Dumper/XliffFileDumperTest.php
new file mode 100644
index 0000000..443dccf
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/Dumper/XliffFileDumperTest.php
@@ -0,0 +1,115 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Dumper\XliffFileDumper;
+use Symfony\Component\Translation\MessageCatalogue;
+
+class XliffFileDumperTest extends TestCase
+{
+ public function testFormatCatalogue()
+ {
+ $catalogue = new MessageCatalogue('en_US');
+ $catalogue->add(array(
+ 'foo' => 'bar',
+ 'key' => '',
+ 'key.with.cdata' => ' & ',
+ ));
+ $catalogue->setMetadata('foo', array('notes' => array(array('priority' => 1, 'from' => 'bar', 'content' => 'baz'))));
+ $catalogue->setMetadata('key', array('notes' => array(array('content' => 'baz'), array('content' => 'qux'))));
+
+ $dumper = new XliffFileDumper();
+
+ $this->assertStringEqualsFile(
+ __DIR__.'/../fixtures/resources-clean.xlf',
+ $dumper->formatCatalogue($catalogue, 'messages', array('default_locale' => 'fr_FR'))
+ );
+ }
+
+ public function testFormatCatalogueXliff2()
+ {
+ $catalogue = new MessageCatalogue('en_US');
+ $catalogue->add(array(
+ 'foo' => 'bar',
+ 'key' => '',
+ 'key.with.cdata' => ' & ',
+ ));
+ $catalogue->setMetadata('key', array('target-attributes' => array('order' => 1)));
+
+ $dumper = new XliffFileDumper();
+
+ $this->assertStringEqualsFile(
+ __DIR__.'/../fixtures/resources-2.0-clean.xlf',
+ $dumper->formatCatalogue($catalogue, 'messages', array('default_locale' => 'fr_FR', 'xliff_version' => '2.0'))
+ );
+ }
+
+ public function testFormatCatalogueWithCustomToolInfo()
+ {
+ $options = array(
+ 'default_locale' => 'en_US',
+ 'tool_info' => array('tool-id' => 'foo', 'tool-name' => 'foo', 'tool-version' => '0.0', 'tool-company' => 'Foo'),
+ );
+
+ $catalogue = new MessageCatalogue('en_US');
+ $catalogue->add(array('foo' => 'bar'));
+
+ $dumper = new XliffFileDumper();
+
+ $this->assertStringEqualsFile(
+ __DIR__.'/../fixtures/resources-tool-info.xlf',
+ $dumper->formatCatalogue($catalogue, 'messages', $options)
+ );
+ }
+
+ public function testFormatCatalogueWithTargetAttributesMetadata()
+ {
+ $catalogue = new MessageCatalogue('en_US');
+ $catalogue->add(array(
+ 'foo' => 'bar',
+ ));
+ $catalogue->setMetadata('foo', array('target-attributes' => array('state' => 'needs-translation')));
+
+ $dumper = new XliffFileDumper();
+
+ $this->assertStringEqualsFile(
+ __DIR__.'/../fixtures/resources-target-attributes.xlf',
+ $dumper->formatCatalogue($catalogue, 'messages', array('default_locale' => 'fr_FR'))
+ );
+ }
+
+ public function testFormatCatalogueWithNotesMetadata()
+ {
+ $catalogue = new MessageCatalogue('en_US');
+ $catalogue->add(array(
+ 'foo' => 'bar',
+ 'baz' => 'biz',
+ ));
+ $catalogue->setMetadata('foo', array('notes' => array(
+ array('category' => 'state', 'content' => 'new'),
+ array('category' => 'approved', 'content' => 'true'),
+ array('category' => 'section', 'content' => 'user login', 'priority' => '1'),
+ )));
+ $catalogue->setMetadata('baz', array('notes' => array(
+ array('id' => 'x', 'content' => 'x_content'),
+ array('appliesTo' => 'target', 'category' => 'quality', 'content' => 'Fuzzy'),
+ )));
+
+ $dumper = new XliffFileDumper();
+
+ $this->assertStringEqualsFile(
+ __DIR__.'/../fixtures/resources-notes-meta.xlf',
+ $dumper->formatCatalogue($catalogue, 'messages', array('default_locale' => 'fr_FR', 'xliff_version' => '2.0'))
+ );
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/Dumper/YamlFileDumperTest.php b/_include/calendar/symfony/translation/Tests/Dumper/YamlFileDumperTest.php
new file mode 100644
index 0000000..a5549a2
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/Dumper/YamlFileDumperTest.php
@@ -0,0 +1,47 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Dumper\YamlFileDumper;
+use Symfony\Component\Translation\MessageCatalogue;
+
+class YamlFileDumperTest extends TestCase
+{
+ public function testTreeFormatCatalogue()
+ {
+ $catalogue = new MessageCatalogue('en');
+ $catalogue->add(
+ array(
+ 'foo.bar1' => 'value1',
+ 'foo.bar2' => 'value2',
+ ));
+
+ $dumper = new YamlFileDumper();
+
+ $this->assertStringEqualsFile(__DIR__.'/../fixtures/messages.yml', $dumper->formatCatalogue($catalogue, 'messages', array('as_tree' => true, 'inline' => 999)));
+ }
+
+ public function testLinearFormatCatalogue()
+ {
+ $catalogue = new MessageCatalogue('en');
+ $catalogue->add(
+ array(
+ 'foo.bar1' => 'value1',
+ 'foo.bar2' => 'value2',
+ ));
+
+ $dumper = new YamlFileDumper();
+
+ $this->assertStringEqualsFile(__DIR__.'/../fixtures/messages_linear.yml', $dumper->formatCatalogue($catalogue, 'messages'));
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/Extractor/PhpExtractorTest.php b/_include/calendar/symfony/translation/Tests/Extractor/PhpExtractorTest.php
new file mode 100644
index 0000000..3487dd6
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/Extractor/PhpExtractorTest.php
@@ -0,0 +1,95 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Extractor;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Extractor\PhpExtractor;
+use Symfony\Component\Translation\MessageCatalogue;
+
+class PhpExtractorTest extends TestCase
+{
+ /**
+ * @dataProvider resourcesProvider
+ *
+ * @param array|string $resource
+ */
+ public function testExtraction($resource)
+ {
+ // Arrange
+ $extractor = new PhpExtractor();
+ $extractor->setPrefix('prefix');
+ $catalogue = new MessageCatalogue('en');
+
+ // Act
+ $extractor->extract($resource, $catalogue);
+
+ $expectedHeredoc = << array(
+ 'single-quoted key' => 'prefixsingle-quoted key',
+ 'double-quoted key' => 'prefixdouble-quoted key',
+ 'heredoc key' => 'prefixheredoc key',
+ 'nowdoc key' => 'prefixnowdoc key',
+ "double-quoted key with whitespace and escaped \$\n\" sequences" => "prefixdouble-quoted key with whitespace and escaped \$\n\" sequences",
+ 'single-quoted key with whitespace and nonescaped \$\n\' sequences' => 'prefixsingle-quoted key with whitespace and nonescaped \$\n\' sequences',
+ 'single-quoted key with "quote mark at the end"' => 'prefixsingle-quoted key with "quote mark at the end"',
+ $expectedHeredoc => 'prefix'.$expectedHeredoc,
+ $expectedNowdoc => 'prefix'.$expectedNowdoc,
+ '{0} There is no apples|{1} There is one apple|]1,Inf[ There are %count% apples' => 'prefix{0} There is no apples|{1} There is one apple|]1,Inf[ There are %count% apples',
+ ),
+ 'not_messages' => array(
+ 'other-domain-test-no-params-short-array' => 'prefixother-domain-test-no-params-short-array',
+ 'other-domain-test-no-params-long-array' => 'prefixother-domain-test-no-params-long-array',
+ 'other-domain-test-params-short-array' => 'prefixother-domain-test-params-short-array',
+ 'other-domain-test-params-long-array' => 'prefixother-domain-test-params-long-array',
+ 'other-domain-test-trans-choice-short-array-%count%' => 'prefixother-domain-test-trans-choice-short-array-%count%',
+ 'other-domain-test-trans-choice-long-array-%count%' => 'prefixother-domain-test-trans-choice-long-array-%count%',
+ 'typecast' => 'prefixtypecast',
+ 'msg1' => 'prefixmsg1',
+ 'msg2' => 'prefixmsg2',
+ ),
+ );
+ $actualCatalogue = $catalogue->all();
+
+ $this->assertEquals($expectedCatalogue, $actualCatalogue);
+ }
+
+ public function resourcesProvider()
+ {
+ $directory = __DIR__.'/../fixtures/extractor/';
+ $splFiles = array();
+ foreach (new \DirectoryIterator($directory) as $fileInfo) {
+ if ($fileInfo->isDot()) {
+ continue;
+ }
+ if ('translation.html.php' === $fileInfo->getBasename()) {
+ $phpFile = $fileInfo->getPathname();
+ }
+ $splFiles[] = $fileInfo->getFileInfo();
+ }
+
+ return array(
+ array($directory),
+ array($phpFile),
+ array(glob($directory.'*')),
+ array($splFiles),
+ array(new \ArrayObject(glob($directory.'*'))),
+ array(new \ArrayObject($splFiles)),
+ );
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/Formatter/MessageFormatterTest.php b/_include/calendar/symfony/translation/Tests/Formatter/MessageFormatterTest.php
new file mode 100644
index 0000000..1fa736e
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/Formatter/MessageFormatterTest.php
@@ -0,0 +1,82 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Formatter;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Formatter\MessageFormatter;
+
+class MessageFormatterTest extends TestCase
+{
+ /**
+ * @dataProvider getTransMessages
+ */
+ public function testFormat($expected, $message, $parameters = array())
+ {
+ $this->assertEquals($expected, $this->getMessageFormatter()->format($message, 'en', $parameters));
+ }
+
+ /**
+ * @dataProvider getTransChoiceMessages
+ */
+ public function testFormatPlural($expected, $message, $number, $parameters)
+ {
+ $this->assertEquals($expected, $this->getMessageFormatter()->choiceFormat($message, $number, 'fr', $parameters));
+ }
+
+ public function getTransMessages()
+ {
+ return array(
+ array(
+ 'There is one apple',
+ 'There is one apple',
+ ),
+ array(
+ 'There are 5 apples',
+ 'There are %count% apples',
+ array('%count%' => 5),
+ ),
+ array(
+ 'There are 5 apples',
+ 'There are {{count}} apples',
+ array('{{count}}' => 5),
+ ),
+ );
+ }
+
+ public function getTransChoiceMessages()
+ {
+ return array(
+ array('Il y a 0 pomme', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 0, array('%count%' => 0)),
+ array('Il y a 1 pomme', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 1, array('%count%' => 1)),
+ array('Il y a 10 pommes', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 10, array('%count%' => 10)),
+
+ array('Il y a 0 pomme', 'Il y a %count% pomme|Il y a %count% pommes', 0, array('%count%' => 0)),
+ array('Il y a 1 pomme', 'Il y a %count% pomme|Il y a %count% pommes', 1, array('%count%' => 1)),
+ array('Il y a 10 pommes', 'Il y a %count% pomme|Il y a %count% pommes', 10, array('%count%' => 10)),
+
+ array('Il y a 0 pomme', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 0, array('%count%' => 0)),
+ array('Il y a 1 pomme', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 1, array('%count%' => 1)),
+ array('Il y a 10 pommes', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 10, array('%count%' => 10)),
+
+ array('Il n\'y a aucune pomme', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 0, array('%count%' => 0)),
+ array('Il y a 1 pomme', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 1, array('%count%' => 1)),
+ array('Il y a 10 pommes', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 10, array('%count%' => 10)),
+
+ array('Il y a 0 pomme', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 0, array('%count%' => 0)),
+ );
+ }
+
+ private function getMessageFormatter()
+ {
+ return new MessageFormatter();
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/IdentityTranslatorTest.php b/_include/calendar/symfony/translation/Tests/IdentityTranslatorTest.php
new file mode 100644
index 0000000..78288da
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/IdentityTranslatorTest.php
@@ -0,0 +1,96 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Intl\Util\IntlTestHelper;
+use Symfony\Component\Translation\IdentityTranslator;
+
+class IdentityTranslatorTest extends TestCase
+{
+ /**
+ * @dataProvider getTransTests
+ */
+ public function testTrans($expected, $id, $parameters)
+ {
+ $translator = new IdentityTranslator();
+
+ $this->assertEquals($expected, $translator->trans($id, $parameters));
+ }
+
+ /**
+ * @dataProvider getTransChoiceTests
+ */
+ public function testTransChoiceWithExplicitLocale($expected, $id, $number, $parameters)
+ {
+ $translator = new IdentityTranslator();
+ $translator->setLocale('en');
+
+ $this->assertEquals($expected, $translator->transChoice($id, $number, $parameters));
+ }
+
+ /**
+ * @dataProvider getTransChoiceTests
+ */
+ public function testTransChoiceWithDefaultLocale($expected, $id, $number, $parameters)
+ {
+ \Locale::setDefault('en');
+
+ $translator = new IdentityTranslator();
+
+ $this->assertEquals($expected, $translator->transChoice($id, $number, $parameters));
+ }
+
+ public function testGetSetLocale()
+ {
+ $translator = new IdentityTranslator();
+ $translator->setLocale('en');
+
+ $this->assertEquals('en', $translator->getLocale());
+ }
+
+ public function testGetLocaleReturnsDefaultLocaleIfNotSet()
+ {
+ // in order to test with "pt_BR"
+ IntlTestHelper::requireFullIntl($this, false);
+
+ $translator = new IdentityTranslator();
+
+ \Locale::setDefault('en');
+ $this->assertEquals('en', $translator->getLocale());
+
+ \Locale::setDefault('pt_BR');
+ $this->assertEquals('pt_BR', $translator->getLocale());
+ }
+
+ public function getTransTests()
+ {
+ return array(
+ array('Symfony is great!', 'Symfony is great!', array()),
+ array('Symfony is awesome!', 'Symfony is %what%!', array('%what%' => 'awesome')),
+ );
+ }
+
+ public function getTransChoiceTests()
+ {
+ return array(
+ array('There are no apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0, array('%count%' => 0)),
+ array('There is one apple', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 1, array('%count%' => 1)),
+ array('There are 10 apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10, array('%count%' => 10)),
+ array('There are 0 apples', 'There is 1 apple|There are %count% apples', 0, array('%count%' => 0)),
+ array('There is 1 apple', 'There is 1 apple|There are %count% apples', 1, array('%count%' => 1)),
+ array('There are 10 apples', 'There is 1 apple|There are %count% apples', 10, array('%count%' => 10)),
+ // custom validation messages may be coded with a fixed value
+ array('There are 2 apples', 'There are 2 apples', 2, array('%count%' => 2)),
+ );
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/IntervalTest.php b/_include/calendar/symfony/translation/Tests/IntervalTest.php
new file mode 100644
index 0000000..99b209a
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/IntervalTest.php
@@ -0,0 +1,49 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Interval;
+
+class IntervalTest extends TestCase
+{
+ /**
+ * @dataProvider getTests
+ */
+ public function testTest($expected, $number, $interval)
+ {
+ $this->assertEquals($expected, Interval::test($number, $interval));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException
+ */
+ public function testTestException()
+ {
+ Interval::test(1, 'foobar');
+ }
+
+ public function getTests()
+ {
+ return array(
+ array(true, 3, '{1,2, 3 ,4}'),
+ array(false, 10, '{1,2, 3 ,4}'),
+ array(false, 3, '[1,2]'),
+ array(true, 1, '[1,2]'),
+ array(true, 2, '[1,2]'),
+ array(false, 1, ']1,2['),
+ array(false, 2, ']1,2['),
+ array(true, log(0), '[-Inf,2['),
+ array(true, -log(0), '[-2,+Inf]'),
+ );
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/Loader/CsvFileLoaderTest.php b/_include/calendar/symfony/translation/Tests/Loader/CsvFileLoaderTest.php
new file mode 100644
index 0000000..29efdef
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/Loader/CsvFileLoaderTest.php
@@ -0,0 +1,61 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Translation\Loader\CsvFileLoader;
+
+class CsvFileLoaderTest extends TestCase
+{
+ public function testLoad()
+ {
+ $loader = new CsvFileLoader();
+ $resource = __DIR__.'/../fixtures/resources.csv';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array('foo' => 'bar'), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ public function testLoadDoesNothingIfEmpty()
+ {
+ $loader = new CsvFileLoader();
+ $resource = __DIR__.'/../fixtures/empty.csv';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array(), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+ */
+ public function testLoadNonExistingResource()
+ {
+ $loader = new CsvFileLoader();
+ $resource = __DIR__.'/../fixtures/not-exists.csv';
+ $loader->load($resource, 'en', 'domain1');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+ */
+ public function testLoadNonLocalResource()
+ {
+ $loader = new CsvFileLoader();
+ $resource = 'http://example.com/resources.csv';
+ $loader->load($resource, 'en', 'domain1');
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/Loader/IcuDatFileLoaderTest.php b/_include/calendar/symfony/translation/Tests/Loader/IcuDatFileLoaderTest.php
new file mode 100644
index 0000000..e28db60
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/Loader/IcuDatFileLoaderTest.php
@@ -0,0 +1,64 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Translation\Loader\IcuDatFileLoader;
+
+/**
+ * @requires extension intl
+ */
+class IcuDatFileLoaderTest extends LocalizedTestCase
+{
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+ */
+ public function testLoadInvalidResource()
+ {
+ $loader = new IcuDatFileLoader();
+ $loader->load(__DIR__.'/../fixtures/resourcebundle/corrupted/resources', 'es', 'domain2');
+ }
+
+ public function testDatEnglishLoad()
+ {
+ // bundled resource is build using pkgdata command which at least in ICU 4.2 comes in extremely! buggy form
+ // you must specify an temporary build directory which is not the same as current directory and
+ // MUST reside on the same partition. pkgdata -p resources -T /srv -d.packagelist.txt
+ $loader = new IcuDatFileLoader();
+ $resource = __DIR__.'/../fixtures/resourcebundle/dat/resources';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array('symfony' => 'Symfony 2 is great'), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource.'.dat')), $catalogue->getResources());
+ }
+
+ public function testDatFrenchLoad()
+ {
+ $loader = new IcuDatFileLoader();
+ $resource = __DIR__.'/../fixtures/resourcebundle/dat/resources';
+ $catalogue = $loader->load($resource, 'fr', 'domain1');
+
+ $this->assertEquals(array('symfony' => 'Symfony 2 est génial'), $catalogue->all('domain1'));
+ $this->assertEquals('fr', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource.'.dat')), $catalogue->getResources());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+ */
+ public function testLoadNonExistingResource()
+ {
+ $loader = new IcuDatFileLoader();
+ $loader->load(__DIR__.'/../fixtures/non-existing.txt', 'en', 'domain1');
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/Loader/IcuResFileLoaderTest.php b/_include/calendar/symfony/translation/Tests/Loader/IcuResFileLoaderTest.php
new file mode 100644
index 0000000..92d8933
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/Loader/IcuResFileLoaderTest.php
@@ -0,0 +1,51 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use Symfony\Component\Config\Resource\DirectoryResource;
+use Symfony\Component\Translation\Loader\IcuResFileLoader;
+
+/**
+ * @requires extension intl
+ */
+class IcuResFileLoaderTest extends LocalizedTestCase
+{
+ public function testLoad()
+ {
+ // resource is build using genrb command
+ $loader = new IcuResFileLoader();
+ $resource = __DIR__.'/../fixtures/resourcebundle/res';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array('foo' => 'bar'), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new DirectoryResource($resource)), $catalogue->getResources());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+ */
+ public function testLoadNonExistingResource()
+ {
+ $loader = new IcuResFileLoader();
+ $loader->load(__DIR__.'/../fixtures/non-existing.txt', 'en', 'domain1');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+ */
+ public function testLoadInvalidResource()
+ {
+ $loader = new IcuResFileLoader();
+ $loader->load(__DIR__.'/../fixtures/resourcebundle/corrupted', 'en', 'domain1');
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/Loader/IniFileLoaderTest.php b/_include/calendar/symfony/translation/Tests/Loader/IniFileLoaderTest.php
new file mode 100644
index 0000000..d9dcc5e
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/Loader/IniFileLoaderTest.php
@@ -0,0 +1,51 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Translation\Loader\IniFileLoader;
+
+class IniFileLoaderTest extends TestCase
+{
+ public function testLoad()
+ {
+ $loader = new IniFileLoader();
+ $resource = __DIR__.'/../fixtures/resources.ini';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array('foo' => 'bar'), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ public function testLoadDoesNothingIfEmpty()
+ {
+ $loader = new IniFileLoader();
+ $resource = __DIR__.'/../fixtures/empty.ini';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array(), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+ */
+ public function testLoadNonExistingResource()
+ {
+ $loader = new IniFileLoader();
+ $resource = __DIR__.'/../fixtures/non-existing.ini';
+ $loader->load($resource, 'en', 'domain1');
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/Loader/JsonFileLoaderTest.php b/_include/calendar/symfony/translation/Tests/Loader/JsonFileLoaderTest.php
new file mode 100644
index 0000000..cea0aef
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/Loader/JsonFileLoaderTest.php
@@ -0,0 +1,62 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Translation\Loader\JsonFileLoader;
+
+class JsonFileLoaderTest extends TestCase
+{
+ public function testLoad()
+ {
+ $loader = new JsonFileLoader();
+ $resource = __DIR__.'/../fixtures/resources.json';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array('foo' => 'bar'), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ public function testLoadDoesNothingIfEmpty()
+ {
+ $loader = new JsonFileLoader();
+ $resource = __DIR__.'/../fixtures/empty.json';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array(), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+ */
+ public function testLoadNonExistingResource()
+ {
+ $loader = new JsonFileLoader();
+ $resource = __DIR__.'/../fixtures/non-existing.json';
+ $loader->load($resource, 'en', 'domain1');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+ * @expectedExceptionMessage Error parsing JSON - Syntax error, malformed JSON
+ */
+ public function testParseException()
+ {
+ $loader = new JsonFileLoader();
+ $resource = __DIR__.'/../fixtures/malformed.json';
+ $loader->load($resource, 'en', 'domain1');
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/Loader/LocalizedTestCase.php b/_include/calendar/symfony/translation/Tests/Loader/LocalizedTestCase.php
new file mode 100644
index 0000000..279e9fd
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/Loader/LocalizedTestCase.php
@@ -0,0 +1,24 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use PHPUnit\Framework\TestCase;
+
+abstract class LocalizedTestCase extends TestCase
+{
+ protected function setUp()
+ {
+ if (!\extension_loaded('intl')) {
+ $this->markTestSkipped('Extension intl is required.');
+ }
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/Loader/MoFileLoaderTest.php b/_include/calendar/symfony/translation/Tests/Loader/MoFileLoaderTest.php
new file mode 100644
index 0000000..6ad3d44
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/Loader/MoFileLoaderTest.php
@@ -0,0 +1,72 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Translation\Loader\MoFileLoader;
+
+class MoFileLoaderTest extends TestCase
+{
+ public function testLoad()
+ {
+ $loader = new MoFileLoader();
+ $resource = __DIR__.'/../fixtures/resources.mo';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array('foo' => 'bar'), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ public function testLoadPlurals()
+ {
+ $loader = new MoFileLoader();
+ $resource = __DIR__.'/../fixtures/plurals.mo';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array('foo' => 'bar', 'foos' => '{0} bar|{1} bars'), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+ */
+ public function testLoadNonExistingResource()
+ {
+ $loader = new MoFileLoader();
+ $resource = __DIR__.'/../fixtures/non-existing.mo';
+ $loader->load($resource, 'en', 'domain1');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+ */
+ public function testLoadInvalidResource()
+ {
+ $loader = new MoFileLoader();
+ $resource = __DIR__.'/../fixtures/empty.mo';
+ $loader->load($resource, 'en', 'domain1');
+ }
+
+ public function testLoadEmptyTranslation()
+ {
+ $loader = new MoFileLoader();
+ $resource = __DIR__.'/../fixtures/empty-translation.mo';
+ $catalogue = $loader->load($resource, 'en', 'message');
+
+ $this->assertEquals(array(), $catalogue->all('message'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/Loader/PhpFileLoaderTest.php b/_include/calendar/symfony/translation/Tests/Loader/PhpFileLoaderTest.php
new file mode 100644
index 0000000..01b7a5f
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/Loader/PhpFileLoaderTest.php
@@ -0,0 +1,50 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Translation\Loader\PhpFileLoader;
+
+class PhpFileLoaderTest extends TestCase
+{
+ public function testLoad()
+ {
+ $loader = new PhpFileLoader();
+ $resource = __DIR__.'/../fixtures/resources.php';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array('foo' => 'bar'), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+ */
+ public function testLoadNonExistingResource()
+ {
+ $loader = new PhpFileLoader();
+ $resource = __DIR__.'/../fixtures/non-existing.php';
+ $loader->load($resource, 'en', 'domain1');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+ */
+ public function testLoadThrowsAnExceptionIfFileNotLocal()
+ {
+ $loader = new PhpFileLoader();
+ $resource = 'http://example.com/resources.php';
+ $loader->load($resource, 'en', 'domain1');
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/Loader/PoFileLoaderTest.php b/_include/calendar/symfony/translation/Tests/Loader/PoFileLoaderTest.php
new file mode 100644
index 0000000..4176cb7
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/Loader/PoFileLoaderTest.php
@@ -0,0 +1,109 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Translation\Loader\PoFileLoader;
+
+class PoFileLoaderTest extends TestCase
+{
+ public function testLoad()
+ {
+ $loader = new PoFileLoader();
+ $resource = __DIR__.'/../fixtures/resources.po';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array('foo' => 'bar'), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ public function testLoadPlurals()
+ {
+ $loader = new PoFileLoader();
+ $resource = __DIR__.'/../fixtures/plurals.po';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array('foo' => 'bar', 'foos' => 'bar|bars'), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ public function testLoadDoesNothingIfEmpty()
+ {
+ $loader = new PoFileLoader();
+ $resource = __DIR__.'/../fixtures/empty.po';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array(), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+ */
+ public function testLoadNonExistingResource()
+ {
+ $loader = new PoFileLoader();
+ $resource = __DIR__.'/../fixtures/non-existing.po';
+ $loader->load($resource, 'en', 'domain1');
+ }
+
+ public function testLoadEmptyTranslation()
+ {
+ $loader = new PoFileLoader();
+ $resource = __DIR__.'/../fixtures/empty-translation.po';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array('foo' => ''), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ public function testEscapedId()
+ {
+ $loader = new PoFileLoader();
+ $resource = __DIR__.'/../fixtures/escaped-id.po';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $messages = $catalogue->all('domain1');
+ $this->assertArrayHasKey('escaped "foo"', $messages);
+ $this->assertEquals('escaped "bar"', $messages['escaped "foo"']);
+ }
+
+ public function testEscapedIdPlurals()
+ {
+ $loader = new PoFileLoader();
+ $resource = __DIR__.'/../fixtures/escaped-id-plurals.po';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $messages = $catalogue->all('domain1');
+ $this->assertArrayHasKey('escaped "foo"', $messages);
+ $this->assertArrayHasKey('escaped "foos"', $messages);
+ $this->assertEquals('escaped "bar"', $messages['escaped "foo"']);
+ $this->assertEquals('escaped "bar"|escaped "bars"', $messages['escaped "foos"']);
+ }
+
+ public function testSkipFuzzyTranslations()
+ {
+ $loader = new PoFileLoader();
+ $resource = __DIR__.'/../fixtures/fuzzy-translations.po';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $messages = $catalogue->all('domain1');
+ $this->assertArrayHasKey('foo1', $messages);
+ $this->assertArrayNotHasKey('foo2', $messages);
+ $this->assertArrayHasKey('foo3', $messages);
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/Loader/QtFileLoaderTest.php b/_include/calendar/symfony/translation/Tests/Loader/QtFileLoaderTest.php
new file mode 100644
index 0000000..8a00fde
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/Loader/QtFileLoaderTest.php
@@ -0,0 +1,75 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Translation\Loader\QtFileLoader;
+
+class QtFileLoaderTest extends TestCase
+{
+ public function testLoad()
+ {
+ $loader = new QtFileLoader();
+ $resource = __DIR__.'/../fixtures/resources.ts';
+ $catalogue = $loader->load($resource, 'en', 'resources');
+
+ $this->assertEquals(array('foo' => 'bar'), $catalogue->all('resources'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+ */
+ public function testLoadNonExistingResource()
+ {
+ $loader = new QtFileLoader();
+ $resource = __DIR__.'/../fixtures/non-existing.ts';
+ $loader->load($resource, 'en', 'domain1');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+ */
+ public function testLoadNonLocalResource()
+ {
+ $loader = new QtFileLoader();
+ $resource = 'http://domain1.com/resources.ts';
+ $loader->load($resource, 'en', 'domain1');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+ */
+ public function testLoadInvalidResource()
+ {
+ $loader = new QtFileLoader();
+ $resource = __DIR__.'/../fixtures/invalid-xml-resources.xlf';
+ $loader->load($resource, 'en', 'domain1');
+ }
+
+ public function testLoadEmptyResource()
+ {
+ $loader = new QtFileLoader();
+ $resource = __DIR__.'/../fixtures/empty.xlf';
+
+ if (method_exists($this, 'expectException')) {
+ $this->expectException('Symfony\Component\Translation\Exception\InvalidResourceException');
+ $this->expectExceptionMessage(sprintf('Unable to load "%s".', $resource));
+ } else {
+ $this->setExpectedException('Symfony\Component\Translation\Exception\InvalidResourceException', sprintf('Unable to load "%s".', $resource));
+ }
+
+ $loader->load($resource, 'en', 'domain1');
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/Loader/XliffFileLoaderTest.php b/_include/calendar/symfony/translation/Tests/Loader/XliffFileLoaderTest.php
new file mode 100644
index 0000000..94406fd
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/Loader/XliffFileLoaderTest.php
@@ -0,0 +1,260 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Translation\Loader\XliffFileLoader;
+
+class XliffFileLoaderTest extends TestCase
+{
+ public function testLoad()
+ {
+ $loader = new XliffFileLoader();
+ $resource = __DIR__.'/../fixtures/resources.xlf';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ $this->assertSame(array(), libxml_get_errors());
+ $this->assertContainsOnly('string', $catalogue->all('domain1'));
+ }
+
+ public function testLoadWithInternalErrorsEnabled()
+ {
+ $internalErrors = libxml_use_internal_errors(true);
+
+ $this->assertSame(array(), libxml_get_errors());
+
+ $loader = new XliffFileLoader();
+ $resource = __DIR__.'/../fixtures/resources.xlf';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ $this->assertSame(array(), libxml_get_errors());
+
+ libxml_clear_errors();
+ libxml_use_internal_errors($internalErrors);
+ }
+
+ public function testLoadWithExternalEntitiesDisabled()
+ {
+ $disableEntities = libxml_disable_entity_loader(true);
+
+ $loader = new XliffFileLoader();
+ $resource = __DIR__.'/../fixtures/resources.xlf';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ libxml_disable_entity_loader($disableEntities);
+
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ public function testLoadWithResname()
+ {
+ $loader = new XliffFileLoader();
+ $catalogue = $loader->load(__DIR__.'/../fixtures/resname.xlf', 'en', 'domain1');
+
+ $this->assertEquals(array('foo' => 'bar', 'bar' => 'baz', 'baz' => 'foo'), $catalogue->all('domain1'));
+ }
+
+ public function testIncompleteResource()
+ {
+ $loader = new XliffFileLoader();
+ $catalogue = $loader->load(__DIR__.'/../fixtures/resources.xlf', 'en', 'domain1');
+
+ $this->assertEquals(array('foo' => 'bar', 'extra' => 'extra', 'key' => '', 'test' => 'with'), $catalogue->all('domain1'));
+ }
+
+ public function testEncoding()
+ {
+ $loader = new XliffFileLoader();
+ $catalogue = $loader->load(__DIR__.'/../fixtures/encoding.xlf', 'en', 'domain1');
+
+ $this->assertEquals(utf8_decode('föö'), $catalogue->get('bar', 'domain1'));
+ $this->assertEquals(utf8_decode('bär'), $catalogue->get('foo', 'domain1'));
+ $this->assertEquals(array('notes' => array(array('content' => utf8_decode('bäz'))), 'id' => '1'), $catalogue->getMetadata('foo', 'domain1'));
+ }
+
+ public function testTargetAttributesAreStoredCorrectly()
+ {
+ $loader = new XliffFileLoader();
+ $catalogue = $loader->load(__DIR__.'/../fixtures/with-attributes.xlf', 'en', 'domain1');
+
+ $metadata = $catalogue->getMetadata('foo', 'domain1');
+ $this->assertEquals('translated', $metadata['target-attributes']['state']);
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+ */
+ public function testLoadInvalidResource()
+ {
+ $loader = new XliffFileLoader();
+ $loader->load(__DIR__.'/../fixtures/resources.php', 'en', 'domain1');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+ */
+ public function testLoadResourceDoesNotValidate()
+ {
+ $loader = new XliffFileLoader();
+ $loader->load(__DIR__.'/../fixtures/non-valid.xlf', 'en', 'domain1');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+ */
+ public function testLoadNonExistingResource()
+ {
+ $loader = new XliffFileLoader();
+ $resource = __DIR__.'/../fixtures/non-existing.xlf';
+ $loader->load($resource, 'en', 'domain1');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+ */
+ public function testLoadThrowsAnExceptionIfFileNotLocal()
+ {
+ $loader = new XliffFileLoader();
+ $resource = 'http://example.com/resources.xlf';
+ $loader->load($resource, 'en', 'domain1');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+ * @expectedExceptionMessage Document types are not allowed.
+ */
+ public function testDocTypeIsNotAllowed()
+ {
+ $loader = new XliffFileLoader();
+ $loader->load(__DIR__.'/../fixtures/withdoctype.xlf', 'en', 'domain1');
+ }
+
+ public function testParseEmptyFile()
+ {
+ $loader = new XliffFileLoader();
+ $resource = __DIR__.'/../fixtures/empty.xlf';
+
+ if (method_exists($this, 'expectException')) {
+ $this->expectException('Symfony\Component\Translation\Exception\InvalidResourceException');
+ $this->expectExceptionMessage(sprintf('Unable to load "%s":', $resource));
+ } else {
+ $this->setExpectedException('Symfony\Component\Translation\Exception\InvalidResourceException', sprintf('Unable to load "%s":', $resource));
+ }
+
+ $loader->load($resource, 'en', 'domain1');
+ }
+
+ public function testLoadNotes()
+ {
+ $loader = new XliffFileLoader();
+ $catalogue = $loader->load(__DIR__.'/../fixtures/withnote.xlf', 'en', 'domain1');
+
+ $this->assertEquals(array('notes' => array(array('priority' => 1, 'content' => 'foo')), 'id' => '1'), $catalogue->getMetadata('foo', 'domain1'));
+ // message without target
+ $this->assertEquals(array('notes' => array(array('content' => 'bar', 'from' => 'foo')), 'id' => '2'), $catalogue->getMetadata('extra', 'domain1'));
+ // message with empty target
+ $this->assertEquals(array('notes' => array(array('content' => 'baz'), array('priority' => 2, 'from' => 'bar', 'content' => 'qux')), 'id' => '123'), $catalogue->getMetadata('key', 'domain1'));
+ }
+
+ public function testLoadVersion2()
+ {
+ $loader = new XliffFileLoader();
+ $resource = __DIR__.'/../fixtures/resources-2.0.xlf';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ $this->assertSame(array(), libxml_get_errors());
+
+ $domains = $catalogue->all();
+ $this->assertCount(3, $domains['domain1']);
+ $this->assertContainsOnly('string', $catalogue->all('domain1'));
+
+ // target attributes
+ $this->assertEquals(array('target-attributes' => array('order' => 1)), $catalogue->getMetadata('bar', 'domain1'));
+ }
+
+ public function testLoadVersion2WithNoteMeta()
+ {
+ $loader = new XliffFileLoader();
+ $resource = __DIR__.'/../fixtures/resources-notes-meta.xlf';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ $this->assertSame(array(), libxml_get_errors());
+
+ // test for "foo" metadata
+ $this->assertTrue($catalogue->defines('foo', 'domain1'));
+ $metadata = $catalogue->getMetadata('foo', 'domain1');
+ $this->assertNotEmpty($metadata);
+ $this->assertCount(3, $metadata['notes']);
+
+ $this->assertEquals('state', $metadata['notes'][0]['category']);
+ $this->assertEquals('new', $metadata['notes'][0]['content']);
+
+ $this->assertEquals('approved', $metadata['notes'][1]['category']);
+ $this->assertEquals('true', $metadata['notes'][1]['content']);
+
+ $this->assertEquals('section', $metadata['notes'][2]['category']);
+ $this->assertEquals('1', $metadata['notes'][2]['priority']);
+ $this->assertEquals('user login', $metadata['notes'][2]['content']);
+
+ // test for "baz" metadata
+ $this->assertTrue($catalogue->defines('baz', 'domain1'));
+ $metadata = $catalogue->getMetadata('baz', 'domain1');
+ $this->assertNotEmpty($metadata);
+ $this->assertCount(2, $metadata['notes']);
+
+ $this->assertEquals('x', $metadata['notes'][0]['id']);
+ $this->assertEquals('x_content', $metadata['notes'][0]['content']);
+
+ $this->assertEquals('target', $metadata['notes'][1]['appliesTo']);
+ $this->assertEquals('quality', $metadata['notes'][1]['category']);
+ $this->assertEquals('Fuzzy', $metadata['notes'][1]['content']);
+ }
+
+ public function testLoadVersion2WithMultiSegmentUnit()
+ {
+ $loader = new XliffFileLoader();
+ $resource = __DIR__.'/../fixtures/resources-2.0-multi-segment-unit.xlf';
+ $catalog = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertSame('en', $catalog->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalog->getResources());
+ $this->assertFalse(libxml_get_last_error());
+
+ // test for "foo" metadata
+ $this->assertTrue($catalog->defines('foo', 'domain1'));
+ $metadata = $catalog->getMetadata('foo', 'domain1');
+ $this->assertNotEmpty($metadata);
+ $this->assertCount(1, $metadata['notes']);
+
+ $this->assertSame('processed', $metadata['notes'][0]['category']);
+ $this->assertSame('true', $metadata['notes'][0]['content']);
+
+ // test for "bar" metadata
+ $this->assertTrue($catalog->defines('bar', 'domain1'));
+ $metadata = $catalog->getMetadata('bar', 'domain1');
+ $this->assertNotEmpty($metadata);
+ $this->assertCount(1, $metadata['notes']);
+
+ $this->assertSame('processed', $metadata['notes'][0]['category']);
+ $this->assertSame('true', $metadata['notes'][0]['content']);
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/Loader/YamlFileLoaderTest.php b/_include/calendar/symfony/translation/Tests/Loader/YamlFileLoaderTest.php
new file mode 100644
index 0000000..bb3c1a9
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/Loader/YamlFileLoaderTest.php
@@ -0,0 +1,71 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Translation\Loader\YamlFileLoader;
+
+class YamlFileLoaderTest extends TestCase
+{
+ public function testLoad()
+ {
+ $loader = new YamlFileLoader();
+ $resource = __DIR__.'/../fixtures/resources.yml';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array('foo' => 'bar'), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ public function testLoadDoesNothingIfEmpty()
+ {
+ $loader = new YamlFileLoader();
+ $resource = __DIR__.'/../fixtures/empty.yml';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array(), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+ */
+ public function testLoadNonExistingResource()
+ {
+ $loader = new YamlFileLoader();
+ $resource = __DIR__.'/../fixtures/non-existing.yml';
+ $loader->load($resource, 'en', 'domain1');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+ */
+ public function testLoadThrowsAnExceptionIfFileNotLocal()
+ {
+ $loader = new YamlFileLoader();
+ $resource = 'http://example.com/resources.yml';
+ $loader->load($resource, 'en', 'domain1');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+ */
+ public function testLoadThrowsAnExceptionIfNotAnArray()
+ {
+ $loader = new YamlFileLoader();
+ $resource = __DIR__.'/../fixtures/non-valid.yml';
+ $loader->load($resource, 'en', 'domain1');
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/LoggingTranslatorTest.php b/_include/calendar/symfony/translation/Tests/LoggingTranslatorTest.php
new file mode 100644
index 0000000..022379e
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/LoggingTranslatorTest.php
@@ -0,0 +1,50 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Loader\ArrayLoader;
+use Symfony\Component\Translation\LoggingTranslator;
+use Symfony\Component\Translation\Translator;
+
+class LoggingTranslatorTest extends TestCase
+{
+ public function testTransWithNoTranslationIsLogged()
+ {
+ $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
+ $logger->expects($this->exactly(2))
+ ->method('warning')
+ ->with('Translation not found.')
+ ;
+
+ $translator = new Translator('ar');
+ $loggableTranslator = new LoggingTranslator($translator, $logger);
+ $loggableTranslator->transChoice('some_message2', 10, array('%count%' => 10));
+ $loggableTranslator->trans('bar');
+ }
+
+ public function testTransChoiceFallbackIsLogged()
+ {
+ $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
+ $logger->expects($this->once())
+ ->method('debug')
+ ->with('Translation use fallback catalogue.')
+ ;
+
+ $translator = new Translator('ar');
+ $translator->setFallbackLocales(array('en'));
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('some_message2' => 'one thing|%count% things'), 'en');
+ $loggableTranslator = new LoggingTranslator($translator, $logger);
+ $loggableTranslator->transChoice('some_message2', 10, array('%count%' => 10));
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/MessageCatalogueTest.php b/_include/calendar/symfony/translation/Tests/MessageCatalogueTest.php
new file mode 100644
index 0000000..1ab8246
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/MessageCatalogueTest.php
@@ -0,0 +1,222 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\MessageCatalogue;
+
+class MessageCatalogueTest extends TestCase
+{
+ public function testGetLocale()
+ {
+ $catalogue = new MessageCatalogue('en');
+
+ $this->assertEquals('en', $catalogue->getLocale());
+ }
+
+ public function testGetDomains()
+ {
+ $catalogue = new MessageCatalogue('en', array('domain1' => array(), 'domain2' => array()));
+
+ $this->assertEquals(array('domain1', 'domain2'), $catalogue->getDomains());
+ }
+
+ public function testAll()
+ {
+ $catalogue = new MessageCatalogue('en', $messages = array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar')));
+
+ $this->assertEquals(array('foo' => 'foo'), $catalogue->all('domain1'));
+ $this->assertEquals(array(), $catalogue->all('domain88'));
+ $this->assertEquals($messages, $catalogue->all());
+ }
+
+ public function testHas()
+ {
+ $catalogue = new MessageCatalogue('en', array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar')));
+
+ $this->assertTrue($catalogue->has('foo', 'domain1'));
+ $this->assertFalse($catalogue->has('bar', 'domain1'));
+ $this->assertFalse($catalogue->has('foo', 'domain88'));
+ }
+
+ public function testGetSet()
+ {
+ $catalogue = new MessageCatalogue('en', array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar')));
+ $catalogue->set('foo1', 'foo1', 'domain1');
+
+ $this->assertEquals('foo', $catalogue->get('foo', 'domain1'));
+ $this->assertEquals('foo1', $catalogue->get('foo1', 'domain1'));
+ }
+
+ public function testAdd()
+ {
+ $catalogue = new MessageCatalogue('en', array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar')));
+ $catalogue->add(array('foo1' => 'foo1'), 'domain1');
+
+ $this->assertEquals('foo', $catalogue->get('foo', 'domain1'));
+ $this->assertEquals('foo1', $catalogue->get('foo1', 'domain1'));
+
+ $catalogue->add(array('foo' => 'bar'), 'domain1');
+ $this->assertEquals('bar', $catalogue->get('foo', 'domain1'));
+ $this->assertEquals('foo1', $catalogue->get('foo1', 'domain1'));
+
+ $catalogue->add(array('foo' => 'bar'), 'domain88');
+ $this->assertEquals('bar', $catalogue->get('foo', 'domain88'));
+ }
+
+ public function testReplace()
+ {
+ $catalogue = new MessageCatalogue('en', array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar')));
+ $catalogue->replace($messages = array('foo1' => 'foo1'), 'domain1');
+
+ $this->assertEquals($messages, $catalogue->all('domain1'));
+ }
+
+ public function testAddCatalogue()
+ {
+ $r = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
+ $r->expects($this->any())->method('__toString')->will($this->returnValue('r'));
+
+ $r1 = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
+ $r1->expects($this->any())->method('__toString')->will($this->returnValue('r1'));
+
+ $catalogue = new MessageCatalogue('en', array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar')));
+ $catalogue->addResource($r);
+
+ $catalogue1 = new MessageCatalogue('en', array('domain1' => array('foo1' => 'foo1')));
+ $catalogue1->addResource($r1);
+
+ $catalogue->addCatalogue($catalogue1);
+
+ $this->assertEquals('foo', $catalogue->get('foo', 'domain1'));
+ $this->assertEquals('foo1', $catalogue->get('foo1', 'domain1'));
+
+ $this->assertEquals(array($r, $r1), $catalogue->getResources());
+ }
+
+ public function testAddFallbackCatalogue()
+ {
+ $r = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
+ $r->expects($this->any())->method('__toString')->will($this->returnValue('r'));
+
+ $r1 = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
+ $r1->expects($this->any())->method('__toString')->will($this->returnValue('r1'));
+
+ $r2 = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
+ $r2->expects($this->any())->method('__toString')->will($this->returnValue('r2'));
+
+ $catalogue = new MessageCatalogue('fr_FR', array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar')));
+ $catalogue->addResource($r);
+
+ $catalogue1 = new MessageCatalogue('fr', array('domain1' => array('foo' => 'bar', 'foo1' => 'foo1')));
+ $catalogue1->addResource($r1);
+
+ $catalogue2 = new MessageCatalogue('en');
+ $catalogue2->addResource($r2);
+
+ $catalogue->addFallbackCatalogue($catalogue1);
+ $catalogue1->addFallbackCatalogue($catalogue2);
+
+ $this->assertEquals('foo', $catalogue->get('foo', 'domain1'));
+ $this->assertEquals('foo1', $catalogue->get('foo1', 'domain1'));
+
+ $this->assertEquals(array($r, $r1, $r2), $catalogue->getResources());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\LogicException
+ */
+ public function testAddFallbackCatalogueWithParentCircularReference()
+ {
+ $main = new MessageCatalogue('en_US');
+ $fallback = new MessageCatalogue('fr_FR');
+
+ $fallback->addFallbackCatalogue($main);
+ $main->addFallbackCatalogue($fallback);
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\LogicException
+ */
+ public function testAddFallbackCatalogueWithFallbackCircularReference()
+ {
+ $fr = new MessageCatalogue('fr');
+ $en = new MessageCatalogue('en');
+ $es = new MessageCatalogue('es');
+
+ $fr->addFallbackCatalogue($en);
+ $es->addFallbackCatalogue($en);
+ $en->addFallbackCatalogue($fr);
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\LogicException
+ */
+ public function testAddCatalogueWhenLocaleIsNotTheSameAsTheCurrentOne()
+ {
+ $catalogue = new MessageCatalogue('en');
+ $catalogue->addCatalogue(new MessageCatalogue('fr', array()));
+ }
+
+ public function testGetAddResource()
+ {
+ $catalogue = new MessageCatalogue('en');
+ $r = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
+ $r->expects($this->any())->method('__toString')->will($this->returnValue('r'));
+ $catalogue->addResource($r);
+ $catalogue->addResource($r);
+ $r1 = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
+ $r1->expects($this->any())->method('__toString')->will($this->returnValue('r1'));
+ $catalogue->addResource($r1);
+
+ $this->assertEquals(array($r, $r1), $catalogue->getResources());
+ }
+
+ public function testMetadataDelete()
+ {
+ $catalogue = new MessageCatalogue('en');
+ $this->assertEquals(array(), $catalogue->getMetadata('', ''), 'Metadata is empty');
+ $catalogue->deleteMetadata('key', 'messages');
+ $catalogue->deleteMetadata('', 'messages');
+ $catalogue->deleteMetadata();
+ }
+
+ public function testMetadataSetGetDelete()
+ {
+ $catalogue = new MessageCatalogue('en');
+ $catalogue->setMetadata('key', 'value');
+ $this->assertEquals('value', $catalogue->getMetadata('key', 'messages'), "Metadata 'key' = 'value'");
+
+ $catalogue->setMetadata('key2', array());
+ $this->assertEquals(array(), $catalogue->getMetadata('key2', 'messages'), 'Metadata key2 is array');
+
+ $catalogue->deleteMetadata('key2', 'messages');
+ $this->assertNull($catalogue->getMetadata('key2', 'messages'), 'Metadata key2 should is deleted.');
+
+ $catalogue->deleteMetadata('key2', 'domain');
+ $this->assertNull($catalogue->getMetadata('key2', 'domain'), 'Metadata key2 should is deleted.');
+ }
+
+ public function testMetadataMerge()
+ {
+ $cat1 = new MessageCatalogue('en');
+ $cat1->setMetadata('a', 'b');
+ $this->assertEquals(array('messages' => array('a' => 'b')), $cat1->getMetadata('', ''), 'Cat1 contains messages metadata.');
+
+ $cat2 = new MessageCatalogue('en');
+ $cat2->setMetadata('b', 'c', 'domain');
+ $this->assertEquals(array('domain' => array('b' => 'c')), $cat2->getMetadata('', ''), 'Cat2 contains domain metadata.');
+
+ $cat1->addCatalogue($cat2);
+ $this->assertEquals(array('messages' => array('a' => 'b'), 'domain' => array('b' => 'c')), $cat1->getMetadata('', ''), 'Cat1 contains merged metadata.');
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/MessageSelectorTest.php b/_include/calendar/symfony/translation/Tests/MessageSelectorTest.php
new file mode 100644
index 0000000..42b7e0a
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/MessageSelectorTest.php
@@ -0,0 +1,137 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\MessageSelector;
+
+class MessageSelectorTest extends TestCase
+{
+ /**
+ * @dataProvider getChooseTests
+ */
+ public function testChoose($expected, $id, $number)
+ {
+ $selector = new MessageSelector();
+
+ $this->assertEquals($expected, $selector->choose($id, $number, 'en'));
+ }
+
+ public function testReturnMessageIfExactlyOneStandardRuleIsGiven()
+ {
+ $selector = new MessageSelector();
+
+ $this->assertEquals('There are two apples', $selector->choose('There are two apples', 2, 'en'));
+ }
+
+ /**
+ * @dataProvider getNonMatchingMessages
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException
+ */
+ public function testThrowExceptionIfMatchingMessageCannotBeFound($id, $number)
+ {
+ $selector = new MessageSelector();
+
+ $selector->choose($id, $number, 'en');
+ }
+
+ public function getNonMatchingMessages()
+ {
+ return array(
+ array('{0} There are no apples|{1} There is one apple', 2),
+ array('{1} There is one apple|]1,Inf] There are %count% apples', 0),
+ array('{1} There is one apple|]2,Inf] There are %count% apples', 2),
+ array('{0} There are no apples|There is one apple', 2),
+ );
+ }
+
+ public function getChooseTests()
+ {
+ return array(
+ array('There are no apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0),
+ array('There are no apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0),
+ array('There are no apples', '{0}There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0),
+
+ array('There is one apple', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 1),
+
+ array('There are %count% apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10),
+ array('There are %count% apples', '{0} There are no apples|{1} There is one apple|]1,Inf]There are %count% apples', 10),
+ array('There are %count% apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10),
+
+ array('There are %count% apples', 'There is one apple|There are %count% apples', 0),
+ array('There is one apple', 'There is one apple|There are %count% apples', 1),
+ array('There are %count% apples', 'There is one apple|There are %count% apples', 10),
+
+ array('There are %count% apples', 'one: There is one apple|more: There are %count% apples', 0),
+ array('There is one apple', 'one: There is one apple|more: There are %count% apples', 1),
+ array('There are %count% apples', 'one: There is one apple|more: There are %count% apples', 10),
+
+ array('There are no apples', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 0),
+ array('There is one apple', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 1),
+ array('There are %count% apples', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 10),
+
+ array('', '{0}|{1} There is one apple|]1,Inf] There are %count% apples', 0),
+ array('', '{0} There are no apples|{1}|]1,Inf] There are %count% apples', 1),
+
+ // Indexed only tests which are Gettext PoFile* compatible strings.
+ array('There are %count% apples', 'There is one apple|There are %count% apples', 0),
+ array('There is one apple', 'There is one apple|There are %count% apples', 1),
+ array('There are %count% apples', 'There is one apple|There are %count% apples', 2),
+
+ // Tests for float numbers
+ array('There is almost one apple', '{0} There are no apples|]0,1[ There is almost one apple|{1} There is one apple|[1,Inf] There is more than one apple', 0.7),
+ array('There is one apple', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 1),
+ array('There is more than one apple', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 1.7),
+ array('There are no apples', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0),
+ array('There are no apples', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0.0),
+ array('There are no apples', '{0.0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0),
+
+ // Test texts with new-lines
+ // with double-quotes and \n in id & double-quotes and actual newlines in text
+ array("This is a text with a\n new-line in it. Selector = 0.", '{0}This is a text with a
+ new-line in it. Selector = 0.|{1}This is a text with a
+ new-line in it. Selector = 1.|[1,Inf]This is a text with a
+ new-line in it. Selector > 1.', 0),
+ // with double-quotes and \n in id and single-quotes and actual newlines in text
+ array("This is a text with a\n new-line in it. Selector = 1.", '{0}This is a text with a
+ new-line in it. Selector = 0.|{1}This is a text with a
+ new-line in it. Selector = 1.|[1,Inf]This is a text with a
+ new-line in it. Selector > 1.', 1),
+ array("This is a text with a\n new-line in it. Selector > 1.", '{0}This is a text with a
+ new-line in it. Selector = 0.|{1}This is a text with a
+ new-line in it. Selector = 1.|[1,Inf]This is a text with a
+ new-line in it. Selector > 1.', 5),
+ // with double-quotes and id split accros lines
+ array('This is a text with a
+ new-line in it. Selector = 1.', '{0}This is a text with a
+ new-line in it. Selector = 0.|{1}This is a text with a
+ new-line in it. Selector = 1.|[1,Inf]This is a text with a
+ new-line in it. Selector > 1.', 1),
+ // with single-quotes and id split accros lines
+ array('This is a text with a
+ new-line in it. Selector > 1.', '{0}This is a text with a
+ new-line in it. Selector = 0.|{1}This is a text with a
+ new-line in it. Selector = 1.|[1,Inf]This is a text with a
+ new-line in it. Selector > 1.', 5),
+ // with single-quotes and \n in text
+ array('This is a text with a\nnew-line in it. Selector = 0.', '{0}This is a text with a\nnew-line in it. Selector = 0.|{1}This is a text with a\nnew-line in it. Selector = 1.|[1,Inf]This is a text with a\nnew-line in it. Selector > 1.', 0),
+ // with double-quotes and id split accros lines
+ array("This is a text with a\nnew-line in it. Selector = 1.", "{0}This is a text with a\nnew-line in it. Selector = 0.|{1}This is a text with a\nnew-line in it. Selector = 1.|[1,Inf]This is a text with a\nnew-line in it. Selector > 1.", 1),
+ // esacape pipe
+ array('This is a text with | in it. Selector = 0.', '{0}This is a text with || in it. Selector = 0.|{1}This is a text with || in it. Selector = 1.', 0),
+ // Empty plural set (2 plural forms) from a .PO file
+ array('', '|', 1),
+ // Empty plural set (3 plural forms) from a .PO file
+ array('', '||', 1),
+ );
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/PluralizationRulesTest.php b/_include/calendar/symfony/translation/Tests/PluralizationRulesTest.php
new file mode 100644
index 0000000..5eb6c01
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/PluralizationRulesTest.php
@@ -0,0 +1,122 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\PluralizationRules;
+
+/**
+ * Test should cover all languages mentioned on http://translate.sourceforge.net/wiki/l10n/pluralforms
+ * and Plural forms mentioned on http://www.gnu.org/software/gettext/manual/gettext.html#Plural-forms.
+ *
+ * See also https://developer.mozilla.org/en/Localization_and_Plurals which mentions 15 rules having a maximum of 6 forms.
+ * The mozilla code is also interesting to check for.
+ *
+ * As mentioned by chx http://drupal.org/node/1273968 we can cover all by testing number from 0 to 199
+ *
+ * The goal to cover all languages is to far fetched so this test case is smaller.
+ *
+ * @author Clemens Tolboom clemens@build2be.nl
+ */
+class PluralizationRulesTest extends TestCase
+{
+ /**
+ * We test failed langcode here.
+ *
+ * TODO: The languages mentioned in the data provide need to get fixed somehow within PluralizationRules.
+ *
+ * @dataProvider failingLangcodes
+ */
+ public function testFailedLangcodes($nplural, $langCodes)
+ {
+ $matrix = $this->generateTestData($langCodes);
+ $this->validateMatrix($nplural, $matrix, false);
+ }
+
+ /**
+ * @dataProvider successLangcodes
+ */
+ public function testLangcodes($nplural, $langCodes)
+ {
+ $matrix = $this->generateTestData($langCodes);
+ $this->validateMatrix($nplural, $matrix);
+ }
+
+ /**
+ * This array should contain all currently known langcodes.
+ *
+ * As it is impossible to have this ever complete we should try as hard as possible to have it almost complete.
+ *
+ * @return array
+ */
+ public function successLangcodes()
+ {
+ return array(
+ array('1', array('ay', 'bo', 'cgg', 'dz', 'id', 'ja', 'jbo', 'ka', 'kk', 'km', 'ko', 'ky')),
+ array('2', array('nl', 'fr', 'en', 'de', 'de_GE', 'hy', 'hy_AM')),
+ array('3', array('be', 'bs', 'cs', 'hr')),
+ array('4', array('cy', 'mt', 'sl')),
+ array('6', array('ar')),
+ );
+ }
+
+ /**
+ * This array should be at least empty within the near future.
+ *
+ * This both depends on a complete list trying to add above as understanding
+ * the plural rules of the current failing languages.
+ *
+ * @return array with nplural together with langcodes
+ */
+ public function failingLangcodes()
+ {
+ return array(
+ array('1', array('fa')),
+ array('2', array('jbo')),
+ array('3', array('cbs')),
+ array('4', array('gd', 'kw')),
+ array('5', array('ga')),
+ );
+ }
+
+ /**
+ * We validate only on the plural coverage. Thus the real rules is not tested.
+ *
+ * @param string $nplural Plural expected
+ * @param array $matrix Containing langcodes and their plural index values
+ * @param bool $expectSuccess
+ */
+ protected function validateMatrix($nplural, $matrix, $expectSuccess = true)
+ {
+ foreach ($matrix as $langCode => $data) {
+ $indexes = array_flip($data);
+ if ($expectSuccess) {
+ $this->assertEquals($nplural, \count($indexes), "Langcode '$langCode' has '$nplural' plural forms.");
+ } else {
+ $this->assertNotEquals((int) $nplural, \count($indexes), "Langcode '$langCode' has '$nplural' plural forms.");
+ }
+ }
+ }
+
+ protected function generateTestData($langCodes)
+ {
+ $matrix = array();
+ foreach ($langCodes as $langCode) {
+ for ($count = 0; $count < 200; ++$count) {
+ $plural = PluralizationRules::get($count, $langCode);
+ $matrix[$langCode][$count] = $plural;
+ }
+ }
+
+ return $matrix;
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/TranslatorCacheTest.php b/_include/calendar/symfony/translation/Tests/TranslatorCacheTest.php
new file mode 100644
index 0000000..3e71ae7
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/TranslatorCacheTest.php
@@ -0,0 +1,311 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Config\Resource\SelfCheckingResourceInterface;
+use Symfony\Component\Translation\Loader\ArrayLoader;
+use Symfony\Component\Translation\Loader\LoaderInterface;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Translator;
+
+class TranslatorCacheTest extends TestCase
+{
+ protected $tmpDir;
+
+ protected function setUp()
+ {
+ $this->tmpDir = sys_get_temp_dir().'/sf2_translation';
+ $this->deleteTmpDir();
+ }
+
+ protected function tearDown()
+ {
+ $this->deleteTmpDir();
+ }
+
+ protected function deleteTmpDir()
+ {
+ if (!file_exists($dir = $this->tmpDir)) {
+ return;
+ }
+
+ $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->tmpDir), \RecursiveIteratorIterator::CHILD_FIRST);
+ foreach ($iterator as $path) {
+ if (preg_match('#[/\\\\]\.\.?$#', $path->__toString())) {
+ continue;
+ }
+ if ($path->isDir()) {
+ rmdir($path->__toString());
+ } else {
+ unlink($path->__toString());
+ }
+ }
+ rmdir($this->tmpDir);
+ }
+
+ /**
+ * @dataProvider runForDebugAndProduction
+ */
+ public function testThatACacheIsUsed($debug)
+ {
+ $locale = 'any_locale';
+ $format = 'some_format';
+ $msgid = 'test';
+
+ // Prime the cache
+ $translator = new Translator($locale, null, $this->tmpDir, $debug);
+ $translator->addLoader($format, new ArrayLoader());
+ $translator->addResource($format, array($msgid => 'OK'), $locale);
+ $translator->trans($msgid);
+
+ // Try again and see we get a valid result whilst no loader can be used
+ $translator = new Translator($locale, null, $this->tmpDir, $debug);
+ $translator->addLoader($format, $this->createFailingLoader());
+ $translator->addResource($format, array($msgid => 'OK'), $locale);
+ $this->assertEquals('OK', $translator->trans($msgid), '-> caching does not work in '.($debug ? 'debug' : 'production'));
+ }
+
+ public function testCatalogueIsReloadedWhenResourcesAreNoLongerFresh()
+ {
+ /*
+ * The testThatACacheIsUsed() test showed that we don't need the loader as long as the cache
+ * is fresh.
+ *
+ * Now we add a Resource that is never fresh and make sure that the
+ * cache is discarded (the loader is called twice).
+ *
+ * We need to run this for debug=true only because in production the cache
+ * will never be revalidated.
+ */
+
+ $locale = 'any_locale';
+ $format = 'some_format';
+ $msgid = 'test';
+
+ $catalogue = new MessageCatalogue($locale, array());
+ $catalogue->addResource(new StaleResource()); // better use a helper class than a mock, because it gets serialized in the cache and re-loaded
+
+ /** @var LoaderInterface|\PHPUnit_Framework_MockObject_MockObject $loader */
+ $loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock();
+ $loader
+ ->expects($this->exactly(2))
+ ->method('load')
+ ->will($this->returnValue($catalogue))
+ ;
+
+ // 1st pass
+ $translator = new Translator($locale, null, $this->tmpDir, true);
+ $translator->addLoader($format, $loader);
+ $translator->addResource($format, null, $locale);
+ $translator->trans($msgid);
+
+ // 2nd pass
+ $translator = new Translator($locale, null, $this->tmpDir, true);
+ $translator->addLoader($format, $loader);
+ $translator->addResource($format, null, $locale);
+ $translator->trans($msgid);
+ }
+
+ /**
+ * @dataProvider runForDebugAndProduction
+ */
+ public function testDifferentTranslatorsForSameLocaleDoNotOverwriteEachOthersCache($debug)
+ {
+ /*
+ * Similar to the previous test. After we used the second translator, make
+ * sure there's still a useable cache for the first one.
+ */
+
+ $locale = 'any_locale';
+ $format = 'some_format';
+ $msgid = 'test';
+
+ // Create a Translator and prime its cache
+ $translator = new Translator($locale, null, $this->tmpDir, $debug);
+ $translator->addLoader($format, new ArrayLoader());
+ $translator->addResource($format, array($msgid => 'OK'), $locale);
+ $translator->trans($msgid);
+
+ // Create another Translator with a different catalogue for the same locale
+ $translator = new Translator($locale, null, $this->tmpDir, $debug);
+ $translator->addLoader($format, new ArrayLoader());
+ $translator->addResource($format, array($msgid => 'FAIL'), $locale);
+ $translator->trans($msgid);
+
+ // Now the first translator must still have a useable cache.
+ $translator = new Translator($locale, null, $this->tmpDir, $debug);
+ $translator->addLoader($format, $this->createFailingLoader());
+ $translator->addResource($format, array($msgid => 'OK'), $locale);
+ $this->assertEquals('OK', $translator->trans($msgid), '-> the cache was overwritten by another translator instance in '.($debug ? 'debug' : 'production'));
+ }
+
+ public function testGeneratedCacheFilesAreOnlyBelongRequestedLocales()
+ {
+ $translator = new Translator('a', null, $this->tmpDir);
+ $translator->setFallbackLocales(array('b'));
+ $translator->trans('bar');
+
+ $cachedFiles = glob($this->tmpDir.'/*.php');
+
+ $this->assertCount(1, $cachedFiles);
+ }
+
+ public function testDifferentCacheFilesAreUsedForDifferentSetsOfFallbackLocales()
+ {
+ /*
+ * Because the cache file contains a catalogue including all of its fallback
+ * catalogues, we must take the set of fallback locales into consideration when
+ * loading a catalogue from the cache.
+ */
+ $translator = new Translator('a', null, $this->tmpDir);
+ $translator->setFallbackLocales(array('b'));
+
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('foo' => 'foo (a)'), 'a');
+ $translator->addResource('array', array('bar' => 'bar (b)'), 'b');
+
+ $this->assertEquals('bar (b)', $translator->trans('bar'));
+
+ // Remove fallback locale
+ $translator->setFallbackLocales(array());
+ $this->assertEquals('bar', $translator->trans('bar'));
+
+ // Use a fresh translator with no fallback locales, result should be the same
+ $translator = new Translator('a', null, $this->tmpDir);
+
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('foo' => 'foo (a)'), 'a');
+ $translator->addResource('array', array('bar' => 'bar (b)'), 'b');
+
+ $this->assertEquals('bar', $translator->trans('bar'));
+ }
+
+ public function testPrimaryAndFallbackCataloguesContainTheSameMessagesRegardlessOfCaching()
+ {
+ /*
+ * As a safeguard against potential BC breaks, make sure that primary and fallback
+ * catalogues (reachable via getFallbackCatalogue()) always contain the full set of
+ * messages provided by the loader. This must also be the case when these catalogues
+ * are (internally) read from a cache.
+ *
+ * Optimizations inside the translator must not change this behaviour.
+ */
+
+ /*
+ * Create a translator that loads two catalogues for two different locales.
+ * The catalogues contain distinct sets of messages.
+ */
+ $translator = new Translator('a', null, $this->tmpDir);
+ $translator->setFallbackLocales(array('b'));
+
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('foo' => 'foo (a)'), 'a');
+ $translator->addResource('array', array('foo' => 'foo (b)'), 'b');
+ $translator->addResource('array', array('bar' => 'bar (b)'), 'b');
+
+ $catalogue = $translator->getCatalogue('a');
+ $this->assertFalse($catalogue->defines('bar')); // Sure, the "a" catalogue does not contain that message.
+
+ $fallback = $catalogue->getFallbackCatalogue();
+ $this->assertTrue($fallback->defines('foo')); // "foo" is present in "a" and "b"
+
+ /*
+ * Now, repeat the same test.
+ * Behind the scenes, the cache is used. But that should not matter, right?
+ */
+ $translator = new Translator('a', null, $this->tmpDir);
+ $translator->setFallbackLocales(array('b'));
+
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('foo' => 'foo (a)'), 'a');
+ $translator->addResource('array', array('foo' => 'foo (b)'), 'b');
+ $translator->addResource('array', array('bar' => 'bar (b)'), 'b');
+
+ $catalogue = $translator->getCatalogue('a');
+ $this->assertFalse($catalogue->defines('bar'));
+
+ $fallback = $catalogue->getFallbackCatalogue();
+ $this->assertTrue($fallback->defines('foo'));
+ }
+
+ public function testRefreshCacheWhenResourcesAreNoLongerFresh()
+ {
+ $resource = $this->getMockBuilder('Symfony\Component\Config\Resource\SelfCheckingResourceInterface')->getMock();
+ $loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock();
+ $resource->method('isFresh')->will($this->returnValue(false));
+ $loader
+ ->expects($this->exactly(2))
+ ->method('load')
+ ->will($this->returnValue($this->getCatalogue('fr', array(), array($resource))));
+
+ // prime the cache
+ $translator = new Translator('fr', null, $this->tmpDir, true);
+ $translator->addLoader('loader', $loader);
+ $translator->addResource('loader', 'foo', 'fr');
+ $translator->trans('foo');
+
+ // prime the cache second time
+ $translator = new Translator('fr', null, $this->tmpDir, true);
+ $translator->addLoader('loader', $loader);
+ $translator->addResource('loader', 'foo', 'fr');
+ $translator->trans('foo');
+ }
+
+ protected function getCatalogue($locale, $messages, $resources = array())
+ {
+ $catalogue = new MessageCatalogue($locale);
+ foreach ($messages as $key => $translation) {
+ $catalogue->set($key, $translation);
+ }
+ foreach ($resources as $resource) {
+ $catalogue->addResource($resource);
+ }
+
+ return $catalogue;
+ }
+
+ public function runForDebugAndProduction()
+ {
+ return array(array(true), array(false));
+ }
+
+ /**
+ * @return LoaderInterface
+ */
+ private function createFailingLoader()
+ {
+ $loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock();
+ $loader
+ ->expects($this->never())
+ ->method('load');
+
+ return $loader;
+ }
+}
+
+class StaleResource implements SelfCheckingResourceInterface
+{
+ public function isFresh($timestamp)
+ {
+ return false;
+ }
+
+ public function getResource()
+ {
+ }
+
+ public function __toString()
+ {
+ return '';
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/TranslatorTest.php b/_include/calendar/symfony/translation/Tests/TranslatorTest.php
new file mode 100644
index 0000000..3e54839
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/TranslatorTest.php
@@ -0,0 +1,549 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Loader\ArrayLoader;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Translator;
+
+class TranslatorTest extends TestCase
+{
+ /**
+ * @dataProvider getInvalidLocalesTests
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException
+ */
+ public function testConstructorInvalidLocale($locale)
+ {
+ new Translator($locale);
+ }
+
+ /**
+ * @dataProvider getValidLocalesTests
+ */
+ public function testConstructorValidLocale($locale)
+ {
+ $translator = new Translator($locale);
+
+ $this->assertEquals($locale, $translator->getLocale());
+ }
+
+ public function testConstructorWithoutLocale()
+ {
+ $translator = new Translator(null);
+
+ $this->assertNull($translator->getLocale());
+ }
+
+ public function testSetGetLocale()
+ {
+ $translator = new Translator('en');
+
+ $this->assertEquals('en', $translator->getLocale());
+
+ $translator->setLocale('fr');
+ $this->assertEquals('fr', $translator->getLocale());
+ }
+
+ /**
+ * @dataProvider getInvalidLocalesTests
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException
+ */
+ public function testSetInvalidLocale($locale)
+ {
+ $translator = new Translator('fr');
+ $translator->setLocale($locale);
+ }
+
+ /**
+ * @dataProvider getValidLocalesTests
+ */
+ public function testSetValidLocale($locale)
+ {
+ $translator = new Translator($locale);
+ $translator->setLocale($locale);
+
+ $this->assertEquals($locale, $translator->getLocale());
+ }
+
+ public function testGetCatalogue()
+ {
+ $translator = new Translator('en');
+
+ $this->assertEquals(new MessageCatalogue('en'), $translator->getCatalogue());
+
+ $translator->setLocale('fr');
+ $this->assertEquals(new MessageCatalogue('fr'), $translator->getCatalogue('fr'));
+ }
+
+ public function testGetCatalogueReturnsConsolidatedCatalogue()
+ {
+ /*
+ * This will be useful once we refactor so that different domains will be loaded lazily (on-demand).
+ * In that case, getCatalogue() will probably have to load all missing domains in order to return
+ * one complete catalogue.
+ */
+
+ $locale = 'whatever';
+ $translator = new Translator($locale);
+ $translator->addLoader('loader-a', new ArrayLoader());
+ $translator->addLoader('loader-b', new ArrayLoader());
+ $translator->addResource('loader-a', array('foo' => 'foofoo'), $locale, 'domain-a');
+ $translator->addResource('loader-b', array('bar' => 'foobar'), $locale, 'domain-b');
+
+ /*
+ * Test that we get a single catalogue comprising messages
+ * from different loaders and different domains
+ */
+ $catalogue = $translator->getCatalogue($locale);
+ $this->assertTrue($catalogue->defines('foo', 'domain-a'));
+ $this->assertTrue($catalogue->defines('bar', 'domain-b'));
+ }
+
+ public function testSetFallbackLocales()
+ {
+ $translator = new Translator('en');
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('foo' => 'foofoo'), 'en');
+ $translator->addResource('array', array('bar' => 'foobar'), 'fr');
+
+ // force catalogue loading
+ $translator->trans('bar');
+
+ $translator->setFallbackLocales(array('fr'));
+ $this->assertEquals('foobar', $translator->trans('bar'));
+ }
+
+ public function testSetFallbackLocalesMultiple()
+ {
+ $translator = new Translator('en');
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('foo' => 'foo (en)'), 'en');
+ $translator->addResource('array', array('bar' => 'bar (fr)'), 'fr');
+
+ // force catalogue loading
+ $translator->trans('bar');
+
+ $translator->setFallbackLocales(array('fr_FR', 'fr'));
+ $this->assertEquals('bar (fr)', $translator->trans('bar'));
+ }
+
+ /**
+ * @dataProvider getInvalidLocalesTests
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException
+ */
+ public function testSetFallbackInvalidLocales($locale)
+ {
+ $translator = new Translator('fr');
+ $translator->setFallbackLocales(array('fr', $locale));
+ }
+
+ /**
+ * @dataProvider getValidLocalesTests
+ */
+ public function testSetFallbackValidLocales($locale)
+ {
+ $translator = new Translator($locale);
+ $translator->setFallbackLocales(array('fr', $locale));
+ // no assertion. this method just asserts that no exception is thrown
+ $this->addToAssertionCount(1);
+ }
+
+ public function testTransWithFallbackLocale()
+ {
+ $translator = new Translator('fr_FR');
+ $translator->setFallbackLocales(array('en'));
+
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('bar' => 'foobar'), 'en');
+
+ $this->assertEquals('foobar', $translator->trans('bar'));
+ }
+
+ /**
+ * @dataProvider getInvalidLocalesTests
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException
+ */
+ public function testAddResourceInvalidLocales($locale)
+ {
+ $translator = new Translator('fr');
+ $translator->addResource('array', array('foo' => 'foofoo'), $locale);
+ }
+
+ /**
+ * @dataProvider getValidLocalesTests
+ */
+ public function testAddResourceValidLocales($locale)
+ {
+ $translator = new Translator('fr');
+ $translator->addResource('array', array('foo' => 'foofoo'), $locale);
+ // no assertion. this method just asserts that no exception is thrown
+ $this->addToAssertionCount(1);
+ }
+
+ public function testAddResourceAfterTrans()
+ {
+ $translator = new Translator('fr');
+ $translator->addLoader('array', new ArrayLoader());
+
+ $translator->setFallbackLocales(array('en'));
+
+ $translator->addResource('array', array('foo' => 'foofoo'), 'en');
+ $this->assertEquals('foofoo', $translator->trans('foo'));
+
+ $translator->addResource('array', array('bar' => 'foobar'), 'en');
+ $this->assertEquals('foobar', $translator->trans('bar'));
+ }
+
+ /**
+ * @dataProvider getTransFileTests
+ * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+ */
+ public function testTransWithoutFallbackLocaleFile($format, $loader)
+ {
+ $loaderClass = 'Symfony\\Component\\Translation\\Loader\\'.$loader;
+ $translator = new Translator('en');
+ $translator->addLoader($format, new $loaderClass());
+ $translator->addResource($format, __DIR__.'/fixtures/non-existing', 'en');
+ $translator->addResource($format, __DIR__.'/fixtures/resources.'.$format, 'en');
+
+ // force catalogue loading
+ $translator->trans('foo');
+ }
+
+ /**
+ * @dataProvider getTransFileTests
+ */
+ public function testTransWithFallbackLocaleFile($format, $loader)
+ {
+ $loaderClass = 'Symfony\\Component\\Translation\\Loader\\'.$loader;
+ $translator = new Translator('en_GB');
+ $translator->addLoader($format, new $loaderClass());
+ $translator->addResource($format, __DIR__.'/fixtures/non-existing', 'en_GB');
+ $translator->addResource($format, __DIR__.'/fixtures/resources.'.$format, 'en', 'resources');
+
+ $this->assertEquals('bar', $translator->trans('foo', array(), 'resources'));
+ }
+
+ public function testTransWithFallbackLocaleBis()
+ {
+ $translator = new Translator('en_US');
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('foo' => 'foofoo'), 'en_US');
+ $translator->addResource('array', array('bar' => 'foobar'), 'en');
+ $this->assertEquals('foobar', $translator->trans('bar'));
+ }
+
+ public function testTransWithFallbackLocaleTer()
+ {
+ $translator = new Translator('fr_FR');
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('foo' => 'foo (en_US)'), 'en_US');
+ $translator->addResource('array', array('bar' => 'bar (en)'), 'en');
+
+ $translator->setFallbackLocales(array('en_US', 'en'));
+
+ $this->assertEquals('foo (en_US)', $translator->trans('foo'));
+ $this->assertEquals('bar (en)', $translator->trans('bar'));
+ }
+
+ public function testTransNonExistentWithFallback()
+ {
+ $translator = new Translator('fr');
+ $translator->setFallbackLocales(array('en'));
+ $translator->addLoader('array', new ArrayLoader());
+ $this->assertEquals('non-existent', $translator->trans('non-existent'));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\RuntimeException
+ */
+ public function testWhenAResourceHasNoRegisteredLoader()
+ {
+ $translator = new Translator('en');
+ $translator->addResource('array', array('foo' => 'foofoo'), 'en');
+
+ $translator->trans('foo');
+ }
+
+ public function testNestedFallbackCatalogueWhenUsingMultipleLocales()
+ {
+ $translator = new Translator('fr');
+ $translator->setFallbackLocales(array('ru', 'en'));
+
+ $translator->getCatalogue('fr');
+
+ $this->assertNotNull($translator->getCatalogue('ru')->getFallbackCatalogue());
+ }
+
+ public function testFallbackCatalogueResources()
+ {
+ $translator = new Translator('en_GB');
+ $translator->addLoader('yml', new \Symfony\Component\Translation\Loader\YamlFileLoader());
+ $translator->addResource('yml', __DIR__.'/fixtures/empty.yml', 'en_GB');
+ $translator->addResource('yml', __DIR__.'/fixtures/resources.yml', 'en');
+
+ // force catalogue loading
+ $this->assertEquals('bar', $translator->trans('foo', array()));
+
+ $resources = $translator->getCatalogue('en')->getResources();
+ $this->assertCount(1, $resources);
+ $this->assertContains(__DIR__.\DIRECTORY_SEPARATOR.'fixtures'.\DIRECTORY_SEPARATOR.'resources.yml', $resources);
+
+ $resources = $translator->getCatalogue('en_GB')->getResources();
+ $this->assertCount(2, $resources);
+ $this->assertContains(__DIR__.\DIRECTORY_SEPARATOR.'fixtures'.\DIRECTORY_SEPARATOR.'empty.yml', $resources);
+ $this->assertContains(__DIR__.\DIRECTORY_SEPARATOR.'fixtures'.\DIRECTORY_SEPARATOR.'resources.yml', $resources);
+ }
+
+ /**
+ * @dataProvider getTransTests
+ */
+ public function testTrans($expected, $id, $translation, $parameters, $locale, $domain)
+ {
+ $translator = new Translator('en');
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array((string) $id => $translation), $locale, $domain);
+
+ $this->assertEquals($expected, $translator->trans($id, $parameters, $domain, $locale));
+ }
+
+ /**
+ * @dataProvider getInvalidLocalesTests
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException
+ */
+ public function testTransInvalidLocale($locale)
+ {
+ $translator = new Translator('en');
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('foo' => 'foofoo'), 'en');
+
+ $translator->trans('foo', array(), '', $locale);
+ }
+
+ /**
+ * @dataProvider getValidLocalesTests
+ */
+ public function testTransValidLocale($locale)
+ {
+ $translator = new Translator($locale);
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('test' => 'OK'), $locale);
+
+ $this->assertEquals('OK', $translator->trans('test'));
+ $this->assertEquals('OK', $translator->trans('test', array(), null, $locale));
+ }
+
+ /**
+ * @dataProvider getFlattenedTransTests
+ */
+ public function testFlattenedTrans($expected, $messages, $id)
+ {
+ $translator = new Translator('en');
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', $messages, 'fr', '');
+
+ $this->assertEquals($expected, $translator->trans($id, array(), '', 'fr'));
+ }
+
+ /**
+ * @dataProvider getTransChoiceTests
+ */
+ public function testTransChoice($expected, $id, $translation, $number, $parameters, $locale, $domain)
+ {
+ $translator = new Translator('en');
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array((string) $id => $translation), $locale, $domain);
+
+ $this->assertEquals($expected, $translator->transChoice($id, $number, $parameters, $domain, $locale));
+ }
+
+ /**
+ * @dataProvider getInvalidLocalesTests
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException
+ */
+ public function testTransChoiceInvalidLocale($locale)
+ {
+ $translator = new Translator('en');
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('foo' => 'foofoo'), 'en');
+
+ $translator->transChoice('foo', 1, array(), '', $locale);
+ }
+
+ /**
+ * @dataProvider getValidLocalesTests
+ */
+ public function testTransChoiceValidLocale($locale)
+ {
+ $translator = new Translator('en');
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('foo' => 'foofoo'), 'en');
+
+ $translator->transChoice('foo', 1, array(), '', $locale);
+ // no assertion. this method just asserts that no exception is thrown
+ $this->addToAssertionCount(1);
+ }
+
+ public function getTransFileTests()
+ {
+ return array(
+ array('csv', 'CsvFileLoader'),
+ array('ini', 'IniFileLoader'),
+ array('mo', 'MoFileLoader'),
+ array('po', 'PoFileLoader'),
+ array('php', 'PhpFileLoader'),
+ array('ts', 'QtFileLoader'),
+ array('xlf', 'XliffFileLoader'),
+ array('yml', 'YamlFileLoader'),
+ array('json', 'JsonFileLoader'),
+ );
+ }
+
+ public function getTransTests()
+ {
+ return array(
+ array('Symfony est super !', 'Symfony is great!', 'Symfony est super !', array(), 'fr', ''),
+ array('Symfony est awesome !', 'Symfony is %what%!', 'Symfony est %what% !', array('%what%' => 'awesome'), 'fr', ''),
+ array('Symfony est super !', new StringClass('Symfony is great!'), 'Symfony est super !', array(), 'fr', ''),
+ );
+ }
+
+ public function getFlattenedTransTests()
+ {
+ $messages = array(
+ 'symfony' => array(
+ 'is' => array(
+ 'great' => 'Symfony est super!',
+ ),
+ ),
+ 'foo' => array(
+ 'bar' => array(
+ 'baz' => 'Foo Bar Baz',
+ ),
+ 'baz' => 'Foo Baz',
+ ),
+ );
+
+ return array(
+ array('Symfony est super!', $messages, 'symfony.is.great'),
+ array('Foo Bar Baz', $messages, 'foo.bar.baz'),
+ array('Foo Baz', $messages, 'foo.baz'),
+ );
+ }
+
+ public function getTransChoiceTests()
+ {
+ return array(
+ array('Il y a 0 pomme', '{0} There are no appless|{1} There is one apple|]1,Inf] There is %count% apples', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 0, array(), 'fr', ''),
+ array('Il y a 1 pomme', '{0} There are no appless|{1} There is one apple|]1,Inf] There is %count% apples', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 1, array(), 'fr', ''),
+ array('Il y a 10 pommes', '{0} There are no appless|{1} There is one apple|]1,Inf] There is %count% apples', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 10, array(), 'fr', ''),
+
+ array('Il y a 0 pomme', 'There is one apple|There is %count% apples', 'Il y a %count% pomme|Il y a %count% pommes', 0, array(), 'fr', ''),
+ array('Il y a 1 pomme', 'There is one apple|There is %count% apples', 'Il y a %count% pomme|Il y a %count% pommes', 1, array(), 'fr', ''),
+ array('Il y a 10 pommes', 'There is one apple|There is %count% apples', 'Il y a %count% pomme|Il y a %count% pommes', 10, array(), 'fr', ''),
+
+ array('Il y a 0 pomme', 'one: There is one apple|more: There is %count% apples', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 0, array(), 'fr', ''),
+ array('Il y a 1 pomme', 'one: There is one apple|more: There is %count% apples', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 1, array(), 'fr', ''),
+ array('Il y a 10 pommes', 'one: There is one apple|more: There is %count% apples', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 10, array(), 'fr', ''),
+
+ array('Il n\'y a aucune pomme', '{0} There are no apples|one: There is one apple|more: There is %count% apples', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 0, array(), 'fr', ''),
+ array('Il y a 1 pomme', '{0} There are no apples|one: There is one apple|more: There is %count% apples', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 1, array(), 'fr', ''),
+ array('Il y a 10 pommes', '{0} There are no apples|one: There is one apple|more: There is %count% apples', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 10, array(), 'fr', ''),
+
+ array('Il y a 0 pomme', new StringClass('{0} There are no appless|{1} There is one apple|]1,Inf] There is %count% apples'), '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 0, array(), 'fr', ''),
+
+ // Override %count% with a custom value
+ array('Il y a quelques pommes', 'one: There is one apple|more: There are %count% apples', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 2, array('%count%' => 'quelques'), 'fr', ''),
+ );
+ }
+
+ public function getInvalidLocalesTests()
+ {
+ return array(
+ array('fr FR'),
+ array('français'),
+ array('fr+en'),
+ array('utf#8'),
+ array('fr&en'),
+ array('fr~FR'),
+ array(' fr'),
+ array('fr '),
+ array('fr*'),
+ array('fr/FR'),
+ array('fr\\FR'),
+ );
+ }
+
+ public function getValidLocalesTests()
+ {
+ return array(
+ array(''),
+ array(null),
+ array('fr'),
+ array('francais'),
+ array('FR'),
+ array('frFR'),
+ array('fr-FR'),
+ array('fr_FR'),
+ array('fr.FR'),
+ array('fr-FR.UTF8'),
+ array('sr@latin'),
+ );
+ }
+
+ public function testTransChoiceFallback()
+ {
+ $translator = new Translator('ru');
+ $translator->setFallbackLocales(array('en'));
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('some_message2' => 'one thing|%count% things'), 'en');
+
+ $this->assertEquals('10 things', $translator->transChoice('some_message2', 10, array('%count%' => 10)));
+ }
+
+ public function testTransChoiceFallbackBis()
+ {
+ $translator = new Translator('ru');
+ $translator->setFallbackLocales(array('en_US', 'en'));
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('some_message2' => 'one thing|%count% things'), 'en_US');
+
+ $this->assertEquals('10 things', $translator->transChoice('some_message2', 10, array('%count%' => 10)));
+ }
+
+ public function testTransChoiceFallbackWithNoTranslation()
+ {
+ $translator = new Translator('ru');
+ $translator->setFallbackLocales(array('en'));
+ $translator->addLoader('array', new ArrayLoader());
+
+ // consistent behavior with Translator::trans(), which returns the string
+ // unchanged if it can't be found
+ $this->assertEquals('some_message2', $translator->transChoice('some_message2', 10, array('%count%' => 10)));
+ }
+}
+
+class StringClass
+{
+ protected $str;
+
+ public function __construct($str)
+ {
+ $this->str = $str;
+ }
+
+ public function __toString()
+ {
+ return $this->str;
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/Util/ArrayConverterTest.php b/_include/calendar/symfony/translation/Tests/Util/ArrayConverterTest.php
new file mode 100644
index 0000000..dbb5424
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/Util/ArrayConverterTest.php
@@ -0,0 +1,74 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Util;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Util\ArrayConverter;
+
+class ArrayConverterTest extends TestCase
+{
+ /**
+ * @dataProvider messagesData
+ */
+ public function testDump($input, $expectedOutput)
+ {
+ $this->assertEquals($expectedOutput, ArrayConverter::expandToTree($input));
+ }
+
+ public function messagesData()
+ {
+ return array(
+ array(
+ // input
+ array(
+ 'foo1' => 'bar',
+ 'foo.bar' => 'value',
+ ),
+ // expected output
+ array(
+ 'foo1' => 'bar',
+ 'foo' => array('bar' => 'value'),
+ ),
+ ),
+ array(
+ // input
+ array(
+ 'foo.bar' => 'value1',
+ 'foo.bar.test' => 'value2',
+ ),
+ // expected output
+ array(
+ 'foo' => array(
+ 'bar' => 'value1',
+ 'bar.test' => 'value2',
+ ),
+ ),
+ ),
+ array(
+ // input
+ array(
+ 'foo.level2.level3.level4' => 'value1',
+ 'foo.level2' => 'value2',
+ 'foo.bar' => 'value3',
+ ),
+ // expected output
+ array(
+ 'foo' => array(
+ 'level2' => 'value2',
+ 'level2.level3.level4' => 'value1',
+ 'bar' => 'value3',
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/Writer/TranslationWriterTest.php b/_include/calendar/symfony/translation/Tests/Writer/TranslationWriterTest.php
new file mode 100644
index 0000000..ab66af1
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/Writer/TranslationWriterTest.php
@@ -0,0 +1,69 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Writer;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Dumper\DumperInterface;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Writer\TranslationWriter;
+
+class TranslationWriterTest extends TestCase
+{
+ public function testWrite()
+ {
+ $dumper = $this->getMockBuilder('Symfony\Component\Translation\Dumper\DumperInterface')->getMock();
+ $dumper
+ ->expects($this->once())
+ ->method('dump');
+
+ $writer = new TranslationWriter();
+ $writer->addDumper('test', $dumper);
+ $writer->write(new MessageCatalogue('en'), 'test');
+ }
+
+ /**
+ * @group legacy
+ */
+ public function testDisableBackup()
+ {
+ $nonBackupDumper = new NonBackupDumper();
+ $backupDumper = new BackupDumper();
+
+ $writer = new TranslationWriter();
+ $writer->addDumper('non_backup', $nonBackupDumper);
+ $writer->addDumper('backup', $backupDumper);
+ $writer->disableBackup();
+
+ $this->assertFalse($backupDumper->backup, 'backup can be disabled if setBackup() method does exist');
+ }
+}
+
+class NonBackupDumper implements DumperInterface
+{
+ public function dump(MessageCatalogue $messages, $options = array())
+ {
+ }
+}
+
+class BackupDumper implements DumperInterface
+{
+ public $backup = true;
+
+ public function dump(MessageCatalogue $messages, $options = array())
+ {
+ }
+
+ public function setBackup($backup)
+ {
+ $this->backup = $backup;
+ }
+}
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/empty-translation.mo b/_include/calendar/symfony/translation/Tests/fixtures/empty-translation.mo
new file mode 100644
index 0000000000000000000000000000000000000000..ed01000c9b1b3fdf40fc2369a7799e72e41a1edf
GIT binary patch
literal 49
ncmca7#4?ou2pEA_28dNa93apEVrC%L1#y7D5JaTq=Q98RUv2}O
literal 0
HcmV?d00001
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/empty-translation.po b/_include/calendar/symfony/translation/Tests/fixtures/empty-translation.po
new file mode 100644
index 0000000..ff6f22a
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/empty-translation.po
@@ -0,0 +1,3 @@
+msgid "foo"
+msgstr ""
+
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/empty.csv b/_include/calendar/symfony/translation/Tests/fixtures/empty.csv
new file mode 100644
index 0000000..e69de29
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/empty.ini b/_include/calendar/symfony/translation/Tests/fixtures/empty.ini
new file mode 100644
index 0000000..e69de29
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/empty.json b/_include/calendar/symfony/translation/Tests/fixtures/empty.json
new file mode 100644
index 0000000..e69de29
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/empty.mo b/_include/calendar/symfony/translation/Tests/fixtures/empty.mo
new file mode 100644
index 0000000..e69de29
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/empty.po b/_include/calendar/symfony/translation/Tests/fixtures/empty.po
new file mode 100644
index 0000000..e69de29
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/empty.xlf b/_include/calendar/symfony/translation/Tests/fixtures/empty.xlf
new file mode 100644
index 0000000..e69de29
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/empty.yml b/_include/calendar/symfony/translation/Tests/fixtures/empty.yml
new file mode 100644
index 0000000..e69de29
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/encoding.xlf b/_include/calendar/symfony/translation/Tests/fixtures/encoding.xlf
new file mode 100644
index 0000000..0a88f92
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/encoding.xlf
@@ -0,0 +1,16 @@
+
+
+
+
+
+ foo
+ br
+ bz
+
+
+ bar
+ f
+
+
+
+
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/escaped-id-plurals.po b/_include/calendar/symfony/translation/Tests/fixtures/escaped-id-plurals.po
new file mode 100644
index 0000000..c412aa2
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/escaped-id-plurals.po
@@ -0,0 +1,10 @@
+msgid ""
+msgstr ""
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: en\n"
+
+msgid "escaped \"foo\""
+msgid_plural "escaped \"foos\""
+msgstr[0] "escaped \"bar\""
+msgstr[1] "escaped \"bars\""
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/escaped-id.po b/_include/calendar/symfony/translation/Tests/fixtures/escaped-id.po
new file mode 100644
index 0000000..308eadd
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/escaped-id.po
@@ -0,0 +1,8 @@
+msgid ""
+msgstr ""
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: en\n"
+
+msgid "escaped \"foo\""
+msgstr "escaped \"bar\""
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/extractor/resource.format.engine b/_include/calendar/symfony/translation/Tests/fixtures/extractor/resource.format.engine
new file mode 100644
index 0000000..e69de29
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/extractor/this.is.a.template.format.engine b/_include/calendar/symfony/translation/Tests/fixtures/extractor/this.is.a.template.format.engine
new file mode 100644
index 0000000..e69de29
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/extractor/translation.html.php b/_include/calendar/symfony/translation/Tests/fixtures/extractor/translation.html.php
new file mode 100644
index 0000000..1ce8ea9
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/extractor/translation.html.php
@@ -0,0 +1,49 @@
+This template is used for translation message extraction tests
+trans('single-quoted key'); ?>
+trans('double-quoted key'); ?>
+trans(<<<'EOF'
+heredoc key
+EOF
+); ?>
+trans(<<<'EOF'
+nowdoc key
+EOF
+); ?>
+trans(
+ "double-quoted key with whitespace and escaped \$\n\" sequences"
+); ?>
+trans(
+ 'single-quoted key with whitespace and nonescaped \$\n\' sequences'
+); ?>
+trans(<<
+trans(<<<'EOF'
+nowdoc key with whitespace and nonescaped \$\n sequences
+EOF
+); ?>
+
+trans('single-quoted key with "quote mark at the end"'); ?>
+
+transChoice(
+ '{0} There is no apples|{1} There is one apple|]1,Inf[ There are %count% apples',
+ 10,
+ array('%count%' => 10)
+); ?>
+
+trans('other-domain-test-no-params-short-array', array(), 'not_messages'); ?>
+
+trans('other-domain-test-no-params-long-array', array(), 'not_messages'); ?>
+
+trans('other-domain-test-params-short-array', array('foo' => 'bar'), 'not_messages'); ?>
+
+trans('other-domain-test-params-long-array', array('foo' => 'bar'), 'not_messages'); ?>
+
+transChoice('other-domain-test-trans-choice-short-array-%count%', 10, array('%count%' => 10), 'not_messages'); ?>
+
+transChoice('other-domain-test-trans-choice-long-array-%count%', 10, array('%count%' => 10), 'not_messages'); ?>
+
+trans('typecast', array('a' => (int) '123'), 'not_messages'); ?>
+transChoice('msg1', 10 + 1, array(), 'not_messages'); ?>
+transChoice('msg2', ceil(4.5), array(), 'not_messages'); ?>
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/fuzzy-translations.po b/_include/calendar/symfony/translation/Tests/fixtures/fuzzy-translations.po
new file mode 100644
index 0000000..04d4047
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/fuzzy-translations.po
@@ -0,0 +1,10 @@
+#, php-format
+msgid "foo1"
+msgstr "bar1"
+
+#, fuzzy, php-format
+msgid "foo2"
+msgstr "fuzzy bar2"
+
+msgid "foo3"
+msgstr "bar3"
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/invalid-xml-resources.xlf b/_include/calendar/symfony/translation/Tests/fixtures/invalid-xml-resources.xlf
new file mode 100644
index 0000000..7bf6c98
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/invalid-xml-resources.xlf
@@ -0,0 +1,23 @@
+
+
+
+
+
+ foo
+ bar
+
+
+ extra
+
+
+ key
+
+
+
+ test
+ with
+ note
+
+
+
+
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/malformed.json b/_include/calendar/symfony/translation/Tests/fixtures/malformed.json
new file mode 100644
index 0000000..4563ec6
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/malformed.json
@@ -0,0 +1,3 @@
+{
+ "foo" "bar"
+}
\ No newline at end of file
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/messages.yml b/_include/calendar/symfony/translation/Tests/fixtures/messages.yml
new file mode 100644
index 0000000..d4f82d7
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/messages.yml
@@ -0,0 +1,3 @@
+foo:
+ bar1: value1
+ bar2: value2
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/messages_linear.yml b/_include/calendar/symfony/translation/Tests/fixtures/messages_linear.yml
new file mode 100644
index 0000000..6c1687d
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/messages_linear.yml
@@ -0,0 +1,2 @@
+foo.bar1: value1
+foo.bar2: value2
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/non-valid.xlf b/_include/calendar/symfony/translation/Tests/fixtures/non-valid.xlf
new file mode 100644
index 0000000..734fc97
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/non-valid.xlf
@@ -0,0 +1,11 @@
+
+
+
+
+
+ foo
+ bar
+
+
+
+
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/non-valid.yml b/_include/calendar/symfony/translation/Tests/fixtures/non-valid.yml
new file mode 100644
index 0000000..257cc56
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/non-valid.yml
@@ -0,0 +1 @@
+foo
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/plurals.mo b/_include/calendar/symfony/translation/Tests/fixtures/plurals.mo
new file mode 100644
index 0000000000000000000000000000000000000000..6445e77beab595289cd154ea253c4e49dfd6af47
GIT binary patch
literal 74
zcmca7#4?ou2pEA_28dOFm>Gz5fS3b_Eugd`kOrxNfwcU51|TkGNJ=aM;bH~=*B}U7
literal 0
HcmV?d00001
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/plurals.po b/_include/calendar/symfony/translation/Tests/fixtures/plurals.po
new file mode 100644
index 0000000..439c41a
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/plurals.po
@@ -0,0 +1,5 @@
+msgid "foo"
+msgid_plural "foos"
+msgstr[0] "bar"
+msgstr[1] "bars"
+
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/resname.xlf b/_include/calendar/symfony/translation/Tests/fixtures/resname.xlf
new file mode 100644
index 0000000..2df16af
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/resname.xlf
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+ bar
+
+
+ bar source
+ baz
+
+
+ baz
+ foo
+
+
+
+
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/resourcebundle/corrupted/resources.dat b/_include/calendar/symfony/translation/Tests/fixtures/resourcebundle/corrupted/resources.dat
new file mode 100644
index 0000000..391250c
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/resourcebundle/corrupted/resources.dat
@@ -0,0 +1 @@
+XXX
\ No newline at end of file
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/resourcebundle/dat/en.res b/_include/calendar/symfony/translation/Tests/fixtures/resourcebundle/dat/en.res
new file mode 100644
index 0000000000000000000000000000000000000000..1fc1436d6641b7290ad5d9f4d331111e4a0419dd
GIT binary patch
literal 120
zcmY#jxTP+_00K-5L8-+~j7$s+j4WUQFaeZPU<0x^fmjTR8No6P48@hXY594T3_?JD
sFheCnE<+kaK0_XmrNCeW#F-4mKr)@7h#{3Bk)Z^rYSk)61{ttf0N1AuGXMYp
literal 0
HcmV?d00001
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/resourcebundle/dat/en.txt b/_include/calendar/symfony/translation/Tests/fixtures/resourcebundle/dat/en.txt
new file mode 100644
index 0000000..3d9e9ea
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/resourcebundle/dat/en.txt
@@ -0,0 +1,3 @@
+en{
+ symfony{"Symfony is great"}
+}
\ No newline at end of file
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/resourcebundle/dat/fr.res b/_include/calendar/symfony/translation/Tests/fixtures/resourcebundle/dat/fr.res
new file mode 100644
index 0000000000000000000000000000000000000000..f58416094be89f5fb64f78f6c3b873c6458696d0
GIT binary patch
literal 124
zcmY#jxTP+_00K-5L8-+~j7$s+j4WUQFd@popuh%XaRRY86f=Tl7#NBxbJOzkDj7if
wgBdCrav9PX@)`1gECmK5AWmf{W+(yD=?pJ{qL~bd3^_oRt5z{G$biiQ03-qri2wiq
literal 0
HcmV?d00001
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/resourcebundle/dat/fr.txt b/_include/calendar/symfony/translation/Tests/fixtures/resourcebundle/dat/fr.txt
new file mode 100644
index 0000000..182d0a0
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/resourcebundle/dat/fr.txt
@@ -0,0 +1,3 @@
+fr{
+ symfony{"Symfony est génial"}
+}
\ No newline at end of file
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/resourcebundle/dat/packagelist.txt b/_include/calendar/symfony/translation/Tests/fixtures/resourcebundle/dat/packagelist.txt
new file mode 100644
index 0000000..c5783ed
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/resourcebundle/dat/packagelist.txt
@@ -0,0 +1,2 @@
+en.res
+fr.res
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/resourcebundle/dat/resources.dat b/_include/calendar/symfony/translation/Tests/fixtures/resourcebundle/dat/resources.dat
new file mode 100644
index 0000000000000000000000000000000000000000..563b0eaef2e5a0a6e9623bf7b93b44beb0a7dc27
GIT binary patch
literal 352
zcmY#jxTP+_00K-5&bfImj6fDMm=7VCfD}mH0f<$B_y7!;@F0Xawl
zX+>axRdAqyWPVU;u@fWEKt>jzAy5D`TY(M8<^*CfC^BI_d>?DRn
Nh9V%%$RGn&2LK231)%@{
literal 0
HcmV?d00001
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/resources-2.0-clean.xlf b/_include/calendar/symfony/translation/Tests/fixtures/resources-2.0-clean.xlf
new file mode 100644
index 0000000..efa69b2
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/resources-2.0-clean.xlf
@@ -0,0 +1,23 @@
+
+
+
+
+
+ foo
+ bar
+
+
+
+
+ key
+
+
+
+
+
+ key.with.cdata
+ & ]]>
+
+
+
+
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/resources-2.0-multi-segment-unit.xlf b/_include/calendar/symfony/translation/Tests/fixtures/resources-2.0-multi-segment-unit.xlf
new file mode 100644
index 0000000..d0dc2a8
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/resources-2.0-multi-segment-unit.xlf
@@ -0,0 +1,17 @@
+
+
+
+
+ true
+
+
+ foo
+ foo (translated)
+
+
+ bar
+ bar (translated)
+
+
+
+
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/resources-2.0.xlf b/_include/calendar/symfony/translation/Tests/fixtures/resources-2.0.xlf
new file mode 100644
index 0000000..166172a
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/resources-2.0.xlf
@@ -0,0 +1,25 @@
+
+
+
+
+
+ Quetzal
+ Quetzal
+
+
+
+
+
+ foo
+ XLIFF 文書を編集、または処理 するアプリケーションです。
+
+
+
+
+ bar
+ XLIFF データ・マネージャ
+
+
+
+
+
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/resources-clean.xlf b/_include/calendar/symfony/translation/Tests/fixtures/resources-clean.xlf
new file mode 100644
index 0000000..00c8a5c
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/resources-clean.xlf
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+ foo
+ bar
+ baz
+
+
+ key
+
+ baz
+ qux
+
+
+ key.with.cdata
+ & ]]>
+
+
+
+
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/resources-notes-meta.xlf b/_include/calendar/symfony/translation/Tests/fixtures/resources-notes-meta.xlf
new file mode 100644
index 0000000..7d5bbd4
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/resources-notes-meta.xlf
@@ -0,0 +1,26 @@
+
+
+
+
+
+ new
+ true
+ user login
+
+
+ foo
+ bar
+
+
+
+
+ x_content
+ Fuzzy
+
+
+ baz
+ biz
+
+
+
+
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/resources-target-attributes.xlf b/_include/calendar/symfony/translation/Tests/fixtures/resources-target-attributes.xlf
new file mode 100644
index 0000000..700d281
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/resources-target-attributes.xlf
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+ foo
+ bar
+
+
+
+
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/resources-tool-info.xlf b/_include/calendar/symfony/translation/Tests/fixtures/resources-tool-info.xlf
new file mode 100644
index 0000000..1c2ae95
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/resources-tool-info.xlf
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+ foo
+ bar
+
+
+
+
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/resources.csv b/_include/calendar/symfony/translation/Tests/fixtures/resources.csv
new file mode 100644
index 0000000..374b9eb
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/resources.csv
@@ -0,0 +1,4 @@
+"foo"; "bar"
+#"bar"; "foo"
+"incorrect"; "number"; "columns"; "will"; "be"; "ignored"
+"incorrect"
\ No newline at end of file
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/resources.dump.json b/_include/calendar/symfony/translation/Tests/fixtures/resources.dump.json
new file mode 100644
index 0000000..335965d
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/resources.dump.json
@@ -0,0 +1 @@
+{"foo":"\u0022bar\u0022"}
\ No newline at end of file
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/resources.ini b/_include/calendar/symfony/translation/Tests/fixtures/resources.ini
new file mode 100644
index 0000000..4953062
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/resources.ini
@@ -0,0 +1 @@
+foo="bar"
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/resources.json b/_include/calendar/symfony/translation/Tests/fixtures/resources.json
new file mode 100644
index 0000000..8a79687
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/resources.json
@@ -0,0 +1,3 @@
+{
+ "foo": "bar"
+}
\ No newline at end of file
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/resources.mo b/_include/calendar/symfony/translation/Tests/fixtures/resources.mo
new file mode 100644
index 0000000000000000000000000000000000000000..0a9660257c07afef243a011d9806d6217e4f1379
GIT binary patch
literal 52
pcmca7#4?ou2pEA_28dNa93apEVrC%Lh0=yVnjtMepCKu+2mox%1k?Zk
literal 0
HcmV?d00001
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/resources.php b/_include/calendar/symfony/translation/Tests/fixtures/resources.php
new file mode 100644
index 0000000..c291398
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/resources.php
@@ -0,0 +1,5 @@
+ 'bar',
+);
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/resources.po b/_include/calendar/symfony/translation/Tests/fixtures/resources.po
new file mode 100644
index 0000000..ccfce6b
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/resources.po
@@ -0,0 +1,8 @@
+msgid ""
+msgstr ""
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: en\n"
+
+msgid "foo"
+msgstr "bar"
\ No newline at end of file
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/resources.ts b/_include/calendar/symfony/translation/Tests/fixtures/resources.ts
new file mode 100644
index 0000000..40e1852
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/resources.ts
@@ -0,0 +1,10 @@
+
+
+
+ resources
+
+ foo
+ bar
+
+
+
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/resources.xlf b/_include/calendar/symfony/translation/Tests/fixtures/resources.xlf
new file mode 100644
index 0000000..b0e5988
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/resources.xlf
@@ -0,0 +1,23 @@
+
+
+
+
+
+ foo
+ bar
+
+
+ extra
+
+
+ key
+
+
+
+ test
+ with
+ note
+
+
+
+
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/resources.yml b/_include/calendar/symfony/translation/Tests/fixtures/resources.yml
new file mode 100644
index 0000000..20e9ff3
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/resources.yml
@@ -0,0 +1 @@
+foo: bar
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/valid.csv b/_include/calendar/symfony/translation/Tests/fixtures/valid.csv
new file mode 100644
index 0000000..59882e5
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/valid.csv
@@ -0,0 +1,4 @@
+foo;bar
+bar;"foo
+foo"
+"foo;foo";bar
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/with-attributes.xlf b/_include/calendar/symfony/translation/Tests/fixtures/with-attributes.xlf
new file mode 100644
index 0000000..7873062
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/with-attributes.xlf
@@ -0,0 +1,21 @@
+
+
+
+
+
+ foo
+ bar
+
+
+ extra
+ bar
+
+
+ key
+
+ baz
+ qux
+
+
+
+
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/withdoctype.xlf b/_include/calendar/symfony/translation/Tests/fixtures/withdoctype.xlf
new file mode 100644
index 0000000..f83e834
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/withdoctype.xlf
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+ foo
+ bar
+
+
+
+
diff --git a/_include/calendar/symfony/translation/Tests/fixtures/withnote.xlf b/_include/calendar/symfony/translation/Tests/fixtures/withnote.xlf
new file mode 100644
index 0000000..c045e21
--- /dev/null
+++ b/_include/calendar/symfony/translation/Tests/fixtures/withnote.xlf
@@ -0,0 +1,22 @@
+
+
+
+
+
+ foo
+ bar
+ foo
+
+
+ extra
+ bar
+
+
+ key
+
+ baz
+ qux
+
+
+
+
diff --git a/_include/calendar/symfony/translation/Translator.php b/_include/calendar/symfony/translation/Translator.php
new file mode 100644
index 0000000..9769041
--- /dev/null
+++ b/_include/calendar/symfony/translation/Translator.php
@@ -0,0 +1,437 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation;
+
+use Symfony\Component\Config\ConfigCacheFactory;
+use Symfony\Component\Config\ConfigCacheFactoryInterface;
+use Symfony\Component\Config\ConfigCacheInterface;
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+use Symfony\Component\Translation\Exception\LogicException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+use Symfony\Component\Translation\Exception\RuntimeException;
+use Symfony\Component\Translation\Formatter\ChoiceMessageFormatterInterface;
+use Symfony\Component\Translation\Formatter\MessageFormatter;
+use Symfony\Component\Translation\Formatter\MessageFormatterInterface;
+use Symfony\Component\Translation\Loader\LoaderInterface;
+
+/**
+ * @author Fabien Potencier
+ */
+class Translator implements TranslatorInterface, TranslatorBagInterface
+{
+ /**
+ * @var MessageCatalogueInterface[]
+ */
+ protected $catalogues = array();
+
+ /**
+ * @var string
+ */
+ private $locale;
+
+ /**
+ * @var array
+ */
+ private $fallbackLocales = array();
+
+ /**
+ * @var LoaderInterface[]
+ */
+ private $loaders = array();
+
+ /**
+ * @var array
+ */
+ private $resources = array();
+
+ /**
+ * @var MessageFormatterInterface
+ */
+ private $formatter;
+
+ /**
+ * @var string
+ */
+ private $cacheDir;
+
+ /**
+ * @var bool
+ */
+ private $debug;
+
+ /**
+ * @var ConfigCacheFactoryInterface|null
+ */
+ private $configCacheFactory;
+
+ /**
+ * @throws InvalidArgumentException If a locale contains invalid characters
+ */
+ public function __construct(?string $locale, MessageFormatterInterface $formatter = null, string $cacheDir = null, bool $debug = false)
+ {
+ $this->setLocale($locale);
+
+ if (null === $formatter) {
+ $formatter = new MessageFormatter();
+ }
+
+ $this->formatter = $formatter;
+ $this->cacheDir = $cacheDir;
+ $this->debug = $debug;
+ }
+
+ public function setConfigCacheFactory(ConfigCacheFactoryInterface $configCacheFactory)
+ {
+ $this->configCacheFactory = $configCacheFactory;
+ }
+
+ /**
+ * Adds a Loader.
+ *
+ * @param string $format The name of the loader (@see addResource())
+ * @param LoaderInterface $loader A LoaderInterface instance
+ */
+ public function addLoader($format, LoaderInterface $loader)
+ {
+ $this->loaders[$format] = $loader;
+ }
+
+ /**
+ * Adds a Resource.
+ *
+ * @param string $format The name of the loader (@see addLoader())
+ * @param mixed $resource The resource name
+ * @param string $locale The locale
+ * @param string $domain The domain
+ *
+ * @throws InvalidArgumentException If the locale contains invalid characters
+ */
+ public function addResource($format, $resource, $locale, $domain = null)
+ {
+ if (null === $domain) {
+ $domain = 'messages';
+ }
+
+ $this->assertValidLocale($locale);
+
+ $this->resources[$locale][] = array($format, $resource, $domain);
+
+ if (\in_array($locale, $this->fallbackLocales)) {
+ $this->catalogues = array();
+ } else {
+ unset($this->catalogues[$locale]);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setLocale($locale)
+ {
+ $this->assertValidLocale($locale);
+ $this->locale = $locale;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getLocale()
+ {
+ return $this->locale;
+ }
+
+ /**
+ * Sets the fallback locales.
+ *
+ * @param array $locales The fallback locales
+ *
+ * @throws InvalidArgumentException If a locale contains invalid characters
+ */
+ public function setFallbackLocales(array $locales)
+ {
+ // needed as the fallback locales are linked to the already loaded catalogues
+ $this->catalogues = array();
+
+ foreach ($locales as $locale) {
+ $this->assertValidLocale($locale);
+ }
+
+ $this->fallbackLocales = $locales;
+ }
+
+ /**
+ * Gets the fallback locales.
+ *
+ * @return array $locales The fallback locales
+ */
+ public function getFallbackLocales()
+ {
+ return $this->fallbackLocales;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function trans($id, array $parameters = array(), $domain = null, $locale = null)
+ {
+ if (null === $domain) {
+ $domain = 'messages';
+ }
+
+ return $this->formatter->format($this->getCatalogue($locale)->get((string) $id, $domain), $locale, $parameters);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null)
+ {
+ if (!$this->formatter instanceof ChoiceMessageFormatterInterface) {
+ throw new LogicException(sprintf('The formatter "%s" does not support plural translations.', \get_class($this->formatter)));
+ }
+
+ if (null === $domain) {
+ $domain = 'messages';
+ }
+
+ $id = (string) $id;
+ $catalogue = $this->getCatalogue($locale);
+ $locale = $catalogue->getLocale();
+ while (!$catalogue->defines($id, $domain)) {
+ if ($cat = $catalogue->getFallbackCatalogue()) {
+ $catalogue = $cat;
+ $locale = $catalogue->getLocale();
+ } else {
+ break;
+ }
+ }
+
+ return $this->formatter->choiceFormat($catalogue->get($id, $domain), $number, $locale, $parameters);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getCatalogue($locale = null)
+ {
+ if (null === $locale) {
+ $locale = $this->getLocale();
+ } else {
+ $this->assertValidLocale($locale);
+ }
+
+ if (!isset($this->catalogues[$locale])) {
+ $this->loadCatalogue($locale);
+ }
+
+ return $this->catalogues[$locale];
+ }
+
+ /**
+ * Gets the loaders.
+ *
+ * @return array LoaderInterface[]
+ */
+ protected function getLoaders()
+ {
+ return $this->loaders;
+ }
+
+ /**
+ * @param string $locale
+ */
+ protected function loadCatalogue($locale)
+ {
+ if (null === $this->cacheDir) {
+ $this->initializeCatalogue($locale);
+ } else {
+ $this->initializeCacheCatalogue($locale);
+ }
+ }
+
+ /**
+ * @param string $locale
+ */
+ protected function initializeCatalogue($locale)
+ {
+ $this->assertValidLocale($locale);
+
+ try {
+ $this->doLoadCatalogue($locale);
+ } catch (NotFoundResourceException $e) {
+ if (!$this->computeFallbackLocales($locale)) {
+ throw $e;
+ }
+ }
+ $this->loadFallbackCatalogues($locale);
+ }
+
+ private function initializeCacheCatalogue(string $locale): void
+ {
+ if (isset($this->catalogues[$locale])) {
+ /* Catalogue already initialized. */
+ return;
+ }
+
+ $this->assertValidLocale($locale);
+ $cache = $this->getConfigCacheFactory()->cache($this->getCatalogueCachePath($locale),
+ function (ConfigCacheInterface $cache) use ($locale) {
+ $this->dumpCatalogue($locale, $cache);
+ }
+ );
+
+ if (isset($this->catalogues[$locale])) {
+ /* Catalogue has been initialized as it was written out to cache. */
+ return;
+ }
+
+ /* Read catalogue from cache. */
+ $this->catalogues[$locale] = include $cache->getPath();
+ }
+
+ private function dumpCatalogue($locale, ConfigCacheInterface $cache): void
+ {
+ $this->initializeCatalogue($locale);
+ $fallbackContent = $this->getFallbackContent($this->catalogues[$locale]);
+
+ $content = sprintf(<<catalogues[$locale]->all(), true),
+ $fallbackContent
+ );
+
+ $cache->write($content, $this->catalogues[$locale]->getResources());
+ }
+
+ private function getFallbackContent(MessageCatalogue $catalogue): string
+ {
+ $fallbackContent = '';
+ $current = '';
+ $replacementPattern = '/[^a-z0-9_]/i';
+ $fallbackCatalogue = $catalogue->getFallbackCatalogue();
+ while ($fallbackCatalogue) {
+ $fallback = $fallbackCatalogue->getLocale();
+ $fallbackSuffix = ucfirst(preg_replace($replacementPattern, '_', $fallback));
+ $currentSuffix = ucfirst(preg_replace($replacementPattern, '_', $current));
+
+ $fallbackContent .= sprintf(<<<'EOF'
+$catalogue%s = new MessageCatalogue('%s', %s);
+$catalogue%s->addFallbackCatalogue($catalogue%s);
+
+EOF
+ ,
+ $fallbackSuffix,
+ $fallback,
+ var_export($fallbackCatalogue->all(), true),
+ $currentSuffix,
+ $fallbackSuffix
+ );
+ $current = $fallbackCatalogue->getLocale();
+ $fallbackCatalogue = $fallbackCatalogue->getFallbackCatalogue();
+ }
+
+ return $fallbackContent;
+ }
+
+ private function getCatalogueCachePath($locale)
+ {
+ return $this->cacheDir.'/catalogue.'.$locale.'.'.strtr(substr(base64_encode(hash('sha256', serialize($this->fallbackLocales), true)), 0, 7), '/', '_').'.php';
+ }
+
+ private function doLoadCatalogue($locale): void
+ {
+ $this->catalogues[$locale] = new MessageCatalogue($locale);
+
+ if (isset($this->resources[$locale])) {
+ foreach ($this->resources[$locale] as $resource) {
+ if (!isset($this->loaders[$resource[0]])) {
+ throw new RuntimeException(sprintf('The "%s" translation loader is not registered.', $resource[0]));
+ }
+ $this->catalogues[$locale]->addCatalogue($this->loaders[$resource[0]]->load($resource[1], $locale, $resource[2]));
+ }
+ }
+ }
+
+ private function loadFallbackCatalogues($locale): void
+ {
+ $current = $this->catalogues[$locale];
+
+ foreach ($this->computeFallbackLocales($locale) as $fallback) {
+ if (!isset($this->catalogues[$fallback])) {
+ $this->initializeCatalogue($fallback);
+ }
+
+ $fallbackCatalogue = new MessageCatalogue($fallback, $this->catalogues[$fallback]->all());
+ foreach ($this->catalogues[$fallback]->getResources() as $resource) {
+ $fallbackCatalogue->addResource($resource);
+ }
+ $current->addFallbackCatalogue($fallbackCatalogue);
+ $current = $fallbackCatalogue;
+ }
+ }
+
+ protected function computeFallbackLocales($locale)
+ {
+ $locales = array();
+ foreach ($this->fallbackLocales as $fallback) {
+ if ($fallback === $locale) {
+ continue;
+ }
+
+ $locales[] = $fallback;
+ }
+
+ if (false !== strrchr($locale, '_')) {
+ array_unshift($locales, substr($locale, 0, -\strlen(strrchr($locale, '_'))));
+ }
+
+ return array_unique($locales);
+ }
+
+ /**
+ * Asserts that the locale is valid, throws an Exception if not.
+ *
+ * @param string $locale Locale to tests
+ *
+ * @throws InvalidArgumentException If the locale contains invalid characters
+ */
+ protected function assertValidLocale($locale)
+ {
+ if (1 !== preg_match('/^[a-z0-9@_\\.\\-]*$/i', $locale)) {
+ throw new InvalidArgumentException(sprintf('Invalid "%s" locale.', $locale));
+ }
+ }
+
+ /**
+ * Provides the ConfigCache factory implementation, falling back to a
+ * default implementation if necessary.
+ */
+ private function getConfigCacheFactory(): ConfigCacheFactoryInterface
+ {
+ if (!$this->configCacheFactory) {
+ $this->configCacheFactory = new ConfigCacheFactory($this->debug);
+ }
+
+ return $this->configCacheFactory;
+ }
+}
diff --git a/_include/calendar/symfony/translation/TranslatorBagInterface.php b/_include/calendar/symfony/translation/TranslatorBagInterface.php
new file mode 100644
index 0000000..5e49e2d
--- /dev/null
+++ b/_include/calendar/symfony/translation/TranslatorBagInterface.php
@@ -0,0 +1,33 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation;
+
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+
+/**
+ * TranslatorBagInterface.
+ *
+ * @author Abdellatif Ait boudad
+ */
+interface TranslatorBagInterface
+{
+ /**
+ * Gets the catalogue by locale.
+ *
+ * @param string|null $locale The locale or null to use the default
+ *
+ * @return MessageCatalogueInterface
+ *
+ * @throws InvalidArgumentException If the locale contains invalid characters
+ */
+ public function getCatalogue($locale = null);
+}
diff --git a/_include/calendar/symfony/translation/TranslatorInterface.php b/_include/calendar/symfony/translation/TranslatorInterface.php
new file mode 100644
index 0000000..9fcfd5b
--- /dev/null
+++ b/_include/calendar/symfony/translation/TranslatorInterface.php
@@ -0,0 +1,67 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation;
+
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+
+/**
+ * TranslatorInterface.
+ *
+ * @author Fabien Potencier
+ */
+interface TranslatorInterface
+{
+ /**
+ * Translates the given message.
+ *
+ * @param string $id The message id (may also be an object that can be cast to string)
+ * @param array $parameters An array of parameters for the message
+ * @param string|null $domain The domain for the message or null to use the default
+ * @param string|null $locale The locale or null to use the default
+ *
+ * @return string The translated string
+ *
+ * @throws InvalidArgumentException If the locale contains invalid characters
+ */
+ public function trans($id, array $parameters = array(), $domain = null, $locale = null);
+
+ /**
+ * Translates the given choice message by choosing a translation according to a number.
+ *
+ * @param string $id The message id (may also be an object that can be cast to string)
+ * @param int $number The number to use to find the indice of the message
+ * @param array $parameters An array of parameters for the message
+ * @param string|null $domain The domain for the message or null to use the default
+ * @param string|null $locale The locale or null to use the default
+ *
+ * @return string The translated string
+ *
+ * @throws InvalidArgumentException If the locale contains invalid characters
+ */
+ public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null);
+
+ /**
+ * Sets the current locale.
+ *
+ * @param string $locale The locale
+ *
+ * @throws InvalidArgumentException If the locale contains invalid characters
+ */
+ public function setLocale($locale);
+
+ /**
+ * Returns the current locale.
+ *
+ * @return string The locale
+ */
+ public function getLocale();
+}
diff --git a/_include/calendar/symfony/translation/Util/ArrayConverter.php b/_include/calendar/symfony/translation/Util/ArrayConverter.php
new file mode 100644
index 0000000..b98e7ce
--- /dev/null
+++ b/_include/calendar/symfony/translation/Util/ArrayConverter.php
@@ -0,0 +1,99 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Util;
+
+/**
+ * ArrayConverter generates tree like structure from a message catalogue.
+ * e.g. this
+ * 'foo.bar1' => 'test1',
+ * 'foo.bar2' => 'test2'
+ * converts to follows:
+ * foo:
+ * bar1: test1
+ * bar2: test2.
+ *
+ * @author Gennady Telegin
+ */
+class ArrayConverter
+{
+ /**
+ * Converts linear messages array to tree-like array.
+ * For example this rray('foo.bar' => 'value') will be converted to array('foo' => array('bar' => 'value')).
+ *
+ * @param array $messages Linear messages array
+ *
+ * @return array Tree-like messages array
+ */
+ public static function expandToTree(array $messages)
+ {
+ $tree = array();
+
+ foreach ($messages as $id => $value) {
+ $referenceToElement = &self::getElementByPath($tree, explode('.', $id));
+
+ $referenceToElement = $value;
+
+ unset($referenceToElement);
+ }
+
+ return $tree;
+ }
+
+ private static function &getElementByPath(array &$tree, array $parts)
+ {
+ $elem = &$tree;
+ $parentOfElem = null;
+
+ foreach ($parts as $i => $part) {
+ if (isset($elem[$part]) && \is_string($elem[$part])) {
+ /* Process next case:
+ * 'foo': 'test1',
+ * 'foo.bar': 'test2'
+ *
+ * $tree['foo'] was string before we found array {bar: test2}.
+ * Treat new element as string too, e.g. add $tree['foo.bar'] = 'test2';
+ */
+ $elem = &$elem[implode('.', \array_slice($parts, $i))];
+ break;
+ }
+ $parentOfElem = &$elem;
+ $elem = &$elem[$part];
+ }
+
+ if (\is_array($elem) && \count($elem) > 0 && $parentOfElem) {
+ /* Process next case:
+ * 'foo.bar': 'test1'
+ * 'foo': 'test2'
+ *
+ * $tree['foo'] was array = {bar: 'test1'} before we found string constant `foo`.
+ * Cancel treating $tree['foo'] as array and cancel back it expansion,
+ * e.g. make it $tree['foo.bar'] = 'test1' again.
+ */
+ self::cancelExpand($parentOfElem, $part, $elem);
+ }
+
+ return $elem;
+ }
+
+ private static function cancelExpand(array &$tree, $prefix, array $node)
+ {
+ $prefix .= '.';
+
+ foreach ($node as $id => $value) {
+ if (\is_string($value)) {
+ $tree[$prefix.$id] = $value;
+ } else {
+ self::cancelExpand($tree, $prefix.$id, $value);
+ }
+ }
+ }
+}
diff --git a/_include/calendar/symfony/translation/Writer/TranslationWriter.php b/_include/calendar/symfony/translation/Writer/TranslationWriter.php
new file mode 100644
index 0000000..762733b
--- /dev/null
+++ b/_include/calendar/symfony/translation/Writer/TranslationWriter.php
@@ -0,0 +1,90 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Writer;
+
+use Symfony\Component\Translation\Dumper\DumperInterface;
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+use Symfony\Component\Translation\Exception\RuntimeException;
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * TranslationWriter writes translation messages.
+ *
+ * @author Michel Salib
+ */
+class TranslationWriter implements TranslationWriterInterface
+{
+ private $dumpers = array();
+
+ /**
+ * Adds a dumper to the writer.
+ *
+ * @param string $format The format of the dumper
+ * @param DumperInterface $dumper The dumper
+ */
+ public function addDumper($format, DumperInterface $dumper)
+ {
+ $this->dumpers[$format] = $dumper;
+ }
+
+ /**
+ * Disables dumper backup.
+ *
+ * @deprecated since Symfony 4.1
+ */
+ public function disableBackup()
+ {
+ @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1.', __METHOD__), E_USER_DEPRECATED);
+
+ foreach ($this->dumpers as $dumper) {
+ if (method_exists($dumper, 'setBackup')) {
+ $dumper->setBackup(false);
+ }
+ }
+ }
+
+ /**
+ * Obtains the list of supported formats.
+ *
+ * @return array
+ */
+ public function getFormats()
+ {
+ return array_keys($this->dumpers);
+ }
+
+ /**
+ * Writes translation from the catalogue according to the selected format.
+ *
+ * @param MessageCatalogue $catalogue The message catalogue to write
+ * @param string $format The format to use to dump the messages
+ * @param array $options Options that are passed to the dumper
+ *
+ * @throws InvalidArgumentException
+ */
+ public function write(MessageCatalogue $catalogue, $format, $options = array())
+ {
+ if (!isset($this->dumpers[$format])) {
+ throw new InvalidArgumentException(sprintf('There is no dumper associated with format "%s".', $format));
+ }
+
+ // get the right dumper
+ $dumper = $this->dumpers[$format];
+
+ if (isset($options['path']) && !is_dir($options['path']) && !@mkdir($options['path'], 0777, true) && !is_dir($options['path'])) {
+ throw new RuntimeException(sprintf('Translation Writer was not able to create directory "%s"', $options['path']));
+ }
+
+ // save
+ $dumper->dump($catalogue, $options);
+ }
+}
diff --git a/_include/calendar/symfony/translation/Writer/TranslationWriterInterface.php b/_include/calendar/symfony/translation/Writer/TranslationWriterInterface.php
new file mode 100644
index 0000000..992ab76
--- /dev/null
+++ b/_include/calendar/symfony/translation/Writer/TranslationWriterInterface.php
@@ -0,0 +1,34 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Writer;
+
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * TranslationWriter writes translation messages.
+ *
+ * @author Michel Salib
+ */
+interface TranslationWriterInterface
+{
+ /**
+ * Writes translation from the catalogue according to the selected format.
+ *
+ * @param MessageCatalogue $catalogue The message catalogue to write
+ * @param string $format The format to use to dump the messages
+ * @param array $options Options that are passed to the dumper
+ *
+ * @throws InvalidArgumentException
+ */
+ public function write(MessageCatalogue $catalogue, $format, $options = array());
+}
diff --git a/_include/calendar/symfony/translation/composer.json b/_include/calendar/symfony/translation/composer.json
new file mode 100644
index 0000000..64ab46b
--- /dev/null
+++ b/_include/calendar/symfony/translation/composer.json
@@ -0,0 +1,53 @@
+{
+ "name": "symfony/translation",
+ "type": "library",
+ "description": "Symfony Translation Component",
+ "keywords": [],
+ "homepage": "https://symfony.com",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "require": {
+ "php": "^7.1.3",
+ "symfony/polyfill-mbstring": "~1.0"
+ },
+ "require-dev": {
+ "symfony/config": "~3.4|~4.0",
+ "symfony/console": "~3.4|~4.0",
+ "symfony/dependency-injection": "~3.4|~4.0",
+ "symfony/intl": "~3.4|~4.0",
+ "symfony/yaml": "~3.4|~4.0",
+ "symfony/finder": "~2.8|~3.0|~4.0",
+ "psr/log": "~1.0"
+ },
+ "conflict": {
+ "symfony/config": "<3.4",
+ "symfony/dependency-injection": "<3.4",
+ "symfony/yaml": "<3.4"
+ },
+ "suggest": {
+ "symfony/config": "",
+ "symfony/yaml": "",
+ "psr/log-implementation": "To use logging capability in translator"
+ },
+ "autoload": {
+ "psr-4": { "Symfony\\Component\\Translation\\": "" },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "minimum-stability": "dev",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.1-dev"
+ }
+ }
+}
diff --git a/_include/calendar/symfony/translation/phpunit.xml.dist b/_include/calendar/symfony/translation/phpunit.xml.dist
new file mode 100644
index 0000000..1fafa46
--- /dev/null
+++ b/_include/calendar/symfony/translation/phpunit.xml.dist
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+ ./Tests/
+
+
+
+
+
+ ./
+
+ ./Tests
+ ./vendor
+
+
+
+
diff --git a/_include/database.inc.php b/_include/database.inc.php
new file mode 100644
index 0000000..03a2d7b
--- /dev/null
+++ b/_include/database.inc.php
@@ -0,0 +1,41 @@
+Invalid query: " . mysql_error() . " ";
+ $message .= "Whole query: " . $query . " ";
+ die($message);
+ }
+ }
+ return $result;
+}
+
+function db_close($dbcnx) {
+ mysql_close($dbcnx);
+}
+
+?>
\ No newline at end of file
diff --git a/_include/functions.inc.php b/_include/functions.inc.php
new file mode 100644
index 0000000..ae0c57d
--- /dev/null
+++ b/_include/functions.inc.php
@@ -0,0 +1,120 @@
+ 57) and ($asciirand < 65)) continue;
+ if (($asciirand > 90) and ($asciirand < 97)) continue;
+ $newRand .= chr($asciirand);
+ }
+ return $newRand; //spit it out
+}
+
+function formatBytes($size, $precision = 2)
+{
+ $base = log($size, 1024);
+ $suffixes = array('', 'K', 'M', 'G', 'T');
+
+ return round(pow(1024, $base - floor($base)), $precision) .' '. $suffixes[floor($base)];
+}
+
+?>
\ No newline at end of file
diff --git a/_include/session.inc.php b/_include/session.inc.php
new file mode 100644
index 0000000..48e9978
--- /dev/null
+++ b/_include/session.inc.php
@@ -0,0 +1,18 @@
+
\ No newline at end of file
diff --git a/_include/settings.inc.php b/_include/settings.inc.php
new file mode 100644
index 0000000..e447349
--- /dev/null
+++ b/_include/settings.inc.php
@@ -0,0 +1,12 @@
+
diff --git a/_include/templates.inc.php b/_include/templates.inc.php
new file mode 100644
index 0000000..43e765a
--- /dev/null
+++ b/_include/templates.inc.php
@@ -0,0 +1,16 @@
+",PROJ_HTTP_ROOT,$template);
+ print($template);
+}
+
+function get_template_js($script_path) {
+ return file_get_contents(PROJ_TEMPLATES_ROOT . $script_path);
+}
+
+?>
\ No newline at end of file
diff --git a/_templates/.htaccess b/_templates/.htaccess
new file mode 100644
index 0000000..d57c506
--- /dev/null
+++ b/_templates/.htaccess
@@ -0,0 +1,2 @@
+Satisfy Any
+Deny from all
diff --git a/_templates/about.htm b/_templates/about.htm
new file mode 100644
index 0000000..8714ad7
--- /dev/null
+++ b/_templates/about.htm
@@ -0,0 +1,39 @@
+
+
+
+
+ Was ist der /usr/space?
+
+
+
+
+ © digitalskennedy , CC0
+
+ Derzeit ist der /usr/space ein Verein, der verschiedene Personen mit technischem und/oder netzpolitischem Interesse – unabhängig von jeder Parteipoliktik – zusammen bringen will. Auf mittelfristige Sicht soll daraus auch ein tatsächlicher Raum werden, in dem sich diese Personen treffen können um gemeinsam Erfahrungen auszutauschen, Ideen zu besprechen, und diese eventuell gleich umzusetzen. Diese Ideen können Kunst-Projekte sein, Steuerungen für private Räume, oder auch gemeinsames Lobbying für gesellschaftliche Themen. Der Verein selbst kümmert sich dabei um die Bereitstellung der Infrastruktur, nimmt aber sonst auf die Projekte keinen Einfluss. Weiters organisiert der Verein diverse Veranstaltungen, um die Ideen des Vereins zu präsentieren, zum Beispiel durch Vorträge oder auch sogenannte Repair Cafes , in denen Interessierten bei der Reparatur defekter Geräte beigestanden wird.
+
+
+
+
+
+
+
+ © Karolina Grabowska , CC0
+
+ Das Hauptziel ist es für die Bevölkerung einen Kontakt zur Technik herzustellen, der in der modernen Welt leider viel zu oft ignoriert wird. Tatsache ist aber, dass es heute nicht mehr möglich ist ohne Technik im Alltag zu arbeiten, wobei aber immer weniger Wissen über das "Wie" vorhanden ist. Genau diese Wissenslücke soll überbrückt werden, indem Vorträge und Workshops veranstaltet werden, durch die jeder Interessierte an das Thema herangeführt werden soll.
+
+
+
+
+
+
+
+ © fancycrave1 , CC0
+
+ Gegründet wurde der Verein durch 8 junge Menschen, die ursprünglich aus dem Bezirk Baden kommen. Das bedeutet aber nicht, dass sich die Mitgliedschaft auf eine bestimmte Altersgruppe oder Ausbildung beschränkt. Es sollen sogar explizit Personen aus allen Alters- und Ausbildungsgruppen angesprochen werden, vom Kind, das seine erste Platine lötet, bis zum Pensionisten, der für sein Teleskop eine Steuerung basteln will.
+
+
+
diff --git a/_templates/calendar.htm b/_templates/calendar.htm
new file mode 100644
index 0000000..e3331a4
--- /dev/null
+++ b/_templates/calendar.htm
@@ -0,0 +1,14 @@
+
+
+ Termine der nächsten 7 Tage
+
+ <@NextWeek@>
+
+ <@NextMonth@>
+
+ <@NextYear@>
+
\ No newline at end of file
diff --git a/_templates/calendar.js b/_templates/calendar.js
new file mode 100644
index 0000000..a76c6f6
--- /dev/null
+++ b/_templates/calendar.js
@@ -0,0 +1,20 @@
+
\ No newline at end of file
diff --git a/_templates/datenschutz.htm b/_templates/datenschutz.htm
new file mode 100644
index 0000000..bee8876
--- /dev/null
+++ b/_templates/datenschutz.htm
@@ -0,0 +1,24 @@
+
+
+
+Datenschutzerklärung
+Auf dieser Webseite werden keine Tracking-Pixel, oder andere Software zur Verfolgung des Benutzers zum Zweck des Profilings verwendet. Es werden sogenannte Cookies gesetzt, um einen angemeldeten Benutzer im Wiki oder der GitLab-Subseite eine persistente Sitzung zu ermöglichen.
+Beim Besuch der Seite wird vom Webserver die IP-Adresse des Besuchers aufgezeichnet. Diese wird zur Sicherung der technischen Verfügbarkeit für maximal 3 Tage gespeichert. Danach werden die Aufzeichnungen gelöscht. Diese Daten sind nur den Administratoren zugänglich.
+Falls dem Verein eine E-Mail übermittelt wird, werden die Metadaten dazu ebenfalls für maximal 3 Tage gespeichert, bevor diese gelöscht werden. Dies hat ebenfalls den Zweck möglichen Mißbrauch zu erkennen und zu unterbinden. Diese Daten sind nur den Administratoren zugänglich.
+Für den Fall dass man über die Vereinstätigkeit auf dem laufenden gehalten werden möchte, ohne gleich Mitglied zu werden, kann man sich als „Interessierte Person“ eintragen lassen. Dafür werden Name oder Pseudonym, sowie die E-Mail-Adresse gespeichert. Diese Daten sind nur dem Vorstand zugänglich und werden nicht ohne explizites Einverständnis geteilt.
+Du hast uns gegenüber folgende Rechte:
+
+Das Recht auf Widerruf. Damit dürfen die gespeicherten Daten nicht weiter verwendet werden. Konkret bedeutet das, dass du keine weiteren Benachrichtigungen per E-Mail bekommst.
+Das Recht auf Auskunft. Dadurch kannst du jederzeit eine Auflistung der über dich gespeicherten Daten verlangen. Diese können allerdings ggf. bearbeitet sein um die Rechte anderer zu schützen.
+Das Recht auf Löschung. Damit werden alle über dich gespeicherten Daten entfernt, sofern aus gesetzlichen Anforderungen (z.B. Finanz) nicht überragende Speicherpflichten bestehen. In diesem Fall werden die Daten soweit möglich pseudo- oder anonymisiert.
+
+Um gegen Mißbrauch geschützt zu sein benötigen wir für die Bearbeitung aller dieser Anfragen eine Kopie eines gültigen Ausweises, welche von uns nur für die Dauer der Bearbeitung gespeichert wird. Diese kann entweder an eine verschlüsselte Mail angehängt werden, oder per Link auf einen gesicherten Speicherort deiner Wahl. In diesem Fall trifft uns keine Verantwortung für die Verarbeitungstätigkeit dieses Hosts.
+Verantwortlich für die Einhaltung dieser Erklärung ist der Vorstand des Vereins /usr/space
, erreichbar unter kernel@usrspace.at . Für Anfragen mit personenbezogenen Daten wird die Kommunikation mit verschlüsselten E-Mails bevorzugt. Der Key für die Vereins-Adresse hat die ID 708D FA4D 629D 845C FF55 D62E BB16 C90D 5EC9 8770 , mit der auch die Markdown-Version dieser Erklärung signiert ist.
+Datenschutzerklärung für Vereinsmitglieder
+Für Mitglieder gelten die Bestimmungen der allgemeinen Datenschutzerklärung , sowie die hier notierten Zusätze.
+Zur Verwaltung deines Mitgliedsstatus benötigen wir einen Namen oder ein Pseudonym, sowie deine E-Mail Adresse. Diese Daten werden für die E-Mail-Kommunkation mit dem Vorstand verwendet, um Informationen an die Mitglieder zu versenden. Dein Mitgliedsstatus wird pseudonymisiert mit deiner Mitgliedsnummer verknüpft. Im Falle einer vergünstigten Mitgliedschaft als Schüler/Student oder Pensionist wird ausserdem der gegebenenfalls geforderte Nachweis ebenfalls mit diesem Pseudonym verknüpft. Alle diese Daten sind nur für den Vorstand einsehbar, und werden ohne explizites Einverständnis nicht weitergegeben.
+Sämtliche Angaben von weiteren personenbezogenen Daten in den Diensten des Vereins (GitLab, Wiki, Matrix, …) sind freiwillig, und werden von uns entfernt oder korrigiert falls dies nicht durch den Benutzer selbst geschehen kann.
+Auch von dieser Erklärung gibt es eine signierte Version unter https://usrspace.at/datenschutzerklaerung_mitglieder.md.asc
+
diff --git a/_templates/impressum.htm b/_templates/impressum.htm
new file mode 100644
index 0000000..544b12b
--- /dev/null
+++ b/_templates/impressum.htm
@@ -0,0 +1,20 @@
+
+
+
+ Medieninhaber Verein zur Förderung der technisch-sozialen Kompetenz und Kreativität - /usr/space
+ Zuständige Vereinsbehörde BH Baden
+ ZVR-Zahl 1062104322
+ Sitz & Adresse Obere Setzgasse 6/2 2544 Leobersdorf
+ Vorstand Obmann: Peter Ludikovsky Kassier: Stefan Dastig Schriftführer: Tomasz Kijas
+ Administrativer Kontakt kernel@usrspace.at
+ Cookies & Datenschutz
+ Datenschutzerklärung
+ Links
+ Unsere Webseite enthält Links zu externen Webseiten Dritter, auf deren Inhalte wir keinen Einfluss haben. Deshalb können wir für diese fremden Inhalte auch keine Gewähr übernehmen. Für die Inhalte der verlinkten Seiten ist stets der jeweilige Anbieter oder Betreiber der Seiten verantwortlich. Die verlinkten Seiten wurden zum Zeitpunkt der Verlinkung auf mögliche Rechtsverstöße überprüft. Rechtswidrige Inhalte waren zum Zeitpunkt der Verlinkung nicht erkennbar. Eine permanente inhaltliche Kontrolle der verlinkten Seiten ist jedoch ohne konkrete Anhaltspunkte einer Rechtsverletzung nicht zumutbar. Bei Bekanntwerden von Rechtsverletzungen werden wir derartige Links umgehend entfernen.
+ Haftungsauschluss
+ Die Inhalte unserer Webseite wurden mit größter Sorgfalt erstellt. Für die Richtigkeit, Vollständigkeit und Aktualität der Inhalte kann jedoch keine Gewähr übernommen werden.
+ Copyright
+ Alle Inhalte und Bilder unserer Webseite stehen unter CC BY-SA 3.0 AT – sofern nicht ausdrücklich anders angegeben.
+
\ No newline at end of file
diff --git a/_templates/index.htm b/_templates/index.htm
new file mode 100644
index 0000000..a4bdec8
--- /dev/null
+++ b/_templates/index.htm
@@ -0,0 +1,79 @@
+
+
+ Willkommen beim /usr/space
+
+
+
+ © digitalskennedy , CC0
+
+
+
+
+
+
Makerspace
+
Der /usr/space ist ein, allen Mitgliedern und Interessierten offen stehender, Raum in dem wir die Möglichkeit schaffen wollen sich in jeglicher Art und Weise kreativ mit Technik und/oder Kunst ausseinander zu setzen.
+
+
+
+
+
+
Repair Café
+
Am Samstag den 23. Februar 2019, veranstalten wir wieder ein Repair-Café. Alle Details dazu findest du im Wiki .
+
+
+
+
+
+
Was wir wollen
+
+ Raum für Innovation und Information bieten
+ Möglichkeit der Vernetzung von Gleichgesinnten
+ Verstehen wie unsere Geräte funktionieren
+ Wissen, das wir haben, vermitteln
+ kritisch und sachlich über Probleme der digitalen Gesellschaft informieren
+
+
+
+
+
+
+
Was wir bieten
+
Unsere Mitglieder verfügen über ein breitgefächertes Fachwissen und können sicher bei vielen Fragen mit Rat und Tat zur Seite stehen. Außerdem sind wir als Verein dabei Werkzeug und technische Geräte anzuschaffen, die über die Möglichkeiten von Einzelpersonen hinausgehen.
+
+
+
+
+
+
Wer wir sind
+
Wir sind Menschen unterschiedlichsten Alters die ihre Begeisterung und ihr Wissen in verschiedensten Disziplinen (Mechanik, Elektronik, Informatik, Astronomie, etc.) mit anderen teilen wollen. Andererseits versuchen wir uns auch kritisch und reflektiert mit Problemen und Herausforderungen der digitalen Gesellschaft auseinander zu setzen.
+
+
+
+
+
+
Termine
+
Aktuelle Termine und Veranstaltungen findest du links im Kalender oder im Wiki .
+
+
+
+
+
+
Mitgliedschaft
+
Mitglied kann prinzipiell jeder werden. Wir empfehlen zumindest zu einem Stammtisch zu kommen, um die Leute und Themen erst einmal kennen zu lernen.
+
+
+
+
+
+
\ No newline at end of file
diff --git a/_templates/index_old.htm b/_templates/index_old.htm
new file mode 100644
index 0000000..d714865
--- /dev/null
+++ b/_templates/index_old.htm
@@ -0,0 +1,67 @@
+
+
+
+
+
Willkommen beim /usr/space
+
Der Verein /usr/space hat es sich zur Aufgabe gemacht, die immer stärker vernetzte und digitalisierte Welt begreifbar zu machen. Wir sind eine Gruppe von technisch interessierten Bastlern, die
+
+ verstehen wollen, wie unsere Geräte funktionieren
+ dieses Wissen in die Welt tragen wollen
+ und im Zuge dessen auch aufdecken wo Dinge falsch laufen, und was der normale Nutzer tun kann.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Termine
+
Aktuelle Termine und Veranstaltungen findest du links im Kalender oder im Wiki.
+
+
+
+
+
+
Räumlichkeiten
+
Nach langem Suchen haben wir mit viel Unterstützung einen passenden Raum gefunden. Wir sind gerade dabei ihn an unsere Bedürnisse anzupassen und freuen uns euch schon bald zur Eröffnung einzuladen.
+
+
+
+
+
+
Mitgliedschaft
+
Mitglied kann prinzipiell jeder werden. Wir empfehlen zumindest zu einem Stammtisch zu kommen, um die Leute und Themen erst einmal kennen zu lernen.
+
+
+
+
+
+
Stammtisch
+
Der Stammtisch findet alle 14 Tage bei uns im Space, Mühlgasse 8, 2544 Leobersdorf, ab 19:00 statt.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/_templates/index_v2.htm b/_templates/index_v2.htm
new file mode 100644
index 0000000..f7a567b
--- /dev/null
+++ b/_templates/index_v2.htm
@@ -0,0 +1,72 @@
+
+
+ Willkommen beim /usr/space
+
+
+
+ © digitalskennedy , CC0
+
+
+
+
+
+
Makerspace
+
Der /usr/space ist ein, allen Mitgliedern und Interessierten offen stehender, Raum in dem wir die Möglichkeit schaffen wollen sich in jeglicher Art und Weise kreativ mit Technik und/oder Kunst ausseinander zu setzen.
+
+
+
+
+
+
Was wir wollen
+
+ Raum für Innovation und Information bieten
+ Möglichkeit der Vernetzung von Gleichgesinnten
+ Verstehen wie unsere Geräte funktionieren
+ Wissen, das wir haben, vermitteln
+ kritisch und sachlich über Probleme der digitalen Gesellschaft informieren
+
+
+
+
+
+
+
Was wir bieten
+
Unsere Mitglieder verfügen über ein breitgefächertes Fachwissen und können sicher bei vielen Fragen mit Rat und Tat zur Seite stehen. Außerdem sind wir als Verein dabei Werkzeug und technische Geräte anzuschaffen, die über die Möglichkeiten von Einzelpersonen hinausgehen.
+
+
+
+
+
+
Wer wir sind
+
Wir sind Menschen unterschiedlichsten Alters die ihre Begeisterung und ihr Wissen in verschiedensten Disziplinen (Mechanik, Elektronik, Informatik, Astronomie, etc.) mit anderen teilen wollen. Andererseits versuchen wir uns auch kritisch und reflektiert mit Problemen und Herausforderungen der digitalen Gesellschaft auseinander zu setzen.
+
+
+
+
+
+
Termine
+
Aktuelle Termine und Veranstaltungen findest du links im Kalender oder im Wiki .
+
+
+
+
+
+
Mitgliedschaft
+
Mitglied kann prinzipiell jeder werden. Wir empfehlen zumindest zu einem Stammtisch zu kommen, um die Leute und Themen erst einmal kennen zu lernen.
+
+
+
+
+
+
\ No newline at end of file
diff --git a/_templates/kontakt.htm b/_templates/kontakt.htm
new file mode 100644
index 0000000..c133940
--- /dev/null
+++ b/_templates/kontakt.htm
@@ -0,0 +1,26 @@
+
+
\ No newline at end of file
diff --git a/_templates/main.htm b/_templates/main.htm
new file mode 100644
index 0000000..44f3a41
--- /dev/null
+++ b/_templates/main.htm
@@ -0,0 +1,87 @@
+
+
+
+
+ Willkommen beim /usr/space
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <@Styles@>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <@Content@>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <@Scripts@>
+
+
diff --git a/_templates/main_old.htm b/_templates/main_old.htm
new file mode 100644
index 0000000..2382967
--- /dev/null
+++ b/_templates/main_old.htm
@@ -0,0 +1,81 @@
+
+
+
+
+ Willkommen beim /usr/space
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <@Styles@>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <@Content@>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <@Scripts@>
+
+
diff --git a/_templates/main_v2.htm b/_templates/main_v2.htm
new file mode 100644
index 0000000..f199096
--- /dev/null
+++ b/_templates/main_v2.htm
@@ -0,0 +1,81 @@
+
+
+
+
+ Willkommen beim /usr/space
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <@Styles@>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <@Content@>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <@Scripts@>
+
+
diff --git a/_templates/mitgliedschaft.htm b/_templates/mitgliedschaft.htm
new file mode 100644
index 0000000..3207e91
--- /dev/null
+++ b/_templates/mitgliedschaft.htm
@@ -0,0 +1,40 @@
+
+
+
+ Mitglied kann prinzipiell jeder werden. Wir empfehlen zumindest zu einem Stammtisch zu kommen, um die Leute und Themen erst einmal kennen zu lernen. Danach sollte man die Statuten durchlesen, und einen Mitgliedsantrag ausfüllen. Diesen dann entweder zum nächsten Stammtisch mitnehmen, oder einem Vorstandsmitglied persönlich übergeben. Der Vorstand entscheidet dann über die Aufnahme. Wenn der Entscheid positiv ist wird die Mitgliedschaft mit dem Einlangen des Mitgliedsbeitrages wirksam.
+ Unser Ziel ist es unseren Maker-/Hacker-/UserSpace (Vereinslokal) aus Mitgliedsbeiträgen zu finanzieren, darum beträgt unser monatlicher Mitgliedsbeitrag 20€.
+ Neben "normalen" Mitgliedern gibt es auch die Möglichkeit Fördermitglied zu werden. Diese zeichnen sich durch einen freiwillig höheren Mitgliedsbeitrag aus, und sind die einzige Mitgliedschaft für juristische Personen.
+ Schüler, Studenten, und Pensionisten haben die Option einer vergünstigten Mitgliedschaft von aktuell 10€ pro Monat.
+
+
+
+
Noch mehr Information zu allen Arten der Mitgliedschaft, sowie aller anderen rechtlichen Grundlagen unseres Vereins kannst du in den Statuten nachlesen.
+
Download
+
+
+
+
Alle Mitglieder sind angehalten, sich an den Code of Conduct zu halten, sowohl im Umgang miteinander als auch mit Nicht-Mitgliedern, um ein angenehmes Miteinander zu schaffen.
+
Download
+
+
+
+
Wenn du dich schon entschieden hast, kannst du hier gleich unseren Mitgliedsantrag runterladen und zum nächsten Stammtisch mitbringen. Da zumindest immer ein Vorstand beim Stammtisch anwesend ist, gibt es Anträge auch vor Ort.
+
Download
+
+
+
+
diff --git a/_templates/sidebar.htm b/_templates/sidebar.htm
new file mode 100644
index 0000000..496ad2b
--- /dev/null
+++ b/_templates/sidebar.htm
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
<@CalendarRange@>
+
+
+ Mo
+ Di
+ Mi
+ Do
+ Fr
+ Sa
+ So
+
+
+ <@CalendarContent@>
+
+
+
+
+
+
+
+
+
+
+
diff --git a/_templates/sidebar_old.htm b/_templates/sidebar_old.htm
new file mode 100644
index 0000000..b1467e2
--- /dev/null
+++ b/_templates/sidebar_old.htm
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
<@CalendarRange@>
+
+
+ Mo
+ Di
+ Mi
+ Do
+ Fr
+ Sa
+ So
+
+
+ <@CalendarContent@>
+
+
+
+
+
+
+
+
+
+
+
diff --git a/_templates/sidebar_v2.htm b/_templates/sidebar_v2.htm
new file mode 100644
index 0000000..4d763c4
--- /dev/null
+++ b/_templates/sidebar_v2.htm
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
<@CalendarRange@>
+
+
+ Mo
+ Di
+ Mi
+ Do
+ Fr
+ Sa
+ So
+
+
+ <@CalendarContent@>
+
+
+
+
+
+
+
+
+
+
+
diff --git a/_templates/stammtisch.htm b/_templates/stammtisch.htm
new file mode 100644
index 0000000..bcd3775
--- /dev/null
+++ b/_templates/stammtisch.htm
@@ -0,0 +1,16 @@
+
+
+
+
+
+ Stammtisch
+ Der Stammtisch findet jede Woche, abwechselnd Mittwochs und Donnerstags, in unserem Space ab 19:00 statt.
+
+
Der Stammtisch ist gedacht für alle Mitglieder sich regelmäßig zu treffen und/oder Interressierte in einer größeren Runde kennen zu lernen. Es darf & soll über eigene und fremde Projekte, interessante Nachrichten, netzpolitische Themen, … diskutiert oder auch präsentiert werden.
+
Der nächste Stammtisch findet am <@NextStammtischDate@> ab 19:00 Uhr, in der Mühlgasse 8 in 2544 Leobersdorf, statt.
+
+
+
+
\ No newline at end of file
diff --git a/_templates/stammtisch_map.js b/_templates/stammtisch_map.js
new file mode 100644
index 0000000..707ee89
--- /dev/null
+++ b/_templates/stammtisch_map.js
@@ -0,0 +1,28 @@
+
\ No newline at end of file
diff --git a/about.php b/about.php
new file mode 100644
index 0000000..b162f99
--- /dev/null
+++ b/about.php
@@ -0,0 +1,16 @@
+','',$template);
+$template = str_replace('<@Sidebar@>',get_template('/sidebar.htm'),$template);
+$template = str_replace('<@CalendarRange@>',gen_calendar_header(),$template);
+$template = str_replace('<@CalendarContent@>',gen_calendar_content(),$template);
+$template = str_replace('<@Content@>',$Content,$template);
+$template = str_replace('<@Scripts@>','',$template);
+print_template($template);
+
+?>
\ No newline at end of file
diff --git a/assets/css/font-awesome.min.css b/assets/css/font-awesome.min.css
new file mode 100644
index 0000000..540440c
--- /dev/null
+++ b/assets/css/font-awesome.min.css
@@ -0,0 +1,4 @@
+/*!
+ * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome
+ * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
+ */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.7.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}
diff --git a/assets/css/fullcalendar.css b/assets/css/fullcalendar.css
new file mode 100644
index 0000000..dcbc999
--- /dev/null
+++ b/assets/css/fullcalendar.css
@@ -0,0 +1,1293 @@
+/*!
+ * FullCalendar v3.9.0
+ * Docs & License: https://fullcalendar.io/
+ * (c) 2018 Adam Shaw
+ */
+.fc {
+ direction: ltr;
+ text-align: left; }
+
+.fc-rtl {
+ text-align: right; }
+
+body .fc {
+ /* extra precedence to overcome jqui */
+ font-size: 1em; }
+
+/* Colors
+--------------------------------------------------------------------------------------------------*/
+.fc-highlight {
+ /* when user is selecting cells */
+ background: #bce8f1;
+ opacity: .3; }
+
+.fc-bgevent {
+ /* default look for background events */
+ background: #8fdf82;
+ opacity: .3; }
+
+.fc-nonbusiness {
+ /* default look for non-business-hours areas */
+ /* will inherit .fc-bgevent's styles */
+ background: #d7d7d7; }
+
+/* Buttons (styled tags, normalized to work cross-browser)
+--------------------------------------------------------------------------------------------------*/
+.fc button {
+ /* force height to include the border and padding */
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ /* dimensions */
+ margin: 0;
+ height: 2.1em;
+ padding: 0 .6em;
+ /* text & cursor */
+ font-size: 1em;
+ /* normalize */
+ white-space: nowrap;
+ cursor: pointer; }
+
+/* Firefox has an annoying inner border */
+.fc button::-moz-focus-inner {
+ margin: 0;
+ padding: 0; }
+
+.fc-state-default {
+ /* non-theme */
+ border: 1px solid; }
+
+.fc-state-default.fc-corner-left {
+ /* non-theme */
+ border-top-left-radius: 4px;
+ border-bottom-left-radius: 4px; }
+
+.fc-state-default.fc-corner-right {
+ /* non-theme */
+ border-top-right-radius: 4px;
+ border-bottom-right-radius: 4px; }
+
+/* icons in buttons */
+.fc button .fc-icon {
+ /* non-theme */
+ position: relative;
+ top: -0.05em;
+ /* seems to be a good adjustment across browsers */
+ margin: 0 .2em;
+ vertical-align: middle; }
+
+/*
+ button states
+ borrowed from twitter bootstrap (http://twitter.github.com/bootstrap/)
+*/
+.fc-state-default {
+ background-color: #f5f5f5;
+ background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));
+ background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6);
+ background-image: -o-linear-gradient(top, #ffffff, #e6e6e6);
+ background-image: linear-gradient(to bottom, #ffffff, #e6e6e6);
+ background-repeat: repeat-x;
+ border-color: #e6e6e6 #e6e6e6 #bfbfbf;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ color: #333;
+ text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); }
+
+.fc-state-hover,
+.fc-state-down,
+.fc-state-active,
+.fc-state-disabled {
+ color: #333333;
+ background-color: #e6e6e6; }
+
+.fc-state-hover {
+ color: #333333;
+ text-decoration: none;
+ background-position: 0 -15px;
+ -webkit-transition: background-position 0.1s linear;
+ -moz-transition: background-position 0.1s linear;
+ -o-transition: background-position 0.1s linear;
+ transition: background-position 0.1s linear; }
+
+.fc-state-down,
+.fc-state-active {
+ background-color: #cccccc;
+ background-image: none;
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); }
+
+.fc-state-disabled {
+ cursor: default;
+ background-image: none;
+ opacity: 0.65;
+ box-shadow: none; }
+
+/* Buttons Groups
+--------------------------------------------------------------------------------------------------*/
+.fc-button-group {
+ display: inline-block; }
+
+/*
+every button that is not first in a button group should scootch over one pixel and cover the
+previous button's border...
+*/
+.fc .fc-button-group > * {
+ /* extra precedence b/c buttons have margin set to zero */
+ float: left;
+ margin: 0 0 0 -1px; }
+
+.fc .fc-button-group > :first-child {
+ /* same */
+ margin-left: 0; }
+
+/* Popover
+--------------------------------------------------------------------------------------------------*/
+.fc-popover {
+ position: absolute;
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15); }
+
+.fc-popover .fc-header {
+ /* TODO: be more consistent with fc-head/fc-body */
+ padding: 2px 4px; }
+
+.fc-popover .fc-header .fc-title {
+ margin: 0 2px; }
+
+.fc-popover .fc-header .fc-close {
+ cursor: pointer; }
+
+.fc-ltr .fc-popover .fc-header .fc-title,
+.fc-rtl .fc-popover .fc-header .fc-close {
+ float: left; }
+
+.fc-rtl .fc-popover .fc-header .fc-title,
+.fc-ltr .fc-popover .fc-header .fc-close {
+ float: right; }
+
+/* Misc Reusable Components
+--------------------------------------------------------------------------------------------------*/
+.fc-divider {
+ border-style: solid;
+ border-width: 1px; }
+
+hr.fc-divider {
+ height: 0;
+ margin: 0;
+ padding: 0 0 2px;
+ /* height is unreliable across browsers, so use padding */
+ border-width: 1px 0; }
+
+.fc-clear {
+ clear: both; }
+
+.fc-bg,
+.fc-bgevent-skeleton,
+.fc-highlight-skeleton,
+.fc-helper-skeleton {
+ /* these element should always cling to top-left/right corners */
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0; }
+
+.fc-bg {
+ bottom: 0;
+ /* strech bg to bottom edge */ }
+
+.fc-bg table {
+ height: 100%;
+ /* strech bg to bottom edge */ }
+
+/* Tables
+--------------------------------------------------------------------------------------------------*/
+.fc table {
+ width: 100%;
+ box-sizing: border-box;
+ /* fix scrollbar issue in firefox */
+ table-layout: fixed;
+ border-collapse: collapse;
+ border-spacing: 0;
+ font-size: 1em;
+ /* normalize cross-browser */ }
+
+.fc th {
+ text-align: center; }
+
+.fc th,
+.fc td {
+ border-style: solid;
+ border-width: 1px;
+ padding: 0;
+ vertical-align: top; }
+
+.fc td.fc-today {
+ border-style: double;
+ /* overcome neighboring borders */ }
+
+/* Internal Nav Links
+--------------------------------------------------------------------------------------------------*/
+a[data-goto] {
+ cursor: pointer; }
+
+a[data-goto]:hover {
+ text-decoration: underline; }
+
+/* Fake Table Rows
+--------------------------------------------------------------------------------------------------*/
+.fc .fc-row {
+ /* extra precedence to overcome themes w/ .ui-widget-content forcing a 1px border */
+ /* no visible border by default. but make available if need be (scrollbar width compensation) */
+ border-style: solid;
+ border-width: 0; }
+
+.fc-row table {
+ /* don't put left/right border on anything within a fake row.
+ the outer tbody will worry about this */
+ border-left: 0 hidden transparent;
+ border-right: 0 hidden transparent;
+ /* no bottom borders on rows */
+ border-bottom: 0 hidden transparent; }
+
+.fc-row:first-child table {
+ border-top: 0 hidden transparent;
+ /* no top border on first row */ }
+
+/* Day Row (used within the header and the DayGrid)
+--------------------------------------------------------------------------------------------------*/
+.fc-row {
+ position: relative; }
+
+.fc-row .fc-bg {
+ z-index: 1; }
+
+/* highlighting cells & background event skeleton */
+.fc-row .fc-bgevent-skeleton,
+.fc-row .fc-highlight-skeleton {
+ bottom: 0;
+ /* stretch skeleton to bottom of row */ }
+
+.fc-row .fc-bgevent-skeleton table,
+.fc-row .fc-highlight-skeleton table {
+ height: 100%;
+ /* stretch skeleton to bottom of row */ }
+
+.fc-row .fc-highlight-skeleton td,
+.fc-row .fc-bgevent-skeleton td {
+ border-color: transparent; }
+
+.fc-row .fc-bgevent-skeleton {
+ z-index: 2; }
+
+.fc-row .fc-highlight-skeleton {
+ z-index: 3; }
+
+/*
+row content (which contains day/week numbers and events) as well as "helper" (which contains
+temporary rendered events).
+*/
+.fc-row .fc-content-skeleton {
+ position: relative;
+ z-index: 4;
+ padding-bottom: 2px;
+ /* matches the space above the events */ }
+
+.fc-row .fc-helper-skeleton {
+ z-index: 5; }
+
+.fc .fc-row .fc-content-skeleton table,
+.fc .fc-row .fc-content-skeleton td,
+.fc .fc-row .fc-helper-skeleton td {
+ /* see-through to the background below */
+ /* extra precedence to prevent theme-provided backgrounds */
+ background: none;
+ /* in case s are globally styled */
+ border-color: transparent; }
+
+.fc-row .fc-content-skeleton td,
+.fc-row .fc-helper-skeleton td {
+ /* don't put a border between events and/or the day number */
+ border-bottom: 0; }
+
+.fc-row .fc-content-skeleton tbody td,
+.fc-row .fc-helper-skeleton tbody td {
+ /* don't put a border between event cells */
+ border-top: 0; }
+
+/* Scrolling Container
+--------------------------------------------------------------------------------------------------*/
+.fc-scroller {
+ -webkit-overflow-scrolling: touch; }
+
+/* TODO: move to agenda/basic */
+.fc-scroller > .fc-day-grid,
+.fc-scroller > .fc-time-grid {
+ position: relative;
+ /* re-scope all positions */
+ width: 100%;
+ /* hack to force re-sizing this inner element when scrollbars appear/disappear */ }
+
+/* Global Event Styles
+--------------------------------------------------------------------------------------------------*/
+.fc-event {
+ position: relative;
+ /* for resize handle and other inner positioning */
+ display: block;
+ /* make the tag block */
+ font-size: .85em;
+ line-height: 1.3;
+ border-radius: 3px;
+ border: 1px solid #3a87ad;
+ /* default BORDER color */ }
+
+.fc-event,
+.fc-event-dot {
+ background-color: #3a87ad;
+ /* default BACKGROUND color */ }
+
+.fc-event,
+.fc-event:hover {
+ color: #fff;
+ /* default TEXT color */
+ text-decoration: none;
+ /* if has an href */ }
+
+.fc-event[href],
+.fc-event.fc-draggable {
+ cursor: pointer;
+ /* give events with links and draggable events a hand mouse pointer */ }
+
+.fc-not-allowed,
+.fc-not-allowed .fc-event {
+ /* to override an event's custom cursor */
+ cursor: not-allowed; }
+
+.fc-event .fc-bg {
+ /* the generic .fc-bg already does position */
+ z-index: 1;
+ background: #fff;
+ opacity: .25; }
+
+.fc-event .fc-content {
+ position: relative;
+ z-index: 2; }
+
+/* resizer (cursor AND touch devices) */
+.fc-event .fc-resizer {
+ position: absolute;
+ z-index: 4; }
+
+/* resizer (touch devices) */
+.fc-event .fc-resizer {
+ display: none; }
+
+.fc-event.fc-allow-mouse-resize .fc-resizer,
+.fc-event.fc-selected .fc-resizer {
+ /* only show when hovering or selected (with touch) */
+ display: block; }
+
+/* hit area */
+.fc-event.fc-selected .fc-resizer:before {
+ /* 40x40 touch area */
+ content: "";
+ position: absolute;
+ z-index: 9999;
+ /* user of this util can scope within a lower z-index */
+ top: 50%;
+ left: 50%;
+ width: 40px;
+ height: 40px;
+ margin-left: -20px;
+ margin-top: -20px; }
+
+/* Event Selection (only for touch devices)
+--------------------------------------------------------------------------------------------------*/
+.fc-event.fc-selected {
+ z-index: 9999 !important;
+ /* overcomes inline z-index */
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); }
+
+.fc-event.fc-selected.fc-dragging {
+ box-shadow: 0 2px 7px rgba(0, 0, 0, 0.3); }
+
+/* Horizontal Events
+--------------------------------------------------------------------------------------------------*/
+/* bigger touch area when selected */
+.fc-h-event.fc-selected:before {
+ content: "";
+ position: absolute;
+ z-index: 3;
+ /* below resizers */
+ top: -10px;
+ bottom: -10px;
+ left: 0;
+ right: 0; }
+
+/* events that are continuing to/from another week. kill rounded corners and butt up against edge */
+.fc-ltr .fc-h-event.fc-not-start,
+.fc-rtl .fc-h-event.fc-not-end {
+ margin-left: 0;
+ border-left-width: 0;
+ padding-left: 1px;
+ /* replace the border with padding */
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0; }
+
+.fc-ltr .fc-h-event.fc-not-end,
+.fc-rtl .fc-h-event.fc-not-start {
+ margin-right: 0;
+ border-right-width: 0;
+ padding-right: 1px;
+ /* replace the border with padding */
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0; }
+
+/* resizer (cursor AND touch devices) */
+/* left resizer */
+.fc-ltr .fc-h-event .fc-start-resizer,
+.fc-rtl .fc-h-event .fc-end-resizer {
+ cursor: w-resize;
+ left: -1px;
+ /* overcome border */ }
+
+/* right resizer */
+.fc-ltr .fc-h-event .fc-end-resizer,
+.fc-rtl .fc-h-event .fc-start-resizer {
+ cursor: e-resize;
+ right: -1px;
+ /* overcome border */ }
+
+/* resizer (mouse devices) */
+.fc-h-event.fc-allow-mouse-resize .fc-resizer {
+ width: 7px;
+ top: -1px;
+ /* overcome top border */
+ bottom: -1px;
+ /* overcome bottom border */ }
+
+/* resizer (touch devices) */
+.fc-h-event.fc-selected .fc-resizer {
+ /* 8x8 little dot */
+ border-radius: 4px;
+ border-width: 1px;
+ width: 6px;
+ height: 6px;
+ border-style: solid;
+ border-color: inherit;
+ background: #fff;
+ /* vertically center */
+ top: 50%;
+ margin-top: -4px; }
+
+/* left resizer */
+.fc-ltr .fc-h-event.fc-selected .fc-start-resizer,
+.fc-rtl .fc-h-event.fc-selected .fc-end-resizer {
+ margin-left: -4px;
+ /* centers the 8x8 dot on the left edge */ }
+
+/* right resizer */
+.fc-ltr .fc-h-event.fc-selected .fc-end-resizer,
+.fc-rtl .fc-h-event.fc-selected .fc-start-resizer {
+ margin-right: -4px;
+ /* centers the 8x8 dot on the right edge */ }
+
+/* DayGrid events
+----------------------------------------------------------------------------------------------------
+We use the full "fc-day-grid-event" class instead of using descendants because the event won't
+be a descendant of the grid when it is being dragged.
+*/
+.fc-day-grid-event {
+ margin: 1px 2px 0;
+ /* spacing between events and edges */
+ padding: 0 1px; }
+
+tr:first-child > td > .fc-day-grid-event {
+ margin-top: 2px;
+ /* a little bit more space before the first event */ }
+
+.fc-day-grid-event.fc-selected:after {
+ content: "";
+ position: absolute;
+ z-index: 1;
+ /* same z-index as fc-bg, behind text */
+ /* overcome the borders */
+ top: -1px;
+ right: -1px;
+ bottom: -1px;
+ left: -1px;
+ /* darkening effect */
+ background: #000;
+ opacity: .25; }
+
+.fc-day-grid-event .fc-content {
+ /* force events to be one-line tall */
+ white-space: nowrap;
+ overflow: hidden; }
+
+.fc-day-grid-event .fc-time {
+ font-weight: bold; }
+
+/* resizer (cursor devices) */
+/* left resizer */
+.fc-ltr .fc-day-grid-event.fc-allow-mouse-resize .fc-start-resizer,
+.fc-rtl .fc-day-grid-event.fc-allow-mouse-resize .fc-end-resizer {
+ margin-left: -2px;
+ /* to the day cell's edge */ }
+
+/* right resizer */
+.fc-ltr .fc-day-grid-event.fc-allow-mouse-resize .fc-end-resizer,
+.fc-rtl .fc-day-grid-event.fc-allow-mouse-resize .fc-start-resizer {
+ margin-right: -2px;
+ /* to the day cell's edge */ }
+
+/* Event Limiting
+--------------------------------------------------------------------------------------------------*/
+/* "more" link that represents hidden events */
+a.fc-more {
+ margin: 1px 3px;
+ font-size: .85em;
+ cursor: pointer;
+ text-decoration: none; }
+
+a.fc-more:hover {
+ text-decoration: underline; }
+
+.fc-limited {
+ /* rows and cells that are hidden because of a "more" link */
+ display: none; }
+
+/* popover that appears when "more" link is clicked */
+.fc-day-grid .fc-row {
+ z-index: 1;
+ /* make the "more" popover one higher than this */ }
+
+.fc-more-popover {
+ z-index: 2;
+ width: 220px; }
+
+.fc-more-popover .fc-event-container {
+ padding: 10px; }
+
+/* Now Indicator
+--------------------------------------------------------------------------------------------------*/
+.fc-now-indicator {
+ position: absolute;
+ border: 0 solid red; }
+
+/* Utilities
+--------------------------------------------------------------------------------------------------*/
+.fc-unselectable {
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ -webkit-touch-callout: none;
+ -webkit-tap-highlight-color: transparent; }
+
+/*
+TODO: more distinction between this file and common.css
+*/
+/* Colors
+--------------------------------------------------------------------------------------------------*/
+.fc-unthemed th,
+.fc-unthemed td,
+.fc-unthemed thead,
+.fc-unthemed tbody,
+.fc-unthemed .fc-divider,
+.fc-unthemed .fc-row,
+.fc-unthemed .fc-content,
+.fc-unthemed .fc-popover,
+.fc-unthemed .fc-list-view,
+.fc-unthemed .fc-list-heading td {
+ border-color: #ddd; }
+
+.fc-unthemed .fc-popover {
+ background-color: #fff; }
+
+.fc-unthemed .fc-divider,
+.fc-unthemed .fc-popover .fc-header,
+.fc-unthemed .fc-list-heading td {
+ background: #eee; }
+
+.fc-unthemed .fc-popover .fc-header .fc-close {
+ color: #666; }
+
+.fc-unthemed td.fc-today {
+ background: #fcf8e3; }
+
+.fc-unthemed .fc-disabled-day {
+ background: #d7d7d7;
+ opacity: .3; }
+
+/* Icons (inline elements with styled text that mock arrow icons)
+--------------------------------------------------------------------------------------------------*/
+.fc-icon {
+ display: inline-block;
+ height: 1em;
+ line-height: 1em;
+ font-size: 1em;
+ text-align: center;
+ overflow: hidden;
+ font-family: "Courier New", Courier, monospace;
+ /* don't allow browser text-selection */
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none; }
+
+/*
+Acceptable font-family overrides for individual icons:
+ "Arial", sans-serif
+ "Times New Roman", serif
+
+NOTE: use percentage font sizes or else old IE chokes
+*/
+.fc-icon:after {
+ position: relative; }
+
+.fc-icon-left-single-arrow:after {
+ content: "\2039";
+ font-weight: bold;
+ font-size: 200%;
+ top: -7%; }
+
+.fc-icon-right-single-arrow:after {
+ content: "\203A";
+ font-weight: bold;
+ font-size: 200%;
+ top: -7%; }
+
+.fc-icon-left-double-arrow:after {
+ content: "\AB";
+ font-size: 160%;
+ top: -7%; }
+
+.fc-icon-right-double-arrow:after {
+ content: "\BB";
+ font-size: 160%;
+ top: -7%; }
+
+.fc-icon-left-triangle:after {
+ content: "\25C4";
+ font-size: 125%;
+ top: 3%; }
+
+.fc-icon-right-triangle:after {
+ content: "\25BA";
+ font-size: 125%;
+ top: 3%; }
+
+.fc-icon-down-triangle:after {
+ content: "\25BC";
+ font-size: 125%;
+ top: 2%; }
+
+.fc-icon-x:after {
+ content: "\D7";
+ font-size: 200%;
+ top: 6%; }
+
+/* Popover
+--------------------------------------------------------------------------------------------------*/
+.fc-unthemed .fc-popover {
+ border-width: 1px;
+ border-style: solid; }
+
+.fc-unthemed .fc-popover .fc-header .fc-close {
+ font-size: .9em;
+ margin-top: 2px; }
+
+/* List View
+--------------------------------------------------------------------------------------------------*/
+.fc-unthemed .fc-list-item:hover td {
+ background-color: #f5f5f5; }
+
+/* Colors
+--------------------------------------------------------------------------------------------------*/
+.ui-widget .fc-disabled-day {
+ background-image: none; }
+
+/* Popover
+--------------------------------------------------------------------------------------------------*/
+.fc-popover > .ui-widget-header + .ui-widget-content {
+ border-top: 0;
+ /* where they meet, let the header have the border */ }
+
+/* Global Event Styles
+--------------------------------------------------------------------------------------------------*/
+.ui-widget .fc-event {
+ /* overpower jqui's styles on tags. TODO: more DRY */
+ color: #fff;
+ /* default TEXT color */
+ text-decoration: none;
+ /* if has an href */
+ /* undo ui-widget-header bold */
+ font-weight: normal; }
+
+/* TimeGrid axis running down the side (for both the all-day area and the slot area)
+--------------------------------------------------------------------------------------------------*/
+.ui-widget td.fc-axis {
+ font-weight: normal;
+ /* overcome bold */ }
+
+/* TimeGrid Slats (lines that run horizontally)
+--------------------------------------------------------------------------------------------------*/
+.fc-time-grid .fc-slats .ui-widget-content {
+ background: none;
+ /* see through to fc-bg */ }
+
+.fc.fc-bootstrap3 a {
+ text-decoration: none; }
+
+.fc.fc-bootstrap3 a[data-goto]:hover {
+ text-decoration: underline; }
+
+.fc-bootstrap3 hr.fc-divider {
+ border-color: inherit; }
+
+.fc-bootstrap3 .fc-today.alert {
+ border-radius: 0; }
+
+/* Popover
+--------------------------------------------------------------------------------------------------*/
+.fc-bootstrap3 .fc-popover .panel-body {
+ padding: 0; }
+
+/* TimeGrid Slats (lines that run horizontally)
+--------------------------------------------------------------------------------------------------*/
+.fc-bootstrap3 .fc-time-grid .fc-slats table {
+ /* some themes have background color. see through to slats */
+ background: none; }
+
+.fc.fc-bootstrap4 a {
+ text-decoration: none; }
+
+.fc.fc-bootstrap4 a[data-goto]:hover {
+ text-decoration: underline; }
+
+.fc-bootstrap4 hr.fc-divider {
+ border-color: inherit; }
+
+.fc-bootstrap4 .fc-today.alert {
+ border-radius: 0; }
+
+.fc-bootstrap4 a.fc-event:not([href]):not([tabindex]) {
+ color: #fff; }
+
+.fc-bootstrap4 .fc-popover.card {
+ position: absolute; }
+
+/* Popover
+--------------------------------------------------------------------------------------------------*/
+.fc-bootstrap4 .fc-popover .card-body {
+ padding: 0; }
+
+/* TimeGrid Slats (lines that run horizontally)
+--------------------------------------------------------------------------------------------------*/
+.fc-bootstrap4 .fc-time-grid .fc-slats table {
+ /* some themes have background color. see through to slats */
+ background: none; }
+
+/* Toolbar
+--------------------------------------------------------------------------------------------------*/
+.fc-toolbar {
+ text-align: center; }
+
+.fc-toolbar.fc-header-toolbar {
+ margin-bottom: 1em; }
+
+.fc-toolbar.fc-footer-toolbar {
+ margin-top: 1em; }
+
+.fc-toolbar .fc-left {
+ float: left; }
+
+.fc-toolbar .fc-right {
+ float: right; }
+
+.fc-toolbar .fc-center {
+ display: inline-block; }
+
+/* the things within each left/right/center section */
+.fc .fc-toolbar > * > * {
+ /* extra precedence to override button border margins */
+ float: left;
+ margin-left: .75em; }
+
+/* the first thing within each left/center/right section */
+.fc .fc-toolbar > * > :first-child {
+ /* extra precedence to override button border margins */
+ margin-left: 0; }
+
+/* title text */
+.fc-toolbar h2 {
+ margin: 0; }
+
+/* button layering (for border precedence) */
+.fc-toolbar button {
+ position: relative; }
+
+.fc-toolbar .fc-state-hover,
+.fc-toolbar .ui-state-hover {
+ z-index: 2; }
+
+.fc-toolbar .fc-state-down {
+ z-index: 3; }
+
+.fc-toolbar .fc-state-active,
+.fc-toolbar .ui-state-active {
+ z-index: 4; }
+
+.fc-toolbar button:focus {
+ z-index: 5; }
+
+/* View Structure
+--------------------------------------------------------------------------------------------------*/
+/* undo twitter bootstrap's box-sizing rules. normalizes positioning techniques */
+/* don't do this for the toolbar because we'll want bootstrap to style those buttons as some pt */
+.fc-view-container *,
+.fc-view-container *:before,
+.fc-view-container *:after {
+ -webkit-box-sizing: content-box;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box; }
+
+.fc-view,
+.fc-view > table {
+ /* so dragged elements can be above the view's main element */
+ position: relative;
+ z-index: 1; }
+
+/* BasicView
+--------------------------------------------------------------------------------------------------*/
+/* day row structure */
+.fc-basicWeek-view .fc-content-skeleton,
+.fc-basicDay-view .fc-content-skeleton {
+ /* there may be week numbers in these views, so no padding-top */
+ padding-bottom: 1em;
+ /* ensure a space at bottom of cell for user selecting/clicking */ }
+
+.fc-basic-view .fc-body .fc-row {
+ min-height: 4em;
+ /* ensure that all rows are at least this tall */ }
+
+/* a "rigid" row will take up a constant amount of height because content-skeleton is absolute */
+.fc-row.fc-rigid {
+ overflow: hidden; }
+
+.fc-row.fc-rigid .fc-content-skeleton {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0; }
+
+/* week and day number styling */
+.fc-day-top.fc-other-month {
+ opacity: 0.3; }
+
+.fc-basic-view .fc-week-number,
+.fc-basic-view .fc-day-number {
+ padding: 2px; }
+
+.fc-basic-view th.fc-week-number,
+.fc-basic-view th.fc-day-number {
+ padding: 0 2px;
+ /* column headers can't have as much v space */ }
+
+.fc-ltr .fc-basic-view .fc-day-top .fc-day-number {
+ float: right; }
+
+.fc-rtl .fc-basic-view .fc-day-top .fc-day-number {
+ float: left; }
+
+.fc-ltr .fc-basic-view .fc-day-top .fc-week-number {
+ float: left;
+ border-radius: 0 0 3px 0; }
+
+.fc-rtl .fc-basic-view .fc-day-top .fc-week-number {
+ float: right;
+ border-radius: 0 0 0 3px; }
+
+.fc-basic-view .fc-day-top .fc-week-number {
+ min-width: 1.5em;
+ text-align: center;
+ background-color: #f2f2f2;
+ color: #808080; }
+
+/* when week/day number have own column */
+.fc-basic-view td.fc-week-number {
+ text-align: center; }
+
+.fc-basic-view td.fc-week-number > * {
+ /* work around the way we do column resizing and ensure a minimum width */
+ display: inline-block;
+ min-width: 1.25em; }
+
+/* AgendaView all-day area
+--------------------------------------------------------------------------------------------------*/
+.fc-agenda-view .fc-day-grid {
+ position: relative;
+ z-index: 2;
+ /* so the "more.." popover will be over the time grid */ }
+
+.fc-agenda-view .fc-day-grid .fc-row {
+ min-height: 3em;
+ /* all-day section will never get shorter than this */ }
+
+.fc-agenda-view .fc-day-grid .fc-row .fc-content-skeleton {
+ padding-bottom: 1em;
+ /* give space underneath events for clicking/selecting days */ }
+
+/* TimeGrid axis running down the side (for both the all-day area and the slot area)
+--------------------------------------------------------------------------------------------------*/
+.fc .fc-axis {
+ /* .fc to overcome default cell styles */
+ vertical-align: middle;
+ padding: 0 4px;
+ white-space: nowrap; }
+
+.fc-ltr .fc-axis {
+ text-align: right; }
+
+.fc-rtl .fc-axis {
+ text-align: left; }
+
+/* TimeGrid Structure
+--------------------------------------------------------------------------------------------------*/
+.fc-time-grid-container,
+.fc-time-grid {
+ /* so slats/bg/content/etc positions get scoped within here */
+ position: relative;
+ z-index: 1; }
+
+.fc-time-grid {
+ min-height: 100%;
+ /* so if height setting is 'auto', .fc-bg stretches to fill height */ }
+
+.fc-time-grid table {
+ /* don't put outer borders on slats/bg/content/etc */
+ border: 0 hidden transparent; }
+
+.fc-time-grid > .fc-bg {
+ z-index: 1; }
+
+.fc-time-grid .fc-slats,
+.fc-time-grid > hr {
+ /* the AgendaView injects when grid is shorter than scroller */
+ position: relative;
+ z-index: 2; }
+
+.fc-time-grid .fc-content-col {
+ position: relative;
+ /* because now-indicator lives directly inside */ }
+
+.fc-time-grid .fc-content-skeleton {
+ position: absolute;
+ z-index: 3;
+ top: 0;
+ left: 0;
+ right: 0; }
+
+/* divs within a cell within the fc-content-skeleton */
+.fc-time-grid .fc-business-container {
+ position: relative;
+ z-index: 1; }
+
+.fc-time-grid .fc-bgevent-container {
+ position: relative;
+ z-index: 2; }
+
+.fc-time-grid .fc-highlight-container {
+ position: relative;
+ z-index: 3; }
+
+.fc-time-grid .fc-event-container {
+ position: relative;
+ z-index: 4; }
+
+.fc-time-grid .fc-now-indicator-line {
+ z-index: 5; }
+
+.fc-time-grid .fc-helper-container {
+ /* also is fc-event-container */
+ position: relative;
+ z-index: 6; }
+
+/* TimeGrid Slats (lines that run horizontally)
+--------------------------------------------------------------------------------------------------*/
+.fc-time-grid .fc-slats td {
+ height: 1.5em;
+ border-bottom: 0;
+ /* each cell is responsible for its top border */ }
+
+.fc-time-grid .fc-slats .fc-minor td {
+ border-top-style: dotted; }
+
+/* TimeGrid Highlighting Slots
+--------------------------------------------------------------------------------------------------*/
+.fc-time-grid .fc-highlight-container {
+ /* a div within a cell within the fc-highlight-skeleton */
+ position: relative;
+ /* scopes the left/right of the fc-highlight to be in the column */ }
+
+.fc-time-grid .fc-highlight {
+ position: absolute;
+ left: 0;
+ right: 0;
+ /* top and bottom will be in by JS */ }
+
+/* TimeGrid Event Containment
+--------------------------------------------------------------------------------------------------*/
+.fc-ltr .fc-time-grid .fc-event-container {
+ /* space on the sides of events for LTR (default) */
+ margin: 0 2.5% 0 2px; }
+
+.fc-rtl .fc-time-grid .fc-event-container {
+ /* space on the sides of events for RTL */
+ margin: 0 2px 0 2.5%; }
+
+.fc-time-grid .fc-event,
+.fc-time-grid .fc-bgevent {
+ position: absolute;
+ z-index: 1;
+ /* scope inner z-index's */ }
+
+.fc-time-grid .fc-bgevent {
+ /* background events always span full width */
+ left: 0;
+ right: 0; }
+
+/* Generic Vertical Event
+--------------------------------------------------------------------------------------------------*/
+.fc-v-event.fc-not-start {
+ /* events that are continuing from another day */
+ /* replace space made by the top border with padding */
+ border-top-width: 0;
+ padding-top: 1px;
+ /* remove top rounded corners */
+ border-top-left-radius: 0;
+ border-top-right-radius: 0; }
+
+.fc-v-event.fc-not-end {
+ /* replace space made by the top border with padding */
+ border-bottom-width: 0;
+ padding-bottom: 1px;
+ /* remove bottom rounded corners */
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0; }
+
+/* TimeGrid Event Styling
+----------------------------------------------------------------------------------------------------
+We use the full "fc-time-grid-event" class instead of using descendants because the event won't
+be a descendant of the grid when it is being dragged.
+*/
+.fc-time-grid-event {
+ overflow: hidden;
+ /* don't let the bg flow over rounded corners */ }
+
+.fc-time-grid-event.fc-selected {
+ /* need to allow touch resizers to extend outside event's bounding box */
+ /* common fc-selected styles hide the fc-bg, so don't need this anyway */
+ overflow: visible; }
+
+.fc-time-grid-event.fc-selected .fc-bg {
+ display: none;
+ /* hide semi-white background, to appear darker */ }
+
+.fc-time-grid-event .fc-content {
+ overflow: hidden;
+ /* for when .fc-selected */ }
+
+.fc-time-grid-event .fc-time,
+.fc-time-grid-event .fc-title {
+ padding: 0 1px; }
+
+.fc-time-grid-event .fc-time {
+ font-size: .85em;
+ white-space: nowrap; }
+
+/* short mode, where time and title are on the same line */
+.fc-time-grid-event.fc-short .fc-content {
+ /* don't wrap to second line (now that contents will be inline) */
+ white-space: nowrap; }
+
+.fc-time-grid-event.fc-short .fc-time,
+.fc-time-grid-event.fc-short .fc-title {
+ /* put the time and title on the same line */
+ display: inline-block;
+ vertical-align: top; }
+
+.fc-time-grid-event.fc-short .fc-time span {
+ display: none;
+ /* don't display the full time text... */ }
+
+.fc-time-grid-event.fc-short .fc-time:before {
+ content: attr(data-start);
+ /* ...instead, display only the start time */ }
+
+.fc-time-grid-event.fc-short .fc-time:after {
+ content: "\A0-\A0";
+ /* seperate with a dash, wrapped in nbsp's */ }
+
+.fc-time-grid-event.fc-short .fc-title {
+ font-size: .85em;
+ /* make the title text the same size as the time */
+ padding: 0;
+ /* undo padding from above */ }
+
+/* resizer (cursor device) */
+.fc-time-grid-event.fc-allow-mouse-resize .fc-resizer {
+ left: 0;
+ right: 0;
+ bottom: 0;
+ height: 8px;
+ overflow: hidden;
+ line-height: 8px;
+ font-size: 11px;
+ font-family: monospace;
+ text-align: center;
+ cursor: s-resize; }
+
+.fc-time-grid-event.fc-allow-mouse-resize .fc-resizer:after {
+ content: "="; }
+
+/* resizer (touch device) */
+.fc-time-grid-event.fc-selected .fc-resizer {
+ /* 10x10 dot */
+ border-radius: 5px;
+ border-width: 1px;
+ width: 8px;
+ height: 8px;
+ border-style: solid;
+ border-color: inherit;
+ background: #fff;
+ /* horizontally center */
+ left: 50%;
+ margin-left: -5px;
+ /* center on the bottom edge */
+ bottom: -5px; }
+
+/* Now Indicator
+--------------------------------------------------------------------------------------------------*/
+.fc-time-grid .fc-now-indicator-line {
+ border-top-width: 1px;
+ left: 0;
+ right: 0; }
+
+/* arrow on axis */
+.fc-time-grid .fc-now-indicator-arrow {
+ margin-top: -5px;
+ /* vertically center on top coordinate */ }
+
+.fc-ltr .fc-time-grid .fc-now-indicator-arrow {
+ left: 0;
+ /* triangle pointing right... */
+ border-width: 5px 0 5px 6px;
+ border-top-color: transparent;
+ border-bottom-color: transparent; }
+
+.fc-rtl .fc-time-grid .fc-now-indicator-arrow {
+ right: 0;
+ /* triangle pointing left... */
+ border-width: 5px 6px 5px 0;
+ border-top-color: transparent;
+ border-bottom-color: transparent; }
+
+/* List View
+--------------------------------------------------------------------------------------------------*/
+/* possibly reusable */
+.fc-event-dot {
+ display: inline-block;
+ width: 10px;
+ height: 10px;
+ border-radius: 5px; }
+
+/* view wrapper */
+.fc-rtl .fc-list-view {
+ direction: rtl;
+ /* unlike core views, leverage browser RTL */ }
+
+.fc-list-view {
+ border-width: 1px;
+ border-style: solid; }
+
+/* table resets */
+.fc .fc-list-table {
+ table-layout: auto;
+ /* for shrinkwrapping cell content */ }
+
+.fc-list-table td {
+ border-width: 1px 0 0;
+ padding: 8px 14px; }
+
+.fc-list-table tr:first-child td {
+ border-top-width: 0; }
+
+/* day headings with the list */
+.fc-list-heading {
+ border-bottom-width: 1px; }
+
+.fc-list-heading td {
+ font-weight: bold; }
+
+.fc-ltr .fc-list-heading-main {
+ float: left; }
+
+.fc-ltr .fc-list-heading-alt {
+ float: right; }
+
+.fc-rtl .fc-list-heading-main {
+ float: right; }
+
+.fc-rtl .fc-list-heading-alt {
+ float: left; }
+
+/* event list items */
+.fc-list-item.fc-has-url {
+ cursor: pointer;
+ /* whole row will be clickable */ }
+
+.fc-list-item-marker,
+.fc-list-item-time {
+ white-space: nowrap;
+ width: 1px; }
+
+/* make the dot closer to the event title */
+.fc-ltr .fc-list-item-marker {
+ padding-right: 0; }
+
+.fc-rtl .fc-list-item-marker {
+ padding-left: 0; }
+
+.fc-list-item-title a {
+ /* every event title cell has an tag */
+ text-decoration: none;
+ color: inherit; }
+
+.fc-list-item-title a[href]:hover {
+ /* hover effect only on titles with hrefs */
+ text-decoration: underline; }
+
+/* message when no events */
+.fc-list-empty-wrap2 {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0; }
+
+.fc-list-empty-wrap1 {
+ width: 100%;
+ height: 100%;
+ display: table; }
+
+.fc-list-empty {
+ display: table-cell;
+ vertical-align: middle;
+ text-align: center; }
+
+.fc-unthemed .fc-list-empty {
+ /* theme will provide own background */
+ background-color: #eee; }
diff --git a/assets/css/fullcalendar.min.css b/assets/css/fullcalendar.min.css
new file mode 100644
index 0000000..cf86d29
--- /dev/null
+++ b/assets/css/fullcalendar.min.css
@@ -0,0 +1,5 @@
+/*!
+ * FullCalendar v3.9.0
+ * Docs & License: https://fullcalendar.io/
+ * (c) 2018 Adam Shaw
+ */.fc button,.fc table,body .fc{font-size:1em}.fc-bg,.fc-row .fc-bgevent-skeleton,.fc-row .fc-highlight-skeleton{bottom:0}.fc-icon,.fc-unselectable{-webkit-touch-callout:none;-khtml-user-select:none}.fc{direction:ltr;text-align:left}.fc-rtl{text-align:right}.fc th,.fc-basic-view td.fc-week-number,.fc-icon,.fc-toolbar{text-align:center}.fc-highlight{background:#bce8f1;opacity:.3}.fc-bgevent{background:#8fdf82;opacity:.3}.fc-nonbusiness{background:#d7d7d7}.fc button{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;margin:0;height:2.1em;padding:0 .6em;white-space:nowrap;cursor:pointer}.fc button::-moz-focus-inner{margin:0;padding:0}.fc-state-default{border:1px solid;background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);color:#333;text-shadow:0 1px 1px rgba(255,255,255,.75);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05)}.fc-state-default.fc-corner-left{border-top-left-radius:4px;border-bottom-left-radius:4px}.fc-state-default.fc-corner-right{border-top-right-radius:4px;border-bottom-right-radius:4px}.fc button .fc-icon{position:relative;top:-.05em;margin:0 .2em;vertical-align:middle}.fc-state-active,.fc-state-disabled,.fc-state-down,.fc-state-hover{color:#333;background-color:#e6e6e6}.fc-state-hover{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.fc-state-active,.fc-state-down{background-color:#ccc;background-image:none;box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05)}.fc-state-disabled{cursor:default;background-image:none;opacity:.65;box-shadow:none}.fc-event.fc-draggable,.fc-event[href],.fc-popover .fc-header .fc-close,a[data-goto]{cursor:pointer}.fc-button-group{display:inline-block}.fc .fc-button-group>*{float:left;margin:0 0 0 -1px}.fc .fc-button-group>:first-child{margin-left:0}.fc-popover{position:absolute;box-shadow:0 2px 6px rgba(0,0,0,.15)}.fc-popover .fc-header{padding:2px 4px}.fc-popover .fc-header .fc-title{margin:0 2px}.fc-ltr .fc-popover .fc-header .fc-title,.fc-rtl .fc-popover .fc-header .fc-close{float:left}.fc-ltr .fc-popover .fc-header .fc-close,.fc-rtl .fc-popover .fc-header .fc-title{float:right}.fc-divider{border-style:solid;border-width:1px}hr.fc-divider{height:0;margin:0;padding:0 0 2px;border-width:1px 0}.fc-bg table,.fc-row .fc-bgevent-skeleton table,.fc-row .fc-highlight-skeleton table{height:100%}.fc-clear{clear:both}.fc-bg,.fc-bgevent-skeleton,.fc-helper-skeleton,.fc-highlight-skeleton{position:absolute;top:0;left:0;right:0}.fc table{width:100%;box-sizing:border-box;table-layout:fixed;border-collapse:collapse;border-spacing:0}.fc td,.fc th{border-style:solid;border-width:1px;padding:0;vertical-align:top}.fc td.fc-today{border-style:double}a[data-goto]:hover{text-decoration:underline}.fc .fc-row{border-style:solid;border-width:0}.fc-row table{border-left:0 hidden transparent;border-right:0 hidden transparent;border-bottom:0 hidden transparent}.fc-row:first-child table{border-top:0 hidden transparent}.fc-row{position:relative}.fc-row .fc-bg{z-index:1}.fc-row .fc-bgevent-skeleton td,.fc-row .fc-highlight-skeleton td{border-color:transparent}.fc-row .fc-bgevent-skeleton{z-index:2}.fc-row .fc-highlight-skeleton{z-index:3}.fc-row .fc-content-skeleton{position:relative;z-index:4;padding-bottom:2px}.fc-row .fc-helper-skeleton{z-index:5}.fc .fc-row .fc-content-skeleton table,.fc .fc-row .fc-content-skeleton td,.fc .fc-row .fc-helper-skeleton td{background:0 0;border-color:transparent}.fc-row .fc-content-skeleton td,.fc-row .fc-helper-skeleton td{border-bottom:0}.fc-row .fc-content-skeleton tbody td,.fc-row .fc-helper-skeleton tbody td{border-top:0}.fc-scroller{-webkit-overflow-scrolling:touch}.fc-icon,.fc-row.fc-rigid,.fc-time-grid-event{overflow:hidden}.fc-scroller>.fc-day-grid,.fc-scroller>.fc-time-grid{position:relative;width:100%}.fc-event{position:relative;display:block;font-size:.85em;line-height:1.3;border-radius:3px;border:1px solid #3a87ad}.fc-event,.fc-event-dot{background-color:#3a87ad}.fc-event,.fc-event:hover{color:#fff;text-decoration:none}.fc-not-allowed,.fc-not-allowed .fc-event{cursor:not-allowed}.fc-event .fc-bg{z-index:1;background:#fff;opacity:.25}.fc-event .fc-content{position:relative;z-index:2}.fc-event .fc-resizer{position:absolute;z-index:4;display:none}.fc-event.fc-allow-mouse-resize .fc-resizer,.fc-event.fc-selected .fc-resizer{display:block}.fc-event.fc-selected .fc-resizer:before{content:"";position:absolute;z-index:9999;top:50%;left:50%;width:40px;height:40px;margin-left:-20px;margin-top:-20px}.fc-event.fc-selected{z-index:9999!important;box-shadow:0 2px 5px rgba(0,0,0,.2)}.fc-event.fc-selected.fc-dragging{box-shadow:0 2px 7px rgba(0,0,0,.3)}.fc-h-event.fc-selected:before{content:"";position:absolute;z-index:3;top:-10px;bottom:-10px;left:0;right:0}.fc-ltr .fc-h-event.fc-not-start,.fc-rtl .fc-h-event.fc-not-end{margin-left:0;border-left-width:0;padding-left:1px;border-top-left-radius:0;border-bottom-left-radius:0}.fc-ltr .fc-h-event.fc-not-end,.fc-rtl .fc-h-event.fc-not-start{margin-right:0;border-right-width:0;padding-right:1px;border-top-right-radius:0;border-bottom-right-radius:0}.fc-ltr .fc-h-event .fc-start-resizer,.fc-rtl .fc-h-event .fc-end-resizer{cursor:w-resize;left:-1px}.fc-ltr .fc-h-event .fc-end-resizer,.fc-rtl .fc-h-event .fc-start-resizer{cursor:e-resize;right:-1px}.fc-h-event.fc-allow-mouse-resize .fc-resizer{width:7px;top:-1px;bottom:-1px}.fc-h-event.fc-selected .fc-resizer{border-radius:4px;border-width:1px;width:6px;height:6px;border-style:solid;border-color:inherit;background:#fff;top:50%;margin-top:-4px}.fc-ltr .fc-h-event.fc-selected .fc-start-resizer,.fc-rtl .fc-h-event.fc-selected .fc-end-resizer{margin-left:-4px}.fc-ltr .fc-h-event.fc-selected .fc-end-resizer,.fc-rtl .fc-h-event.fc-selected .fc-start-resizer{margin-right:-4px}.fc-day-grid-event{margin:1px 2px 0;padding:0 1px}tr:first-child>td>.fc-day-grid-event{margin-top:2px}.fc-day-grid-event.fc-selected:after{content:"";position:absolute;z-index:1;top:-1px;right:-1px;bottom:-1px;left:-1px;background:#000;opacity:.25}.fc-day-grid-event .fc-content{white-space:nowrap;overflow:hidden}.fc-day-grid-event .fc-time{font-weight:700}.fc-ltr .fc-day-grid-event.fc-allow-mouse-resize .fc-start-resizer,.fc-rtl .fc-day-grid-event.fc-allow-mouse-resize .fc-end-resizer{margin-left:-2px}.fc-ltr .fc-day-grid-event.fc-allow-mouse-resize .fc-end-resizer,.fc-rtl .fc-day-grid-event.fc-allow-mouse-resize .fc-start-resizer{margin-right:-2px}a.fc-more{margin:1px 3px;font-size:.85em;cursor:pointer;text-decoration:none}a.fc-more:hover{text-decoration:underline}.fc.fc-bootstrap3 a,.ui-widget .fc-event{text-decoration:none}.fc-limited{display:none}.fc-icon,.fc-toolbar .fc-center{display:inline-block}.fc-day-grid .fc-row{z-index:1}.fc-more-popover{z-index:2;width:220px}.fc-more-popover .fc-event-container{padding:10px}.fc-bootstrap3 .fc-popover .panel-body,.fc-bootstrap4 .fc-popover .card-body{padding:0}.fc-now-indicator{position:absolute;border:0 solid red}.fc-bootstrap3 .fc-today.alert,.fc-bootstrap4 .fc-today.alert{border-radius:0}.fc-unselectable{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent}.fc-unthemed .fc-content,.fc-unthemed .fc-divider,.fc-unthemed .fc-list-heading td,.fc-unthemed .fc-list-view,.fc-unthemed .fc-popover,.fc-unthemed .fc-row,.fc-unthemed tbody,.fc-unthemed td,.fc-unthemed th,.fc-unthemed thead{border-color:#ddd}.fc-unthemed .fc-popover{background-color:#fff;border-width:1px;border-style:solid}.fc-unthemed .fc-divider,.fc-unthemed .fc-list-heading td,.fc-unthemed .fc-popover .fc-header{background:#eee}.fc-unthemed td.fc-today{background:#fcf8e3}.fc-unthemed .fc-disabled-day{background:#d7d7d7;opacity:.3}.fc-icon{height:1em;line-height:1em;font-size:1em;font-family:"Courier New",Courier,monospace;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.fc-icon:after{position:relative}.fc-icon-left-single-arrow:after{content:"\2039";font-weight:700;font-size:200%;top:-7%}.fc-icon-right-single-arrow:after{content:"\203A";font-weight:700;font-size:200%;top:-7%}.fc-icon-left-double-arrow:after{content:"\AB";font-size:160%;top:-7%}.fc-icon-right-double-arrow:after{content:"\BB";font-size:160%;top:-7%}.fc-icon-left-triangle:after{content:"\25C4";font-size:125%;top:3%}.fc-icon-right-triangle:after{content:"\25BA";font-size:125%;top:3%}.fc-icon-down-triangle:after{content:"\25BC";font-size:125%;top:2%}.fc-icon-x:after{content:"\D7";font-size:200%;top:6%}.fc-unthemed .fc-popover .fc-header .fc-close{color:#666;font-size:.9em;margin-top:2px}.fc-unthemed .fc-list-item:hover td{background-color:#f5f5f5}.ui-widget .fc-disabled-day{background-image:none}.fc-bootstrap3 .fc-time-grid .fc-slats table,.fc-bootstrap4 .fc-time-grid .fc-slats table,.fc-time-grid .fc-slats .ui-widget-content{background:0 0}.fc-popover>.ui-widget-header+.ui-widget-content{border-top:0}.fc-bootstrap3 hr.fc-divider,.fc-bootstrap4 hr.fc-divider{border-color:inherit}.ui-widget .fc-event{color:#fff;font-weight:400}.ui-widget td.fc-axis{font-weight:400}.fc.fc-bootstrap3 a[data-goto]:hover{text-decoration:underline}.fc.fc-bootstrap4 a{text-decoration:none}.fc.fc-bootstrap4 a[data-goto]:hover{text-decoration:underline}.fc-bootstrap4 a.fc-event:not([href]):not([tabindex]){color:#fff}.fc-bootstrap4 .fc-popover.card{position:absolute}.fc-toolbar.fc-header-toolbar{margin-bottom:1em}.fc-toolbar.fc-footer-toolbar{margin-top:1em}.fc-toolbar .fc-left{float:left}.fc-toolbar .fc-right{float:right}.fc .fc-toolbar>*>*{float:left;margin-left:.75em}.fc .fc-toolbar>*>:first-child{margin-left:0}.fc-toolbar h2{margin:0}.fc-toolbar button{position:relative}.fc-toolbar .fc-state-hover,.fc-toolbar .ui-state-hover{z-index:2}.fc-toolbar .fc-state-down{z-index:3}.fc-toolbar .fc-state-active,.fc-toolbar .ui-state-active{z-index:4}.fc-toolbar button:focus{z-index:5}.fc-view-container *,.fc-view-container :after,.fc-view-container :before{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.fc-view,.fc-view>table{position:relative;z-index:1}.fc-basicDay-view .fc-content-skeleton,.fc-basicWeek-view .fc-content-skeleton{padding-bottom:1em}.fc-basic-view .fc-body .fc-row{min-height:4em}.fc-row.fc-rigid .fc-content-skeleton{position:absolute;top:0;left:0;right:0}.fc-day-top.fc-other-month{opacity:.3}.fc-basic-view .fc-day-number,.fc-basic-view .fc-week-number{padding:2px}.fc-basic-view th.fc-day-number,.fc-basic-view th.fc-week-number{padding:0 2px}.fc-ltr .fc-basic-view .fc-day-top .fc-day-number{float:right}.fc-rtl .fc-basic-view .fc-day-top .fc-day-number{float:left}.fc-ltr .fc-basic-view .fc-day-top .fc-week-number{float:left;border-radius:0 0 3px}.fc-rtl .fc-basic-view .fc-day-top .fc-week-number{float:right;border-radius:0 0 0 3px}.fc-basic-view .fc-day-top .fc-week-number{min-width:1.5em;text-align:center;background-color:#f2f2f2;color:grey}.fc-basic-view td.fc-week-number>*{display:inline-block;min-width:1.25em}.fc-agenda-view .fc-day-grid{position:relative;z-index:2}.fc-agenda-view .fc-day-grid .fc-row{min-height:3em}.fc-agenda-view .fc-day-grid .fc-row .fc-content-skeleton{padding-bottom:1em}.fc .fc-axis{vertical-align:middle;padding:0 4px;white-space:nowrap}.fc-ltr .fc-axis{text-align:right}.fc-rtl .fc-axis{text-align:left}.fc-time-grid,.fc-time-grid-container{position:relative;z-index:1}.fc-time-grid{min-height:100%}.fc-time-grid table{border:0 hidden transparent}.fc-time-grid>.fc-bg{z-index:1}.fc-time-grid .fc-slats,.fc-time-grid>hr{position:relative;z-index:2}.fc-time-grid .fc-content-col{position:relative}.fc-time-grid .fc-content-skeleton{position:absolute;z-index:3;top:0;left:0;right:0}.fc-time-grid .fc-business-container{position:relative;z-index:1}.fc-time-grid .fc-bgevent-container{position:relative;z-index:2}.fc-time-grid .fc-highlight-container{z-index:3;position:relative}.fc-time-grid .fc-event-container{position:relative;z-index:4}.fc-time-grid .fc-now-indicator-line{z-index:5}.fc-time-grid .fc-helper-container{position:relative;z-index:6}.fc-time-grid .fc-slats td{height:1.5em;border-bottom:0}.fc-time-grid .fc-slats .fc-minor td{border-top-style:dotted}.fc-time-grid .fc-highlight{position:absolute;left:0;right:0}.fc-ltr .fc-time-grid .fc-event-container{margin:0 2.5% 0 2px}.fc-rtl .fc-time-grid .fc-event-container{margin:0 2px 0 2.5%}.fc-time-grid .fc-bgevent,.fc-time-grid .fc-event{position:absolute;z-index:1}.fc-time-grid .fc-bgevent{left:0;right:0}.fc-v-event.fc-not-start{border-top-width:0;padding-top:1px;border-top-left-radius:0;border-top-right-radius:0}.fc-v-event.fc-not-end{border-bottom-width:0;padding-bottom:1px;border-bottom-left-radius:0;border-bottom-right-radius:0}.fc-time-grid-event.fc-selected{overflow:visible}.fc-time-grid-event.fc-selected .fc-bg{display:none}.fc-time-grid-event .fc-content{overflow:hidden}.fc-time-grid-event .fc-time,.fc-time-grid-event .fc-title{padding:0 1px}.fc-time-grid-event .fc-time{font-size:.85em;white-space:nowrap}.fc-time-grid-event.fc-short .fc-content{white-space:nowrap}.fc-time-grid-event.fc-short .fc-time,.fc-time-grid-event.fc-short .fc-title{display:inline-block;vertical-align:top}.fc-time-grid-event.fc-short .fc-time span{display:none}.fc-time-grid-event.fc-short .fc-time:before{content:attr(data-start)}.fc-time-grid-event.fc-short .fc-time:after{content:"\A0-\A0"}.fc-time-grid-event.fc-short .fc-title{font-size:.85em;padding:0}.fc-time-grid-event.fc-allow-mouse-resize .fc-resizer{left:0;right:0;bottom:0;height:8px;overflow:hidden;line-height:8px;font-size:11px;font-family:monospace;text-align:center;cursor:s-resize}.fc-time-grid-event.fc-allow-mouse-resize .fc-resizer:after{content:"="}.fc-time-grid-event.fc-selected .fc-resizer{border-radius:5px;border-width:1px;width:8px;height:8px;border-style:solid;border-color:inherit;background:#fff;left:50%;margin-left:-5px;bottom:-5px}.fc-time-grid .fc-now-indicator-line{border-top-width:1px;left:0;right:0}.fc-time-grid .fc-now-indicator-arrow{margin-top:-5px}.fc-ltr .fc-time-grid .fc-now-indicator-arrow{left:0;border-width:5px 0 5px 6px;border-top-color:transparent;border-bottom-color:transparent}.fc-rtl .fc-time-grid .fc-now-indicator-arrow{right:0;border-width:5px 6px 5px 0;border-top-color:transparent;border-bottom-color:transparent}.fc-event-dot{display:inline-block;width:10px;height:10px;border-radius:5px}.fc-rtl .fc-list-view{direction:rtl}.fc-list-view{border-width:1px;border-style:solid}.fc .fc-list-table{table-layout:auto}.fc-list-table td{border-width:1px 0 0;padding:8px 14px}.fc-list-table tr:first-child td{border-top-width:0}.fc-list-heading{border-bottom-width:1px}.fc-list-heading td{font-weight:700}.fc-ltr .fc-list-heading-main{float:left}.fc-ltr .fc-list-heading-alt,.fc-rtl .fc-list-heading-main{float:right}.fc-rtl .fc-list-heading-alt{float:left}.fc-list-item.fc-has-url{cursor:pointer}.fc-list-item-marker,.fc-list-item-time{white-space:nowrap;width:1px}.fc-ltr .fc-list-item-marker{padding-right:0}.fc-rtl .fc-list-item-marker{padding-left:0}.fc-list-item-title a{text-decoration:none;color:inherit}.fc-list-item-title a[href]:hover{text-decoration:underline}.fc-list-empty-wrap2{position:absolute;top:0;left:0;right:0;bottom:0}.fc-list-empty-wrap1{width:100%;height:100%;display:table}.fc-list-empty{display:table-cell;vertical-align:middle;text-align:center}.fc-unthemed .fc-list-empty{background-color:#eee}
\ No newline at end of file
diff --git a/assets/css/fullcalendar.print.css b/assets/css/fullcalendar.print.css
new file mode 100644
index 0000000..fb858cd
--- /dev/null
+++ b/assets/css/fullcalendar.print.css
@@ -0,0 +1,176 @@
+/*!
+ * FullCalendar v3.9.0
+ * Docs & License: https://fullcalendar.io/
+ * (c) 2018 Adam Shaw
+ */
+/*!
+ * FullCalendar v3.9.0 Print Stylesheet
+ * Docs & License: https://fullcalendar.io/
+ * (c) 2018 Adam Shaw
+ */
+/*
+ * Include this stylesheet on your page to get a more printer-friendly calendar.
+ * When including this stylesheet, use the media='print' attribute of the tag.
+ * Make sure to include this stylesheet IN ADDITION to the regular fullcalendar.css.
+ */
+.fc {
+ max-width: 100% !important; }
+
+/* Global Event Restyling
+--------------------------------------------------------------------------------------------------*/
+.fc-event {
+ background: #fff !important;
+ color: #000 !important;
+ page-break-inside: avoid; }
+
+.fc-event .fc-resizer {
+ display: none; }
+
+/* Table & Day-Row Restyling
+--------------------------------------------------------------------------------------------------*/
+.fc th,
+.fc td,
+.fc hr,
+.fc thead,
+.fc tbody,
+.fc-row {
+ border-color: #ccc !important;
+ background: #fff !important; }
+
+/* kill the overlaid, absolutely-positioned components */
+/* common... */
+.fc-bg,
+.fc-bgevent-skeleton,
+.fc-highlight-skeleton,
+.fc-helper-skeleton,
+.fc-bgevent-container,
+.fc-business-container,
+.fc-highlight-container,
+.fc-helper-container {
+ display: none; }
+
+/* don't force a min-height on rows (for DayGrid) */
+.fc tbody .fc-row {
+ height: auto !important;
+ /* undo height that JS set in distributeHeight */
+ min-height: 0 !important;
+ /* undo the min-height from each view's specific stylesheet */ }
+
+.fc tbody .fc-row .fc-content-skeleton {
+ position: static;
+ /* undo .fc-rigid */
+ padding-bottom: 0 !important;
+ /* use a more border-friendly method for this... */ }
+
+.fc tbody .fc-row .fc-content-skeleton tbody tr:last-child td {
+ /* only works in newer browsers */
+ padding-bottom: 1em;
+ /* ...gives space within the skeleton. also ensures min height in a way */ }
+
+.fc tbody .fc-row .fc-content-skeleton table {
+ /* provides a min-height for the row, but only effective for IE, which exaggerates this value,
+ making it look more like 3em. for other browers, it will already be this tall */
+ height: 1em; }
+
+/* Undo month-view event limiting. Display all events and hide the "more" links
+--------------------------------------------------------------------------------------------------*/
+.fc-more-cell,
+.fc-more {
+ display: none !important; }
+
+.fc tr.fc-limited {
+ display: table-row !important; }
+
+.fc td.fc-limited {
+ display: table-cell !important; }
+
+.fc-popover {
+ display: none;
+ /* never display the "more.." popover in print mode */ }
+
+/* TimeGrid Restyling
+--------------------------------------------------------------------------------------------------*/
+/* undo the min-height 100% trick used to fill the container's height */
+.fc-time-grid {
+ min-height: 0 !important; }
+
+/* don't display the side axis at all ("all-day" and time cells) */
+.fc-agenda-view .fc-axis {
+ display: none; }
+
+/* don't display the horizontal lines */
+.fc-slats,
+.fc-time-grid hr {
+ /* this hr is used when height is underused and needs to be filled */
+ display: none !important;
+ /* important overrides inline declaration */ }
+
+/* let the container that holds the events be naturally positioned and create real height */
+.fc-time-grid .fc-content-skeleton {
+ position: static; }
+
+/* in case there are no events, we still want some height */
+.fc-time-grid .fc-content-skeleton table {
+ height: 4em; }
+
+/* kill the horizontal spacing made by the event container. event margins will be done below */
+.fc-time-grid .fc-event-container {
+ margin: 0 !important; }
+
+/* TimeGrid *Event* Restyling
+--------------------------------------------------------------------------------------------------*/
+/* naturally position events, vertically stacking them */
+.fc-time-grid .fc-event {
+ position: static !important;
+ margin: 3px 2px !important; }
+
+/* for events that continue to a future day, give the bottom border back */
+.fc-time-grid .fc-event.fc-not-end {
+ border-bottom-width: 1px !important; }
+
+/* indicate the event continues via "..." text */
+.fc-time-grid .fc-event.fc-not-end:after {
+ content: "..."; }
+
+/* for events that are continuations from previous days, give the top border back */
+.fc-time-grid .fc-event.fc-not-start {
+ border-top-width: 1px !important; }
+
+/* indicate the event is a continuation via "..." text */
+.fc-time-grid .fc-event.fc-not-start:before {
+ content: "..."; }
+
+/* time */
+/* undo a previous declaration and let the time text span to a second line */
+.fc-time-grid .fc-event .fc-time {
+ white-space: normal !important; }
+
+/* hide the the time that is normally displayed... */
+.fc-time-grid .fc-event .fc-time span {
+ display: none; }
+
+/* ...replace it with a more verbose version (includes AM/PM) stored in an html attribute */
+.fc-time-grid .fc-event .fc-time:after {
+ content: attr(data-full); }
+
+/* Vertical Scroller & Containers
+--------------------------------------------------------------------------------------------------*/
+/* kill the scrollbars and allow natural height */
+.fc-scroller,
+.fc-day-grid-container,
+.fc-time-grid-container {
+ /* */
+ overflow: visible !important;
+ height: auto !important; }
+
+/* kill the horizontal border/padding used to compensate for scrollbars */
+.fc-row {
+ border: 0 !important;
+ margin: 0 !important; }
+
+/* Button Controls
+--------------------------------------------------------------------------------------------------*/
+.fc-button-group,
+.fc button {
+ display: none;
+ /* don't display any button-related controls */ }
diff --git a/assets/css/fullcalendar.print.min.css b/assets/css/fullcalendar.print.min.css
new file mode 100644
index 0000000..59a405c
--- /dev/null
+++ b/assets/css/fullcalendar.print.min.css
@@ -0,0 +1,9 @@
+/*!
+ * FullCalendar v3.9.0
+ * Docs & License: https://fullcalendar.io/
+ * (c) 2018 Adam Shaw
+ *//*!
+ * FullCalendar v3.9.0 Print Stylesheet
+ * Docs & License: https://fullcalendar.io/
+ * (c) 2018 Adam Shaw
+ */.fc-bg,.fc-bgevent-container,.fc-bgevent-skeleton,.fc-business-container,.fc-event .fc-resizer,.fc-helper-container,.fc-helper-skeleton,.fc-highlight-container,.fc-highlight-skeleton{display:none}.fc tbody .fc-row,.fc-time-grid{min-height:0!important}.fc-time-grid .fc-event.fc-not-end:after,.fc-time-grid .fc-event.fc-not-start:before{content:"..."}.fc{max-width:100%!important}.fc-event{background:#fff!important;color:#000!important;page-break-inside:avoid}.fc hr,.fc tbody,.fc td,.fc th,.fc thead,.fc-row{border-color:#ccc!important;background:#fff!important}.fc tbody .fc-row{height:auto!important}.fc tbody .fc-row .fc-content-skeleton{position:static;padding-bottom:0!important}.fc tbody .fc-row .fc-content-skeleton tbody tr:last-child td{padding-bottom:1em}.fc tbody .fc-row .fc-content-skeleton table{height:1em}.fc-more,.fc-more-cell{display:none!important}.fc tr.fc-limited{display:table-row!important}.fc td.fc-limited{display:table-cell!important}.fc-agenda-view .fc-axis,.fc-popover{display:none}.fc-slats,.fc-time-grid hr{display:none!important}.fc button,.fc-button-group,.fc-time-grid .fc-event .fc-time span{display:none}.fc-time-grid .fc-content-skeleton{position:static}.fc-time-grid .fc-content-skeleton table{height:4em}.fc-time-grid .fc-event-container{margin:0!important}.fc-time-grid .fc-event{position:static!important;margin:3px 2px!important}.fc-time-grid .fc-event.fc-not-end{border-bottom-width:1px!important}.fc-time-grid .fc-event.fc-not-start{border-top-width:1px!important}.fc-time-grid .fc-event .fc-time{white-space:normal!important}.fc-time-grid .fc-event .fc-time:after{content:attr(data-full)}.fc-day-grid-container,.fc-scroller,.fc-time-grid-container{overflow:visible!important;height:auto!important}.fc-row{border:0!important;margin:0!important}
\ No newline at end of file
diff --git a/assets/css/ie8.css b/assets/css/ie8.css
new file mode 100644
index 0000000..dee86c7
--- /dev/null
+++ b/assets/css/ie8.css
@@ -0,0 +1,21 @@
+/*
+ Editorial by HTML5 UP
+ html5up.net | @ajlkn
+ Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
+*/
+
+/* Button */
+
+ input[type="submit"],
+ input[type="reset"],
+ input[type="button"],
+ button,
+ .button {
+ border: solid 2px #f56a6a;
+ }
+
+/* Posts */
+
+ .posts article {
+ width: 40%;
+ }
\ No newline at end of file
diff --git a/assets/css/ie9.css b/assets/css/ie9.css
new file mode 100644
index 0000000..386d248
--- /dev/null
+++ b/assets/css/ie9.css
@@ -0,0 +1,73 @@
+/*
+ Editorial by HTML5 UP
+ html5up.net | @ajlkn
+ Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
+*/
+
+/* Features */
+
+ .features:after {
+ clear: both;
+ content: '';
+ display: block;
+ }
+
+ .features article {
+ float: left;
+ }
+
+ .features article:after {
+ clear: both;
+ content: '';
+ display: block;
+ }
+
+ .features article .icon {
+ float: left;
+ }
+
+/* Posts */
+
+ .posts:after {
+ clear: both;
+ content: '';
+ display: block;
+ }
+
+ .posts article {
+ float: left;
+ }
+
+/* Main */
+
+ #main {
+ padding-left: 24em;
+ }
+
+/* Sidebar */
+
+ #sidebar {
+ position: absolute;
+ top: 0;
+ left: 0;
+ min-height: 100%;
+ width: 24em;
+ }
+
+/* Banner */
+
+ #banner:after {
+ clear: both;
+ content: '';
+ display: block;
+ }
+
+ #banner .content {
+ float: left;
+ padding-right: 4em;
+ }
+
+ #banner .image {
+ float: left;
+ margin-left: 0;
+ }
\ No newline at end of file
diff --git a/assets/css/images/layers-2x.png b/assets/css/images/layers-2x.png
new file mode 100644
index 0000000000000000000000000000000000000000..200c333dca9652ac4cba004d609e5af4eee168c1
GIT binary patch
literal 1259
zcmVFhCYNy;#0irRPomHqW|G1C*;4?@4#E?jH>?v@U%cy?3dQAc-DchXVErpOh~
z-jbon+tNbnl6hoEb;)TVk+%hTDDi_G%i3*RZ&15!$Fjr^f;Ke&A@|?=`2&+{zr+3a
z{D*=t(`AXyS%X7N
z%a#RZw6vD^t_rnM`L4E>m=U&R!A-&}nZIi$BOPvkhrCuUe@BN~-lRD)f44;J%TwgE
zcze8u!PQ_NR7?o(NylLXVTfDO
zxs5=@|GsYEsNo4M#nT%N!UE(?dnS)t2+{ELYAFp*3=iF=|EQnTp`#vlSXuGVraYo?
z+RCzXo6h3qA8{KG?S4nE(lM+;Eb4nT3XV;7gcAxUi5m)`k5tv}cPy()8ZR3TLW3I-
zAS^}cq-IJvL7a4RgR!yk@~RT%$lA7{L5ES*hyx)M4(yxI$Ub(4f)K|^v1>zvwQY!_
zIrWw8q9GS^!Dp~}+?mbnB6jDF8mVlbQ!jFKDY;w=7;XO{9bq7>LXGK24WA`;rL)_Z
z)&j}pbV(;6gY;VMhbxgvn`X;6x}VUEE-7
z%)7j-%t8S=ZL3yc)HbXDAqJZvBTPoiW_A-+a8m3_Z?v{DN7Tnr#O_VUMT0UBt$;p`
zDh6JbGHN8JJ*JN%y2%msb97@_S>9!%Egwk;?PEkU9ntz&3uR}%Fj5d$JHQbQb3}a{
zSzFT^#n=VInPpcAS}CNxj?_
zVscANk5Cfz(51EI1pz};AWWb|kgbYNb4wCEGUn3+eMUMV?1-{=I4TlmLJMot@rd07
zZuo2hk1ccu{YmGkcYdWAVdk{Z4Nm?^cTD&}jGm+Q1SYIXMwmG*oO*83>l%nbR`G
zhh=lZ%xIb7kU3#;TBbfECrnC9P=-XpL|TG2BoZdj61*XiFbW8?1Z_wp%#;>${SUIy
V$8qr;L*)Pf002ovPDHLkV1hYLS~36t
literal 0
HcmV?d00001
diff --git a/assets/css/images/layers.png b/assets/css/images/layers.png
new file mode 100644
index 0000000000000000000000000000000000000000..1a72e5784b2b456eac5d7670738db80697af3377
GIT binary patch
literal 696
zcmV;p0!RIcP)*@&l2<6p=!C&s@#ZL+%BQvF&b?w6S%wp=I>1QHj7AP5C)IWy#b
znXXB;g;j=$a-tW89K%FbDceHVq&unY*Wx3L#=EGWH=rjqnp|4c_Ulec!ql3#G-5ZF
zVlbBA@XP=)C8U&+Lrc)S4O5%1$&{(;7R^K(CSnvSr$v;+B$8q&7Bf|h$#PARo1^%M
zf1H^nG-EiXVXr07OH(*8R)xa|FD;lXUlg_-%)~ZGsL2cX0NXaAzN2q%jqLRR6ruVk8`Jb7n#{`T;o@`F=
z#3YcynIR^s83UNF3D!f5m#Mg)NJ24&Qfrqb&_z=yF;=B)#9Iq7u-@^O!(mW{D;qvr
zPc)gVb%aowtS8m@ElL4A9G>w#ffQ~q{i&_i)*6f^)Sz|C?C>zb4Uo?H<-&Hz@a?J;
z$ml@zGygWofb9$ZBj6aLjpLhsT2AzjOu=-*u_gSCULuqp7Vds@BKcX=lOk~^Pb;%&wG9p3o}FL;Zuhp5D3)R
z2yY2yCGfH2=LJmOkQw^9n>daF6?Fz>oOD64$CM+_T`j0x%{zb|G
zWolt{H|diO#S`|$6RM$
zYQuE4RW{2yZ`>fAt>jzyYyOB?)~HrQBlbbLT5yF%Xq8FEuzo80dd{%Q!{_)^mTE`^
z2$xe>TH$qiDJ+}(ajTp$Y*4vgGRrt^_?JwUO3+hm&{Mb<8aRtf7%F@*!JJv*
zmcB*cag=-t4U&g79u1krRAKHHm?ZXHP8z-#KdAM9?vU7sxldD%A5;r0Rk~kblro}5
z9YhoJP18m~=v^kMBWPltYTV$TD;r4n^eZVWmDs^6;ZN_RG+a#^(N18a+%xd;JvScL
zu54_hiMdFR4767cmcp!KOryQBQG{$|3e)h(z_sY-NRM>A$84n-CdxAt6V242bQmV|
z86*uGCJtVTXCvLyz=eM@jE-Vz#IeA4DY~VhqL`R_>D;YIh9amQX~+l$Sfbohb*X)d
zKiDG!?8t|64T_+_Jzbv6K)P|KG-6qDVGPYUwpPqb#c;-juz~ZW0bFF4JlB>cOB#?3
z9XJ~@0J1u{T_(66oVpmpLOkqOk6}qY=vN7820OS|_L-o5(4!i~Ivv=j{IKzS2m>C_
zhm9Npo09&0s*wy#K%InNpSW)yCZOhAFheUQtcXnn!x)WSjonNUm7@fguKPg0C3ESs~`Bd3Pyd$@XU8m
z0JZWv0l=fZ{{jH?{!9Nt!mEGL|9_Oug?i>9H?4E!|Krk+(hy9WRiM;!>w8@J9&fq&
z${#rK1z4j2$*KVGO=b{ivL6FFEPprv0No7|9RPB_H>dzW{;{(>P`XWmKn^Y#<8`e9
zc*;k@X>z(^khkvlh3UB1ICnF@RRHbZaQhkI;sl{txVGnBEzaFKZpw96Fm8qu^5@!a
z+db!omc48o>}VvJr!j9Mpo^ZMPs2FKikZu-3edWhZ~5&Mp15G60gsVYic)|~eH4Q6
zF8d5^efqo~DD}CwRpRO|j91O-zygw(bv;<>V5MDzeC#nk
zosJI@GCU;ylx)tp87H~!5Gl8^4UxdZ-ZLrRy7g=zwjIe|v>O(6W-QBuv-7h4HTLcz&ce9H!^9o^4XLD_t08@f%uD+tdxMAHzHi
z6>y1>XBw|wNRu9u6j`13s*X9iz%Z1zep^?+<}$-U*uzd9$?LD0QWc+GSyhyvx<?!6YcvM{vC6CN2-dD>XyCsuOMe
zdjA0H)tFMHvR%5Uqd_swkzDP0t5)bhy5xwusp(WsD}~`13N0NuN78MHcc03G_@3v-
zZOvStb!W8+G+$o+mNh5)?USue0<9~5nql|l&C!mcb^cmUZGk2gF&p9IOMcs@2-WZX
z+M_WESiwx34!IyuOY(`!=Sit;If5uuYqSJm`D>ogL1P7x5=v2W{zicaAxUs>WGzTn
zQv?x3HR!VK$IB{-D-)cU&hLE;M2}umynSZBHRVLCW#WkaY>!>~#*V;;^Ck!H4Swwp
zDHCGo7gMu}4-?)ga$s&da$6}|l&eSgpl~CnG5lbg
z7&|&nHy^@(l0;d(4qw!>Pc+03BPqwvhV@DjJr)KAb74dUY>mzPErgW+cGhAfAE(Hx
zg7S551PZuugrt1qVHk*xE*1`NeDO|ZnOO1ye(Ps{N=r+Q=S*|(%4dYb+TIr5*H@Ka
z&IFce5q4snQ7O4sQm?Pxu??B#U>#Bu+HC!Ti{Sl150Y#4pk06Ac+lU@`2YRqk-uHH
zZoIWi#kr-H+gi|P?w*2JMQ7U)c>*fCAPTksemc#0N4+Zgz+o*bN1@=(#&Q(RLz+r2
zQx|up>q>^w^^^t*`_3bp*JBDwCvP3iT>oMu+dLrW{Yd*GhC1Kx;_L$zF%*j;?iDxZ
zrao$m-Bw;}qtlD8Ts>}{*(A|it9iEx_ZRY$yVv3y#q}J<;l}p;3_y0NqKJBW%sac-
z#s<-=rSr4%CNFQcuf<8$A3ba|hx+!=-B0jwr*}bFG1p0OLTqz#DYd
z16dVY=E5n{UkaA*7{FAF7c$=SE0gV@(AxW_6rfOFvBFyfQpO=ChwyqQo?nZOT`6__
zP3(sCcoy|xktOO{hUoSFKDM)^*yWXvlS$9yTyC~k^q#t~$$O;oU_E7XGiY~S^b+mS
zVh=RZHn+0(T-ooM5xx%AW=ZUqv
zgKQURIr-z7x5ejdVPYlT>F)dyou|#!MM#5qXK_BVQyz*bJ!*A&^rr((=SaeGlUNwV
z01+e{DcnsPPIth+gTfMc34NrqGRM-T5f0=)<0vZ6?K`I0Z1Y3GdqxI|$iyh%qoeNX
UQO-*oc+)|Q_08}VdXD6O0C*xx%>V!Z
literal 0
HcmV?d00001
diff --git a/assets/css/images/marker-icon.png b/assets/css/images/marker-icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..950edf24677ded147df13b26f91baa2b0fa70513
GIT binary patch
literal 1466
zcmV;r1x5OaP)P001cn1^@s6z>|W`000GnNklGNuHDcIX17Zdjl&3`L?0sTjIws<{((Dh&g-s0<@jYQyl?D*X^?%13;ml^gy>
ziMrY_^1WI=(g@LMizu=zCoA>C`6|QEq1eV92k*7m>G65*&@&6)aC&e}G
zI)pf-Za|N`DT&Cn1J|o`19mumxW~hiKiKyc-P`S@q)rdTo84@QI@;0yXrG%9uhI>A
zG5QHb6s4=<6xy{1
z@NMxEkryp{LS44%z$3lP^cX!9+2-;CTt3wM4(k*#C{aiIiLuB>jJj;KPhPzIC00bL
zU3a#;aJld94lCW=`4&aAy8M7PY=HQ>O%$YEP4c4UY#CRxfgbE~(|uiI=YS8q;O9y6
zmIkXzR`}p7ti|PrM3a}WMnR=3NVnWdAAR>b9X@)DKL6=YsvmH%?I24wdq?Gh54_;#
z$?_LvgjEdspdQlft#4CQ
z`2Zyvy?*)N1Ftw|{_hakhG9WjS?Az@I@+IZ8JbWewR!XUK4&6346+d#~gsE0SY(LX8&JfY>Aj)RxGy96nwhs2rv
zzW6pTnMpFkDSkT*a*6Dx|u@ds6ISVn0@^RmIsKZ5Y;bazbc;tTSq(kg(=481ODrPyNB6n
z-$+U}(w$m6U6H$w17Bw+wDaFIe~GvNMYvnw31MpY0eQKT9l>SU``8k7w4)z!GZKMI
z#_cEKq7k~i%nlK@6c-K?+R;B#5$?T#YpKD`t_4bAs^#E+@5QW$@OX3*`;(#{U^d-vY)&xEE>n5lYl&T?Amke9$Lam@{1K@O
ze*LXqlKQHiv=gx+V^Cbb2?z@ISBQ*3amF;9UJ3SBg(N|710TLamQmYZ&Qjn2LuO<*
zCZlB4n%@pc&7NNnY1}x+NWpHlq`OJEo|`aYN9<`RBUB+79g;>dgb6YlfN#kGL?lO_
z!6~M^7sOnbsUkKk<@Ysie&`G>ruxH&Mgy&8;i=A
zB9OO!xR{AyODw>DS-q5YM{0ExFEAzt
zm>RdS+ssW(-8|?xr0(?$vBVB*%(xDLtq3Hf0I5yFm<_g=W2`QWAax{1rWVH=I!VrP
zs(rTFX@W#t$hXNvbgX`gK&^w_YD;CQ!B@e0QbLIWaKAXQe2-kkloo;{iF#6}z!4=W
zi$giRj1{
zt;2w`VSCF#WE&*ev7jpsC=6175@(~nTE2;7M-L((0bH@yG}-TB$R~WXd?tA$s3|%y
zA`9$sA(>F%J3ioz<-LJl*^o1|w84l>HBR`>3l9c8$5Xr@xCiIQ7{x$fMCzOk_-M=%
z+{a_Q#;42`#KfUte@$NT77uaTz?b-fBe)1s5XE$yA79fm?KqM^VgLXD07*qoM6N<$
Ef<_J(9smFU
literal 0
HcmV?d00001
diff --git a/assets/css/leaflet.css b/assets/css/leaflet.css
new file mode 100644
index 0000000..72998d0
--- /dev/null
+++ b/assets/css/leaflet.css
@@ -0,0 +1,624 @@
+/* required styles */
+
+.leaflet-pane,
+.leaflet-tile,
+.leaflet-marker-icon,
+.leaflet-marker-shadow,
+.leaflet-tile-container,
+.leaflet-pane > svg,
+.leaflet-pane > canvas,
+.leaflet-zoom-box,
+.leaflet-image-layer,
+.leaflet-layer {
+ position: absolute;
+ left: 0;
+ top: 0;
+ }
+.leaflet-container {
+ overflow: hidden;
+ }
+.leaflet-tile,
+.leaflet-marker-icon,
+.leaflet-marker-shadow {
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ user-select: none;
+ -webkit-user-drag: none;
+ }
+/* Safari renders non-retina tile on retina better with this, but Chrome is worse */
+.leaflet-safari .leaflet-tile {
+ image-rendering: -webkit-optimize-contrast;
+ }
+/* hack that prevents hw layers "stretching" when loading new tiles */
+.leaflet-safari .leaflet-tile-container {
+ width: 1600px;
+ height: 1600px;
+ -webkit-transform-origin: 0 0;
+ }
+.leaflet-marker-icon,
+.leaflet-marker-shadow {
+ display: block;
+ }
+/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */
+/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */
+.leaflet-container .leaflet-overlay-pane svg,
+.leaflet-container .leaflet-marker-pane img,
+.leaflet-container .leaflet-shadow-pane img,
+.leaflet-container .leaflet-tile-pane img,
+.leaflet-container img.leaflet-image-layer {
+ max-width: none !important;
+ }
+
+.leaflet-container.leaflet-touch-zoom {
+ -ms-touch-action: pan-x pan-y;
+ touch-action: pan-x pan-y;
+ }
+.leaflet-container.leaflet-touch-drag {
+ -ms-touch-action: pinch-zoom;
+ }
+.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom {
+ -ms-touch-action: none;
+ touch-action: none;
+}
+.leaflet-tile {
+ filter: inherit;
+ visibility: hidden;
+ }
+.leaflet-tile-loaded {
+ visibility: inherit;
+ }
+.leaflet-zoom-box {
+ width: 0;
+ height: 0;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ z-index: 800;
+ }
+/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
+.leaflet-overlay-pane svg {
+ -moz-user-select: none;
+ }
+
+.leaflet-pane { z-index: 400; }
+
+.leaflet-tile-pane { z-index: 200; }
+.leaflet-overlay-pane { z-index: 400; }
+.leaflet-shadow-pane { z-index: 500; }
+.leaflet-marker-pane { z-index: 600; }
+.leaflet-tooltip-pane { z-index: 650; }
+.leaflet-popup-pane { z-index: 700; }
+
+.leaflet-map-pane canvas { z-index: 100; }
+.leaflet-map-pane svg { z-index: 200; }
+
+.leaflet-vml-shape {
+ width: 1px;
+ height: 1px;
+ }
+.lvml {
+ behavior: url(#default#VML);
+ display: inline-block;
+ position: absolute;
+ }
+
+
+/* control positioning */
+
+.leaflet-control {
+ position: relative;
+ z-index: 800;
+ pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
+ pointer-events: auto;
+ }
+.leaflet-top,
+.leaflet-bottom {
+ position: absolute;
+ z-index: 1000;
+ pointer-events: none;
+ }
+.leaflet-top {
+ top: 0;
+ }
+.leaflet-right {
+ right: 0;
+ }
+.leaflet-bottom {
+ bottom: 0;
+ }
+.leaflet-left {
+ left: 0;
+ }
+.leaflet-control {
+ float: left;
+ clear: both;
+ }
+.leaflet-right .leaflet-control {
+ float: right;
+ }
+.leaflet-top .leaflet-control {
+ margin-top: 10px;
+ }
+.leaflet-bottom .leaflet-control {
+ margin-bottom: 10px;
+ }
+.leaflet-left .leaflet-control {
+ margin-left: 10px;
+ }
+.leaflet-right .leaflet-control {
+ margin-right: 10px;
+ }
+
+
+/* zoom and fade animations */
+
+.leaflet-fade-anim .leaflet-tile {
+ will-change: opacity;
+ }
+.leaflet-fade-anim .leaflet-popup {
+ opacity: 0;
+ -webkit-transition: opacity 0.2s linear;
+ -moz-transition: opacity 0.2s linear;
+ -o-transition: opacity 0.2s linear;
+ transition: opacity 0.2s linear;
+ }
+.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
+ opacity: 1;
+ }
+.leaflet-zoom-animated {
+ -webkit-transform-origin: 0 0;
+ -ms-transform-origin: 0 0;
+ transform-origin: 0 0;
+ }
+.leaflet-zoom-anim .leaflet-zoom-animated {
+ will-change: transform;
+ }
+.leaflet-zoom-anim .leaflet-zoom-animated {
+ -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
+ -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
+ -o-transition: -o-transform 0.25s cubic-bezier(0,0,0.25,1);
+ transition: transform 0.25s cubic-bezier(0,0,0.25,1);
+ }
+.leaflet-zoom-anim .leaflet-tile,
+.leaflet-pan-anim .leaflet-tile {
+ -webkit-transition: none;
+ -moz-transition: none;
+ -o-transition: none;
+ transition: none;
+ }
+
+.leaflet-zoom-anim .leaflet-zoom-hide {
+ visibility: hidden;
+ }
+
+
+/* cursors */
+
+.leaflet-interactive {
+ cursor: pointer;
+ }
+.leaflet-grab {
+ cursor: -webkit-grab;
+ cursor: -moz-grab;
+ }
+.leaflet-crosshair,
+.leaflet-crosshair .leaflet-interactive {
+ cursor: crosshair;
+ }
+.leaflet-popup-pane,
+.leaflet-control {
+ cursor: auto;
+ }
+.leaflet-dragging .leaflet-grab,
+.leaflet-dragging .leaflet-grab .leaflet-interactive,
+.leaflet-dragging .leaflet-marker-draggable {
+ cursor: move;
+ cursor: -webkit-grabbing;
+ cursor: -moz-grabbing;
+ }
+
+/* marker & overlays interactivity */
+.leaflet-marker-icon,
+.leaflet-marker-shadow,
+.leaflet-image-layer,
+.leaflet-pane > svg path,
+.leaflet-tile-container {
+ pointer-events: none;
+ }
+
+.leaflet-marker-icon.leaflet-interactive,
+.leaflet-image-layer.leaflet-interactive,
+.leaflet-pane > svg path.leaflet-interactive {
+ pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
+ pointer-events: auto;
+ }
+
+/* visual tweaks */
+
+.leaflet-container {
+ background: #ddd;
+ outline: 0;
+ }
+.leaflet-container a {
+ color: #0078A8;
+ }
+.leaflet-container a.leaflet-active {
+ outline: 2px solid orange;
+ }
+.leaflet-zoom-box {
+ border: 2px dotted #38f;
+ background: rgba(255,255,255,0.5);
+ }
+
+
+/* general typography */
+.leaflet-container {
+ font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif;
+ }
+
+
+/* general toolbar styles */
+
+.leaflet-bar {
+ box-shadow: 0 1px 5px rgba(0,0,0,0.65);
+ border-radius: 4px;
+ }
+.leaflet-bar a,
+.leaflet-bar a:hover {
+ background-color: #fff;
+ border-bottom: 1px solid #ccc;
+ width: 26px;
+ height: 26px;
+ line-height: 26px;
+ display: block;
+ text-align: center;
+ text-decoration: none;
+ color: black;
+ }
+.leaflet-bar a,
+.leaflet-control-layers-toggle {
+ background-position: 50% 50%;
+ background-repeat: no-repeat;
+ display: block;
+ }
+.leaflet-bar a:hover {
+ background-color: #f4f4f4;
+ }
+.leaflet-bar a:first-child {
+ border-top-left-radius: 4px;
+ border-top-right-radius: 4px;
+ }
+.leaflet-bar a:last-child {
+ border-bottom-left-radius: 4px;
+ border-bottom-right-radius: 4px;
+ border-bottom: none;
+ }
+.leaflet-bar a.leaflet-disabled {
+ cursor: default;
+ background-color: #f4f4f4;
+ color: #bbb;
+ }
+
+.leaflet-touch .leaflet-bar a {
+ width: 30px;
+ height: 30px;
+ line-height: 30px;
+ }
+
+
+/* zoom control */
+
+.leaflet-control-zoom-in,
+.leaflet-control-zoom-out {
+ font: bold 18px 'Lucida Console', Monaco, monospace;
+ text-indent: 1px;
+ }
+.leaflet-control-zoom-out {
+ font-size: 20px;
+ }
+
+.leaflet-touch .leaflet-control-zoom-in {
+ font-size: 22px;
+ }
+.leaflet-touch .leaflet-control-zoom-out {
+ font-size: 24px;
+ }
+
+
+/* layers control */
+
+.leaflet-control-layers {
+ box-shadow: 0 1px 5px rgba(0,0,0,0.4);
+ background: #fff;
+ border-radius: 5px;
+ }
+.leaflet-control-layers-toggle {
+ background-image: url(images/layers.png);
+ width: 36px;
+ height: 36px;
+ }
+.leaflet-retina .leaflet-control-layers-toggle {
+ background-image: url(images/layers-2x.png);
+ background-size: 26px 26px;
+ }
+.leaflet-touch .leaflet-control-layers-toggle {
+ width: 44px;
+ height: 44px;
+ }
+.leaflet-control-layers .leaflet-control-layers-list,
+.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
+ display: none;
+ }
+.leaflet-control-layers-expanded .leaflet-control-layers-list {
+ display: block;
+ position: relative;
+ }
+.leaflet-control-layers-expanded {
+ padding: 6px 10px 6px 6px;
+ color: #333;
+ background: #fff;
+ }
+.leaflet-control-layers-scrollbar {
+ overflow-y: scroll;
+ padding-right: 5px;
+ }
+.leaflet-control-layers-selector {
+ margin-top: 2px;
+ position: relative;
+ top: 1px;
+ }
+.leaflet-control-layers label {
+ display: block;
+ }
+.leaflet-control-layers-separator {
+ height: 0;
+ border-top: 1px solid #ddd;
+ margin: 5px -10px 5px -6px;
+ }
+
+/* Default icon URLs */
+.leaflet-default-icon-path {
+ background-image: url(images/marker-icon.png);
+ }
+
+
+/* attribution and scale controls */
+
+.leaflet-container .leaflet-control-attribution {
+ background: #fff;
+ background: rgba(255, 255, 255, 0.7);
+ margin: 0;
+ }
+.leaflet-control-attribution,
+.leaflet-control-scale-line {
+ padding: 0 5px;
+ color: #333;
+ }
+.leaflet-control-attribution a {
+ text-decoration: none;
+ }
+.leaflet-control-attribution a:hover {
+ text-decoration: underline;
+ }
+.leaflet-container .leaflet-control-attribution,
+.leaflet-container .leaflet-control-scale {
+ font-size: 11px;
+ }
+.leaflet-left .leaflet-control-scale {
+ margin-left: 5px;
+ }
+.leaflet-bottom .leaflet-control-scale {
+ margin-bottom: 5px;
+ }
+.leaflet-control-scale-line {
+ border: 2px solid #777;
+ border-top: none;
+ line-height: 1.1;
+ padding: 2px 5px 1px;
+ font-size: 11px;
+ white-space: nowrap;
+ overflow: hidden;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+
+ background: #fff;
+ background: rgba(255, 255, 255, 0.5);
+ }
+.leaflet-control-scale-line:not(:first-child) {
+ border-top: 2px solid #777;
+ border-bottom: none;
+ margin-top: -2px;
+ }
+.leaflet-control-scale-line:not(:first-child):not(:last-child) {
+ border-bottom: 2px solid #777;
+ }
+
+.leaflet-touch .leaflet-control-attribution,
+.leaflet-touch .leaflet-control-layers,
+.leaflet-touch .leaflet-bar {
+ box-shadow: none;
+ }
+.leaflet-touch .leaflet-control-layers,
+.leaflet-touch .leaflet-bar {
+ border: 2px solid rgba(0,0,0,0.2);
+ background-clip: padding-box;
+ }
+
+
+/* popup */
+
+.leaflet-popup {
+ position: absolute;
+ text-align: center;
+ margin-bottom: 20px;
+ }
+.leaflet-popup-content-wrapper {
+ padding: 1px;
+ text-align: left;
+ border-radius: 12px;
+ }
+.leaflet-popup-content {
+ margin: 13px 19px;
+ line-height: 1.4;
+ }
+.leaflet-popup-content p {
+ margin: 18px 0;
+ }
+.leaflet-popup-tip-container {
+ width: 40px;
+ height: 20px;
+ position: absolute;
+ left: 50%;
+ margin-left: -20px;
+ overflow: hidden;
+ pointer-events: none;
+ }
+.leaflet-popup-tip {
+ width: 17px;
+ height: 17px;
+ padding: 1px;
+
+ margin: -10px auto 0;
+
+ -webkit-transform: rotate(45deg);
+ -moz-transform: rotate(45deg);
+ -ms-transform: rotate(45deg);
+ -o-transform: rotate(45deg);
+ transform: rotate(45deg);
+ }
+.leaflet-popup-content-wrapper,
+.leaflet-popup-tip {
+ background: white;
+ color: #333;
+ box-shadow: 0 3px 14px rgba(0,0,0,0.4);
+ }
+.leaflet-container a.leaflet-popup-close-button {
+ position: absolute;
+ top: 0;
+ right: 0;
+ padding: 4px 4px 0 0;
+ border: none;
+ text-align: center;
+ width: 18px;
+ height: 14px;
+ font: 16px/14px Tahoma, Verdana, sans-serif;
+ color: #c3c3c3;
+ text-decoration: none;
+ font-weight: bold;
+ background: transparent;
+ }
+.leaflet-container a.leaflet-popup-close-button:hover {
+ color: #999;
+ }
+.leaflet-popup-scrolled {
+ overflow: auto;
+ border-bottom: 1px solid #ddd;
+ border-top: 1px solid #ddd;
+ }
+
+.leaflet-oldie .leaflet-popup-content-wrapper {
+ zoom: 1;
+ }
+.leaflet-oldie .leaflet-popup-tip {
+ width: 24px;
+ margin: 0 auto;
+
+ -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
+ filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
+ }
+.leaflet-oldie .leaflet-popup-tip-container {
+ margin-top: -1px;
+ }
+
+.leaflet-oldie .leaflet-control-zoom,
+.leaflet-oldie .leaflet-control-layers,
+.leaflet-oldie .leaflet-popup-content-wrapper,
+.leaflet-oldie .leaflet-popup-tip {
+ border: 1px solid #999;
+ }
+
+
+/* div icon */
+
+.leaflet-div-icon {
+ background: #fff;
+ border: 1px solid #666;
+ }
+
+
+/* Tooltip */
+/* Base styles for the element that has a tooltip */
+.leaflet-tooltip {
+ position: absolute;
+ padding: 6px;
+ background-color: #fff;
+ border: 1px solid #fff;
+ border-radius: 3px;
+ color: #222;
+ white-space: nowrap;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ pointer-events: none;
+ box-shadow: 0 1px 3px rgba(0,0,0,0.4);
+ }
+.leaflet-tooltip.leaflet-clickable {
+ cursor: pointer;
+ pointer-events: auto;
+ }
+.leaflet-tooltip-top:before,
+.leaflet-tooltip-bottom:before,
+.leaflet-tooltip-left:before,
+.leaflet-tooltip-right:before {
+ position: absolute;
+ pointer-events: none;
+ border: 6px solid transparent;
+ background: transparent;
+ content: "";
+ }
+
+/* Directions */
+
+.leaflet-tooltip-bottom {
+ margin-top: 6px;
+}
+.leaflet-tooltip-top {
+ margin-top: -6px;
+}
+.leaflet-tooltip-bottom:before,
+.leaflet-tooltip-top:before {
+ left: 50%;
+ margin-left: -6px;
+ }
+.leaflet-tooltip-top:before {
+ bottom: 0;
+ margin-bottom: -12px;
+ border-top-color: #fff;
+ }
+.leaflet-tooltip-bottom:before {
+ top: 0;
+ margin-top: -12px;
+ margin-left: -6px;
+ border-bottom-color: #fff;
+ }
+.leaflet-tooltip-left {
+ margin-left: -6px;
+}
+.leaflet-tooltip-right {
+ margin-left: 6px;
+}
+.leaflet-tooltip-left:before,
+.leaflet-tooltip-right:before {
+ top: 50%;
+ margin-top: -6px;
+ }
+.leaflet-tooltip-left:before {
+ right: 0;
+ margin-right: -12px;
+ border-left-color: #fff;
+ }
+.leaflet-tooltip-right:before {
+ left: 0;
+ margin-left: -12px;
+ border-right-color: #fff;
+ }
diff --git a/assets/css/main.css b/assets/css/main.css
new file mode 100644
index 0000000..d47e8b6
--- /dev/null
+++ b/assets/css/main.css
@@ -0,0 +1,4112 @@
+@import url(font-awesome.min.css);
+@import url("https://fonts.googleapis.com/css?family=Open+Sans:400,600,400italic,600italic|Roboto+Slab:400,700");
+
+/*
+ Editorial by HTML5 UP
+ html5up.net | @ajlkn
+ Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
+*/
+
+/* Reset */
+
+ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font-size: 100%;
+ font: inherit;
+ vertical-align: baseline;
+ }
+
+ article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section {
+ display: block;
+ }
+
+ body {
+ line-height: 1;
+ }
+
+ ol, ul {
+ list-style: none;
+ }
+
+ blockquote, q {
+ quotes: none;
+ }
+
+ blockquote:before, blockquote:after, q:before, q:after {
+ content: '';
+ content: none;
+ }
+
+ table {
+ border-collapse: collapse;
+ border-spacing: 0;
+ }
+
+ body {
+ -webkit-text-size-adjust: none;
+ }
+
+/* Box Model */
+
+ *, *:before, *:after {
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ }
+
+/* Grid */
+
+ .row {
+ border-bottom: solid 1px transparent;
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ }
+
+ .row > * {
+ float: left;
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ }
+
+ .row:after, .row:before {
+ content: '';
+ display: block;
+ clear: both;
+ height: 0;
+ }
+
+ .row.uniform > * > :first-child {
+ margin-top: 0;
+ }
+
+ .row.uniform > * > :last-child {
+ margin-bottom: 0;
+ }
+
+ .row.\30 \25 > * {
+ padding: 0 0 0 0em;
+ }
+
+ .row.\30 \25 {
+ margin: 0 0 -1px 0em;
+ }
+
+ .row.uniform.\30 \25 > * {
+ padding: 0em 0 0 0em;
+ }
+
+ .row.uniform.\30 \25 {
+ margin: 0em 0 -1px 0em;
+ }
+
+ .row > * {
+ padding: 0 0 0 1.5em;
+ }
+
+ .row {
+ margin: 0 0 -1px -1.5em;
+ }
+
+ .row.uniform > * {
+ padding: 1.5em 0 0 1.5em;
+ }
+
+ .row.uniform {
+ margin: -1.5em 0 -1px -1.5em;
+ }
+
+ .row.\32 00\25 > * {
+ padding: 0 0 0 3em;
+ }
+
+ .row.\32 00\25 {
+ margin: 0 0 -1px -3em;
+ }
+
+ .row.uniform.\32 00\25 > * {
+ padding: 3em 0 0 3em;
+ }
+
+ .row.uniform.\32 00\25 {
+ margin: -3em 0 -1px -3em;
+ }
+
+ .row.\31 50\25 > * {
+ padding: 0 0 0 2.25em;
+ }
+
+ .row.\31 50\25 {
+ margin: 0 0 -1px -2.25em;
+ }
+
+ .row.uniform.\31 50\25 > * {
+ padding: 2.25em 0 0 2.25em;
+ }
+
+ .row.uniform.\31 50\25 {
+ margin: -2.25em 0 -1px -2.25em;
+ }
+
+ .row.\35 0\25 > * {
+ padding: 0 0 0 0.75em;
+ }
+
+ .row.\35 0\25 {
+ margin: 0 0 -1px -0.75em;
+ }
+
+ .row.uniform.\35 0\25 > * {
+ padding: 0.75em 0 0 0.75em;
+ }
+
+ .row.uniform.\35 0\25 {
+ margin: -0.75em 0 -1px -0.75em;
+ }
+
+ .row.\32 5\25 > * {
+ padding: 0 0 0 0.375em;
+ }
+
+ .row.\32 5\25 {
+ margin: 0 0 -1px -0.375em;
+ }
+
+ .row.uniform.\32 5\25 > * {
+ padding: 0.375em 0 0 0.375em;
+ }
+
+ .row.uniform.\32 5\25 {
+ margin: -0.375em 0 -1px -0.375em;
+ }
+
+ .\31 2u, .\31 2u\24 {
+ width: 100%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 1u, .\31 1u\24 {
+ width: 91.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 0u, .\31 0u\24 {
+ width: 83.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\39 u, .\39 u\24 {
+ width: 75%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\38 u, .\38 u\24 {
+ width: 66.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\37 u, .\37 u\24 {
+ width: 58.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\36 u, .\36 u\24 {
+ width: 50%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\35 u, .\35 u\24 {
+ width: 41.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\34 u, .\34 u\24 {
+ width: 33.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\33 u, .\33 u\24 {
+ width: 25%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\32 u, .\32 u\24 {
+ width: 16.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 u, .\31 u\24 {
+ width: 8.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 2u\24 + *,
+ .\31 1u\24 + *,
+ .\31 0u\24 + *,
+ .\39 u\24 + *,
+ .\38 u\24 + *,
+ .\37 u\24 + *,
+ .\36 u\24 + *,
+ .\35 u\24 + *,
+ .\34 u\24 + *,
+ .\33 u\24 + *,
+ .\32 u\24 + *,
+ .\31 u\24 + * {
+ clear: left;
+ }
+
+ .\-11u {
+ margin-left: 91.66667%;
+ }
+
+ .\-10u {
+ margin-left: 83.33333%;
+ }
+
+ .\-9u {
+ margin-left: 75%;
+ }
+
+ .\-8u {
+ margin-left: 66.66667%;
+ }
+
+ .\-7u {
+ margin-left: 58.33333%;
+ }
+
+ .\-6u {
+ margin-left: 50%;
+ }
+
+ .\-5u {
+ margin-left: 41.66667%;
+ }
+
+ .\-4u {
+ margin-left: 33.33333%;
+ }
+
+ .\-3u {
+ margin-left: 25%;
+ }
+
+ .\-2u {
+ margin-left: 16.66667%;
+ }
+
+ .\-1u {
+ margin-left: 8.33333%;
+ }
+
+ @media screen and (max-width: 1680px) {
+
+ .row > * {
+ padding: 0 0 0 1.5em;
+ }
+
+ .row {
+ margin: 0 0 -1px -1.5em;
+ }
+
+ .row.uniform > * {
+ padding: 1.5em 0 0 1.5em;
+ }
+
+ .row.uniform {
+ margin: -1.5em 0 -1px -1.5em;
+ }
+
+ .row.\32 00\25 > * {
+ padding: 0 0 0 3em;
+ }
+
+ .row.\32 00\25 {
+ margin: 0 0 -1px -3em;
+ }
+
+ .row.uniform.\32 00\25 > * {
+ padding: 3em 0 0 3em;
+ }
+
+ .row.uniform.\32 00\25 {
+ margin: -3em 0 -1px -3em;
+ }
+
+ .row.\31 50\25 > * {
+ padding: 0 0 0 2.25em;
+ }
+
+ .row.\31 50\25 {
+ margin: 0 0 -1px -2.25em;
+ }
+
+ .row.uniform.\31 50\25 > * {
+ padding: 2.25em 0 0 2.25em;
+ }
+
+ .row.uniform.\31 50\25 {
+ margin: -2.25em 0 -1px -2.25em;
+ }
+
+ .row.\35 0\25 > * {
+ padding: 0 0 0 0.75em;
+ }
+
+ .row.\35 0\25 {
+ margin: 0 0 -1px -0.75em;
+ }
+
+ .row.uniform.\35 0\25 > * {
+ padding: 0.75em 0 0 0.75em;
+ }
+
+ .row.uniform.\35 0\25 {
+ margin: -0.75em 0 -1px -0.75em;
+ }
+
+ .row.\32 5\25 > * {
+ padding: 0 0 0 0.375em;
+ }
+
+ .row.\32 5\25 {
+ margin: 0 0 -1px -0.375em;
+ }
+
+ .row.uniform.\32 5\25 > * {
+ padding: 0.375em 0 0 0.375em;
+ }
+
+ .row.uniform.\32 5\25 {
+ margin: -0.375em 0 -1px -0.375em;
+ }
+
+ .\31 2u\28xlarge\29, .\31 2u\24\28xlarge\29 {
+ width: 100%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 1u\28xlarge\29, .\31 1u\24\28xlarge\29 {
+ width: 91.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 0u\28xlarge\29, .\31 0u\24\28xlarge\29 {
+ width: 83.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\39 u\28xlarge\29, .\39 u\24\28xlarge\29 {
+ width: 75%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\38 u\28xlarge\29, .\38 u\24\28xlarge\29 {
+ width: 66.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\37 u\28xlarge\29, .\37 u\24\28xlarge\29 {
+ width: 58.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\36 u\28xlarge\29, .\36 u\24\28xlarge\29 {
+ width: 50%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\35 u\28xlarge\29, .\35 u\24\28xlarge\29 {
+ width: 41.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\34 u\28xlarge\29, .\34 u\24\28xlarge\29 {
+ width: 33.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\33 u\28xlarge\29, .\33 u\24\28xlarge\29 {
+ width: 25%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\32 u\28xlarge\29, .\32 u\24\28xlarge\29 {
+ width: 16.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 u\28xlarge\29, .\31 u\24\28xlarge\29 {
+ width: 8.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 2u\24\28xlarge\29 + *,
+ .\31 1u\24\28xlarge\29 + *,
+ .\31 0u\24\28xlarge\29 + *,
+ .\39 u\24\28xlarge\29 + *,
+ .\38 u\24\28xlarge\29 + *,
+ .\37 u\24\28xlarge\29 + *,
+ .\36 u\24\28xlarge\29 + *,
+ .\35 u\24\28xlarge\29 + *,
+ .\34 u\24\28xlarge\29 + *,
+ .\33 u\24\28xlarge\29 + *,
+ .\32 u\24\28xlarge\29 + *,
+ .\31 u\24\28xlarge\29 + * {
+ clear: left;
+ }
+
+ .\-11u\28xlarge\29 {
+ margin-left: 91.66667%;
+ }
+
+ .\-10u\28xlarge\29 {
+ margin-left: 83.33333%;
+ }
+
+ .\-9u\28xlarge\29 {
+ margin-left: 75%;
+ }
+
+ .\-8u\28xlarge\29 {
+ margin-left: 66.66667%;
+ }
+
+ .\-7u\28xlarge\29 {
+ margin-left: 58.33333%;
+ }
+
+ .\-6u\28xlarge\29 {
+ margin-left: 50%;
+ }
+
+ .\-5u\28xlarge\29 {
+ margin-left: 41.66667%;
+ }
+
+ .\-4u\28xlarge\29 {
+ margin-left: 33.33333%;
+ }
+
+ .\-3u\28xlarge\29 {
+ margin-left: 25%;
+ }
+
+ .\-2u\28xlarge\29 {
+ margin-left: 16.66667%;
+ }
+
+ .\-1u\28xlarge\29 {
+ margin-left: 8.33333%;
+ }
+
+ }
+
+ @media screen and (max-width: 1280px) {
+
+ .row > * {
+ padding: 0 0 0 1.5em;
+ }
+
+ .row {
+ margin: 0 0 -1px -1.5em;
+ }
+
+ .row.uniform > * {
+ padding: 1.5em 0 0 1.5em;
+ }
+
+ .row.uniform {
+ margin: -1.5em 0 -1px -1.5em;
+ }
+
+ .row.\32 00\25 > * {
+ padding: 0 0 0 3em;
+ }
+
+ .row.\32 00\25 {
+ margin: 0 0 -1px -3em;
+ }
+
+ .row.uniform.\32 00\25 > * {
+ padding: 3em 0 0 3em;
+ }
+
+ .row.uniform.\32 00\25 {
+ margin: -3em 0 -1px -3em;
+ }
+
+ .row.\31 50\25 > * {
+ padding: 0 0 0 2.25em;
+ }
+
+ .row.\31 50\25 {
+ margin: 0 0 -1px -2.25em;
+ }
+
+ .row.uniform.\31 50\25 > * {
+ padding: 2.25em 0 0 2.25em;
+ }
+
+ .row.uniform.\31 50\25 {
+ margin: -2.25em 0 -1px -2.25em;
+ }
+
+ .row.\35 0\25 > * {
+ padding: 0 0 0 0.75em;
+ }
+
+ .row.\35 0\25 {
+ margin: 0 0 -1px -0.75em;
+ }
+
+ .row.uniform.\35 0\25 > * {
+ padding: 0.75em 0 0 0.75em;
+ }
+
+ .row.uniform.\35 0\25 {
+ margin: -0.75em 0 -1px -0.75em;
+ }
+
+ .row.\32 5\25 > * {
+ padding: 0 0 0 0.375em;
+ }
+
+ .row.\32 5\25 {
+ margin: 0 0 -1px -0.375em;
+ }
+
+ .row.uniform.\32 5\25 > * {
+ padding: 0.375em 0 0 0.375em;
+ }
+
+ .row.uniform.\32 5\25 {
+ margin: -0.375em 0 -1px -0.375em;
+ }
+
+ .\31 2u\28large\29, .\31 2u\24\28large\29 {
+ width: 100%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 1u\28large\29, .\31 1u\24\28large\29 {
+ width: 91.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 0u\28large\29, .\31 0u\24\28large\29 {
+ width: 83.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\39 u\28large\29, .\39 u\24\28large\29 {
+ width: 75%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\38 u\28large\29, .\38 u\24\28large\29 {
+ width: 66.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\37 u\28large\29, .\37 u\24\28large\29 {
+ width: 58.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\36 u\28large\29, .\36 u\24\28large\29 {
+ width: 50%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\35 u\28large\29, .\35 u\24\28large\29 {
+ width: 41.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\34 u\28large\29, .\34 u\24\28large\29 {
+ width: 33.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\33 u\28large\29, .\33 u\24\28large\29 {
+ width: 25%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\32 u\28large\29, .\32 u\24\28large\29 {
+ width: 16.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 u\28large\29, .\31 u\24\28large\29 {
+ width: 8.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 2u\24\28large\29 + *,
+ .\31 1u\24\28large\29 + *,
+ .\31 0u\24\28large\29 + *,
+ .\39 u\24\28large\29 + *,
+ .\38 u\24\28large\29 + *,
+ .\37 u\24\28large\29 + *,
+ .\36 u\24\28large\29 + *,
+ .\35 u\24\28large\29 + *,
+ .\34 u\24\28large\29 + *,
+ .\33 u\24\28large\29 + *,
+ .\32 u\24\28large\29 + *,
+ .\31 u\24\28large\29 + * {
+ clear: left;
+ }
+
+ .\-11u\28large\29 {
+ margin-left: 91.66667%;
+ }
+
+ .\-10u\28large\29 {
+ margin-left: 83.33333%;
+ }
+
+ .\-9u\28large\29 {
+ margin-left: 75%;
+ }
+
+ .\-8u\28large\29 {
+ margin-left: 66.66667%;
+ }
+
+ .\-7u\28large\29 {
+ margin-left: 58.33333%;
+ }
+
+ .\-6u\28large\29 {
+ margin-left: 50%;
+ }
+
+ .\-5u\28large\29 {
+ margin-left: 41.66667%;
+ }
+
+ .\-4u\28large\29 {
+ margin-left: 33.33333%;
+ }
+
+ .\-3u\28large\29 {
+ margin-left: 25%;
+ }
+
+ .\-2u\28large\29 {
+ margin-left: 16.66667%;
+ }
+
+ .\-1u\28large\29 {
+ margin-left: 8.33333%;
+ }
+
+ }
+
+ @media screen and (max-width: 980px) {
+
+ .row > * {
+ padding: 0 0 0 1.5em;
+ }
+
+ .row {
+ margin: 0 0 -1px -1.5em;
+ }
+
+ .row.uniform > * {
+ padding: 1.5em 0 0 1.5em;
+ }
+
+ .row.uniform {
+ margin: -1.5em 0 -1px -1.5em;
+ }
+
+ .row.\32 00\25 > * {
+ padding: 0 0 0 3em;
+ }
+
+ .row.\32 00\25 {
+ margin: 0 0 -1px -3em;
+ }
+
+ .row.uniform.\32 00\25 > * {
+ padding: 3em 0 0 3em;
+ }
+
+ .row.uniform.\32 00\25 {
+ margin: -3em 0 -1px -3em;
+ }
+
+ .row.\31 50\25 > * {
+ padding: 0 0 0 2.25em;
+ }
+
+ .row.\31 50\25 {
+ margin: 0 0 -1px -2.25em;
+ }
+
+ .row.uniform.\31 50\25 > * {
+ padding: 2.25em 0 0 2.25em;
+ }
+
+ .row.uniform.\31 50\25 {
+ margin: -2.25em 0 -1px -2.25em;
+ }
+
+ .row.\35 0\25 > * {
+ padding: 0 0 0 0.75em;
+ }
+
+ .row.\35 0\25 {
+ margin: 0 0 -1px -0.75em;
+ }
+
+ .row.uniform.\35 0\25 > * {
+ padding: 0.75em 0 0 0.75em;
+ }
+
+ .row.uniform.\35 0\25 {
+ margin: -0.75em 0 -1px -0.75em;
+ }
+
+ .row.\32 5\25 > * {
+ padding: 0 0 0 0.375em;
+ }
+
+ .row.\32 5\25 {
+ margin: 0 0 -1px -0.375em;
+ }
+
+ .row.uniform.\32 5\25 > * {
+ padding: 0.375em 0 0 0.375em;
+ }
+
+ .row.uniform.\32 5\25 {
+ margin: -0.375em 0 -1px -0.375em;
+ }
+
+ .\31 2u\28medium\29, .\31 2u\24\28medium\29 {
+ width: 100%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 1u\28medium\29, .\31 1u\24\28medium\29 {
+ width: 91.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 0u\28medium\29, .\31 0u\24\28medium\29 {
+ width: 83.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\39 u\28medium\29, .\39 u\24\28medium\29 {
+ width: 75%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\38 u\28medium\29, .\38 u\24\28medium\29 {
+ width: 66.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\37 u\28medium\29, .\37 u\24\28medium\29 {
+ width: 58.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\36 u\28medium\29, .\36 u\24\28medium\29 {
+ width: 50%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\35 u\28medium\29, .\35 u\24\28medium\29 {
+ width: 41.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\34 u\28medium\29, .\34 u\24\28medium\29 {
+ width: 33.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\33 u\28medium\29, .\33 u\24\28medium\29 {
+ width: 25%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\32 u\28medium\29, .\32 u\24\28medium\29 {
+ width: 16.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 u\28medium\29, .\31 u\24\28medium\29 {
+ width: 8.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 2u\24\28medium\29 + *,
+ .\31 1u\24\28medium\29 + *,
+ .\31 0u\24\28medium\29 + *,
+ .\39 u\24\28medium\29 + *,
+ .\38 u\24\28medium\29 + *,
+ .\37 u\24\28medium\29 + *,
+ .\36 u\24\28medium\29 + *,
+ .\35 u\24\28medium\29 + *,
+ .\34 u\24\28medium\29 + *,
+ .\33 u\24\28medium\29 + *,
+ .\32 u\24\28medium\29 + *,
+ .\31 u\24\28medium\29 + * {
+ clear: left;
+ }
+
+ .\-11u\28medium\29 {
+ margin-left: 91.66667%;
+ }
+
+ .\-10u\28medium\29 {
+ margin-left: 83.33333%;
+ }
+
+ .\-9u\28medium\29 {
+ margin-left: 75%;
+ }
+
+ .\-8u\28medium\29 {
+ margin-left: 66.66667%;
+ }
+
+ .\-7u\28medium\29 {
+ margin-left: 58.33333%;
+ }
+
+ .\-6u\28medium\29 {
+ margin-left: 50%;
+ }
+
+ .\-5u\28medium\29 {
+ margin-left: 41.66667%;
+ }
+
+ .\-4u\28medium\29 {
+ margin-left: 33.33333%;
+ }
+
+ .\-3u\28medium\29 {
+ margin-left: 25%;
+ }
+
+ .\-2u\28medium\29 {
+ margin-left: 16.66667%;
+ }
+
+ .\-1u\28medium\29 {
+ margin-left: 8.33333%;
+ }
+
+ }
+
+ @media screen and (max-width: 736px) {
+
+ .row > * {
+ padding: 0 0 0 1.5em;
+ }
+
+ .row {
+ margin: 0 0 -1px -1.5em;
+ }
+
+ .row.uniform > * {
+ padding: 1.5em 0 0 1.5em;
+ }
+
+ .row.uniform {
+ margin: -1.5em 0 -1px -1.5em;
+ }
+
+ .row.\32 00\25 > * {
+ padding: 0 0 0 3em;
+ }
+
+ .row.\32 00\25 {
+ margin: 0 0 -1px -3em;
+ }
+
+ .row.uniform.\32 00\25 > * {
+ padding: 3em 0 0 3em;
+ }
+
+ .row.uniform.\32 00\25 {
+ margin: -3em 0 -1px -3em;
+ }
+
+ .row.\31 50\25 > * {
+ padding: 0 0 0 2.25em;
+ }
+
+ .row.\31 50\25 {
+ margin: 0 0 -1px -2.25em;
+ }
+
+ .row.uniform.\31 50\25 > * {
+ padding: 2.25em 0 0 2.25em;
+ }
+
+ .row.uniform.\31 50\25 {
+ margin: -2.25em 0 -1px -2.25em;
+ }
+
+ .row.\35 0\25 > * {
+ padding: 0 0 0 0.75em;
+ }
+
+ .row.\35 0\25 {
+ margin: 0 0 -1px -0.75em;
+ }
+
+ .row.uniform.\35 0\25 > * {
+ padding: 0.75em 0 0 0.75em;
+ }
+
+ .row.uniform.\35 0\25 {
+ margin: -0.75em 0 -1px -0.75em;
+ }
+
+ .row.\32 5\25 > * {
+ padding: 0 0 0 0.375em;
+ }
+
+ .row.\32 5\25 {
+ margin: 0 0 -1px -0.375em;
+ }
+
+ .row.uniform.\32 5\25 > * {
+ padding: 0.375em 0 0 0.375em;
+ }
+
+ .row.uniform.\32 5\25 {
+ margin: -0.375em 0 -1px -0.375em;
+ }
+
+ .\31 2u\28small\29, .\31 2u\24\28small\29 {
+ width: 100%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 1u\28small\29, .\31 1u\24\28small\29 {
+ width: 91.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 0u\28small\29, .\31 0u\24\28small\29 {
+ width: 83.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\39 u\28small\29, .\39 u\24\28small\29 {
+ width: 75%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\38 u\28small\29, .\38 u\24\28small\29 {
+ width: 66.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\37 u\28small\29, .\37 u\24\28small\29 {
+ width: 58.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\36 u\28small\29, .\36 u\24\28small\29 {
+ width: 50%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\35 u\28small\29, .\35 u\24\28small\29 {
+ width: 41.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\34 u\28small\29, .\34 u\24\28small\29 {
+ width: 33.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\33 u\28small\29, .\33 u\24\28small\29 {
+ width: 25%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\32 u\28small\29, .\32 u\24\28small\29 {
+ width: 16.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 u\28small\29, .\31 u\24\28small\29 {
+ width: 8.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 2u\24\28small\29 + *,
+ .\31 1u\24\28small\29 + *,
+ .\31 0u\24\28small\29 + *,
+ .\39 u\24\28small\29 + *,
+ .\38 u\24\28small\29 + *,
+ .\37 u\24\28small\29 + *,
+ .\36 u\24\28small\29 + *,
+ .\35 u\24\28small\29 + *,
+ .\34 u\24\28small\29 + *,
+ .\33 u\24\28small\29 + *,
+ .\32 u\24\28small\29 + *,
+ .\31 u\24\28small\29 + * {
+ clear: left;
+ }
+
+ .\-11u\28small\29 {
+ margin-left: 91.66667%;
+ }
+
+ .\-10u\28small\29 {
+ margin-left: 83.33333%;
+ }
+
+ .\-9u\28small\29 {
+ margin-left: 75%;
+ }
+
+ .\-8u\28small\29 {
+ margin-left: 66.66667%;
+ }
+
+ .\-7u\28small\29 {
+ margin-left: 58.33333%;
+ }
+
+ .\-6u\28small\29 {
+ margin-left: 50%;
+ }
+
+ .\-5u\28small\29 {
+ margin-left: 41.66667%;
+ }
+
+ .\-4u\28small\29 {
+ margin-left: 33.33333%;
+ }
+
+ .\-3u\28small\29 {
+ margin-left: 25%;
+ }
+
+ .\-2u\28small\29 {
+ margin-left: 16.66667%;
+ }
+
+ .\-1u\28small\29 {
+ margin-left: 8.33333%;
+ }
+
+ }
+
+ @media screen and (max-width: 480px) {
+
+ .row > * {
+ padding: 0 0 0 1.5em;
+ }
+
+ .row {
+ margin: 0 0 -1px -1.5em;
+ }
+
+ .row.uniform > * {
+ padding: 1.5em 0 0 1.5em;
+ }
+
+ .row.uniform {
+ margin: -1.5em 0 -1px -1.5em;
+ }
+
+ .row.\32 00\25 > * {
+ padding: 0 0 0 3em;
+ }
+
+ .row.\32 00\25 {
+ margin: 0 0 -1px -3em;
+ }
+
+ .row.uniform.\32 00\25 > * {
+ padding: 3em 0 0 3em;
+ }
+
+ .row.uniform.\32 00\25 {
+ margin: -3em 0 -1px -3em;
+ }
+
+ .row.\31 50\25 > * {
+ padding: 0 0 0 2.25em;
+ }
+
+ .row.\31 50\25 {
+ margin: 0 0 -1px -2.25em;
+ }
+
+ .row.uniform.\31 50\25 > * {
+ padding: 2.25em 0 0 2.25em;
+ }
+
+ .row.uniform.\31 50\25 {
+ margin: -2.25em 0 -1px -2.25em;
+ }
+
+ .row.\35 0\25 > * {
+ padding: 0 0 0 0.75em;
+ }
+
+ .row.\35 0\25 {
+ margin: 0 0 -1px -0.75em;
+ }
+
+ .row.uniform.\35 0\25 > * {
+ padding: 0.75em 0 0 0.75em;
+ }
+
+ .row.uniform.\35 0\25 {
+ margin: -0.75em 0 -1px -0.75em;
+ }
+
+ .row.\32 5\25 > * {
+ padding: 0 0 0 0.375em;
+ }
+
+ .row.\32 5\25 {
+ margin: 0 0 -1px -0.375em;
+ }
+
+ .row.uniform.\32 5\25 > * {
+ padding: 0.375em 0 0 0.375em;
+ }
+
+ .row.uniform.\32 5\25 {
+ margin: -0.375em 0 -1px -0.375em;
+ }
+
+ .\31 2u\28xsmall\29, .\31 2u\24\28xsmall\29 {
+ width: 100%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 1u\28xsmall\29, .\31 1u\24\28xsmall\29 {
+ width: 91.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 0u\28xsmall\29, .\31 0u\24\28xsmall\29 {
+ width: 83.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\39 u\28xsmall\29, .\39 u\24\28xsmall\29 {
+ width: 75%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\38 u\28xsmall\29, .\38 u\24\28xsmall\29 {
+ width: 66.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\37 u\28xsmall\29, .\37 u\24\28xsmall\29 {
+ width: 58.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\36 u\28xsmall\29, .\36 u\24\28xsmall\29 {
+ width: 50%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\35 u\28xsmall\29, .\35 u\24\28xsmall\29 {
+ width: 41.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\34 u\28xsmall\29, .\34 u\24\28xsmall\29 {
+ width: 33.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\33 u\28xsmall\29, .\33 u\24\28xsmall\29 {
+ width: 25%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\32 u\28xsmall\29, .\32 u\24\28xsmall\29 {
+ width: 16.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 u\28xsmall\29, .\31 u\24\28xsmall\29 {
+ width: 8.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 2u\24\28xsmall\29 + *,
+ .\31 1u\24\28xsmall\29 + *,
+ .\31 0u\24\28xsmall\29 + *,
+ .\39 u\24\28xsmall\29 + *,
+ .\38 u\24\28xsmall\29 + *,
+ .\37 u\24\28xsmall\29 + *,
+ .\36 u\24\28xsmall\29 + *,
+ .\35 u\24\28xsmall\29 + *,
+ .\34 u\24\28xsmall\29 + *,
+ .\33 u\24\28xsmall\29 + *,
+ .\32 u\24\28xsmall\29 + *,
+ .\31 u\24\28xsmall\29 + * {
+ clear: left;
+ }
+
+ .\-11u\28xsmall\29 {
+ margin-left: 91.66667%;
+ }
+
+ .\-10u\28xsmall\29 {
+ margin-left: 83.33333%;
+ }
+
+ .\-9u\28xsmall\29 {
+ margin-left: 75%;
+ }
+
+ .\-8u\28xsmall\29 {
+ margin-left: 66.66667%;
+ }
+
+ .\-7u\28xsmall\29 {
+ margin-left: 58.33333%;
+ }
+
+ .\-6u\28xsmall\29 {
+ margin-left: 50%;
+ }
+
+ .\-5u\28xsmall\29 {
+ margin-left: 41.66667%;
+ }
+
+ .\-4u\28xsmall\29 {
+ margin-left: 33.33333%;
+ }
+
+ .\-3u\28xsmall\29 {
+ margin-left: 25%;
+ }
+
+ .\-2u\28xsmall\29 {
+ margin-left: 16.66667%;
+ }
+
+ .\-1u\28xsmall\29 {
+ margin-left: 8.33333%;
+ }
+
+ }
+
+ @media screen and (max-width: 360px) {
+
+ .row > * {
+ padding: 0 0 0 1.5em;
+ }
+
+ .row {
+ margin: 0 0 -1px -1.5em;
+ }
+
+ .row.uniform > * {
+ padding: 1.5em 0 0 1.5em;
+ }
+
+ .row.uniform {
+ margin: -1.5em 0 -1px -1.5em;
+ }
+
+ .row.\32 00\25 > * {
+ padding: 0 0 0 3em;
+ }
+
+ .row.\32 00\25 {
+ margin: 0 0 -1px -3em;
+ }
+
+ .row.uniform.\32 00\25 > * {
+ padding: 3em 0 0 3em;
+ }
+
+ .row.uniform.\32 00\25 {
+ margin: -3em 0 -1px -3em;
+ }
+
+ .row.\31 50\25 > * {
+ padding: 0 0 0 2.25em;
+ }
+
+ .row.\31 50\25 {
+ margin: 0 0 -1px -2.25em;
+ }
+
+ .row.uniform.\31 50\25 > * {
+ padding: 2.25em 0 0 2.25em;
+ }
+
+ .row.uniform.\31 50\25 {
+ margin: -2.25em 0 -1px -2.25em;
+ }
+
+ .row.\35 0\25 > * {
+ padding: 0 0 0 0.75em;
+ }
+
+ .row.\35 0\25 {
+ margin: 0 0 -1px -0.75em;
+ }
+
+ .row.uniform.\35 0\25 > * {
+ padding: 0.75em 0 0 0.75em;
+ }
+
+ .row.uniform.\35 0\25 {
+ margin: -0.75em 0 -1px -0.75em;
+ }
+
+ .row.\32 5\25 > * {
+ padding: 0 0 0 0.375em;
+ }
+
+ .row.\32 5\25 {
+ margin: 0 0 -1px -0.375em;
+ }
+
+ .row.uniform.\32 5\25 > * {
+ padding: 0.375em 0 0 0.375em;
+ }
+
+ .row.uniform.\32 5\25 {
+ margin: -0.375em 0 -1px -0.375em;
+ }
+
+ .\31 2u\28xxsmall\29, .\31 2u\24\28xxsmall\29 {
+ width: 100%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 1u\28xxsmall\29, .\31 1u\24\28xxsmall\29 {
+ width: 91.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 0u\28xxsmall\29, .\31 0u\24\28xxsmall\29 {
+ width: 83.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\39 u\28xxsmall\29, .\39 u\24\28xxsmall\29 {
+ width: 75%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\38 u\28xxsmall\29, .\38 u\24\28xxsmall\29 {
+ width: 66.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\37 u\28xxsmall\29, .\37 u\24\28xxsmall\29 {
+ width: 58.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\36 u\28xxsmall\29, .\36 u\24\28xxsmall\29 {
+ width: 50%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\35 u\28xxsmall\29, .\35 u\24\28xxsmall\29 {
+ width: 41.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\34 u\28xxsmall\29, .\34 u\24\28xxsmall\29 {
+ width: 33.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\33 u\28xxsmall\29, .\33 u\24\28xxsmall\29 {
+ width: 25%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\32 u\28xxsmall\29, .\32 u\24\28xxsmall\29 {
+ width: 16.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 u\28xxsmall\29, .\31 u\24\28xxsmall\29 {
+ width: 8.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 2u\24\28xxsmall\29 + *,
+ .\31 1u\24\28xxsmall\29 + *,
+ .\31 0u\24\28xxsmall\29 + *,
+ .\39 u\24\28xxsmall\29 + *,
+ .\38 u\24\28xxsmall\29 + *,
+ .\37 u\24\28xxsmall\29 + *,
+ .\36 u\24\28xxsmall\29 + *,
+ .\35 u\24\28xxsmall\29 + *,
+ .\34 u\24\28xxsmall\29 + *,
+ .\33 u\24\28xxsmall\29 + *,
+ .\32 u\24\28xxsmall\29 + *,
+ .\31 u\24\28xxsmall\29 + * {
+ clear: left;
+ }
+
+ .\-11u\28xxsmall\29 {
+ margin-left: 91.66667%;
+ }
+
+ .\-10u\28xxsmall\29 {
+ margin-left: 83.33333%;
+ }
+
+ .\-9u\28xxsmall\29 {
+ margin-left: 75%;
+ }
+
+ .\-8u\28xxsmall\29 {
+ margin-left: 66.66667%;
+ }
+
+ .\-7u\28xxsmall\29 {
+ margin-left: 58.33333%;
+ }
+
+ .\-6u\28xxsmall\29 {
+ margin-left: 50%;
+ }
+
+ .\-5u\28xxsmall\29 {
+ margin-left: 41.66667%;
+ }
+
+ .\-4u\28xxsmall\29 {
+ margin-left: 33.33333%;
+ }
+
+ .\-3u\28xxsmall\29 {
+ margin-left: 25%;
+ }
+
+ .\-2u\28xxsmall\29 {
+ margin-left: 16.66667%;
+ }
+
+ .\-1u\28xxsmall\29 {
+ margin-left: 8.33333%;
+ }
+
+ }
+
+ @media screen and (min-width: 1681px) {
+
+ .row > * {
+ padding: 0 0 0 1.5em;
+ }
+
+ .row {
+ margin: 0 0 -1px -1.5em;
+ }
+
+ .row.uniform > * {
+ padding: 1.5em 0 0 1.5em;
+ }
+
+ .row.uniform {
+ margin: -1.5em 0 -1px -1.5em;
+ }
+
+ .row.\32 00\25 > * {
+ padding: 0 0 0 3em;
+ }
+
+ .row.\32 00\25 {
+ margin: 0 0 -1px -3em;
+ }
+
+ .row.uniform.\32 00\25 > * {
+ padding: 3em 0 0 3em;
+ }
+
+ .row.uniform.\32 00\25 {
+ margin: -3em 0 -1px -3em;
+ }
+
+ .row.\31 50\25 > * {
+ padding: 0 0 0 2.25em;
+ }
+
+ .row.\31 50\25 {
+ margin: 0 0 -1px -2.25em;
+ }
+
+ .row.uniform.\31 50\25 > * {
+ padding: 2.25em 0 0 2.25em;
+ }
+
+ .row.uniform.\31 50\25 {
+ margin: -2.25em 0 -1px -2.25em;
+ }
+
+ .row.\35 0\25 > * {
+ padding: 0 0 0 0.75em;
+ }
+
+ .row.\35 0\25 {
+ margin: 0 0 -1px -0.75em;
+ }
+
+ .row.uniform.\35 0\25 > * {
+ padding: 0.75em 0 0 0.75em;
+ }
+
+ .row.uniform.\35 0\25 {
+ margin: -0.75em 0 -1px -0.75em;
+ }
+
+ .row.\32 5\25 > * {
+ padding: 0 0 0 0.375em;
+ }
+
+ .row.\32 5\25 {
+ margin: 0 0 -1px -0.375em;
+ }
+
+ .row.uniform.\32 5\25 > * {
+ padding: 0.375em 0 0 0.375em;
+ }
+
+ .row.uniform.\32 5\25 {
+ margin: -0.375em 0 -1px -0.375em;
+ }
+
+ .\31 2u\28xlarge-to-max\29, .\31 2u\24\28xlarge-to-max\29 {
+ width: 100%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 1u\28xlarge-to-max\29, .\31 1u\24\28xlarge-to-max\29 {
+ width: 91.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 0u\28xlarge-to-max\29, .\31 0u\24\28xlarge-to-max\29 {
+ width: 83.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\39 u\28xlarge-to-max\29, .\39 u\24\28xlarge-to-max\29 {
+ width: 75%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\38 u\28xlarge-to-max\29, .\38 u\24\28xlarge-to-max\29 {
+ width: 66.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\37 u\28xlarge-to-max\29, .\37 u\24\28xlarge-to-max\29 {
+ width: 58.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\36 u\28xlarge-to-max\29, .\36 u\24\28xlarge-to-max\29 {
+ width: 50%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\35 u\28xlarge-to-max\29, .\35 u\24\28xlarge-to-max\29 {
+ width: 41.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\34 u\28xlarge-to-max\29, .\34 u\24\28xlarge-to-max\29 {
+ width: 33.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\33 u\28xlarge-to-max\29, .\33 u\24\28xlarge-to-max\29 {
+ width: 25%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\32 u\28xlarge-to-max\29, .\32 u\24\28xlarge-to-max\29 {
+ width: 16.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 u\28xlarge-to-max\29, .\31 u\24\28xlarge-to-max\29 {
+ width: 8.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 2u\24\28xlarge-to-max\29 + *,
+ .\31 1u\24\28xlarge-to-max\29 + *,
+ .\31 0u\24\28xlarge-to-max\29 + *,
+ .\39 u\24\28xlarge-to-max\29 + *,
+ .\38 u\24\28xlarge-to-max\29 + *,
+ .\37 u\24\28xlarge-to-max\29 + *,
+ .\36 u\24\28xlarge-to-max\29 + *,
+ .\35 u\24\28xlarge-to-max\29 + *,
+ .\34 u\24\28xlarge-to-max\29 + *,
+ .\33 u\24\28xlarge-to-max\29 + *,
+ .\32 u\24\28xlarge-to-max\29 + *,
+ .\31 u\24\28xlarge-to-max\29 + * {
+ clear: left;
+ }
+
+ .\-11u\28xlarge-to-max\29 {
+ margin-left: 91.66667%;
+ }
+
+ .\-10u\28xlarge-to-max\29 {
+ margin-left: 83.33333%;
+ }
+
+ .\-9u\28xlarge-to-max\29 {
+ margin-left: 75%;
+ }
+
+ .\-8u\28xlarge-to-max\29 {
+ margin-left: 66.66667%;
+ }
+
+ .\-7u\28xlarge-to-max\29 {
+ margin-left: 58.33333%;
+ }
+
+ .\-6u\28xlarge-to-max\29 {
+ margin-left: 50%;
+ }
+
+ .\-5u\28xlarge-to-max\29 {
+ margin-left: 41.66667%;
+ }
+
+ .\-4u\28xlarge-to-max\29 {
+ margin-left: 33.33333%;
+ }
+
+ .\-3u\28xlarge-to-max\29 {
+ margin-left: 25%;
+ }
+
+ .\-2u\28xlarge-to-max\29 {
+ margin-left: 16.66667%;
+ }
+
+ .\-1u\28xlarge-to-max\29 {
+ margin-left: 8.33333%;
+ }
+
+ }
+
+ @media screen and (min-width: 481px) and (max-width: 1680px) {
+
+ .row > * {
+ padding: 0 0 0 1.5em;
+ }
+
+ .row {
+ margin: 0 0 -1px -1.5em;
+ }
+
+ .row.uniform > * {
+ padding: 1.5em 0 0 1.5em;
+ }
+
+ .row.uniform {
+ margin: -1.5em 0 -1px -1.5em;
+ }
+
+ .row.\32 00\25 > * {
+ padding: 0 0 0 3em;
+ }
+
+ .row.\32 00\25 {
+ margin: 0 0 -1px -3em;
+ }
+
+ .row.uniform.\32 00\25 > * {
+ padding: 3em 0 0 3em;
+ }
+
+ .row.uniform.\32 00\25 {
+ margin: -3em 0 -1px -3em;
+ }
+
+ .row.\31 50\25 > * {
+ padding: 0 0 0 2.25em;
+ }
+
+ .row.\31 50\25 {
+ margin: 0 0 -1px -2.25em;
+ }
+
+ .row.uniform.\31 50\25 > * {
+ padding: 2.25em 0 0 2.25em;
+ }
+
+ .row.uniform.\31 50\25 {
+ margin: -2.25em 0 -1px -2.25em;
+ }
+
+ .row.\35 0\25 > * {
+ padding: 0 0 0 0.75em;
+ }
+
+ .row.\35 0\25 {
+ margin: 0 0 -1px -0.75em;
+ }
+
+ .row.uniform.\35 0\25 > * {
+ padding: 0.75em 0 0 0.75em;
+ }
+
+ .row.uniform.\35 0\25 {
+ margin: -0.75em 0 -1px -0.75em;
+ }
+
+ .row.\32 5\25 > * {
+ padding: 0 0 0 0.375em;
+ }
+
+ .row.\32 5\25 {
+ margin: 0 0 -1px -0.375em;
+ }
+
+ .row.uniform.\32 5\25 > * {
+ padding: 0.375em 0 0 0.375em;
+ }
+
+ .row.uniform.\32 5\25 {
+ margin: -0.375em 0 -1px -0.375em;
+ }
+
+ .\31 2u\28small-to-xlarge\29, .\31 2u\24\28small-to-xlarge\29 {
+ width: 100%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 1u\28small-to-xlarge\29, .\31 1u\24\28small-to-xlarge\29 {
+ width: 91.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 0u\28small-to-xlarge\29, .\31 0u\24\28small-to-xlarge\29 {
+ width: 83.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\39 u\28small-to-xlarge\29, .\39 u\24\28small-to-xlarge\29 {
+ width: 75%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\38 u\28small-to-xlarge\29, .\38 u\24\28small-to-xlarge\29 {
+ width: 66.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\37 u\28small-to-xlarge\29, .\37 u\24\28small-to-xlarge\29 {
+ width: 58.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\36 u\28small-to-xlarge\29, .\36 u\24\28small-to-xlarge\29 {
+ width: 50%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\35 u\28small-to-xlarge\29, .\35 u\24\28small-to-xlarge\29 {
+ width: 41.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\34 u\28small-to-xlarge\29, .\34 u\24\28small-to-xlarge\29 {
+ width: 33.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\33 u\28small-to-xlarge\29, .\33 u\24\28small-to-xlarge\29 {
+ width: 25%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\32 u\28small-to-xlarge\29, .\32 u\24\28small-to-xlarge\29 {
+ width: 16.6666666667%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 u\28small-to-xlarge\29, .\31 u\24\28small-to-xlarge\29 {
+ width: 8.3333333333%;
+ clear: none;
+ margin-left: 0;
+ }
+
+ .\31 2u\24\28small-to-xlarge\29 + *,
+ .\31 1u\24\28small-to-xlarge\29 + *,
+ .\31 0u\24\28small-to-xlarge\29 + *,
+ .\39 u\24\28small-to-xlarge\29 + *,
+ .\38 u\24\28small-to-xlarge\29 + *,
+ .\37 u\24\28small-to-xlarge\29 + *,
+ .\36 u\24\28small-to-xlarge\29 + *,
+ .\35 u\24\28small-to-xlarge\29 + *,
+ .\34 u\24\28small-to-xlarge\29 + *,
+ .\33 u\24\28small-to-xlarge\29 + *,
+ .\32 u\24\28small-to-xlarge\29 + *,
+ .\31 u\24\28small-to-xlarge\29 + * {
+ clear: left;
+ }
+
+ .\-11u\28small-to-xlarge\29 {
+ margin-left: 91.66667%;
+ }
+
+ .\-10u\28small-to-xlarge\29 {
+ margin-left: 83.33333%;
+ }
+
+ .\-9u\28small-to-xlarge\29 {
+ margin-left: 75%;
+ }
+
+ .\-8u\28small-to-xlarge\29 {
+ margin-left: 66.66667%;
+ }
+
+ .\-7u\28small-to-xlarge\29 {
+ margin-left: 58.33333%;
+ }
+
+ .\-6u\28small-to-xlarge\29 {
+ margin-left: 50%;
+ }
+
+ .\-5u\28small-to-xlarge\29 {
+ margin-left: 41.66667%;
+ }
+
+ .\-4u\28small-to-xlarge\29 {
+ margin-left: 33.33333%;
+ }
+
+ .\-3u\28small-to-xlarge\29 {
+ margin-left: 25%;
+ }
+
+ .\-2u\28small-to-xlarge\29 {
+ margin-left: 16.66667%;
+ }
+
+ .\-1u\28small-to-xlarge\29 {
+ margin-left: 8.33333%;
+ }
+
+ }
+
+/* Basic */
+
+ @-ms-viewport {
+ width: device-width;
+ }
+
+ body {
+ -ms-overflow-style: scrollbar;
+ }
+
+ @media screen and (max-width: 480px) {
+
+ html, body {
+ min-width: 320px;
+ }
+
+ }
+
+ body {
+ background: #ffffff;
+ }
+
+ body.is-loading *, body.is-loading *:before, body.is-loading *:after, body.is-resizing *, body.is-resizing *:before, body.is-resizing *:after {
+ -moz-animation: none !important;
+ -webkit-animation: none !important;
+ -ms-animation: none !important;
+ animation: none !important;
+ -moz-transition: none !important;
+ -webkit-transition: none !important;
+ -ms-transition: none !important;
+ transition: none !important;
+ }
+
+/* Type */
+
+ body, input, select, textarea {
+ color: #7f888f;
+ font-family: "Open Sans", sans-serif;
+ font-size: 13pt;
+ font-weight: 400;
+ line-height: 1.65;
+ }
+
+ @media screen and (max-width: 1680px) {
+
+ body, input, select, textarea {
+ font-size: 11pt;
+ }
+
+ }
+
+ @media screen and (max-width: 1280px) {
+
+ body, input, select, textarea {
+ font-size: 10pt;
+ }
+
+ }
+
+ @media screen and (max-width: 360px) {
+
+ body, input, select, textarea {
+ font-size: 9pt;
+ }
+
+ }
+
+ a {
+ -moz-transition: color 0.2s ease-in-out, border-bottom-color 0.2s ease-in-out;
+ -webkit-transition: color 0.2s ease-in-out, border-bottom-color 0.2s ease-in-out;
+ -ms-transition: color 0.2s ease-in-out, border-bottom-color 0.2s ease-in-out;
+ transition: color 0.2s ease-in-out, border-bottom-color 0.2s ease-in-out;
+ border-bottom: dotted 1px;
+ color: #0083f8;
+ text-decoration: none;
+ }
+
+ a:hover {
+ border-bottom-color: #0083f8;
+ color: #0083f8 !important;
+ }
+
+ a:hover strong {
+ color: inherit;
+ }
+
+ strong, b {
+ color: #3d4449;
+ font-weight: 600;
+ }
+
+ em, i {
+ font-style: italic;
+ }
+
+ p {
+ margin: 0 0 2em 0;
+ }
+
+ h1, h2, h3, h4, h5, h6 {
+ color: #3d4449;
+ font-family: "Roboto Slab", serif;
+ font-weight: 700;
+ line-height: 1.5;
+ margin: 0 0 1em 0;
+ }
+
+ h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {
+ color: inherit;
+ text-decoration: none;
+ }
+
+ h1 {
+ font-size: 4em;
+ margin: 0 0 0.5em 0;
+ line-height: 1.3;
+ }
+
+ h2 {
+ font-size: 1.75em;
+ }
+
+ h3 {
+ font-size: 1.25em;
+ }
+
+ h4 {
+ font-size: 1.1em;
+ }
+
+ h5 {
+ font-size: 0.9em;
+ }
+
+ h6 {
+ font-size: 0.7em;
+ }
+
+ @media screen and (max-width: 1680px) {
+
+ h1 {
+ font-size: 3.5em;
+ }
+
+ }
+
+ @media screen and (max-width: 980px) {
+
+ h1 {
+ font-size: 3.25em;
+ }
+
+ }
+
+ @media screen and (max-width: 736px) {
+
+ h1 {
+ font-size: 2em;
+ line-height: 1.4;
+ }
+
+ h2 {
+ font-size: 1.5em;
+ }
+
+ }
+
+ sub {
+ font-size: 0.8em;
+ position: relative;
+ top: 0.5em;
+ }
+
+ sup {
+ font-size: 0.8em;
+ position: relative;
+ top: -0.5em;
+ }
+
+ blockquote {
+ border-left: solid 3px rgba(210, 215, 217, 0.75);
+ font-style: italic;
+ margin: 0 0 2em 0;
+ padding: 0.5em 0 0.5em 2em;
+ }
+
+ code {
+ background: rgba(230, 235, 237, 0.25);
+ border-radius: 0.375em;
+ border: solid 1px rgba(210, 215, 217, 0.75);
+ font-family: "Courier New", monospace;
+ font-size: 0.9em;
+ margin: 0 0.25em;
+ padding: 0.25em 0.65em;
+ }
+
+ pre {
+ -webkit-overflow-scrolling: touch;
+ font-family: "Courier New", monospace;
+ font-size: 0.9em;
+ margin: 0 0 2em 0;
+ }
+
+ pre code {
+ display: block;
+ line-height: 1.75;
+ padding: 1em 1.5em;
+ overflow-x: auto;
+ }
+
+ hr {
+ border: 0;
+ border-bottom: solid 1px rgba(210, 215, 217, 0.75);
+ margin: 2em 0;
+ }
+
+ hr.major {
+ margin: 3em 0;
+ }
+
+ .align-left {
+ text-align: left;
+ }
+
+ .align-center {
+ text-align: center;
+ }
+
+ .align-right {
+ text-align: right;
+ }
+
+/* Section/Article */
+
+ section.special, article.special {
+ text-align: center;
+ }
+
+ header p {
+ font-family: "Roboto Slab", serif;
+ font-size: 1em;
+ font-weight: 400;
+ letter-spacing: 0.075em;
+ margin-top: -0.5em;
+ text-transform: uppercase;
+ }
+
+ header.major > :last-child {
+ border-bottom: solid 3px #0083f8;
+ display: inline-block;
+ margin: 0 0 2em 0;
+ padding: 0 0.75em 0.5em 0;
+ }
+
+ header.main > :last-child {
+ margin: 0 0 1em 0;
+ }
+
+/* Form */
+
+ form {
+ margin: 0 0 2em 0;
+ }
+
+ label {
+ color: #3d4449;
+ display: block;
+ font-size: 0.9em;
+ font-weight: 600;
+ margin: 0 0 1em 0;
+ }
+
+ input[type="text"],
+ input[type="password"],
+ input[type="email"],
+ input[type="tel"],
+ input[type="search"],
+ input[type="url"],
+ select,
+ textarea {
+ -moz-appearance: none;
+ -webkit-appearance: none;
+ -ms-appearance: none;
+ appearance: none;
+ background: #ffffff;
+ border-radius: 0.375em;
+ border: none;
+ border: solid 1px rgba(210, 215, 217, 0.75);
+ color: inherit;
+ display: block;
+ outline: 0;
+ padding: 0 1em;
+ text-decoration: none;
+ width: 100%;
+ }
+
+ input[type="text"]:invalid,
+ input[type="password"]:invalid,
+ input[type="email"]:invalid,
+ input[type="tel"]:invalid,
+ input[type="search"]:invalid,
+ input[type="url"]:invalid,
+ select:invalid,
+ textarea:invalid {
+ box-shadow: none;
+ }
+
+ input[type="text"]:focus,
+ input[type="password"]:focus,
+ input[type="email"]:focus,
+ input[type="tel"]:focus,
+ input[type="search"]:focus,
+ input[type="url"]:focus,
+ select:focus,
+ textarea:focus {
+ border-color: #0083f8;
+ box-shadow: 0 0 0 1px #0083f8;
+ }
+
+ .select-wrapper {
+ text-decoration: none;
+ display: block;
+ position: relative;
+ }
+
+ .select-wrapper:before {
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-font-smoothing: antialiased;
+ font-family: FontAwesome;
+ font-style: normal;
+ font-weight: normal;
+ text-transform: none !important;
+ }
+
+ .select-wrapper:before {
+ color: rgba(210, 215, 217, 0.75);
+ content: '\f078';
+ display: block;
+ height: 2.75em;
+ line-height: 2.75em;
+ pointer-events: none;
+ position: absolute;
+ right: 0;
+ text-align: center;
+ top: 0;
+ width: 2.75em;
+ }
+
+ .select-wrapper select::-ms-expand {
+ display: none;
+ }
+
+ input[type="text"],
+ input[type="password"],
+ input[type="email"],
+ input[type="tel"],
+ input[type="search"],
+ input[type="url"],
+ select {
+ height: 2.75em;
+ }
+
+ textarea {
+ padding: 0.75em 1em;
+ }
+
+ input[type="checkbox"],
+ input[type="radio"] :not(#leaflet-control-layers-selector){
+ -moz-appearance: none;
+ -webkit-appearance: none;
+ -ms-appearance: none;
+ appearance: none;
+ display: block;
+ float: left;
+ margin-right: -2em;
+ opacity: 0;
+ width: 1em;
+ z-index: -1;
+ }
+
+ input[type="checkbox"] + label,
+ input[type="radio"] + label :not(#leaflet-control-layers-selector){
+ text-decoration: none;
+ color: #7f888f;
+ cursor: pointer;
+ display: inline-block;
+ font-size: 1em;
+ font-weight: 400;
+ padding-left: 2.4em;
+ padding-right: 0.75em;
+ position: relative;
+ }
+
+ input[type="checkbox"] + label:before,
+ input[type="radio"] + label:before :not(#leaflet-control-layers-selector) {
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-font-smoothing: antialiased;
+ font-family: FontAwesome;
+ font-style: normal;
+ font-weight: normal;
+ text-transform: none !important;
+ }
+
+ input[type="checkbox"] + label:before,
+ input[type="radio"] + label:before :not(#leaflet-control-layers-selector) {
+ background: #ffffff;
+ border-radius: 0.375em;
+ border: solid 1px rgba(210, 215, 217, 0.75);
+ content: '';
+ display: inline-block;
+ height: 1.65em;
+ left: 0;
+ line-height: 1.58125em;
+ position: absolute;
+ text-align: center;
+ top: 0;
+ width: 1.65em;
+ }
+
+ input[type="checkbox"]:checked + label:before,
+ input[type="radio"]:checked + label:before :not(#leaflet-control-layers-selector){
+ background: #3d4449;
+ border-color: #3d4449;
+ color: #ffffff;
+ content: '\f00c';
+ }
+
+ input[type="checkbox"]:focus + label:before,
+ input[type="radio"]:focus + label:before :not(#leaflet-control-layers-selector){
+ border-color: #0083f8;
+ box-shadow: 0 0 0 1px #0083f8;
+ }
+
+ input[type="checkbox"] + label:before {
+ border-radius: 0.375em;
+ }
+
+ input[type="radio"] + label:before :not(#leaflet-control-layers-selector){
+ border-radius: 100%;
+ }
+
+ ::-webkit-input-placeholder {
+ color: #9fa3a6 !important;
+ opacity: 1.0;
+ }
+
+ :-moz-placeholder {
+ color: #9fa3a6 !important;
+ opacity: 1.0;
+ }
+
+ ::-moz-placeholder {
+ color: #9fa3a6 !important;
+ opacity: 1.0;
+ }
+
+ :-ms-input-placeholder {
+ color: #9fa3a6 !important;
+ opacity: 1.0;
+ }
+
+ .formerize-placeholder {
+ color: #9fa3a6 !important;
+ opacity: 1.0;
+ }
+
+/* Box */
+
+ .box {
+ border-radius: 0.375em;
+ border: solid 1px rgba(210, 215, 217, 0.75);
+ margin-bottom: 2em;
+ padding: 1.5em;
+ }
+
+ .box > :last-child,
+ .box > :last-child > :last-child,
+ .box > :last-child > :last-child > :last-child {
+ margin-bottom: 0;
+ }
+
+ .box.alt {
+ border: 0;
+ border-radius: 0;
+ padding: 0;
+ }
+
+/* Icon */
+
+ .icon {
+ text-decoration: none;
+ border-bottom: none;
+ position: relative;
+ }
+
+ .icon:before {
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-font-smoothing: antialiased;
+ font-family: FontAwesome;
+ font-style: normal;
+ font-weight: normal;
+ text-transform: none !important;
+ }
+
+ .icon > .label {
+ display: none;
+ }
+
+/* Image */
+
+ .image {
+ border-radius: 0.375em;
+ border: 0;
+ display: inline-block;
+ position: relative;
+ }
+
+ .image img {
+ border-radius: 0.375em;
+ display: block;
+ }
+
+ .image.left, .image.right {
+ max-width: 40%;
+ }
+
+ .image.left img, .image.right img {
+ width: 100%;
+ }
+
+ .image.left {
+ float: left;
+ padding: 0 1.5em 1em 0;
+ top: 0.25em;
+ }
+
+ .image.right {
+ float: right;
+ padding: 0 0 1em 1.5em;
+ top: 0.25em;
+ }
+
+ .image.fit {
+ display: block;
+ margin: 0 0 2em 0;
+ width: 100%;
+ }
+
+ .image.fit img {
+ width: 100%;
+ }
+
+ .image.main {
+ display: block;
+ margin: 0 0 3em 0;
+ width: 100%;
+ }
+
+ .image.main img {
+ width: 100%;
+ }
+
+ a.image {
+ overflow: hidden;
+ }
+
+ a.image img {
+ -moz-transition: -moz-transform 0.2s ease;
+ -webkit-transition: -webkit-transform 0.2s ease;
+ -ms-transition: -ms-transform 0.2s ease;
+ transition: transform 0.2s ease;
+ }
+
+ a.image:hover img {
+ -moz-transform: scale(1.075);
+ -webkit-transform: scale(1.075);
+ -ms-transform: scale(1.075);
+ transform: scale(1.075);
+ }
+
+/* List */
+
+ ol {
+ list-style: decimal;
+ margin: 0 0 2em 0;
+ padding-left: 1.25em;
+ }
+
+ ol li {
+ padding-left: 0.25em;
+ }
+
+ ul {
+ list-style: disc;
+ margin: 0 0 2em 0;
+ padding-left: 1em;
+ }
+
+ ul li {
+ padding-left: 0.5em;
+ }
+
+ ul.alt {
+ list-style: none;
+ padding-left: 0;
+ }
+
+ ul.alt li {
+ border-top: solid 1px rgba(210, 215, 217, 0.75);
+ padding: 0.5em 0;
+ }
+
+ ul.alt li:first-child {
+ border-top: 0;
+ padding-top: 0;
+ }
+
+ ul.icons {
+ cursor: default;
+ list-style: none;
+ padding-left: 0;
+ }
+
+ ul.icons li {
+ display: inline-block;
+ padding: 0 1em 0 0;
+ }
+
+ ul.icons li:last-child {
+ padding-right: 0;
+ }
+
+ ul.icons li .icon {
+ color: inherit;
+ }
+
+ ul.icons li .icon:before {
+ font-size: 1.25em;
+ }
+
+ ul.contact {
+ list-style: none;
+ padding: 0;
+ }
+
+ ul.contact li {
+ text-decoration: none;
+ border-top: solid 1px rgba(210, 215, 217, 0.75);
+ margin: 1.5em 0 0 0;
+ padding: 1.5em 0 0 3em;
+ position: relative;
+ }
+
+ ul.contact li:before {
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-font-smoothing: antialiased;
+ font-family: FontAwesome;
+ font-style: normal;
+ font-weight: normal;
+ text-transform: none !important;
+ }
+
+ ul.contact li:before {
+ color: #0083f8;
+ display: inline-block;
+ font-size: 1.5em;
+ height: 1.125em;
+ left: 0;
+ line-height: 1.125em;
+ position: absolute;
+ text-align: center;
+ top: 1em;
+ width: 1.5em;
+ }
+
+ ul.contact li:first-child {
+ border-top: 0;
+ margin-top: 0;
+ padding-top: 0;
+ }
+
+ ul.contact li:first-child:before {
+ top: 0;
+ }
+
+ ul.contact li a {
+ color: inherit;
+ }
+
+ ul.actions {
+ cursor: default;
+ list-style: none;
+ padding-left: 0;
+ }
+
+ ul.actions li {
+ display: inline-block;
+ padding: 0 1em 0 0;
+ vertical-align: middle;
+ }
+
+ ul.actions li:last-child {
+ padding-right: 0;
+ }
+
+ ul.actions.small li {
+ padding: 0 0.5em 0 0;
+ }
+
+ ul.actions.vertical li {
+ display: block;
+ padding: 1em 0 0 0;
+ }
+
+ ul.actions.vertical li:first-child {
+ padding-top: 0;
+ }
+
+ ul.actions.vertical li > * {
+ margin-bottom: 0;
+ }
+
+ ul.actions.vertical.small li {
+ padding: 0.5em 0 0 0;
+ }
+
+ ul.actions.vertical.small li:first-child {
+ padding-top: 0;
+ }
+
+ ul.actions.fit {
+ display: table;
+ margin-left: -1em;
+ padding: 0;
+ table-layout: fixed;
+ width: calc(100% + 1em);
+ }
+
+ ul.actions.fit li {
+ display: table-cell;
+ padding: 0 0 0 1em;
+ }
+
+ ul.actions.fit li > * {
+ margin-bottom: 0;
+ }
+
+ ul.actions.fit.small {
+ margin-left: -0.5em;
+ width: calc(100% + 0.5em);
+ }
+
+ ul.actions.fit.small li {
+ padding: 0 0 0 0.5em;
+ }
+
+ ul.pagination {
+ cursor: default;
+ list-style: none;
+ padding-left: 0;
+ }
+
+ ul.pagination li {
+ display: inline-block;
+ padding-left: 0;
+ vertical-align: middle;
+ }
+
+ ul.pagination li > .page {
+ -moz-transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
+ -webkit-transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
+ -ms-transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
+ transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
+ border-bottom: 0;
+ border-radius: 0.375em;
+ display: inline-block;
+ font-size: 0.8em;
+ font-weight: 600;
+ height: 2em;
+ line-height: 2em;
+ margin: 0 0.125em;
+ min-width: 2em;
+ padding: 0 0.5em;
+ text-align: center;
+ }
+
+ ul.pagination li > .page.active {
+ background-color: #0083f8;
+ color: #ffffff !important;
+ }
+
+ ul.pagination li > .page.active:hover {
+ background-color: #f67878;
+ }
+
+ ul.pagination li > .page.active:active {
+ background-color: #f45c5c;
+ }
+
+ ul.pagination li:first-child {
+ padding-right: 0.75em;
+ }
+
+ ul.pagination li:last-child {
+ padding-left: 0.75em;
+ }
+
+ @media screen and (max-width: 480px) {
+
+ ul.pagination li:nth-child(n+2):nth-last-child(n+2) {
+ display: none;
+ }
+
+ ul.pagination li:first-child {
+ padding-right: 0;
+ }
+
+ }
+
+ dl {
+ margin: 0 0 2em 0;
+ }
+
+ dl dt {
+ display: block;
+ font-weight: 600;
+ margin: 0 0 1em 0;
+ }
+
+ dl dd {
+ margin-left: 2em;
+ }
+
+/* Table */
+
+ .table-wrapper {
+ -webkit-overflow-scrolling: touch;
+ overflow-x: auto;
+ }
+
+ table {
+ margin: 0 0 2em 0;
+ width: 100%;
+ }
+
+ table tbody tr {
+ border: solid 1px rgba(210, 215, 217, 0.75);
+ border-left: 0;
+ border-right: 0;
+ }
+
+ table tbody tr:nth-child(2n + 1) {
+ background-color: rgba(230, 235, 237, 0.25);
+ }
+
+ table td {
+ padding: 0.75em 0.75em;
+ }
+
+ table th {
+ color: #3d4449;
+ font-size: 0.9em;
+ font-weight: 600;
+ padding: 0 0.75em 0.75em 0.75em;
+ text-align: left;
+ }
+
+ table thead {
+ border-bottom: solid 2px rgba(210, 215, 217, 0.75);
+ }
+
+ table tfoot {
+ border-top: solid 2px rgba(210, 215, 217, 0.75);
+ }
+
+ table.alt {
+ border-collapse: separate;
+ }
+
+ table.alt tbody tr td {
+ border: solid 1px rgba(210, 215, 217, 0.75);
+ border-left-width: 0;
+ border-top-width: 0;
+ }
+
+ table.alt tbody tr td:first-child {
+ border-left-width: 1px;
+ }
+
+ table.alt tbody tr:first-child td {
+ border-top-width: 1px;
+ }
+
+ table.alt thead {
+ border-bottom: 0;
+ }
+
+ table.alt tfoot {
+ border-top: 0;
+ }
+
+/* Button */
+
+ input[type="submit"],
+ input[type="reset"],
+ input[type="button"],
+ button,
+ .button {
+ -moz-appearance: none;
+ -webkit-appearance: none;
+ -ms-appearance: none;
+ appearance: none;
+ -moz-transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
+ -webkit-transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
+ -ms-transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
+ transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
+ background-color: transparent;
+ border-radius: 0.375em;
+ border: 0;
+ box-shadow: inset 0 0 0 2px #0083f8;
+ color: #0083f8 !important;
+ cursor: pointer;
+ display: inline-block;
+ font-family: "Roboto Slab", serif;
+ font-size: 0.8em;
+ font-weight: 700;
+ height: 3.5em;
+ letter-spacing: 0.075em;
+ line-height: 3.5em;
+ padding: 0 2.25em;
+ text-align: center;
+ text-decoration: none;
+ text-transform: uppercase;
+ white-space: nowrap;
+ }
+
+ input[type="submit"]:hover,
+ input[type="reset"]:hover,
+ input[type="button"]:hover,
+ button:hover,
+ .button:hover {
+ background-color: rgba(0, 131, 248, 0.05);
+ }
+
+ input[type="submit"]:active,
+ input[type="reset"]:active,
+ input[type="button"]:active,
+ button:active,
+ .button:active {
+ background-color: rgba(0, 131, 248, 0.15);
+ }
+
+ input[type="submit"].icon:before,
+ input[type="reset"].icon:before,
+ input[type="button"].icon:before,
+ button.icon:before,
+ .button.icon:before {
+ margin-right: 0.5em;
+ }
+
+ input[type="submit"].fit,
+ input[type="reset"].fit,
+ input[type="button"].fit,
+ button.fit,
+ .button.fit {
+ display: block;
+ margin: 0 0 1em 0;
+ width: 100%;
+ }
+
+ input[type="submit"].small,
+ input[type="reset"].small,
+ input[type="button"].small,
+ button.small,
+ .button.small {
+ font-size: 0.6em;
+ }
+
+ input[type="submit"].big,
+ input[type="reset"].big,
+ input[type="button"].big,
+ button.big,
+ .button.big {
+ font-size: 1em;
+ height: 3.65em;
+ line-height: 3.65em;
+ }
+
+ input[type="submit"].special,
+ input[type="reset"].special,
+ input[type="button"].special,
+ button.special,
+ .button.special {
+ background-color: #0083f8;
+ box-shadow: none;
+ color: #ffffff !important;
+ }
+
+ input[type="submit"].special:hover,
+ input[type="reset"].special:hover,
+ input[type="button"].special:hover,
+ button.special:hover,
+ .button.special:hover {
+ background-color: #f67878;
+ }
+
+ input[type="submit"].special:active,
+ input[type="reset"].special:active,
+ input[type="button"].special:active,
+ button.special:active,
+ .button.special:active {
+ background-color: #f45c5c;
+ }
+
+ input[type="submit"].disabled, input[type="submit"]:disabled,
+ input[type="reset"].disabled,
+ input[type="reset"]:disabled,
+ input[type="button"].disabled,
+ input[type="button"]:disabled,
+ button.disabled,
+ button:disabled,
+ .button.disabled,
+ .button:disabled {
+ -moz-pointer-events: none;
+ -webkit-pointer-events: none;
+ -ms-pointer-events: none;
+ pointer-events: none;
+ opacity: 0.25;
+ }
+
+/* Mini Posts */
+
+ .mini-posts article {
+ border-top: solid 1px rgba(210, 215, 217, 0.75);
+ margin-top: 2em;
+ padding-top: 2em;
+ }
+
+ .mini-posts article .image {
+ display: block;
+ margin: 0 0 1.5em 0;
+ }
+
+ .mini-posts article .image img {
+ display: block;
+ width: 100%;
+ }
+
+ .mini-posts article:first-child {
+ border-top: 0;
+ margin-top: 0;
+ padding-top: 0;
+ }
+
+/* Features */
+
+ .features {
+ display: -moz-flex;
+ display: -webkit-flex;
+ display: -ms-flex;
+ display: flex;
+ -moz-flex-wrap: wrap;
+ -webkit-flex-wrap: wrap;
+ -ms-flex-wrap: wrap;
+ flex-wrap: wrap;
+ margin: 0 0 2em -3em;
+ width: calc(100% + 3em);
+ }
+
+ .features article {
+ -moz-align-items: center;
+ -webkit-align-items: center;
+ -ms-align-items: center;
+ align-items: center;
+ display: -moz-flex;
+ display: -webkit-flex;
+ display: -ms-flex;
+ display: flex;
+ margin: 0 0 3em 3em;
+ position: relative;
+ width: calc(50% - 3em);
+ }
+
+ .features article:nth-child(2n - 1) {
+ margin-right: 1.5em;
+ }
+
+ .features article:nth-child(2n) {
+ margin-left: 1.5em;
+ }
+
+ .features article:nth-last-child(1), .features article:nth-last-child(2) {
+ margin-bottom: 0;
+ }
+
+ .features article .icon {
+ -moz-flex-grow: 0;
+ -webkit-flex-grow: 0;
+ -ms-flex-grow: 0;
+ flex-grow: 0;
+ -moz-flex-shrink: 0;
+ -webkit-flex-shrink: 0;
+ -ms-flex-shrink: 0;
+ flex-shrink: 0;
+ display: block;
+ height: 10em;
+ line-height: 10em;
+ margin: 0 2em 0 0;
+ text-align: center;
+ width: 10em;
+ }
+
+ .features article .icon:before {
+ color: #0083f8;
+ font-size: 2.75rem;
+ position: relative;
+ top: 0.05em;
+ }
+
+ .features article .icon:after {
+ -moz-transform: rotate(45deg);
+ -webkit-transform: rotate(45deg);
+ -ms-transform: rotate(45deg);
+ transform: rotate(45deg);
+ border-radius: 0.25rem;
+ border: solid 2px rgba(210, 215, 217, 0.75);
+ content: '';
+ display: block;
+ height: 7em;
+ left: 50%;
+ margin: -3.5em 0 0 -3.5em;
+ position: absolute;
+ top: 50%;
+ width: 7em;
+ }
+
+ .features article .content {
+ -moz-flex-grow: 1;
+ -webkit-flex-grow: 1;
+ -ms-flex-grow: 1;
+ flex-grow: 1;
+ -moz-flex-shrink: 1;
+ -webkit-flex-shrink: 1;
+ -ms-flex-shrink: 1;
+ flex-shrink: 1;
+ width: 100%;
+ }
+
+ .features article .content > :last-child {
+ margin-bottom: 0;
+ }
+
+ @media screen and (max-width: 980px) {
+
+ .features {
+ margin: 0 0 2em 0;
+ width: 100%;
+ }
+
+ .features article {
+ margin: 0 0 3em 0;
+ width: 100%;
+ }
+
+ .features article:nth-child(2n - 1) {
+ margin-right: 0;
+ }
+
+ .features article:nth-child(2n) {
+ margin-left: 0;
+ }
+
+ .features article:nth-last-child(1), .features article:nth-last-child(2) {
+ margin-bottom: 3em;
+ }
+
+ .features article:last-child {
+ margin-bottom: 0;
+ }
+
+ .features article .icon {
+ height: 8em;
+ line-height: 8em;
+ width: 8em;
+ }
+
+ .features article .icon:before {
+ font-size: 2.25rem;
+ }
+
+ .features article .icon:after {
+ height: 6em;
+ margin: -3em 0 0 -3em;
+ width: 6em;
+ }
+
+ }
+
+ @media screen and (max-width: 480px) {
+
+ .features article {
+ -moz-flex-direction: column;
+ -webkit-flex-direction: column;
+ -ms-flex-direction: column;
+ flex-direction: column;
+ -moz-align-items: -moz-flex-start;
+ -webkit-align-items: -webkit-flex-start;
+ -ms-align-items: -ms-flex-start;
+ align-items: flex-start;
+ }
+
+ .features article .icon {
+ height: 6em;
+ line-height: 6em;
+ margin: 0 0 1.5em 0;
+ width: 6em;
+ }
+
+ .features article .icon:before {
+ font-size: 1.5rem;
+ }
+
+ .features article .icon:after {
+ height: 4em;
+ margin: -2em 0 0 -2em;
+ width: 4em;
+ }
+
+ }
+
+ @media screen and (max-width: 480px) {
+
+ .features article .icon:before {
+ font-size: 1.25rem;
+ }
+
+ }
+
+/* Posts */
+
+ .posts {
+ display: -moz-flex;
+ display: -webkit-flex;
+ display: -ms-flex;
+ display: flex;
+ -moz-flex-wrap: wrap;
+ -webkit-flex-wrap: wrap;
+ -ms-flex-wrap: wrap;
+ flex-wrap: wrap;
+ margin: 0 0 2em -6em;
+ width: calc(100% + 6em);
+ }
+
+ .posts article {
+ -moz-flex-grow: 0;
+ -webkit-flex-grow: 0;
+ -ms-flex-grow: 0;
+ flex-grow: 0;
+ -moz-flex-shrink: 1;
+ -webkit-flex-shrink: 1;
+ -ms-flex-shrink: 1;
+ flex-shrink: 1;
+ margin: 0 0 6em 6em;
+ position: relative;
+ width: calc(33.33333% - 6em);
+ }
+
+ .posts article:before {
+ background: rgba(210, 215, 217, 0.75);
+ content: '';
+ display: block;
+ height: calc(100% + 6em);
+ left: -3em;
+ position: absolute;
+ top: 0;
+ width: 1px;
+ }
+
+ .posts article:after {
+ background: rgba(210, 215, 217, 0.75);
+ bottom: -3em;
+ content: '';
+ display: block;
+ height: 1px;
+ position: absolute;
+ right: 0;
+ width: calc(100% + 6em);
+ }
+
+ .posts article > :last-child {
+ margin-bottom: 0;
+ }
+
+ .posts article .image {
+ display: block;
+ margin: 0 0 2em 0;
+ }
+
+ .posts article .image img {
+ display: block;
+ width: 100%;
+ }
+
+ @media screen and (min-width: 1681px) {
+
+ .posts article:nth-child(3n + 1):before {
+ display: none;
+ }
+
+ .posts article:nth-child(3n + 1):after {
+ width: 100%;
+ }
+
+ .posts article:nth-last-child(1), .posts article:nth-last-child(2), .posts article:nth-last-child(3) {
+ margin-bottom: 0;
+ }
+
+ .posts article:nth-last-child(1):before, .posts article:nth-last-child(2):before, .posts article:nth-last-child(3):before {
+ height: 100%;
+ }
+
+ .posts article:nth-last-child(1):after, .posts article:nth-last-child(2):after, .posts article:nth-last-child(3):after {
+ display: none;
+ }
+
+ }
+
+ @media screen and (max-width: 1680px) {
+
+ .posts article {
+ width: calc(50% - 6em);
+ }
+
+ .posts article:nth-last-child(3) {
+ margin-bottom: 6em;
+ }
+
+ }
+
+ @media screen and (min-width: 481px) and (max-width: 1680px) {
+
+ .posts article:nth-child(2n + 1):before {
+ display: none;
+ }
+
+ .posts article:nth-child(2n + 1):after {
+ width: 100%;
+ }
+
+ .posts article:nth-last-child(1), .posts article:nth-last-child(2) {
+ margin-bottom: 0;
+ }
+
+ .posts article:nth-last-child(1):before, .posts article:nth-last-child(2):before {
+ height: 100%;
+ }
+
+ .posts article:nth-last-child(1):after, .posts article:nth-last-child(2):after {
+ display: none;
+ }
+
+ }
+
+ @media screen and (max-width: 736px) {
+
+ .posts {
+ margin: 0 0 2em -4.5em;
+ width: calc(100% + 4.5em);
+ }
+
+ .posts article {
+ margin: 0 0 4.5em 4.5em;
+ width: calc(50% - 4.5em);
+ }
+
+ .posts article:before {
+ height: calc(100% + 4.5em);
+ left: -2.25em;
+ }
+
+ .posts article:after {
+ bottom: -2.25em;
+ width: calc(100% + 4.5em);
+ }
+
+ .posts article:nth-last-child(3) {
+ margin-bottom: 4.5em;
+ }
+
+ }
+
+ @media screen and (max-width: 480px) {
+
+ .posts {
+ margin: 0 0 2em 0;
+ width: 100%;
+ }
+
+ .posts article {
+ margin: 0 0 4.5em 0;
+ width: 100%;
+ }
+
+ .posts article:before {
+ display: none;
+ }
+
+ .posts article:after {
+ width: 100%;
+ }
+
+ .posts article:last-child {
+ margin-bottom: 0;
+ }
+
+ .posts article:last-child:after {
+ display: none;
+ }
+
+ }
+
+/* Wrapper */
+
+ #wrapper {
+ display: -moz-flex;
+ display: -webkit-flex;
+ display: -ms-flex;
+ display: flex;
+ -moz-flex-direction: row-reverse;
+ -webkit-flex-direction: row-reverse;
+ -ms-flex-direction: row-reverse;
+ flex-direction: row-reverse;
+ min-height: 100vh;
+ }
+
+/* Main */
+
+ #main {
+ -moz-flex-grow: 1;
+ -webkit-flex-grow: 1;
+ -ms-flex-grow: 1;
+ flex-grow: 1;
+ -moz-flex-shrink: 1;
+ -webkit-flex-shrink: 1;
+ -ms-flex-shrink: 1;
+ flex-shrink: 1;
+ width: 100%;
+ }
+
+ #main > .inner {
+ padding: 0 6em 0.1em 6em ;
+ margin: 0 auto;
+ max-width: 110em;
+ }
+
+ #main > .inner > section {
+ padding: 6em 0 4em 0 ;
+ border-top: solid 2px rgba(210, 215, 217, 0.75);
+ }
+
+ #main > .inner > section:first-of-type {
+ border-top: 0 !important;
+ }
+
+ @media screen and (max-width: 1680px) {
+
+ #main > .inner {
+ padding: 0 5em 0.1em 5em ;
+ }
+
+ #main > .inner > section {
+ padding: 5em 0 3em 0 ;
+ }
+
+ }
+
+ @media screen and (max-width: 1280px) {
+
+ #main > .inner {
+ padding: 0 4em 0.1em 4em ;
+ }
+
+ #main > .inner > section {
+ padding: 4em 0 2em 0 ;
+ }
+
+ }
+
+ @media screen and (max-width: 736px) {
+
+ #main > .inner {
+ padding: 0 2em 0.1em 2em ;
+ }
+
+ #main > .inner > section {
+ padding: 3em 0 1em 0 ;
+ }
+
+ }
+
+/* Sidebar */
+
+ #search form {
+ text-decoration: none;
+ position: relative;
+ }
+
+ #search form:before {
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-font-smoothing: antialiased;
+ font-family: FontAwesome;
+ font-style: normal;
+ font-weight: normal;
+ text-transform: none !important;
+ }
+
+ #search form:before {
+ -moz-transform: scaleX(-1);
+ -webkit-transform: scaleX(-1);
+ -ms-transform: scaleX(-1);
+ transform: scaleX(-1);
+ color: #7f888f;
+ content: '\f002';
+ cursor: default;
+ display: block;
+ font-size: 1.5em;
+ height: 2em;
+ line-height: 2em;
+ opacity: 0.325;
+ position: absolute;
+ right: 0;
+ text-align: center;
+ top: 0;
+ width: 2em;
+ }
+
+ #search form input[type="text"] {
+ padding-right: 2.75em;
+ }
+
+ #sidebar {
+ -moz-flex-grow: 0;
+ -webkit-flex-grow: 0;
+ -ms-flex-grow: 0;
+ flex-grow: 0;
+ -moz-flex-shrink: 0;
+ -webkit-flex-shrink: 0;
+ -ms-flex-shrink: 0;
+ flex-shrink: 0;
+ -moz-transition: margin-left 0.5s ease, box-shadow 0.5s ease;
+ -webkit-transition: margin-left 0.5s ease, box-shadow 0.5s ease;
+ -ms-transition: margin-left 0.5s ease, box-shadow 0.5s ease;
+ transition: margin-left 0.5s ease, box-shadow 0.5s ease;
+ background-color: #f5f6f7;
+ font-size: 0.9em;
+ position: relative;
+ width: 26em;
+ }
+
+ #sidebar h2 {
+ font-size: 1.38889em;
+ }
+
+ #sidebar > .inner {
+ padding: 2.22222em 2.22222em 2.44444em 2.22222em ;
+ position: relative;
+ width: 26em;
+ }
+
+ #sidebar > .inner > * {
+ border-bottom: solid 2px rgba(210, 215, 217, 0.75);
+ margin: 0 0 3.5em 0;
+ padding: 0 0 3.5em 0;
+ }
+
+ #sidebar > .inner > * > :last-child {
+ margin-bottom: 0;
+ }
+
+ #sidebar > .inner > *:last-child {
+ border-bottom: 0;
+ margin-bottom: 0;
+ padding-bottom: 0;
+ }
+
+ #sidebar > .inner > .alt {
+ /* background-color: #eff1f2; */
+ background-color: #f5f6f7;
+ border-bottom: 0;
+ /* margin: -2.22222em 0 4.44444em -2.22222em; */
+ margin: -2.22222em 0 0 -2.22222em;
+ padding: 2.22222em;
+ width: calc(100% + 4.44444em);
+ }
+
+ #sidebar .toggle {
+ text-decoration: none;
+ -moz-transition: left 0.5s ease;
+ -webkit-transition: left 0.5s ease;
+ -ms-transition: left 0.5s ease;
+ transition: left 0.5s ease;
+ -webkit-tap-highlight-color: rgba(255, 255, 255, 0);
+ border: 0;
+ display: block;
+ height: 7.5em;
+ left: 26em;
+ line-height: 7.5em;
+ outline: 0;
+ overflow: hidden;
+ position: absolute;
+ text-align: center;
+ text-indent: 7.5em;
+ top: 0;
+ width: 6em;
+ z-index: 10000;
+ }
+
+ #sidebar .toggle:before {
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-font-smoothing: antialiased;
+ font-family: FontAwesome;
+ font-style: normal;
+ font-weight: normal;
+ text-transform: none !important;
+ }
+
+ #sidebar .toggle:before {
+ content: '\f0c9';
+ font-size: 2rem;
+ height: inherit;
+ left: 0;
+ line-height: inherit;
+ position: absolute;
+ text-indent: 0;
+ top: 0;
+ width: inherit;
+ }
+
+ #sidebar.inactive {
+ margin-left: -26em;
+ }
+
+ @media screen and (max-width: 1680px) {
+
+ #sidebar {
+ width: 24em;
+ }
+
+ #sidebar > .inner {
+ padding: 1.66667em 1.66667em 1.33333em 1.66667em ;
+ width: 24em;
+ }
+
+ #sidebar > .inner > .alt {
+ margin: -1.66667em 0 3.33333em -1.66667em;
+ padding: 1.66667em;
+ width: calc(100% + 3.33333em);
+ }
+
+ #sidebar .toggle {
+ height: 6.25em;
+ left: 24em;
+ line-height: 6.25em;
+ text-indent: 5em;
+ width: 5em;
+ }
+
+ #sidebar .toggle:before {
+ font-size: 1.5rem;
+ }
+
+ #sidebar.inactive {
+ margin-left: -24em;
+ }
+
+ }
+
+ @media screen and (max-width: 1280px) {
+
+ #sidebar {
+ box-shadow: 0 0 5em 0 rgba(0, 0, 0, 0.175);
+ height: 100%;
+ left: 0;
+ position: fixed;
+ top: 0;
+ z-index: 10000;
+ }
+
+ #sidebar.inactive {
+ box-shadow: none;
+ }
+
+ #sidebar > .inner {
+ -webkit-overflow-scrolling: touch;
+ height: 100%;
+ left: 0;
+ overflow-x: hidden;
+ overflow-y: auto;
+ position: absolute;
+ top: 0;
+ }
+
+ #sidebar > .inner:after {
+ content: '';
+ display: block;
+ height: 4em;
+ width: 100%;
+ }
+
+ #sidebar .toggle {
+ text-indent: 6em;
+ width: 6em;
+ }
+
+ #sidebar .toggle:before {
+ font-size: 1.5rem;
+ margin-left: -0.4375em;
+ }
+
+ body.is-loading #sidebar {
+ display: none;
+ }
+
+ }
+
+ @media screen and (max-width: 736px) {
+
+ #sidebar .toggle {
+ text-indent: 7.25em;
+ width: 7.25em;
+ }
+
+ #sidebar .toggle:before {
+ color: #7f888f;
+ margin-left: -0.0625em;
+ margin-top: -0.25em;
+ font-size: 1.1rem;
+ z-index: 1;
+ }
+
+ #sidebar .toggle:after {
+ background: rgba(222, 225, 226, 0.75);
+ border-radius: 0.375em;
+ content: '';
+ height: 3.5em;
+ left: 1em;
+ position: absolute;
+ top: 1em;
+ width: 5em;
+ }
+
+ }
+
+/* Header */
+
+ #header {
+ display: -moz-flex;
+ display: -webkit-flex;
+ display: -ms-flex;
+ display: flex;
+ border-bottom: solid 5px #0083f8;
+ padding: 1em 0 1em 0;
+ position: relative;
+ }
+
+ #header > * {
+ -moz-flex: 1;
+ -webkit-flex: 1;
+ -ms-flex: 1;
+ flex: 1;
+ margin-bottom: 0;
+ }
+
+ #header .logo {
+ border-bottom: 0;
+ color: inherit;
+ font-family: "Roboto Slab", serif;
+ font-size: 1.125em;
+ }
+
+ #header .icons {
+ text-align: right;
+ }
+
+ @media screen and (max-width: 1680px) {
+
+ #header {
+ padding-top: 1em;
+ }
+
+ }
+
+ @media screen and (max-width: 736px) {
+
+ #header {
+ padding-top: 6.5em;
+ }
+
+ #header .logo {
+ font-size: 1.25em;
+ margin: 0;
+ }
+
+ #header .icons {
+ height: 5em;
+ line-height: 5em;
+ position: absolute;
+ right: -0.5em;
+ top: 0;
+ }
+
+ }
+
+/* Banner */
+
+ #banner {
+ padding: 6em 0 4em 0 ;
+ display: -moz-flex;
+ display: -webkit-flex;
+ display: -ms-flex;
+ display: flex;
+ }
+
+ #banner h1 {
+ margin-top: -0.125em;
+ }
+
+ #banner .content {
+ -moz-flex-grow: 1;
+ -webkit-flex-grow: 1;
+ -ms-flex-grow: 1;
+ flex-grow: 1;
+ -moz-flex-shrink: 1;
+ -webkit-flex-shrink: 1;
+ -ms-flex-shrink: 1;
+ flex-shrink: 1;
+ width: 50%;
+ }
+
+ #banner .image {
+ -moz-flex-grow: 0;
+ -webkit-flex-grow: 0;
+ -ms-flex-grow: 0;
+ flex-grow: 0;
+ -moz-flex-shrink: 0;
+ -webkit-flex-shrink: 0;
+ -ms-flex-shrink: 0;
+ flex-shrink: 0;
+ display: block;
+ margin: 0 0 2em 4em;
+ width: 50%;
+ }
+
+ #banner .image img {
+ height: 100%;
+ -moz-object-fit: cover;
+ -webkit-object-fit: cover;
+ -ms-object-fit: cover;
+ object-fit: cover;
+ -moz-object-position: center;
+ -webkit-object-position: center;
+ -ms-object-position: center;
+ object-position: center;
+ width: 100%;
+ }
+
+ @media screen and (orientation: portrait) {
+
+ #banner {
+ -moz-flex-direction: column-reverse;
+ -webkit-flex-direction: column-reverse;
+ -ms-flex-direction: column-reverse;
+ flex-direction: column-reverse;
+ }
+
+ #banner h1 br {
+ display: none;
+ }
+
+ #banner .content {
+ -moz-flex-grow: 0;
+ -webkit-flex-grow: 0;
+ -ms-flex-grow: 0;
+ flex-grow: 0;
+ -moz-flex-shrink: 0;
+ -webkit-flex-shrink: 0;
+ -ms-flex-shrink: 0;
+ flex-shrink: 0;
+ width: 100%;
+ }
+
+ #banner .image {
+ -moz-flex-grow: 0;
+ -webkit-flex-grow: 0;
+ -ms-flex-grow: 0;
+ flex-grow: 0;
+ -moz-flex-shrink: 0;
+ -webkit-flex-shrink: 0;
+ -ms-flex-shrink: 0;
+ flex-shrink: 0;
+ margin: 0 0 4em 0;
+ height: 25em;
+ max-height: 50vh;
+ min-height: 18em;
+ width: 100%;
+ }
+
+ }
+
+ @media screen and (orientation: portrait) and (max-width: 480px) {
+
+ #banner .image {
+ max-height: 35vh;
+ }
+
+ }
+
+/* Footer */
+
+ #footer .copyright {
+ color: #9fa3a6;
+ font-size: 0.9em;
+ }
+
+ #footer .copyright a {
+ color: inherit;
+ }
+
+/* Menu */
+
+ #menu ul {
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ color: #3d4449;
+ font-family: "Roboto Slab", serif;
+ font-family: 400;
+ letter-spacing: 0.075em;
+ list-style: none;
+ margin-bottom: 0;
+ padding: 0;
+ text-transform: uppercase;
+ }
+
+ #menu ul a, #menu ul span {
+ border-bottom: 0;
+ color: inherit;
+ cursor: pointer;
+ display: block;
+ font-size: 0.9em;
+ padding: 0.625em 0;
+ }
+
+ #menu ul a:hover, #menu ul span:hover {
+ color: #0083f8;
+ }
+
+ #menu ul a.opener, #menu ul span.opener {
+ -moz-transition: color 0.2s ease-in-out;
+ -webkit-transition: color 0.2s ease-in-out;
+ -ms-transition: color 0.2s ease-in-out;
+ transition: color 0.2s ease-in-out;
+ text-decoration: none;
+ -webkit-tap-highlight-color: rgba(255, 255, 255, 0);
+ position: relative;
+ }
+
+ #menu ul a.opener:before, #menu ul span.opener:before {
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-font-smoothing: antialiased;
+ font-family: FontAwesome;
+ font-style: normal;
+ font-weight: normal;
+ text-transform: none !important;
+ }
+
+ #menu ul a.opener:before, #menu ul span.opener:before {
+ -moz-transition: color 0.2s ease-in-out, -moz-transform 0.2s ease-in-out;
+ -webkit-transition: color 0.2s ease-in-out, -webkit-transform 0.2s ease-in-out;
+ -ms-transition: color 0.2s ease-in-out, -ms-transform 0.2s ease-in-out;
+ transition: color 0.2s ease-in-out, transform 0.2s ease-in-out;
+ color: #9fa3a6;
+ content: '\f078';
+ position: absolute;
+ right: 0;
+ }
+
+ #menu ul a.opener:hover:before, #menu ul span.opener:hover:before {
+ color: #0083f8;
+ }
+
+ #menu ul a.opener.active + ul, #menu ul span.opener.active + ul {
+ display: block;
+ }
+
+ #menu ul a.opener.active:before, #menu ul span.opener.active:before {
+ -moz-transform: rotate(-180deg);
+ -webkit-transform: rotate(-180deg);
+ -ms-transform: rotate(-180deg);
+ transform: rotate(-180deg);
+ }
+
+ #menu > ul > li {
+ border-top: solid 1px rgba(210, 215, 217, 0.75);
+ margin: 0.5em 0 0 0;
+ padding: 0.5em 0 0 0;
+ }
+
+ #menu > ul > li > ul {
+ color: #9fa3a6;
+ display: none;
+ margin: 0.5em 0 1.5em 0;
+ padding-left: 1em;
+ }
+
+ #menu > ul > li > ul a, #menu > ul > li > ul span {
+ font-size: 0.8em;
+ }
+
+ #menu > ul > li > ul > li {
+ margin: 0.125em 0 0 0;
+ padding: 0.125em 0 0 0;
+ }
+
+ #menu > ul > li:first-child {
+ border-top: 0;
+ margin-top: 0;
+ padding-top: 0;
+ }
+
+/* Map */
+
+ #stammMap {
+ height: 600px;
+ }
+
+ @media screen and (max-width: 980px) {
+ #stammMap {
+ height: 300px;
+ }
+ }
+
+/* Calendar */
+
+.cal_event > a {
+ font-weight: bold;
+}
+
+.cal_today {
+ padding: 0;
+}
+
+.cal_today > div {
+ border-radius: 50%;
+ background-color: #cce7ff;
+ width: 2.5em;
+ margin: auto;
+ height: 2.5em;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ font-weight: bold;
+ color: black;
+}
+
+.calendar > tbody > tr > td {
+ text-align: center;
+ vertical-align: middle;
+}
+
+.calendar > thead > tr > th {
+ text-align: center;
+ vertical-align: middle;
+ font-size: 1.1em;
+}
\ No newline at end of file
diff --git a/assets/fonts/FontAwesome.otf b/assets/fonts/FontAwesome.otf
new file mode 100644
index 0000000000000000000000000000000000000000..401ec0f36e4f73b8efa40bd6f604fe80d286db70
GIT binary patch
literal 134808
zcmbTed0Z368#p`*x!BDCB%zS7iCT}g-at@1S{090>rJgUas+}vf=M{#z9E1d;RZp(
zTk)*csx3XW+FN?rySCrfT6=x96PQ4M&nDV$`+NU*-_Pr^*_qjA=9!u2oM&cT84zXq}B5k!$BD4Vu&?bM+1pscNs?|}TanB=Gw
z>T*v6IVvN?
z<7If|L2rZi0%KIN{&DZI4@2I75Kod~vRI*C@Lrk$zoRI`^F$Oyi5HuU*7@mriz!*p
z<-;A`Xy{#P=sl02_dFc|Je%0lCgxR=#y~GBP(blD-RPP8(7$Z9zY}6%V9+^PV9-}S
zeJrBBmiT&{^*|I7AO`uM0Hi@<&?Gbsg`hd;akL06LCaAD+KeKR9vM(F+JQ1r4k|#^
zs1dcJZgd2lM9-ss^cuQ?K0u$NAJA{;Pc%#+ibshkZ%Rq2DJ}Id^(YlWJx)DIMNpAc
z5|u*jq{^s9s)OpGj#8(nv(yXJOVn%B73xFkTk0q37wW$hrbawy4?hpJ#{`cMkGUR8
zJl1$@@QCv;d1QK&dhGIO_1Npt2c7Ttc++FR<7`t1o^76cJ&$`{^t|GE>K)k3GNh{I92zC*(@N#&?yeeKjuZ6dlx1V>2carxUub+37cb#{GcawLQFW@Wryy^!4biE!Rvyz
z1Ro2&68s>zBluk~A`}Rv!iR*c@Dbr8VURFXxJ0-?Xb@%!i-a}8CSkYmfbf{`wD2Y2
zHQ|TCuZ2Gd?+E`8Iz?iUS~N~HT@)&sEqYwENVHt^j3`EwC^CsML}j8zQLCs&bWn6u
zbWZe&=$hzV(PyIXMgJ8IdI`P!y)<59y>wnnyw-WednI|Lc%^yedzE{&dmZ&U;dS2Y
zC9k)=KJoh6>nE?fUc)p+Gqf+QqQ}#Z(Ua+EbTA!ChtYHBC+G$AVtOSVNypHsw2f||
z57Ecylk_F}HTnwuKK%v#9sN5!#306#5i&|f&5UPs%mQXL6UD?a$&8iBWb&C3W*5`Q
zv@>1IKIR~ElsV0uWu9j)F|RV0nGcyynO~Sc#7N8&dy5s~(c*F9N5zxH)5SV*n0T&u
zzW7P;)8bX)2=RLHX7M(0tk@t<5~ql*;tX-NIA2^QwuyI%8^q1xc5#<@ulRuYi1@hp
zwD_F(g7_uz8{)Uc?~6Yae=7b${Ehf~@h$Nk@$ce$;z9ASgp!CPGKrr=CDBO6NhV2x
zB{L+mB~M7gB}*jBBr7HBBpW4LCDD>N$##iRVwR*yvLv~ZLP@ElQc@#nl(b4ZC3__M
zB!?u&Bqt@$NzO|yNnVz`E_qY(w&Z=uhmubvUr4@@d@s2rxg+^qa!)cS8J1E~zSK)9
zk@`rL(f}zd9W5OveN;MGI$f%hhDqm2=Svq!mr7Si*GSh%H%hlkqor}u?NX!EEKQSU
zNpq!z(o$)qv_@JlZIZT0cT0Pu`=y7aebQ6Xv(gu&FG^pLz9GFTeMkC%^dspF>6g-P
zrT>xsB>hGDhxAYBkaR@mArr`GnN;R0^OLD$8rc}xc-dpJDY770sBD((aoGadV%bvJ
z3fUUjI@w0qR#~(xPPScUl$m8|vMgDytWZ`etCZEq>Sax`HrZ}jk8Ho}u&ht^oa~~k
zU-p{pitJt4N3t8TFJ<4#{v-QI_KWNf*`Kl@*@(A?x4@hBmU{bo`+2LpHQr;q$9q5K
zJ;gi7JIs5Y_Y&_F-p_b%_Kxx1?!Ci1!#mHr)Vtc-?%nR)<9*2cg!eh`7rkHie#`s1
z_YLoFynpom)%#EHVIQ6kPx>cKQ_h
zRQS~TH2duK+2?cA=d{lYJ}>)R@p;$hBcCsPzVo^5^M}u%FY*=oN_~BO1AIsMPVk-L
ztMi@Xo9LSspA==WB&S*uVl4V7bBsZ6Ow%WsQuJUl%vOsv%FNx7`s5UAW~xPRj!Q^N
zwi+UnqRjDntAR@;SgfW*vp(6Brq42&k|Pt0u7@erYKn`qB*Yt|l44BpR&$iaU;sM-
z4d^4IlC0K*WWCuG6&q_xHzvW8D|?VmP2oxsjM1iyl%%N4$e09kOp@NLPtiwN&H6aA
z-eTa;a#fN{F^O?WQSqF~OEH*?dP|xqDK%Li3CQoKxK{5cQ&V=BV@$F7Xc#FxtWojs
zXNfkM61h7$%AA;DPB2qoM4Ov7+011Nf%sPRE(aRk;t@!SiLC)
z(4}(2HO9bnN2Nq^J%e^*xrU$#s~$RKF+`d5K(ClYZt5*oeM)3>R7_%elsPso3MS`4
z=E0Mj$&@IdAbalxm6OD4U#Myq|K@
z-&JTzbUk*Y0-^+{&H*ME<4mrECC04R8!ZMC(2?u*ebPc5H;tpCU=m%_jxw7~>F%j@
zrQFl$N~Wf`Uvh+X%>u^=z!V8t`pCG{q@?>vOLA0Fl0G9QDJnVY@1Ddb#95Q{QE_nz
z(2-1F6PRS~8IxqP=wV8rtMRU$!gLw+F;Pi+V=Q2cGRB&cV@%1(K)mFrc%%OB*-1@#
zFgILx%zA6OUJtY}rKE5z#efjS0T1cTZVdO+9M=22Ow*gK34rH*)?hLxWC7zvB>|5{
z#sH12*7O8mIkT%*9G`Hk>dLs;G!k%{O^NzUkTT2tE?TUH)Z}POWNL~_)Z7`ae_Ylj
z(7?KJE)jQ&Hb*3o*rWtwBJh@*Xep@{0}KNAUT+2=21z$2x`_$+QVf~#34kTq)f2bC
zy5teaYIF&ri#6S?KM*c=&h^$+?f%Ff49eYLDyV~)MBo$Pac=%%%@&IxHZ~dv3zK7v
z)+Z&!aB~(1vu4#BfHILT-f*QjQFJ9zQ(O;j%x->){2xR8tH4$FUnM|M7YE+2!8H+|
zWQx|On?W8yq%DaSP+~AC(dGnwTuhWj&oP~wvyCRJen%=uy)iDqm|)FJ(pxO9f_SqD
zCJAN`7%eq6S|0`S9FuB|F{OY|rnuN6A;l5}g3RfWXkb3jsU|ZpPHK`V$znApB!a$$
zM&b>rphC>h6sWK0Bt38=XbW>{Od`+XNK_^W~`uM1%SkU{?CLrT|
z*5rU5a4DAt4QsU|SYaF~z_MnbZd3}WFFoi`11Pc7q-YRfpk=(?HFGY!oON*L+>FN=
zrpV-2sAV;nKn7Cumed63yhYD(iyLEHoL(PiGR3;=k4uAd$Ws$QzZ>JBRtl%)qmlt(
zlrcu1tdC7hu*PwHfTp+Wtez}SISAlE3{#BBi@~MV=s9VU~oa*A29jU;4uHLv)t`=cj
zMkBD=0}Gn;Kx|?3|5QxeB>h7H-63>M1rORUPw)_81!IgVnE33zbVFL~|4d{TmH>B{(ST?=mZBvFKDQ
zs6e71u%5ZNZgM&lh)@6d3N{!aL268{00aWAef0lv1i^_}z`hyP%
zyasc1UyCFdAscUwN{$1kE)jexW8Cx^)1woB65NEk+OUEqN;12DT?I)dX#Iaq$3L>1
z0{Z(M#~c61xyK|v7Q!EnR;&(y&k3ik}S
zXTlwpYD`!>eg3q#=~2@ogTnwcEEv)N8U~)gNue|5Zu9Vhq$UQ
zm=4KMxM#pU6K(*VJ`HXtpAMkY0d#r@+&Z`cZaTnC2e|2O?BUZ~t%L(~5I_e3bPzxX
z0dx>R2LW^tKnFpq!O&_jzy$+bFu(=7JFw8*!oumUh8A)!p+c~``Gq=nX{h@Ft%X3%
z5Wo-u7(xI;2v-IbLfjP=0TLY`(Lp;p0M!Ag4nTDPssm6Rfa;(#p#T>OaG?Mf3UHzB
z&MfAN0W@?*-1IoE7(i!0*$e=k0iZLWYz8zr1Dc!>3NSJ7geGSI+)RL*32;EO5TIEI
z&@2RK76LR20h)yX%|d1ZTo}NG0UQu4Bn;rfLgIqB84nAECszh=Krr33X>d=6I|%Mz
zxI^I9!5s?s47g{)9hRo&)&V*omkuiHfLuBtmk!9K19ItrTsk0^ZaOp=1PulO91uze
zgwg?_bU-K_5K0Gx(gC4#Kqws$N(Y3}0ikq2C>;pDE*Ri~0WKKefIhllfC~Y*5P%B-
zI3SA-$f5(X=zuIbAd3#jq6+~y9l!xibU+gw&_o9`(E&|#KocF%L`hz;)DWmLP3;5fv}-Kn^2%lD9|PpXcG#w
z2?g4O0&PNpHlaY9P@qjH&?XdU6AH8m1=@rHZ9;)Ip+K8ZpiO9yi^YTHyZbQTB``tr
zgIpb(AMAd(*f?muyEF4$ViPofhWp)2_v3ym^WC`x?nk)$vC#ck*h}=pfDBO)G+>I#QjVRoW
zDBO)G+>I#QjVRoWDBO)G+>I#QjVRoWDBO)G+>OYsYl7UmCTO7>(Ly((g>FP{jT5xc
zjcB18(Ly((g>FO(-G~;t5iN8hTIfc!(2Z!3d+HXsN3_U|XptMyA~&K%?h!3=BU%JB
z4s&B!kI%_aQR>IrR=x#+$+m
z;mzdD<1ON?aK+rWLd3m{XXDlKF7tlj5kBJc_#(bPKaf9_AIz`iH}m)K`}oiCFYx>M
zm-%n=-{;@vV?KeH`Llwpf*3)(AW4u1G4l#RpWvL}qTr5jrf`mMv2dxdS=b@mD?BVb
zC463ZN%*qxvhY3O_rhO=4pE>e9OBP801EGXWnOSFyAwG
zTv6*$;wj=_@l5eN@nZ2Zh*qaSY`R=r4N>V1@qY0M@g?y!@q6OWAO?L){EI{=882BR
ziIpTnM7d02lhi{L`JCic$vcvdC7(mg_&<_gB)>zHn1$%@bchNskS>9k@H5g)QoS@!
z+A2K_vEG-ZuS?&8IPWLY-yx#=u>zUPB{q&{POCP9RCmd^r+u&(rp@QL@y@~QS|_v!Z8?{m!OIiHIVSH0@lOL9!ke`vC
zm%k`~TmGs1M>&>{C?twN#iNRuig}8ainWUMip`2>g+Y;`$W@dm8Wf$1Ud1uRDa8fF
z%Zkg2w-oOyK2dzBxT(0M_(gG7NhzgDwQ`Jdsxm}5Tls`?vGQr%R{`icA`e!hMW`33q-@SEfp919`B@V$_Hqg<(g&v8BX9I=vHqtmmC?CQiTI)~<@i|)VblQ3H8$=5wV+lKpUN(tkX3=CokeSoksl^f7X+{TA
zIF)6dh2AY2%Q6!H89e$99_(Y*(NEJ_CXL1~&@gHZ!{tKhI3Nu-(Ha=IyBUSBv$eHT
zgB60#)|^Z&R`8NoCM!ETi&2iFnc+MaF`j>W($I9M|{Fdn9I0?i2Fo&$U{Z$8c3Z@s||tuw%~3Wi@-Qn;%~T~t_BQle$H
z(%4@xz~aD7*k|q?4X(!xeC$IzBLc~&skAbfW@1}K{oBs2(=e?$os8k2kr~4h
zJ2O0>T)++~{L*NRd_Vq^9U6!SiC8JPP*C~V5;d_4fTOkv@S@>s{2b%v$CGe8J!BW$
zWJe|m8oOG%dsIDzy=8keLkF>xe{|R014mR+Y`{OWCs<;@^T<4GVD_^hV!}nQuYO;{
z5XCB*xT4s7O{^guzsd)gfXJQqzy2L25&H1IC#;IT7k4stQAl`4B!EN5{B
z%pdSc|Jk$sj4=3m_)QJ7aLt;9j9?+l;Lq7qmdS+Ivq3g^vuWr9Ori3g?wip|f$O8$
zKoRc7K@j_H<&QM^hJ3>(Z90(msVr_2V938oGun{|A+`@ijA8@%`OHKb
zX4RUNno+1Fsm@K#$_0FLSyEoIDzhc4IalLA
zb%1SMvT*GQkdEyv6C56npQmv*NZ^3*=Jo3^6G|OS!ffJ!A0cyp)U<7ESpTewESXBe
z$ZR6j5FVLIBA1gywK2K6+Nce~K6us!{FM628+DDZYQJ1{Yuj%-_7@*4Jyh0S(blr7
zQ-nqAuHCuK`7N>MB2OiJDPqjMF*dWAQ9BcC&ID(IiorKn=&gOoj_sZd&SY^p4GIN6
z$ujr8`Q{!onZ=4VG(+JDv?mkDM~vf;4L=7e7Nj%+!^8^nu>vGj-o{J^t(iXu^z1a6
z0mZ>6lSYiTBz1Onc}b2oGRqXbRTVgdgMEsSh7)?(We#mOJJ+mOJP0
z(|Qi(A6B=uRoAs@&vhI)^SmmM?4jyV%qZQ#(?JiOp<
zO{!&p^j-9@LQu~-JXr0BLP+N0wPX}7F42$#vX!5n)@nGY9y%j9*xJ{XrX>k@D<2ov
z;k9@ap064LgRzKg!4DG~FhVD&S$f$cv~yq~%`67qSK?$420t)W6Gjt0(Gb6%U_j&E
zc%%E!0Zp~w;f&=Ih*)jhQCFX?&9BMdRk$mb@co-hTT9zZMTPrL6hE)Vh1dg|@K!K*
zTZoNO{z3a$X(ofl(}7b#UtVCzXvSV&Z`U&KzyA9B4F4p{ELy#Kk(SYcNpULjSf-&I
zC$NOGes#q~y9(8uDPS^NbFd%F(Htv)nK+TfCuw38tlM_BUwZ`qLE~4!4&lS}a0Gsy
z)i@LaJOb1^3B(c{rnOE5SBkCp2Rcz0O>36T0c(Z(aF&Ay)hz3moP-^ynaT#zZENX=Dem$rBj#FkIX-f$24$w)OS~yvH)(
z;A7l3ngKsZp>)h9ckmtOY_fr@okIf1XkZJh%-n6NwH5?e3U*p|sN8HWU{vQg
zCL+RkEEHe`i*@)@mf6%Uu+exiEpRDX8aihIL)OnReaLhgw+fiIp;iYz59ArZ1N^$W
z8he9^5ti4N)s@r@Zyem{Z|+Sm1c_1NM_Js=uBDk{aG(Y}0$W-k%aA^j1y>(PYAw(T
z+zKnO1%98!@D$>A;fbvRM)^KWHGP|@VZn;bpoa!(Sl4WS1|n(q!%|jb6E0=7PP@Zy
zghoFgO>licKEUwAAHdZF*9VMpB6Jp?IRcHAdma(6LTQ!$uG!tPgz^r867LH@VA>{RgLukD%WQ6OsZCj^x4qz~8LrOebNhkr?
zhA-l$aTnNsJcl$2$S9Iwjw&rKE3POGC>Jna&>Jp23*GpIQ^=f)f@R}>BQhZ34VuY?
zuC(OB3vdOMU^W>c_GFn)xdG!Q_8Z-3M%jIh-&wc2wL|T=E9h*@$t=;PE#qgFWaMP2
zop%M91+ATRTE++?hk@I073jMNb_UCs&9<0cGt&Zt&uwAA!5GR1s|QvN61bM;yqFCe
zz`4P-q;?feYH=;olG|l#X$fGIj>qtqNu8Y&vpO-(hm
zc5O#vb9>EhY+ptD@9Hhso7N_RG2mP_3t9*N6mMs3^hANHvM2Ut83!nEPIqgioI}Ap
z1!jzd;1ZSz)l6Zhy;JQJHyHgbL5aKZA
zb(hGdvC@4#?Ry)wjXk9YGCG;OyqzUk>a3l0&3WL4tcPibPCGDuVP>#WUrwqV58>0~87#&v_za1|68Z4FK;8kSI~i6PbuJ&@4!#2{Vqkt@6*CBW
zq^@pPT}^!eGrVzlV@XL_NqKPqQ_g}FCW-|#)7xu1ZSDo{#df;4m&vN%*__AV_vnc<
ztWQ9f&-r{KOo>#5r5CZsjn6eVW?h8olB$@4yBkiYA0i8Ii+|h6)AqA!ybzBiW646s
z&sK&@$s>5K20Z3KVyGY+Z7N$isbziwvcf!l0qZni2*D?ux8bmZ{_kk7Z*FE>ejwv4
zbdHCs&{^n!r=t+A@o*I~+Qz*6`kiWWejWLhq>&kaPQ)SF!4UxyB<#v;-jSl>Gy!K9
z_c!nB>ePHEWR}vf9AoeXS}I(AX~Ua%53qTT!;@|Wis8qh2iyWg3#%=of#GLn7MRT{
zbECO46BI#;)taIiFG#WW?AHQuh+RiB*5cfVZ=^pjXXMwjsOc
zkew0cLXVfj0@@R=uF#&k)P3!ms3YH}Sa6as
z-+zA+GXolCB%%>8a~>xQfqOv4<#Gf8qw+ZQUkE=Sl(6)xtKZdNR{`&U2{nTY%Z=Gy
zQU@?kaW+rLjjCYpK2>ky-cG170gvZ*bTZ5S3j(38Pj8ECkL-!*sp+ZT(;%wrtK`(y
z01g4q*A56nU{!-dJel_Py5?r>pr_+!zTJ*f@D^OGV%D(a3?88IT_J;)u-qaoyN@E#8N
z^ERHLWduYvems$BhX*iN))}m0fC1Zjm{SewU=_fC!sS8&%w(Ed<}e?+tO*DVTnibc
zjb?5OCxLy>IcnXjVQj0odcrtYOZ@ACHWTkB^Kz9)IrK@#E)UG?-_@
zyb8?I6c$t!s-r5ImuYEjb4^RDid!giOzq+bATcBw*$R$JIHO+5-eYcF4-aNs#yc&Z9}$OTab3Op!K
zsi#?r5kN3(ctA*k8KJ|2W*Y1@b#+WBhy@XXJaSCQxr>XI5JASqMq`;Kld-bAz#$00
ztpcFt_QsBe-J-5)tZZ$AWh9Fys_?{Bn4R>8<~U#wLVSWzwKg=i)@Xj{dgtn?uS85y
zNkc=G_ASRGep6Lr12>{F&gJADOr+tAHu+dj#*69~_v}8z2!d$r2jgt0YpT~ab=W(b
zJ47G74Bb=05~M-RRIo}0>@4_3J@h$l%(1K^1eme4Lj_D}-_=l8r>SE?z=CZ86S8e&
zIUj#3z}tqF^W95v5&=;zj_qMSouCH^rw1L}n$iK99dvpj=Sq}-Dj0CFsFSua$FYND
zPO;olnE~&00?SOH$8oJ(gUJSmPspUu-~}@~tUIj*+5$_hX?G^01!GoJsIuU3WGsOG
zeQ|v1iw{E-Ah;}8oko^b*A#PdasuQbgi|n#U^C0)=GoF(@|bS?1w>+UwkN0(S{Y$D
zjA$O7#}Jli^7AV*8gm0cg@;4M8|<=lUq&}-bjUY<-uw33dw(+NiCU5+%q}j@)-ak$
zV^=|)i7GM?C@UchsS@NB+89kuQDJqV8u;ga?>H6f4(GwZl=v*SS`x%#fq>y#dXDBC
zQ-e)v&&jOPGW^b}cJMHP-VQ#;_zG|&m|oztI3heD0H^c?uuv@gfh7oFhvfqi-60R*koEXQCOtVrdnj{zmqE>_i9bPb`GX62
z%G49LQ6IZ8mJvQn#{n`8INIQ-m3v0MgE_nfH^4OB@{rAN`_R8NF9v=C!@fh5W57ik%-Mi>^{T}
zAofqh{)IFXkmhluc?M}pk>(20Qb_wa(#9a|5E``xjrtsoo`yz$h{jApW459(SJ1=L
z(8JwmtQd{mfyRE0#@D3Q85wBC1vJxu!iLbSwP*{{<~*LE-IaVGUYz04?rEOYWd2m!c<6qo?@jsR*<}jaD?G6#3W6;VBv`UUv
zDbcEj(5e8m>O-_{*1Urv_MvB%pml+0-2t@jI9m56dX`1&r=tz)(Z<)&rip0N
z%V={r+TxA2^rJ0KwAGFxC!)wO6uAUNnowi|iu?dYeupA|N0EP_ZFMNhA4M%e(V-~%
zB^3P~idltXE~D59DE0=@uRw82P+SL!yMy8%NAaH_Lpd_MixMWIgnX3n9ojw$ZNGsM
z(^1kml+=onXQ1RRl>7!t{uLR=BI9giT#1Y^$XJYwmyq!-Wc&=7#voHYGQEaUSd=mz
zr96&O)}tL1+CifoImrAJGS?%^Ok|mbEOU^h8d<(XmLX)VM5&c1Z4OF*3Z)xR`T)vU
zf->GgnWIo<5y~2mc7~#zsc7f(C|irN3sLq*DCb3#%SX9wDEBv%>qL3aq5N=^-+}T!
zK?OdjU^yx%K?S!^VHhg%Mn&PMC>s^EqoT8@I0zNjppu!WWF0Emg-U)!rK?bBIV$r)
zWihDiYgDd4V8{4#1uMy)hzZ9r`lYF~xgO{l#ab@ZdokJ0YwXm=&r
zeFJqphPpCP*Bhw27InXa_PmAmhoA#-=-?D|$P*oU5*_*o9af{m&!8il(UITK(dp>u
zPw3bW==d&l!UvtWicU^IC&SUnbae7CI{7?0wF#XXM5mucr@PUa{ph)JbXJ7UJ%Y})
zq32oj{2g>Y8l8U^z3?`=a2#EnjV^wUE-BEZqv*w@sDCGV`8;}c3VPiez21r5SdHE|
zhAzjU%YEp|W9Z5!=*=tWYCF2tjNYn1ZtWucCJX&^y`a-EHXIBj|&T=z~r)@CX`s
z1%0>_efSdkh(aIzfK(Dxss|NMo1u%aJ6M?c1+A06nYN$97~(e0z?XMgl_8M?Cr
z-T4;%`ULv*F8b{&^t%cDu?78CgYHg8gHebqrBFBpTm7Eh6pu&oj!^t*6#son@FgXT
zr-U~tQ3WOHr9@v*USlbUQ`6s4%nFKWqQotfWHBY3LU{*JJ_5=olk(j``F=<#Kc)Oa
zD8KKhhlVKsbCjxyQct7;HB{hoDzJ@W=TMpwO1q01b(R|aI5qkkYRqhEjDZ^SCH1hJ
zdbo-j8%>Rir^YX@A631k{9TYQkx1!e`WkFQ^G$QI7;tk6fZ2y+l1WhI(u-HL;PJ
z_$4*z32IUbHR&uhc`-Hl87ky)D&!!g%cXR`QK3RAl%+z0snEx%&{}GS7d3MX71lz9
zy-m%UOwC?Q&Hj;^6GqJ;)Z7Ww+|AV7R%-4`)Z>2C6C0>`YpD6}Q420m3l-F&`PAYo
z)RIc-$w#Osd#I=Q)KkgSvL)2hfz;EVP|LScD>hOqFHx&9sMYhRHBxHrIBIPYwe~M+
z-4W{9)71J|)cQ5l`hC>;@2CwTYQq+4!w1yHd}`y%)TW8lCL^`!3bi?w+FVC%iKn)1
zptk-%MFvrkH>qtpYTGp`Y7Z6l3l+0~iuI&oXH&7yQn6`NY&)eNO~v_BaX(P;CMy1I
z%CLemyh0@;QrqWI+drieuTx21P|1aqv5PWwQz=erhk-KJQr7cSY9f`kfl7~~GJdAA
z)=@jnRCXbiGnL8}P`S@jc|}ydlPWkt6+c52S5w6!RB0+zrlraiRK=TAivl7{e^0k;pVIJl=A~4Sr
zmb^S=Ab*r20=5#I5klDC;VB10R?)*D;Aab@fkPikN5!xh;yZTFK>k%nmXhqoQ!w0D
z`nqozt^_Q@9)>G(x>pzi$Zj&3k1q>vKz!ymnp_qFm9B;FD#iR^J1oBn=phB{wUU8ByI>H$
zx8!$q^&C71XwoQrfyNoM=PID%C?&UCEhwxkFVqYV5Ia96*Ay3}8rg(L(}Np?fUSV<
zJO&x*C>!j`DNaJG(1B7|a?Yb+Ls8lddmB)K6#yE|o@S4?6&lz_NK%B
zkq5-McvwqBqNhLl@$vtvtKdW3|Ni*N)sM7Ti$$=S=i!I3M{ifpp6J)(lYyQ1kItoa2CREud1?qW}t
zM4Dkg^u(WZ_eR(ZM4m(7XDhLZ?W2K;DP&7Sv38K>`~~8??IrDMDYinNha}2FiOrT>
z8fWDINp)=E?=H;RV^ycIj%P?dzqq-zv{ikudG9{VMbCj6I~)g<*PUTb3Et$Cl1&4S
zF!BbzGapVPj0g@yT%AR8J2pNGeYam|7_VzY*!nqQF95f6X_??}N
zy}c^XE;S%19?&dkI$yl~L4z+~*L5H4Us%Ws+y(Fdhs9L_Wq|Ns$Xsne`9HBgz|0BS
zI@STA#{FWu!U-$<>onnZrtTk~;dZTr?qf9E#+Bd{t+{3f-o#en+%_)cTwCLKgmtMA7k=EzdSd(S4Zx%j-keF30X!bM3MnU-
z8j66_NCc!Hx&=wlHNVnQJ)A2URP3aIH7R9BUVB!JhAcZ!a5U#=){%f?FPu1c?7XP9
zzNX%;g3X%JI!)9Yi{4y!QB+r42wTR5h2^k^M8=FVwk0x#IF2}DiCZ?|Z$P`9YMsJ2-1-0Jt2
z_iqvv*W1hNYCD9#;9S?}KM!Uf$~#;TaDY6`G?E?Nnnk?C&(U@6xtku6wKg%HhVt
zEeG4Mh9EFTT+L%xjVB!0tF3bl7)na&HF3|!pG&ydez5sa(-FM{#m`cG+2uf29T+j|ZIiwhQQaBtkbmc4h
zV*1L{>(re1uZ-E4u3bcC^U0g_kh{yHmH{o!S;O6yP*aK?eR8GlIrLf!WX=NQ}
zl-0KC%4&`Cy2I$a?lkf%Dk~~fPAeR#xB?(fU;`Fg9OsoyEfw9lO~izk`a33NvE*4H
zDaYHQ`j*(D3<1M2&fB^96=_Ym0dLN)Eomrgs0^@IHq_MD4nFDl(0}kr=ZE~#y84O+
z*T#55Rl}~@x;H=cmzD$PU^(bJoKBC1kexsZf?x%YLg6^$J~snT1>~(@NrtTWEt=dV
zRujbWz^k~ed>8_3pfCq;1O%)v1quT_hi*GgD0fz6=Vhx&xga~cxxGreOSl(62#Z(X
zA$BiBT+4)mHfOx@bpGk=;~J-K=pethAZ1UAn*0C&Z6t!9S(Tdu{5MOGncLb~rEP=Q
zA4JN25TvA}nhUf}-N-?Hc6@$JjLO&$c~UbNA;^NWaaGzbFvNhS7h358Tb@~!1DmVx
z_GH7kgD!P2M1wlDgH!Yx?Ti(0x{x0qw<&$Sdi|!Z<8fM|#({jN9*5Fk5_<})?K|KU
zmm@-em$A+WVi)4C;e?7a!XImBM}#9{cW3Q^g1rIK4463J7MLW(%%QuEyEkF00SI
ztib=vkwqK_V2*(>_Fql>G5CnGwz<5euo0wxz#mR_)WCtYqVkerExAsv^Gk}k5axK;
zxQifne+6VXLfF#W&|Iq}e>l3s*zU9;pvZUhPy=xAB$!U%%Sjj>?+L1FtLmz2vB6R7
zKe%3i4bI}~(yEf`(g3_6S$RCaKj)Z+6gn>QkLJYeGpK>p4KX{m=V(cx^CCYdA%9)G
z%9#ec&S$|3=!WwSJ$c>fO&aGJJdn|Bwx#C>r03)dc5?
zAQ0>a{PHX8IojnXR?+w>n0uP|5v4zdlM-a@4YEOv+h{nRk@Oqv3y#+|w%B&(H3302
zFb9P-psFeh%SwwyME)q55Ke;Ccr1+{!rmJ~ZfWK3!4VwLFF=?C4hb%2TVh3I(i9Rll`K}nIa8lYHz#W$V$QxpPX|K7v9$=H{JrZm
zcO;b$JTV5ZejGomcJT4@usihU*V?LTTTQj97t{otb%O!$v5Jf#YdC#@z-MFdPg<_)c3024Z7yxZ
zX{0cYR~4RM2kwqx@c?f$?fNN&-YH+?3Lg9@h7}K-&Vd2f-t!U`HWFZyYv51X39AI~
zBX9(T6FB=2;R#CsyAn7C`_jOmcwiy~)DvNo8CR06cq{ZBo^VydlqG%zmI)R-aLjT5
z$dyKK>5V>R)dUhLoL@E5fxJJ2r+RwNoQHE^{mbI%NHP~hYPvefSlepSzD2Y|_7Y@a
zY9_B;Mtrq9a*a8bouZ7Kyex}qI7>K%ZEmcoYtnoOJ5IB&!x3QPO*ozPv>IsY^U4*>
z*B)%^X+5Emg1U4M0T>=S!tD|Oe|w&02Q^B^RHqOA)%h%3KIB*DR6=!)KK+QMYa?F1
zolmHPzs$mnI&mQlCiH1I%`|c5y19|sCC&VdHw&)4qr$J?mv9HZ1=mZYgS_%&!Lp3y
znk9MsPa|jcPgEZfcCbf;nEB;%OdZtXwv~GsC3X${ug9SJyOXFjR#4I8w#6b(t)~he;onKx4+XoqKb%twrsn
zZAAyN4`l6wgH|(%)(tK@K4CK-GAA#%E)mvA&e}}LB
zbPKXq<#~VgU-fe&x{oiW!Qm^{3D50t!n3=}wnu%nO4-cj7ufO(*=D<~Nqwt`5sRB&PuCXhsj@dTi<<52H7)AFK>?QUJBFvcpvC)#G_5a`ys+bV
zK%Y6Pd$W4DT9B1hT9&1)sv+{@MTCu79+c&8kM9}+SLzF>e;nb^MU4(oR}p)R0Md691%r!J&2P;SdP_oLMFu6B05;>kLWc4)lfKS#W5?wI%|hoq`hu
zfx>*xp@_k|@M(qn0}BG5U2uozAAEj+p&UwrwSy6k5G4?GJvc;fo9Di~NbR%>7R`O;
zDYJGxI8E>dA7Mun!eUxuWd+Mv?U2Gj!*NnrXHTVJbU#n}+OZll+_5Y9iNS;+y;7d?
z0U39NOnr$=5>;koRA#6jd8DT55v}v3;fIx1->hl6s;zGAs%wRSh*vrmsjKW&cDt&}
zw!3n-W=#W`Q1glEkfXx}Qs8t(5j3uAvN51y4j&X3@w_#tyW_a0#W72@XmpdFU
zwJ9yH+wscx?pEEqr)oTK)^?2gpr4CX53
zcPo2r+|^&z-!C2~cl=iL+i$A+vuEqhsqt()|4CRs?j#ddlj!)ks=9cs^W=y`S&tXv
zr`qw7n>R~ts_}XJHWt7kx;Qcy=3~uSSTJ3~f$!iYD%?V7I(K0-txXmcqySZXyRjTUA+J_CRG|P7^tz5RVVzNI33P*p{0cvi@F5gCc
zd9^pcZTn6w?|%2a%F6e&m9M>#@!Fp5nmy`T)iJ
zi=lMC;hb$h#99HCFYoKypK~Bm9XMDJ$omVwLyP3QFYmJ9%@>Y}x)1)@aYEgJAF9c2
z)i&ppg=eaWmym3&;~XW`(=}vo>PGl*;8;06R*8>kPqf&4t^!sXg3
zyyb<%qV~NwZ_jfNI?$F?O!A_$YqN7y!S&8$^IAY1T7g3=@eIwg!b&{JjXj_hEbf?M
zEK@gLs48#JHgOB#!m5g1=*G$8(2d;8w4Btc06Xa<-6fg9;ABVdud~@CVJga}S!k|L*VRApay+;r@@byUz821q4~J
zRS758;d>ePZy(nsI9jUgbCvnt|COeLwHvZ3H`A^ILubet?!ZuCk*cVsu&zYI9sA)v
zGJ-=ekJDBN!^g7eup%3bP`Z!i!?_^tiz8UTLA=U2kV(7FZo5idXSW0S-A-#P3w{Nj
z#x1Ip`*!wN8(l|0ir~;uNp7CjIl(!ekHdtIfqrddhhbmhzSf3??|2r^5;`V0C-8G2
zp!+swo#B{R1cZqcz)f(j2>j7O#ZZKi9kN3h(-{K00(PezY(t3a>=TKwvclWo?6?j!
zLbP4j$>Kxc+4nnyU_25bKx%^sscYZxnb-e+vHdADl<>_>P5x
zpDIf#N=i#L&Qs1){L)g$sB;VLEp^p(wY6HuDaR>(Z7pQfE%w4(?KAKd+3>*d0H5oW
zaByI7fRDQ{d__>kl02Nt-)q_4nxIbDo@23U$t)7a?PuUwaDneIoL36}2_&4tfiFUa
zAn?UGti?3u(<|zq-WQ>9P{VEf$gcA#7t|Nd??2bAb)dmE{=Qf0uU=8XY8@)wR>FsN
zBLfiN2Ty$z&FzfXNgk*?ya#4VzDi!pZ9pg?WGC|4Kv;H%(9q*lmdqijRqPr8-i7{#0a<#Ka
z5A34sT|ZkS-?m|P(&X__ha89P75E+j!zU9`_u}vNP>7p&4*P8`_~JPv#&?x#Z%=$x
z0Jaepk7N=bf8zK}X)mnIE-WN}kU#tj3$rT=?S=NLHaPY82mZs~Zf~oy7m7Y}{zutT
z)Rb4N$*aw+C@5IA%paJys7M9+aXkw`skXL?vNq5S%{6xW#f$#%HDzN(Q$=I3y>OSP
zBQB;P24VoK*@;6T%HfdV5IzCM6%K|BhVbz;JWYAxgze3^6Pz33A9rH8EiP{ARDVt&
ze)xgU1z#1V^kEjq555e8fJoOlWlN#ED>-F_g*&q|bJGh&`6b2qc`BH$^(^KI>T0X2
zYqckPp6|K@8%Z@yE$yn#?AHIo*qgvNRqXBKAkAX*;*td0q&cU`A_^i%0XJ5GB4sD+
zTiIy~rL^h3rEQvKY11T4_kE*4Tb5E4WZwiS2x8q)@hYHl-79m_N%8kgTD;!(zVGM%
zH_{|0=ggTi=giD^d7ftyIjhwQxcS3R(fs)ulJ3q{k{2{UIQbT(B{>tpbN^YU_X^7vwhtHfNgl_b`YXRm)J{q|E5@CJ!g
zqd#cHJIZvm>6|Iw1xR~&nWMOfhfi_;Qix(^97Aj)aHo)eB0q#H`mMKdbF;H^vRQ=2
zVBmv;+4#Vk*eU5@l*vE&JE!cgMz`2(7MnVsF%yp-?P++w|7v-X+Z(?wB
z-|(ho*6{Fdb+_7=mXWfauYL@R9v*I8))ek1Oz})<3O{CTYVvcRcApmYC*Nz_E(~^$
zU|>Zo0g)MC>L1gzAaWu@9)-GGxE>E)aEz{EsPn)r19p)FYIyX81`QdH4=8}eMqssG
zKt5B9(1>>n`XOm!@tl5Ln;C+#%^Q^l^1Zruv%mNQQm=6@C$X9~_U5k%z%Qh~zgP@=
zf8qV#7|8q=jh`EDqWY*R*It!(U)Wpz{^Cbrw~Eq`h1eqeq1;n$ZQNS!-*wd;>$|l)
zDtU{Fe5u(|pS-7>Llm54^d@bVd0by(#215ydrtv#`~HSdS??add23-sB}j>^dpU_i
z)o{WWG=7XhBkEz$V7tGJT?ZmnuKWA7vEBVKTwptE)qaPlMA^oo@F=7|O%asHB0bQr
zL^!34igLy6RU;+0*Hu*?#j}#raf#{v^dHJka0F;f@C*j~i)ZyEBf6^L8sz)?e83)T
zib2jdUDKV|o#^|E#?9V(Xh&@H^TiIHMxoJHz#q~55^kb^uG{XX+2P%Z?nE4pA@gM%
zE;M=?eLeVt_9fWVAamn)*s==J0r#r|L%H`I=RZmGGWI}-BQ?155^{-Q_FUpE>~WER
zfyj83q@x|f<#GgI*ulLAbz`R<9ws@3$D?FhQzcqZqz7IT3RC6rJ=8r
z*C}53n#6Fmi40de>LwDBhH?;3oQ!xvy!#OBQ)FOl6lXa$-n`ectPr*v
zko3-Sb$L14c5{@dD9xFes7f>>;gswwY&W(sDNzLyL@esgShSB@J2moZf02*-O+qxD
zgPwz|a;Qy`w>C(P-NUJSh%oHbw{DWzG7?K;h2g?5e7wa@XvpnGEm>>I`mp3k^LRWDvH1T?jtan@DV9
z6B+cTl=jWjkiHT!D1_j!H|Zd3c@Rl)q{aGS>LAfbOpv
zKRSdAA!3;yTFATI`*{c*atr;zyNPPpM{M~62e22_;1iA#k#G`>6bB1-=eswvzBTw)
z*0UOEqc44$JdOT5crfc%NOLyGgqMYvMdZmBaRfS-uIp2wzYL>Rfcpt0Jq_p242pl>
z!OdsJaBibJOLTf{(-7KMbuWpYP%ivB>{rrHMNWZcWd?(%-)~{_zvhH3o)t=AJSeU|
zGO{a3uRnUmdnSPN`XeK~{wPe~py3c4*S8(vSD+aXGq|$){A*k{V!4OOVNqRONpp(|
z^nmC(ZqkRar^0*fsc62N@8(205-SU<)p2gVJAho4ee|)YuJ-;BwH!T6-WDNu^1-3=
zSNNXuU>rV)D>{j+LQ86MbS>A-yZQTeT6juyG(TyQC|XB;(1g|LIC7Z2Eka#hTRk_3
z4IM#;=6=9ZHS{n&EQ)65u8ZbAnk3TIHG!*zz>wQpT3syr-n-TJnUZu9im%`Y_HcdF}k_D~uF=<@})!5YYhonVs3Y
zQyu@&N21!gk|uVpN&cetzs?2A9p{>aU+>$WI@q7M!)T0NG!HYuk--+#>Uu3yT{J%#
zSMI&0p7s>!*lBt$Du7w6z=;4~fYCOrUlNOZ?b9&!&kH?^7D+El_0vhPdbHBfaiYJY$^
zPrx*ddC;9L=n6IN8h2-ztUs0bi*EHT#vj~fim4&Iq$)n`ar+=o8&X~P@`35|dVDcl=B09QZcH;~+ee~(4
z5nb2_2K20<$h;5I++h%^t_}vFLfRHi8t&XzCWgrnWXO{|Ka-B5uX8I_uUWBtjWjJa
z#gKqd|E|3i&XS^Hp5&7x5>JMbyJ|Lj3NEr-d1Dj0g=k#l%B5Nk`4L~wjL+!WASvDd
z9Cgq*dQG*(w#5<3<;68D&X`Y^zdTSC>&$W`a;tV$ZoT-=^CaY$`rw^eNk{mtw|+{x
zqb9@2u!C2Knnz@vBP+@3cG4~_Zg*a4XJK||cz9_&G!VKYj5^r^nLyWy!bIQIsU)`m
zi+PRiB62RrV#*QinX`AqG@9?xhI-^GdW-1kYh)LdbC#SuizxiUmhavt`GU4ZkOM}A
zd)Vbe2K5!RWDrs@7!!~{nMilhS@c6S{SbxDBG|zH03z1_gjhy?E?plKJN{Mhp2<#G
z?5FF|HAlVz0{!DZ(5I!{8{lp2h>6)j#m_y5nPipB{Vn{}`b=aPIdU3>-Xv=&QBy*1
z(zO^*XYpyVnL1GK@FSGC`>P}yi|G&XXy*<%rr$(M-)Cg2>Eprs0B
zgP}ULhGSvB$H-&!(JyCFA73IG|HF_EF@TJuMo2JBqi;n`roO(IS86e_#gL_Z>!H@8
zdyY$sYn;^$Xc;yJ5QPaYFB!wScmle3N^ci0DTRmtx;I@QF$*$fswFwSw}%%L^NGSL
zk;7Ktw6h-W=rA2rxJ}JsEo2(`^;xzoQXOSe&z+O2(s^lACr_J|8YRvA)
z%+D^c_~lq34}eGvf9DQ(R-k73G1^!WUQHf5JHTc3v)BO4P&=Kud3GS`?iA$Pi%ms-
zG|)W@f!#58?zEG@;C8?M0VWw~YlmG73RocNJRxgpZ-V6&h@XKj@_t5Wzb_I|&6@TB
zWWTH%dnqyEwE?7v4INC$2q+Rf|JXy&cI%XEC#~E2-t)a#bN`^8eKD?Ug7r9WhpZip
zMi9^3y6(RU?I~-&423siei3y4bLanCkf|CqXB26Z#yz6zpprZ_gg)^lOOorrLq^Ph
zSUXE#p5qUG-}c>^uccjG-3OI0>0J^!EEwU&f6V9CKeuj#c8ru3gN_=!mmE`L;D$iW
zIm~%JJ$rtN@NYH9eEs<71yS=O7D{QKg|kLdzrRlMDaMOx2nh7!>(17n+jT}t`kc9V
zi}frZ-*&i-+9x3?{8imB}-hQDf;E;tR8X9et2nNnd$w?yRZF35m(}
zC@De+7L`4^I;keN)!ypdS3oAeMMi#sRDo1#eEX>BsG12nkydh-_j;1d4j2rpnucbC
zgwRkI35F>l!6wgeME#En^O4{9m>d;`bN5_s@N~h%_Nv`g*#t*Jyg4e%GfZP8J@j4Q0){MqSXa@p0GkwiYhWH)s^sI;KZ@h78Ke`
zfyH86edNLZBI?T{-HHMCp>j+B2{1WmE&Y89C*K7KF2gz8*IhDyj#>Qgx=Tr0S5NwH
z-KDzBT4QaG?vi{QPAALhcANgend4zG<$b1djlMPRjCH?SE
zxUM|3v~V+buR}bV$`%F9=jpee08vsxGU&dmkL&kwU4VNL*{Lh%c=D|fAS$aUt*cYf
zJIK_e$vkau$TD*fK(;%`P5gN0I(hyYc}(r@5Cc>|cyDY4;B0o{eVYFY)!cJI9_Igu
z&R`fve7qW#2C#(wl0FFfV0VS&Dttg#;D3c}$nKsPE^(zGf~r6_qAm{(f~Z@U3!ib2
zOUw>Y`U`plwG}KfF6|@k?)e$nakeX>#?-}twJtAejD-@~@U(Tkpxhp^dDFTGX-N;Znm8HfPX%B!iC5$rRL&dbFsRz#AdJHhgD9v
z@v92*Emp26xjB8WMY`ZXXnTk1K;iz1J>2gw*Pefoyp|!&F13`GsfhIZ?}_yM>8N!F
zxFfDZ6>W7%%fr^L+3}|1VBvvsDQ36D0UGyQ2p?=C$$kArkC9CButwN*Mn>k5*EH21
zYTgyz{GKQ-lP@&wEUb;7E1m#miedm5tYJnax$ad{m<52fjtf|
zT~nr^mE8ld2@W_mx!{Gv!1a~16NShPT#}f|fW{#%B?RculHx7UDuNcpL4=kN(gjep
znsr8`gSDuE_r0IH12xC
zmAhyYDT7*HkF=TY`R8>zzJIwomdEr7b4c`Q=SiI2S4AS|F!C(jMz8n2w&B|_5&<0?
z#mP@QIrr%9(SYQhX>UK{1@`hZl0@FQBZ{rQ{#=8)_V(>s9{pgOCOh_UEL!#!dr}pT
zGa#dULKmK*BsdZtmvY*I`BSIOKYNX=$7AR7*SC8bx%2&VP%lET@g-$RdT|O+s>5qD
z8q;>B?(}PH-Mw#Ds}!OW4yURSLqVS%b(}p5BMJf^W+MQqvKOL@q6&B9`{_W9C@~|E
ztEO|rDQW2`*?j79qt>`AG9xNIDwRrZ`sR5Li~#udACYl95)tq^3^qev7T2_K_ol}6
zsZsi<%pLUkXkSFdlT%f6wj`w>wZzPk;nA+`MUf?uei0kCZHm|^h4KaD$0CRz+bt9ZLT*XdN{n;aOE!w+oRzx`lwePMlm19`sAw>Y<;v{;4A|1U~%Oco*|
z-^k<>D%Sp-QN@uH2t?%gV6%Kmh)kY=pL%|f&%sX&P!0w^9K&uISa(RK(GL;7O1y1+V&ot2&<_2$EwcT0N3d7Hq*F&H4SI1QWS1z&0=&prF=_Fd6?qV`D7tp=xI;;ZU#v3%}Hw36h^
z?R}M}_yf>Q5$`23HNqD1xz(iKhs)4H^11eSGjJ>18@k#Bt5i61bXIg)EY}iVxqhW8
zJY{8UG>3iOwlt2~1em2oi9^pNo((_3IcjWmwJMzASn9E;x47JroYE3idu;oLW1L+g
zf9oWfn*(+?XnktxBc>yuUa^c0;?pBu-nLy$(R6c9{?(8>#jQK8jM}}SWzF7@1MAp|nb3H6p8|Kf2UJp_-Dkw
z^nUo-U+JDnlDcO~O1lD-uPYdJVIj&?m%7sCx(hY_9TdsY{mLAHD+IHS#fb$E_Ymr6A6=HRA6qzDZfUJTj*pk@D7$h
z)P`!hwex{oLgt#KS*G;lji%D6-2vSJK{6KZU8HdbxC02bk@En1!Gu71Q^yk1ILNJN
zX87e!$kGC&yt+7O`=(YqfK<3OMd-m=NhA~L@cz&WaUn>2_78y5+M`n;bTEuQQ7B#%
zR=b~6(q(M`9QgmJx{H=gIZE|Ny&Ge9x;(`D=~3N-mX>M6!vI+DOgC@5vdnIW<*h42wveq+9)&bonRy7rn^5h8L%v`Y@9B
zOl0u?mC7F3E{|5w`WB}pI+BnZ@`5q69xYJjAZ8$)0(TvcT93>Z8x|Orj-!3a6aGH?
z;qnu16y^}bXB1B&i0X5gC;&5+I|Jk|AiSOCUamy6Y&m1Njo>0)q&|ihkW%Tlhl-c2
zj9IRh&kxv^RNKhERrAJSmE2x^J?gXTDw6d+X(p@5bKE;`ebjVir?lnkn|r@g%Z&k;
zU_~p)L#?f@R&}1;YRTi}&PlGMoVfVa>8n?%78OQTuHeenyXYe;F+=1k+x5gxcaB4C
z(wZ_#_8lrXd`R{Cy6aTTZP=K;kv>R8N9aRpxn&aVH)zwk!6+@@)vaSU1uc?nerdP!rjde;9Q??q^o2Mluhw;l}!xu)amWI!Z
zpF2Y};=s5)W4W3+JLk1%JLv>O5Z96kPn`~ZC-Op!bnA_;Hh!mm?|fy`JN%*gGfmY;
zrKQbf@9$%g)BA&6S0`gBu#w0++;xZ%wF$&nW$o^e4E-P4!^p)FWYxXn8wjE}(4P*G
zcwP~nec{FnV?D2Uo)!7~eAeZX0JD~>$z(y~JIWntOVgvd*SFEfS4>yWn6tBXHcz*I
zPBTcxD`dM=_ip5c_f%JpkjF3Y<_hYL7d5Eu4y)PDS7d!ihm>uX7RJ};bZh7nGdHN>
zDxwM!xDToCt&zlcvNXM-KB21h5_#e+b!}~ozLIZDB10xS5~R5pS&SF}-4*By;32)`
zFCK~Jpj>
z9NuWMRJwgdl6J0&`kWp5&-vWq+-0R9byADfY*Eosq#v{|hi>BxkrCMu>e#qkTO8kp
zPV&$Q@{~y$Nc&MhNr$N;qjGFJ_~*fZov@e$tA$(SQ$a6GEU}hYO8AS1PoI6OT?(9m
z`yr?^eoc1u1-#{*eq9UwMV-pL$PxLpj~au|^I%Xocp5?T=~0s3Z6)uxt;8v5B}YZb
zW6c-esC@^nJQ*eKKgwV9nSa;QWHO)}dx*Z>{VLfbKZI<=zY`$5JRU@(NZLlu4dz-6
zC3RJmmheKR8mGfv-OHGxOPOPLs
zm&x0zuXbNKdWy@e+VSZde@NS_$kRius`3k$U6<6CE@vcO;H~88pW5TNH=f)vJ~K{w
zbkXjhaVoG!X3V4$c_Yvb-3jiYtk3b#mm~uh27VBezxZL(tXq?6~(0hH^F}
zXW2}4%ndeBd&~}#&1lY+?g_<^4Qh|w=&(5RY;A2*9Ms~LJY?RWRm4PEOaXJV?eI2{gG
zE`GvPC;d0C1I@2R&_atmLYG!a25FH0=??q~Nd?JD%`nDI0awNKyrv!0o@ej~;RQ)H
zyt%v-8GkX8iv&zJAsKpiKPDH$liXG*a3aQ{SD-+0X
zn54b{OgD$-kX-r&d7A!KA+=bn7FKFn8lReGNJ6OtC1DNQTg;sBX{fN?v%cB$sWddV
zaYu_9Iq`}zCs0botkiNT%d26i4a7eH%kjl+Ac1$h-x1KLXV^NV%>k9eUmqF>(hvnx
zoiNf6S`4k!A@Qd#2s$MhCB%x#?Ult9YIm);qB1oR{_ZGGtcXm<@V7IwHnX0i%Y@%V
z@9Sn9oviMz6;GbAd>YcE%RIk{GNUqekt*8Z)myzNtL{>hfAl3Uu+SPv7z&m{4TP=G
zL3JL5+M`>AIO1kNg2dBk%-3}KIXeCJSW=k#F6sZ|m!qz~PbA|%Zv##Kp@Zb-2&f;f
zK^2Bd5%xn#h@D(paCR!vc%EOBw1ljr4y^FuY?P8(32`xxa)na6~2q<
z9D{ckzl!*shI%KNbJF(+o#%+EjB7CX)o1N=R#YPS#`z*g$B9ykD>EzA4rfk|gRgg1
zRXOU9ka@mj&SF#_JNmIpGt@68b9~9XBlV7|Drdc)!+UAc{$#kby;(tD>j^{r
zaqVVDJKuKrz~SbT#nnYMMK#je!sA5Rs78S|J_;X(=V;i>St_C9-*Je)f)E~=xU|jr
z=36QtP?Z0qqdC-sszT_*5%c+ND?`_9UMCHU2pY43InD5xQIqc8=)=XIHpN`vH~#*|
zR^p>Z#G!hB@j=@gQZil)m2q$#NC1Lrxa4C*jsQ#$QLab7#kI4SJmN(>4j7;0dzaGJ
z=mg}eafW_VjuII!k2qABQ)#Q<*4FCI9#+*k>WZp4`Suq>o8k|?t!gTHySk1w&h&Zj
zT)lGP{ChkuOCI~;#bK9-LUre(rW-qtQIW2QE7BF|N@AK9A6V74N;;+e+NeL&O>h!{
zW%`k|FWL{a`2b!|#Jhif^o
zxH+~srYNRJsw@i(81B157>**V`
z-|{Jx#qV~-$LH7*__ewPx>f4vXh%^j9~!VfdiO}}z67dHKLQH3jE&s5PaJY?u7xY8A4g2Ey=^q|m{
z+oU7r(}^KerJ|$1fiLyy8*e+xT3NG!+KVQ{s2G4ABP9VG&Wsjr%{yGuQYl4k%q69k
z5_Nlf^}%Dj-6E3j+fNo+ekUq23--LCQv-7^ud4)+>KQN@^fHe{jCAmPk^B&Vd;kZ^
zXFyhQtH~t|N~HMKbJ{sxd5&8n8ORWI
zBY6YlhZwAnox=-Vv@__U(t92TqhzSco}wg?C`m$5M^Yz4VeATU9m8cz@8f=Pb_*bj
z-vP1+OUm0O-ZJO0GUX_f)f_ER=WU6e3IY7sbJ;sI9*YFkoZr(d-rCu7{#_hLOsAoy
zFE_i0rj$HhT2WbE3j3P|lD;EKtPOX|b81@15ZsF+WLooQUu4w0-PqtdQk8!qwu(qy
z@-Lol(f@}j{y^kbi|e$WBj%ve1bPVs@d)m7SU)mH&v%S=mtUHoMHl+1VKl$)O2}
zxzc<~RC10g!vYDv4&Z4_}n!6me}HSdsd^V&{SlxW)`I;n+x?$ski2O
zN0K?qk*wF-Oy${``DqrDF+C$U(~(-RJu%rS&B@C)+jvu&!I_oaQ)7b>_z`1qR7!MC
zq%^L0OQoK38F!mqc_j{Wp}ojn>~NIkyqO!e#h73M{KA|jHQVhuc6FZ3Zc{nZt4xj}
zXIe={Zi+M|w>UXool>^ln9CQ&Rb*BbNHa|_dNY@9j<3!uv}Bu1CUbgGq9dcoY>RAj
zP9dzilg$TFurRRbG+d-Lf3L#kA7~7p62h$Bg_>K4h8m_3%4P
zx$7G&mOQ7$nPr#8Cl~BWw;||-Xx6#g*FU*)Qkvt)x8|!W%mvBC8M*fCe3RXlUzF>F
ze^H#9pPl70)wa)zd?0h528FpM>
zm{p`tPIp?GGmNQH2gLC6)hQ`{U0V&7YFoLr%Ft6niLn|_
zTb`rRuj2@_buvO+lsu`#iB%pXtn~$S=q*thCunr1`bsrgBw5vCUG%
z6(m;`Ik^JIk#tv1a$@piC$gEKiL+m+jpo{)uWF+1{{@E~2rTuWh%!-DHd
z&CANmC^Y3|NS%qMq}nW}xw6obEX{)xnxo1|aU_-J0&fv-HgQ=Q$+;OulO;OVW=buM
zwIeIO4Izs;eD(9
z#i0;iXpfM&eT5g5^obKsbuJ-KbdT>I?|UEV`3JJNmu2n=?g=7ye<4U&l~x)TN0aH0
z_%Mzxx+?a-}=DwmHLVrl?oQ0E3%PCPMaq`bEC5si>{F2UFK$
z`2F?Q1GkA~qg~8NMT!;q<$Er;${7Hg0Epe2awdxI4&`Aa|9pD?AcRE~2(+~VQI+KH
z^J%Y`37lUs(=bW*r2BdjB|s5yK>GJm$J~h$AzetnFKWUNHb_}2KutSA9;2P4uZDJlKju*+X(T|_
z_>1~=#lgp?gD@AC87|8NZM@6_?u{-f8Y;~?rqaxQ^##-qFZ>6+b8n?;{p!4uEIkSx
zBvQtHA>O^P-(lJRw#*9Au;qk&Sux%{QLtAdWF$^2Ve%tAXF`&^SA7l%CLWYG5T%8i
z@WYmT6mj#GswTI_R>LKStjSzO)dO$Ds;S&Y>t6;Nc*V~=QHkIC{QE<{+oWA*x*t=L
z*u~^$dYB7EW`(CK@p_c-p?@tvF!t`VJqr*(1pZ%SEO?gwKHVFUNdel?D`+M_f=zkd
zM(TmPj2$?Zs@1F31-WkjjLSE&Hl
zZyj0BWcVQgw!5gdx{3>HZrpHOJzFM!tk3ZcjbY7PbyaQQE_Ho