From 27aeffcfa3a7cee1aea9770bee3fab030cb00450 Mon Sep 17 00:00:00 2001 From: "CHIEFSOFT\\ameye" Date: Sat, 17 Aug 2024 17:19:25 -0400 Subject: [PATCH] first commit --- .env | 71 + .idea/.gitignore | 8 + .idea/MermsProviders2025.iml | 8 + .idea/modules.xml | 8 + .idea/php.xml | 20 + .idea/phpunit.xml | 10 + LICENSE | 22 + README.md | 60 + apache_log/error.log | 4 + apache_log/other_vhosts_access.log | 145 + app/.htaccess | 6 + app/Common.php | 15 + app/Config/App.php | 202 + app/Config/Autoload.php | 94 + app/Config/Boot/development.php | 34 + app/Config/Boot/production.php | 25 + app/Config/Boot/testing.php | 38 + app/Config/CURLRequest.php | 20 + app/Config/Cache.php | 171 + app/Config/Constants.php | 185 + app/Config/ContentSecurityPolicy.php | 176 + app/Config/Cookie.php | 107 + app/Config/Cors.php | 105 + app/Config/Database.php | 201 + app/Config/DocTypes.php | 46 + app/Config/Email.php | 121 + app/Config/Encryption.php | 92 + app/Config/Events.php | 55 + app/Config/Exceptions.php | 106 + app/Config/Feature.php | 29 + app/Config/Filters.php | 107 + app/Config/ForeignCharacters.php | 12 + app/Config/Format.php | 77 + app/Config/Generators.php | 44 + app/Config/Honeypot.php | 42 + app/Config/Images.php | 31 + app/Config/Kint.php | 65 + app/Config/Logger.php | 150 + app/Config/Migrations.php | 50 + app/Config/Mimes.php | 536 + app/Config/Modules.php | 84 + app/Config/Optimize.php | 32 + app/Config/Pager.php | 37 + app/Config/Paths.php | 80 + app/Config/Publisher.php | 28 + app/Config/Routes.php | 10 + app/Config/Routing.php | 140 + app/Config/Security.php | 103 + app/Config/Services.php | 32 + app/Config/Session.php | 127 + app/Config/Toolbar.php | 122 + app/Config/UserAgents.php | 252 + app/Config/Validation.php | 44 + app/Config/View.php | 62 + app/Controllers/BaseController.php | 96 + app/Controllers/CoreController.php | 300 + app/Controllers/Home.php | 26 + app/Controllers/Login.php | 34 + app/Controllers/Logout.php | 28 + app/Controllers/Provider.php | 73 + app/Controllers/SecureBaseController.php | 43 + app/Database/Migrations/.gitkeep | 0 app/Database/Seeds/.gitkeep | 0 app/Filters/.gitkeep | 0 app/Helpers/.gitkeep | 0 app/Language/.gitkeep | 0 app/Language/en/Validation.php | 4 + app/Libraries/.gitkeep | 0 app/Models/.gitkeep | 0 app/Models/Backend_model.php | 42 + app/Models/Chart_model.php | 19 + app/Models/Dash_model.php | 18 + app/Models/Encounter_model.php | 41 + app/Models/Patient_model.php | 29 + app/Models/Reminder_model.php | 18 + app/Models/index.html | 11 + app/ThirdParty/.gitkeep | 0 app/Views/appstart/appstart.php | 251 + app/Views/errors/cli/error_404.php | 8 + app/Views/errors/cli/error_db.php | 8 + app/Views/errors/cli/error_exception.php | 21 + app/Views/errors/cli/error_general.php | 8 + app/Views/errors/cli/error_php.php | 21 + app/Views/errors/cli/index.html | 11 + app/Views/errors/cli/production.php | 5 + app/Views/errors/html/debug.css | 190 + app/Views/errors/html/debug.js | 116 + app/Views/errors/html/error_404.php | 64 + app/Views/errors/html/error_db.php | 64 + app/Views/errors/html/error_exception.php | 32 + app/Views/errors/html/error_general.php | 64 + app/Views/errors/html/error_php.php | 33 + app/Views/errors/html/index.html | 11 + app/Views/errors/html/production.php | 25 + app/Views/errors/index.html | 11 + app/Views/index.html | 11 + app/Views/provider/calendar.php | 279 + .../provider/components/encounter_listing.php | 137 + .../components/large_patient_listing.php | 71 + .../provider/components/patient_listing.php | 68 + app/Views/provider/dash.php | 168 + app/Views/provider/patient/chart/chart.php | 70 + app/Views/provider/patient/findpatient.php | 102 + app/Views/provider/patient/newpatient.php | 212 + .../patientActions/view_chart_action.php | 15 + .../patientActions/view_reminder_action.php | 109 + .../patientActions/view_tracking_action.php | 12 + app/Views/provider/patient/patientreport.php | 132 + app/Views/provider/patient/thispatient.php | 397 + .../provider/patient/thispatientChart.php | 219 + app/Views/provider/profile/inbox.php | 451 + app/Views/provider/profile/profile.php | 431 + app/Views/provider/profile/settings.php | 454 + app/Views/provider/reports/report.php | 0 app/Views/register.php | 140 + app/Views/resetpass.php | 81 + app/Views/template/footer.php | 28 + app/Views/template/header.php | 23 + app/Views/template/menu/sidemain.php | 101 + app/Views/template/provider_footer.php | 56 + app/Views/template/provider_header.php | 239 + app/Views/template/topstrip.php | 53 + app/Views/template/topstrip2.php | 76 + app/Views/welcome_message.php | 123 + app/index.html | 11 + composer.json | 66 + docker-compose.yml | 34 + docker/apache/000-default.conf | 11 + docker/apache/Dockerfile | 56 + env | 69 + phpunit.xml.dist | 63 + preload.php | 104 + public/.htaccess | 49 + public/assets/app/css/bootstrap-grid.css | 3899 + public/assets/app/css/bootstrap-grid.min.css | 7 + public/assets/app/css/bootstrap-reboot.css | 327 + .../assets/app/css/bootstrap-reboot.min.css | 8 + public/assets/app/css/bootstrap.css | 10224 +++ public/assets/app/css/bootstrap.min.css | 7 + public/assets/app/js/bootstrap.bundle.js | 7134 ++ public/assets/app/js/bootstrap.bundle.min.js | 7 + public/assets/app/js/bootstrap.js | 4521 ++ public/assets/app/js/bootstrap.min.js | 7 + public/assets/css/main.css | 1155 + public/assets/css/style.css | 6 + public/assets/css/vendors.css | 38 + public/assets/fonts/cryptocurrency-icons.eot | Bin 0 -> 62984 bytes public/assets/fonts/cryptocurrency-icons.svg | 705 + public/assets/fonts/cryptocurrency-icons.ttf | Bin 0 -> 62768 bytes public/assets/fonts/cryptocurrency-icons.woff | Bin 0 -> 31064 bytes .../assets/fonts/cryptocurrency-icons.woff2 | Bin 0 -> 24288 bytes public/assets/fonts/dashicons.eot | Bin 0 -> 41808 bytes public/assets/fonts/dashicons.svg | 840 + public/assets/fonts/dashicons.ttf | Bin 0 -> 41636 bytes public/assets/fonts/dashicons.woff | Bin 0 -> 20528 bytes public/assets/fonts/dashicons.woff2 | Bin 0 -> 16604 bytes public/assets/fonts/dripicons.eot | Bin 0 -> 40522 bytes public/assets/fonts/dripicons.svg | 210 + public/assets/fonts/dripicons.ttf | Bin 0 -> 40348 bytes public/assets/fonts/dripicons.woff | Bin 0 -> 26004 bytes public/assets/fonts/feather.eot | Bin 0 -> 62084 bytes public/assets/fonts/feather.svg | 849 + public/assets/fonts/feather.ttf | Bin 0 -> 61920 bytes public/assets/fonts/feather.woff | Bin 0 -> 29500 bytes public/assets/fonts/font-awesome.eot | Bin 0 -> 165742 bytes public/assets/fonts/font-awesome.svg | 2671 + public/assets/fonts/font-awesome.ttf | Bin 0 -> 165548 bytes public/assets/fonts/font-awesome.woff | Bin 0 -> 98024 bytes public/assets/fonts/font-awesome.woff2 | Bin 0 -> 77160 bytes public/assets/fonts/ionicons.eot | Bin 0 -> 144114 bytes public/assets/fonts/ionicons.svg | 2630 + public/assets/fonts/ionicons.ttf | Bin 0 -> 143936 bytes public/assets/fonts/ionicons.woff | Bin 0 -> 80356 bytes public/assets/fonts/ionicons.woff2 | Bin 0 -> 61020 bytes public/assets/fonts/linea-weather.eot | Bin 0 -> 21514 bytes public/assets/fonts/linea-weather.svg | 95 + public/assets/fonts/linea-weather.ttf | Bin 0 -> 21308 bytes public/assets/fonts/linea-weather.woff | Bin 0 -> 7936 bytes public/assets/fonts/material-icons.eot | Bin 0 -> 42495 bytes public/assets/fonts/material-icons.svg | 787 + public/assets/fonts/material-icons.ttf | Bin 0 -> 99212 bytes public/assets/fonts/material-icons.woff | Bin 0 -> 50312 bytes public/assets/fonts/material-icons.woff2 | Bin 0 -> 38384 bytes public/assets/fonts/summernote.eot | Bin 0 -> 16746 bytes public/assets/fonts/summernote.ttf | Bin 0 -> 16560 bytes public/assets/fonts/summernote.woff | Bin 0 -> 10324 bytes public/assets/fonts/themify-icons.eot | Bin 0 -> 78748 bytes public/assets/fonts/themify-icons.svg | 362 + public/assets/fonts/themify-icons.ttf | Bin 0 -> 78584 bytes public/assets/fonts/themify-icons.woff | Bin 0 -> 56108 bytes .../assets/img/app-store-icons/apple-dark.svg | 13 + .../img/app-store-icons/apple-white.svg | 26 + public/assets/img/app-store-icons/apple.svg | 26 + .../img/app-store-icons/google-dark.svg | 37 + .../img/app-store-icons/google-white.svg | 19 + public/assets/img/app-store-icons/google.svg | 19 + public/assets/img/avtar/01.jpg | Bin 0 -> 619 bytes public/assets/img/avtar/02.jpg | Bin 0 -> 619 bytes public/assets/img/avtar/03.jpg | Bin 0 -> 619 bytes public/assets/img/avtar/04.jpg | Bin 0 -> 619 bytes public/assets/img/avtar/05.jpg | Bin 0 -> 619 bytes public/assets/img/avtar/06.jpg | Bin 0 -> 619 bytes public/assets/img/avtar/07.jpg | Bin 0 -> 619 bytes public/assets/img/avtar/08.jpg | Bin 0 -> 619 bytes public/assets/img/avtar/09.jpg | Bin 0 -> 619 bytes public/assets/img/avtar/10.jpg | Bin 0 -> 921 bytes public/assets/img/bg/coming-soon-bg.svg | 1 + public/assets/img/bg/login.svg | 1 + public/assets/img/bg/provider-0.jpg | Bin 0 -> 56904 bytes public/assets/img/bg/provider-1.0.jpg | Bin 0 -> 83288 bytes public/assets/img/bg/provider-1.jpg | Bin 0 -> 159074 bytes public/assets/img/bg/provider-1a.jpg | Bin 0 -> 491520 bytes public/assets/img/bg/provider.jpg | Bin 0 -> 83288 bytes public/assets/img/bg/provider2.jpg | Bin 0 -> 41591 bytes public/assets/img/bg/provider3.jpg | Bin 0 -> 76323 bytes public/assets/img/bg/provider4.jpg | Bin 0 -> 56736 bytes public/assets/img/browser-logo/bootstrap.png | Bin 0 -> 3694 bytes public/assets/img/browser-logo/envato.png | Bin 0 -> 786 bytes public/assets/img/browser-logo/google.png | Bin 0 -> 3457 bytes public/assets/img/browser-logo/invision.png | Bin 0 -> 1913 bytes public/assets/img/browser-logo/slack.png | Bin 0 -> 1558 bytes public/assets/img/export/csv.svg | 1 + public/assets/img/export/icsv.png | Bin 0 -> 117 bytes public/assets/img/export/itxt.png | Bin 0 -> 105 bytes public/assets/img/export/ixls.png | Bin 0 -> 108 bytes public/assets/img/export/ixlsx.png | Bin 0 -> 129 bytes public/assets/img/export/txt.svg | 1 + public/assets/img/export/xls.svg | 1 + public/assets/img/export/xlsx.svg | 1 + public/assets/img/favicon.ico | Bin 0 -> 318 bytes public/assets/img/file-icon/ai.png | Bin 0 -> 1182 bytes public/assets/img/file-icon/css.png | Bin 0 -> 1380 bytes public/assets/img/file-icon/dbf.png | Bin 0 -> 948 bytes public/assets/img/file-icon/doc.png | Bin 0 -> 1383 bytes public/assets/img/file-icon/dwg.png | Bin 0 -> 1215 bytes public/assets/img/file-icon/exe.png | Bin 0 -> 1269 bytes public/assets/img/file-icon/html.png | Bin 0 -> 1006 bytes public/assets/img/file-icon/jpg.png | Bin 0 -> 1364 bytes public/assets/img/file-icon/pdf.png | Bin 0 -> 1399 bytes public/assets/img/file-icon/png.png | Bin 0 -> 1397 bytes public/assets/img/file-icon/psd.png | Bin 0 -> 1430 bytes public/assets/img/file-icon/rtf.png | Bin 0 -> 1005 bytes public/assets/img/file-icon/svg.png | Bin 0 -> 1421 bytes public/assets/img/file-icon/xls.png | Bin 0 -> 974 bytes public/assets/img/file-icon/xml.png | Bin 0 -> 1332 bytes public/assets/img/file-icon/zip.png | Bin 0 -> 997 bytes public/assets/img/flags/au.png | Bin 0 -> 2550 bytes public/assets/img/flags/bd.png | Bin 0 -> 197 bytes public/assets/img/flags/ca.png | Bin 0 -> 343 bytes public/assets/img/flags/cn.png | Bin 0 -> 254 bytes public/assets/img/flags/es.png | Bin 0 -> 1444 bytes public/assets/img/flags/gb.png | Bin 0 -> 263 bytes public/assets/img/flags/in.png | Bin 0 -> 250 bytes public/assets/img/flags/iq.png | Bin 0 -> 1029 bytes public/assets/img/flags/ir.png | Bin 0 -> 373 bytes public/assets/img/flags/jp.png | Bin 0 -> 225 bytes public/assets/img/flags/lk.png | Bin 0 -> 2670 bytes public/assets/img/flags/nz.png | Bin 0 -> 2611 bytes public/assets/img/flags/pk.png | Bin 0 -> 1244 bytes public/assets/img/flags/ru.png | Bin 0 -> 205 bytes public/assets/img/flags/us.png | Bin 0 -> 2187 bytes public/assets/img/flags/za.png | Bin 0 -> 1902 bytes public/assets/img/loader/loader.svg | 1 + public/assets/img/logo-icon.png | Bin 0 -> 1929 bytes public/assets/img/logo-light.png | Bin 0 -> 2422 bytes public/assets/img/logo.png | Bin 0 -> 1162 bytes public/assets/img/product.jpg | Bin 0 -> 6306 bytes public/assets/img/real-estate/01.jpg | Bin 0 -> 5274 bytes public/assets/img/real-estate/02.jpg | Bin 0 -> 5274 bytes public/assets/img/real-estate/03.jpg | Bin 0 -> 5274 bytes public/assets/img/real-estate/04.jpg | Bin 0 -> 5274 bytes public/assets/img/real-estate/05.jpg | Bin 0 -> 5274 bytes public/assets/img/real-estate/06.jpg | Bin 0 -> 5274 bytes public/assets/img/slider/slide-1.jpg | Bin 0 -> 21328 bytes public/assets/img/slider/slide-2.jpg | Bin 0 -> 21328 bytes public/assets/img/slider/slide-3.jpg | Bin 0 -> 21328 bytes public/assets/img/slider/slide-4.jpg | Bin 0 -> 21328 bytes public/assets/img/widget/01.jpg | Bin 0 -> 6695 bytes public/assets/img/widget/02.jpg | Bin 0 -> 6695 bytes public/assets/img/widget/03.jpg | Bin 0 -> 6695 bytes public/assets/img/widget/04.jpg | Bin 0 -> 6695 bytes public/assets/img/widget/05.jpg | Bin 0 -> 6695 bytes public/assets/img/widget/06.jpg | Bin 0 -> 6695 bytes public/assets/img/widget/07.jpg | Bin 0 -> 6695 bytes public/assets/img/widget/08.jpg | Bin 0 -> 6695 bytes public/assets/img/widget/09.jpg | Bin 0 -> 6695 bytes public/assets/img/widget/10.jpg | Bin 0 -> 6695 bytes public/assets/js/app-min.js | 1 + public/assets/js/app.js | 14261 ++++ public/assets/js/vendors-min.js | 1 + public/assets/js/vendors.js | 62617 ++++++++++++++++ public/favicon.ico | Bin 0 -> 5430 bytes public/index.php | 56 + public/robots.txt | 2 + spark | 84 + system/.htaccess | 6 + system/API/ResponseTrait.php | 374 + system/Autoloader/Autoloader.php | 561 + system/Autoloader/FileLocator.php | 404 + system/Autoloader/FileLocatorCached.php | 172 + system/Autoloader/FileLocatorInterface.php | 82 + system/BaseModel.php | 1953 + system/Boot.php | 355 + system/CLI/BaseCommand.php | 233 + system/CLI/CLI.php | 1152 + system/CLI/Commands.php | 195 + system/CLI/Console.php | 90 + system/CLI/Exceptions/CLIException.php | 36 + system/CLI/GeneratorTrait.php | 527 + system/CLI/InputOutput.php | 80 + system/Cache/CacheFactory.php | 91 + system/Cache/CacheInterface.php | 110 + system/Cache/Exceptions/CacheException.php | 66 + system/Cache/FactoriesCache.php | 67 + .../FactoriesCache/FileVarExportHandler.php | 46 + system/Cache/Handlers/BaseHandler.php | 113 + system/Cache/Handlers/DummyHandler.php | 121 + system/Cache/Handlers/FileHandler.php | 425 + system/Cache/Handlers/MemcachedHandler.php | 278 + system/Cache/Handlers/PredisHandler.php | 227 + system/Cache/Handlers/RedisHandler.php | 258 + system/Cache/Handlers/WincacheHandler.php | 152 + system/Cache/ResponseCache.php | 157 + system/CodeIgniter.php | 1157 + system/Commands/Cache/ClearCache.php | 90 + system/Commands/Cache/InfoCache.php | 91 + system/Commands/Database/CreateDatabase.php | 154 + system/Commands/Database/Migrate.php | 103 + system/Commands/Database/MigrateRefresh.php | 89 + system/Commands/Database/MigrateRollback.php | 119 + system/Commands/Database/MigrateStatus.php | 168 + system/Commands/Database/Seed.php | 84 + system/Commands/Database/ShowTableInfo.php | 345 + system/Commands/Encryption/GenerateKey.php | 205 + system/Commands/Generators/CellGenerator.php | 107 + .../Commands/Generators/CommandGenerator.php | 121 + .../Commands/Generators/ConfigGenerator.php | 100 + .../Generators/ControllerGenerator.php | 136 + .../Commands/Generators/EntityGenerator.php | 86 + .../Commands/Generators/FilterGenerator.php | 86 + .../Generators/MigrationGenerator.php | 131 + system/Commands/Generators/ModelGenerator.php | 135 + .../Commands/Generators/ScaffoldGenerator.php | 123 + .../Commands/Generators/SeederGenerator.php | 86 + system/Commands/Generators/TestGenerator.php | 192 + .../Generators/ValidationGenerator.php | 86 + system/Commands/Generators/Views/cell.tpl.php | 10 + .../Generators/Views/cell_view.tpl.php | 3 + .../Commands/Generators/Views/command.tpl.php | 76 + .../Commands/Generators/Views/config.tpl.php | 10 + .../Generators/Views/controller.tpl.php | 186 + .../Commands/Generators/Views/entity.tpl.php | 12 + .../Commands/Generators/Views/filter.tpl.php | 47 + .../Generators/Views/migration.tpl.php | 50 + .../Commands/Generators/Views/model.tpl.php | 49 + .../Commands/Generators/Views/seeder.tpl.php | 13 + system/Commands/Generators/Views/test.tpl.php | 18 + .../Generators/Views/validation.tpl.php | 11 + system/Commands/Help.php | 87 + .../Commands/Housekeeping/ClearDebugbar.php | 72 + system/Commands/Housekeeping/ClearLogs.php | 93 + system/Commands/ListCommands.php | 140 + system/Commands/Server/Serve.php | 119 + .../Translation/LocalizationFinder.php | 389 + system/Commands/Utilities/ConfigCheck.php | 156 + system/Commands/Utilities/Environment.php | 157 + system/Commands/Utilities/FilterCheck.php | 155 + system/Commands/Utilities/Namespaces.php | 160 + system/Commands/Utilities/Optimize.php | 149 + system/Commands/Utilities/PhpIniCheck.php | 77 + system/Commands/Utilities/Publish.php | 106 + system/Commands/Utilities/Routes.php | 222 + .../Utilities/Routes/AutoRouteCollector.php | 59 + .../AutoRouterImproved/AutoRouteCollector.php | 151 + .../ControllerMethodReader.php | 243 + .../Utilities/Routes/ControllerFinder.php | 74 + .../Routes/ControllerMethodReader.php | 171 + .../Utilities/Routes/FilterCollector.php | 117 + .../Utilities/Routes/FilterFinder.php | 99 + .../Utilities/Routes/SampleURIGenerator.php | 73 + system/Common.php | 1259 + system/ComposerScripts.php | 174 + system/Config/AutoloadConfig.php | 153 + system/Config/BaseConfig.php | 273 + system/Config/BaseService.php | 423 + system/Config/DotEnv.php | 240 + system/Config/Factories.php | 556 + system/Config/Factory.php | 50 + system/Config/Filters.php | 118 + system/Config/ForeignCharacters.php | 117 + system/Config/Publisher.php | 44 + system/Config/Routing.php | 140 + system/Config/Services.php | 866 + system/Config/View.php | 136 + system/Controller.php | 183 + system/Cookie/CloneableCookieInterface.php | 111 + system/Cookie/Cookie.php | 789 + system/Cookie/CookieInterface.php | 170 + system/Cookie/CookieStore.php | 259 + system/Cookie/Exceptions/CookieException.php | 129 + system/DataCaster/Cast/ArrayCast.php | 47 + system/DataCaster/Cast/BaseCast.php | 45 + system/DataCaster/Cast/BooleanCast.php | 39 + system/DataCaster/Cast/CSVCast.php | 47 + system/DataCaster/Cast/CastInterface.php | 47 + system/DataCaster/Cast/DatetimeCast.php | 67 + system/DataCaster/Cast/FloatCast.php | 35 + system/DataCaster/Cast/IntBoolCast.php | 47 + system/DataCaster/Cast/IntegerCast.php | 35 + system/DataCaster/Cast/JsonCast.php | 63 + system/DataCaster/Cast/TimestampCast.php | 49 + system/DataCaster/Cast/URICast.php | 49 + system/DataCaster/DataCaster.php | 188 + .../DataCaster/Exceptions/CastException.php | 23 + system/DataConverter/DataConverter.php | 202 + system/Database/BaseBuilder.php | 3582 + system/Database/BaseConnection.php | 1854 + system/Database/BasePreparedQuery.php | 262 + system/Database/BaseResult.php | 545 + system/Database/BaseUtils.php | 328 + system/Database/Config.php | 153 + system/Database/ConnectionInterface.php | 168 + system/Database/Database.php | 141 + system/Database/Exceptions/DataException.php | 87 + .../Database/Exceptions/DatabaseException.php | 25 + .../Exceptions/ExceptionInterface.php | 24 + system/Database/Forge.php | 1260 + system/Database/Migration.php | 74 + system/Database/MigrationRunner.php | 871 + system/Database/MySQLi/Builder.php | 147 + system/Database/MySQLi/Connection.php | 634 + system/Database/MySQLi/Forge.php | 266 + system/Database/MySQLi/PreparedQuery.php | 114 + system/Database/MySQLi/Result.php | 170 + system/Database/MySQLi/Utils.php | 47 + system/Database/OCI8/Builder.php | 526 + system/Database/OCI8/Connection.php | 744 + system/Database/OCI8/Forge.php | 314 + system/Database/OCI8/PreparedQuery.php | 120 + system/Database/OCI8/Result.php | 119 + system/Database/OCI8/Utils.php | 40 + system/Database/Postgre/Builder.php | 634 + system/Database/Postgre/Connection.php | 589 + system/Database/Postgre/Forge.php | 220 + system/Database/Postgre/PreparedQuery.php | 128 + system/Database/Postgre/Result.php | 136 + system/Database/Postgre/Utils.php | 47 + system/Database/PreparedQueryInterface.php | 63 + system/Database/Query.php | 431 + system/Database/QueryInterface.php | 89 + system/Database/RawSql.php | 56 + system/Database/ResultInterface.php | 184 + system/Database/SQLSRV/Builder.php | 821 + system/Database/SQLSRV/Connection.php | 569 + system/Database/SQLSRV/Forge.php | 448 + system/Database/SQLSRV/PreparedQuery.php | 137 + system/Database/SQLSRV/Result.php | 176 + system/Database/SQLSRV/Utils.php | 55 + system/Database/SQLite3/Builder.php | 280 + system/Database/SQLite3/Connection.php | 460 + system/Database/SQLite3/Forge.php | 324 + system/Database/SQLite3/PreparedQuery.php | 116 + system/Database/SQLite3/Result.php | 160 + system/Database/SQLite3/Table.php | 490 + system/Database/SQLite3/Utils.php | 40 + system/Database/Seeder.php | 193 + system/Debug/BaseExceptionHandler.php | 271 + system/Debug/ExceptionHandler.php | 161 + system/Debug/ExceptionHandlerInterface.php | 32 + system/Debug/Exceptions.php | 621 + system/Debug/Iterator.php | 134 + system/Debug/Timer.php | 151 + system/Debug/Toolbar.php | 551 + .../Toolbar/Collectors/BaseCollector.php | 238 + system/Debug/Toolbar/Collectors/Config.php | 42 + system/Debug/Toolbar/Collectors/Database.php | 260 + system/Debug/Toolbar/Collectors/Events.php | 125 + system/Debug/Toolbar/Collectors/Files.php | 104 + system/Debug/Toolbar/Collectors/History.php | 145 + system/Debug/Toolbar/Collectors/Logs.php | 97 + system/Debug/Toolbar/Collectors/Routes.php | 170 + system/Debug/Toolbar/Collectors/Timers.php | 73 + system/Debug/Toolbar/Collectors/Views.php | 150 + system/Debug/Toolbar/Views/_config.tpl | 48 + system/Debug/Toolbar/Views/_database.tpl | 26 + system/Debug/Toolbar/Views/_events.tpl | 18 + system/Debug/Toolbar/Views/_files.tpl | 16 + system/Debug/Toolbar/Views/_history.tpl | 28 + system/Debug/Toolbar/Views/_logs.tpl | 20 + system/Debug/Toolbar/Views/_routes.tpl | 52 + system/Debug/Toolbar/Views/toolbar.css | 867 + system/Debug/Toolbar/Views/toolbar.js | 825 + system/Debug/Toolbar/Views/toolbar.tpl.php | 277 + system/Debug/Toolbar/Views/toolbarloader.js | 90 + system/Email/Email.php | 2274 + system/Encryption/EncrypterInterface.php | 48 + system/Encryption/Encryption.php | 176 + .../Exceptions/EncryptionException.php | 86 + system/Encryption/Handlers/BaseHandler.php | 78 + system/Encryption/Handlers/OpenSSLHandler.php | 158 + system/Encryption/Handlers/SodiumHandler.php | 142 + system/Entity/Cast/ArrayCast.php | 40 + system/Entity/Cast/BaseCast.php | 46 + system/Entity/Cast/BooleanCast.php | 28 + system/Entity/Cast/CSVCast.php | 36 + system/Entity/Cast/CastInterface.php | 44 + system/Entity/Cast/DatetimeCast.php | 52 + system/Entity/Cast/FloatCast.php | 28 + system/Entity/Cast/IntBoolCast.php | 38 + system/Entity/Cast/IntegerCast.php | 28 + system/Entity/Cast/JsonCast.php | 67 + system/Entity/Cast/ObjectCast.php | 28 + system/Entity/Cast/StringCast.php | 28 + system/Entity/Cast/TimestampCast.php | 36 + system/Entity/Cast/URICast.php | 30 + system/Entity/Entity.php | 572 + system/Entity/Exceptions/CastException.php | 75 + system/Events/Events.php | 285 + system/Exceptions/ConfigException.php | 35 + system/Exceptions/CriticalError.php | 23 + system/Exceptions/DebugTraceableTrait.php | 43 + system/Exceptions/DownloadException.php | 64 + system/Exceptions/ExceptionInterface.php | 24 + system/Exceptions/FrameworkException.php | 96 + system/Exceptions/HTTPExceptionInterface.php | 21 + system/Exceptions/HasExitCodeInterface.php | 25 + system/Exceptions/ModelException.php | 44 + system/Exceptions/PageNotFoundException.php | 85 + system/Exceptions/TestException.php | 30 + system/Files/Exceptions/FileException.php | 55 + .../Exceptions/FileNotFoundException.php | 31 + system/Files/File.php | 192 + system/Files/FileCollection.php | 370 + system/Filters/CSRF.php | 77 + system/Filters/Cors.php | 111 + system/Filters/DebugToolbar.php | 45 + system/Filters/Exceptions/FilterException.php | 44 + system/Filters/FilterInterface.php | 51 + system/Filters/Filters.php | 869 + system/Filters/ForceHTTPS.php | 64 + system/Filters/Honeypot.php | 57 + system/Filters/InvalidChars.php | 128 + system/Filters/PageCache.php | 77 + system/Filters/PerformanceMetrics.php | 62 + system/Filters/SecureHeaders.php | 75 + system/Format/Exceptions/FormatException.php | 73 + system/Format/Format.php | 76 + system/Format/FormatterInterface.php | 29 + system/Format/JSONFormatter.php | 50 + system/Format/XMLFormatter.php | 104 + system/HTTP/CLIRequest.php | 327 + system/HTTP/CURLRequest.php | 701 + system/HTTP/ContentSecurityPolicy.php | 840 + system/HTTP/Cors.php | 230 + system/HTTP/DownloadResponse.php | 360 + .../HTTP/Exceptions/BadRequestException.php | 30 + system/HTTP/Exceptions/HTTPException.php | 244 + system/HTTP/Exceptions/RedirectException.php | 84 + system/HTTP/Files/FileCollection.php | 262 + system/HTTP/Files/UploadedFile.php | 359 + system/HTTP/Files/UploadedFileInterface.php | 150 + system/HTTP/Header.php | 196 + system/HTTP/IncomingRequest.php | 883 + system/HTTP/Message.php | 142 + system/HTTP/MessageInterface.php | 145 + system/HTTP/MessageTrait.php | 299 + system/HTTP/Method.php | 121 + system/HTTP/Negotiate.php | 363 + system/HTTP/OutgoingRequest.php | 163 + system/HTTP/OutgoingRequestInterface.php | 93 + system/HTTP/RedirectResponse.php | 178 + system/HTTP/Request.php | 86 + system/HTTP/RequestInterface.php | 41 + system/HTTP/RequestTrait.php | 379 + system/HTTP/ResponsableInterface.php | 19 + system/HTTP/Response.php | 245 + system/HTTP/ResponseInterface.php | 418 + system/HTTP/ResponseTrait.php | 751 + system/HTTP/SiteURI.php | 437 + system/HTTP/SiteURIFactory.php | 242 + system/HTTP/URI.php | 1185 + system/HTTP/UserAgent.php | 374 + system/Helpers/Array/ArrayHelper.php | 318 + system/Helpers/array_helper.php | 165 + system/Helpers/cookie_helper.php | 109 + system/Helpers/date_helper.php | 79 + system/Helpers/filesystem_helper.php | 452 + system/Helpers/form_helper.php | 802 + system/Helpers/html_helper.php | 549 + system/Helpers/inflector_helper.php | 339 + system/Helpers/kint_helper.php | 92 + system/Helpers/number_helper.php | 220 + system/Helpers/security_helper.php | 51 + system/Helpers/test_helper.php | 72 + system/Helpers/text_helper.php | 747 + system/Helpers/url_helper.php | 518 + system/Helpers/xml_helper.php | 61 + .../Honeypot/Exceptions/HoneypotException.php | 60 + system/Honeypot/Honeypot.php | 121 + system/HotReloader/DirectoryHasher.php | 82 + system/HotReloader/HotReloader.php | 72 + system/HotReloader/IteratorFilter.php | 57 + system/I18n/Exceptions/I18nException.php | 99 + system/I18n/Time.php | 47 + system/I18n/TimeDifference.php | 306 + system/I18n/TimeLegacy.php | 48 + system/I18n/TimeTrait.php | 1213 + system/Images/Exceptions/ImageException.php | 120 + system/Images/Handlers/BaseHandler.php | 770 + system/Images/Handlers/GDHandler.php | 513 + system/Images/Handlers/ImageMagickHandler.php | 469 + system/Images/Image.php | 137 + system/Images/ImageHandlerInterface.php | 142 + system/Language/Language.php | 299 + system/Language/en/CLI.php | 55 + system/Language/en/Cache.php | 20 + system/Language/en/Cast.php | 25 + system/Language/en/Cookie.php | 26 + system/Language/en/Core.php | 23 + system/Language/en/Database.php | 33 + system/Language/en/Email.php | 35 + system/Language/en/Encryption.php | 22 + system/Language/en/Errors.php | 20 + system/Language/en/Fabricator.php | 19 + system/Language/en/Files.php | 20 + system/Language/en/Filters.php | 18 + system/Language/en/Format.php | 20 + system/Language/en/HTTP.php | 83 + system/Language/en/Images.php | 36 + system/Language/en/Language.php | 17 + system/Language/en/Log.php | 18 + system/Language/en/Migrations.php | 60 + system/Language/en/Number.php | 28 + system/Language/en/Pager.php | 25 + system/Language/en/Publisher.php | 24 + system/Language/en/RESTful.php | 17 + system/Language/en/Router.php | 20 + system/Language/en/Security.php | 21 + system/Language/en/Session.php | 24 + system/Language/en/Test.php | 17 + system/Language/en/Time.php | 35 + system/Language/en/Validation.php | 77 + system/Language/en/View.php | 23 + system/Log/Exceptions/LogException.php | 35 + system/Log/Handlers/BaseHandler.php | 61 + system/Log/Handlers/ChromeLoggerHandler.php | 169 + system/Log/Handlers/ErrorlogHandler.php | 88 + system/Log/Handlers/FileHandler.php | 126 + system/Log/Handlers/HandlerInterface.php | 44 + system/Log/Logger.php | 428 + system/Model.php | 951 + system/Modules/Modules.php | 75 + system/Pager/Exceptions/PagerException.php | 39 + system/Pager/Pager.php | 441 + system/Pager/PagerInterface.php | 120 + system/Pager/PagerRenderer.php | 433 + system/Pager/Views/default_full.php | 47 + system/Pager/Views/default_head.php | 27 + system/Pager/Views/default_simple.php | 23 + system/Publisher/ContentReplacer.php | 101 + .../Exceptions/PublisherException.php | 57 + system/Publisher/Publisher.php | 516 + system/RESTful/BaseResource.php | 77 + system/RESTful/ResourceController.php | 120 + system/RESTful/ResourcePresenter.php | 116 + system/Router/AutoRouter.php | 294 + system/Router/AutoRouterImproved.php | 592 + system/Router/AutoRouterInterface.php | 27 + system/Router/DefinedRouteCollector.php | 64 + .../Exceptions/MethodNotFoundException.php | 23 + .../Router/Exceptions/RedirectException.php | 32 + system/Router/Exceptions/RouterException.php | 83 + system/Router/RouteCollection.php | 1843 + system/Router/RouteCollectionInterface.php | 197 + system/Router/Router.php | 743 + system/Router/RouterInterface.php | 73 + system/Security/CheckPhpIni.php | 156 + .../Security/Exceptions/SecurityException.php | 84 + system/Security/Security.php | 544 + system/Security/SecurityInterface.php | 73 + .../Session/Exceptions/SessionException.php | 54 + system/Session/Handlers/ArrayHandler.php | 93 + system/Session/Handlers/BaseHandler.php | 177 + .../Handlers/Database/MySQLiHandler.php | 57 + .../Handlers/Database/PostgreHandler.php | 104 + system/Session/Handlers/DatabaseHandler.php | 307 + system/Session/Handlers/FileHandler.php | 340 + system/Session/Handlers/MemcachedHandler.php | 324 + system/Session/Handlers/RedisHandler.php | 419 + system/Session/Session.php | 936 + system/Session/SessionInterface.php | 186 + system/Superglobals.php | 63 + system/Test/CIUnitTestCase.php | 529 + system/Test/ConfigFromArrayTrait.php | 48 + system/Test/Constraints/SeeInDatabase.php | 120 + system/Test/ControllerTestTrait.php | 307 + system/Test/DOMParser.php | 300 + system/Test/DatabaseTestTrait.php | 383 + system/Test/Fabricator.php | 616 + system/Test/FeatureTestTrait.php | 429 + system/Test/FilterTestTrait.php | 309 + system/Test/Filters/CITestStreamFilter.php | 104 + system/Test/IniTestTrait.php | 35 + system/Test/Interfaces/FabricatorModel.php | 90 + system/Test/Mock/MockAppConfig.php | 30 + system/Test/Mock/MockAutoload.php | 28 + system/Test/Mock/MockBuilder.php | 25 + system/Test/Mock/MockCLIConfig.php | 36 + system/Test/Mock/MockCURLRequest.php | 58 + system/Test/Mock/MockCache.php | 307 + system/Test/Mock/MockCodeIgniter.php | 31 + system/Test/Mock/MockCommon.php | 34 + system/Test/Mock/MockConnection.php | 258 + system/Test/Mock/MockEmail.php | 42 + system/Test/Mock/MockEvents.php | 42 + system/Test/Mock/MockFileLogger.php | 36 + system/Test/Mock/MockIncomingRequest.php | 20 + system/Test/Mock/MockInputOutput.php | 140 + system/Test/Mock/MockLanguage.php | 58 + system/Test/Mock/MockLogger.php | 105 + system/Test/Mock/MockQuery.php | 20 + system/Test/Mock/MockResourceController.php | 34 + system/Test/Mock/MockResourcePresenter.php | 37 + system/Test/Mock/MockResponse.php | 41 + system/Test/Mock/MockResult.php | 103 + system/Test/Mock/MockSecurity.php | 32 + system/Test/Mock/MockServices.php | 36 + system/Test/Mock/MockSession.php | 73 + system/Test/Mock/MockTable.php | 30 + system/Test/PhpStreamWrapper.php | 77 + system/Test/ReflectionHelper.php | 103 + system/Test/StreamFilterTrait.php | 42 + system/Test/TestLogger.php | 109 + system/Test/TestResponse.php | 495 + system/Test/bootstrap.php | 91 + system/ThirdParty/Escaper/Escaper.php | 424 + .../Escaper/Exception/ExceptionInterface.php | 11 + .../Exception/InvalidArgumentException.php | 13 + .../Escaper/Exception/RuntimeException.php | 13 + system/ThirdParty/Escaper/LICENSE.md | 26 + system/ThirdParty/Kint/CallFinder.php | 568 + system/ThirdParty/Kint/FacadeInterface.php | 49 + system/ThirdParty/Kint/Kint.php | 729 + system/ThirdParty/Kint/LICENSE | 20 + .../ThirdParty/Kint/Parser/AbstractPlugin.php | 45 + .../Kint/Parser/ArrayLimitPlugin.php | 144 + .../Kint/Parser/ArrayObjectPlugin.php | 65 + .../ThirdParty/Kint/Parser/Base64Plugin.php | 96 + .../ThirdParty/Kint/Parser/BinaryPlugin.php | 51 + .../Kint/Parser/BlacklistPlugin.php | 101 + .../Kint/Parser/ClassMethodsPlugin.php | 115 + .../Kint/Parser/ClassStaticsPlugin.php | 154 + .../ThirdParty/Kint/Parser/ClosurePlugin.php | 96 + system/ThirdParty/Kint/Parser/ColorPlugin.php | 65 + .../Parser/ConstructablePluginInterface.php | 33 + .../Kint/Parser/DOMDocumentPlugin.php | 356 + .../ThirdParty/Kint/Parser/DateTimePlugin.php | 57 + system/ThirdParty/Kint/Parser/EnumPlugin.php | 88 + .../ThirdParty/Kint/Parser/FsPathPlugin.php | 80 + .../ThirdParty/Kint/Parser/IteratorPlugin.php | 107 + system/ThirdParty/Kint/Parser/JsonPlugin.php | 75 + .../Kint/Parser/MicrotimePlugin.php | 107 + .../ThirdParty/Kint/Parser/MysqliPlugin.php | 194 + system/ThirdParty/Kint/Parser/Parser.php | 655 + .../Kint/Parser/PluginInterface.php | 44 + system/ThirdParty/Kint/Parser/ProxyPlugin.php | 73 + .../Kint/Parser/SerializePlugin.php | 109 + .../Kint/Parser/SimpleXMLElementPlugin.php | 221 + .../Kint/Parser/SplFileInfoPlugin.php | 57 + .../Kint/Parser/SplObjectStoragePlugin.php | 56 + .../ThirdParty/Kint/Parser/StreamPlugin.php | 83 + system/ThirdParty/Kint/Parser/TablePlugin.php | 95 + .../Kint/Parser/ThrowablePlugin.php | 61 + .../Kint/Parser/TimestampPlugin.php | 77 + .../ThirdParty/Kint/Parser/ToStringPlugin.php | 69 + system/ThirdParty/Kint/Parser/TracePlugin.php | 120 + system/ThirdParty/Kint/Parser/XmlPlugin.php | 152 + .../Kint/Renderer/AbstractRenderer.php | 175 + .../ThirdParty/Kint/Renderer/CliRenderer.php | 182 + .../Kint/Renderer/PlainRenderer.php | 237 + .../Kint/Renderer/RendererInterface.php | 57 + .../Kint/Renderer/Rich/AbstractPlugin.php | 104 + .../Kint/Renderer/Rich/ArrayLimitPlugin.php | 38 + .../Kint/Renderer/Rich/BinaryPlugin.php | 62 + .../Kint/Renderer/Rich/BlacklistPlugin.php | 38 + .../Kint/Renderer/Rich/CallablePlugin.php | 130 + .../Kint/Renderer/Rich/ClosurePlugin.php | 64 + .../Kint/Renderer/Rich/ColorPlugin.php | 102 + .../Kint/Renderer/Rich/DepthLimitPlugin.php | 38 + .../Renderer/Rich/MethodDefinitionPlugin.php | 76 + .../Kint/Renderer/Rich/MicrotimePlugin.php | 74 + .../Kint/Renderer/Rich/PluginInterface.php | 35 + .../Kint/Renderer/Rich/RecursionPlugin.php | 38 + .../Renderer/Rich/SimpleXMLElementPlugin.php | 56 + .../Kint/Renderer/Rich/SourcePlugin.php | 83 + .../Kint/Renderer/Rich/TabPluginInterface.php | 35 + .../Kint/Renderer/Rich/TablePlugin.php | 141 + .../Kint/Renderer/Rich/TimestampPlugin.php | 44 + .../Kint/Renderer/Rich/TraceFramePlugin.php | 70 + .../Renderer/Rich/ValuePluginInterface.php | 35 + .../ThirdParty/Kint/Renderer/RichRenderer.php | 677 + .../Kint/Renderer/Text/AbstractPlugin.php | 63 + .../Kint/Renderer/Text/ArrayLimitPlugin.php | 38 + .../Kint/Renderer/Text/BlacklistPlugin.php | 38 + .../Kint/Renderer/Text/DepthLimitPlugin.php | 38 + .../Kint/Renderer/Text/EnumPlugin.php | 38 + .../Kint/Renderer/Text/MicrotimePlugin.php | 130 + .../Kint/Renderer/Text/PluginInterface.php | 38 + .../Kint/Renderer/Text/RecursionPlugin.php | 38 + .../Kint/Renderer/Text/TracePlugin.php | 115 + .../ThirdParty/Kint/Renderer/TextRenderer.php | 391 + system/ThirdParty/Kint/Utils.php | 296 + system/ThirdParty/Kint/Zval/BlobValue.php | 201 + system/ThirdParty/Kint/Zval/ClosureValue.php | 58 + system/ThirdParty/Kint/Zval/DateTimeValue.php | 55 + system/ThirdParty/Kint/Zval/EnumValue.php | 74 + system/ThirdParty/Kint/Zval/InstanceValue.php | 74 + system/ThirdParty/Kint/Zval/MethodValue.php | 228 + .../Kint/Zval/ParameterHoldingTrait.php | 63 + .../ThirdParty/Kint/Zval/ParameterValue.php | 85 + .../Representation/ColorRepresentation.php | 571 + .../MethodDefinitionRepresentation.php | 76 + .../MicrotimeRepresentation.php | 73 + .../Zval/Representation/Representation.php | 73 + .../Representation/SourceRepresentation.php | 72 + .../SplFileInfoRepresentation.php | 196 + system/ThirdParty/Kint/Zval/ResourceValue.php | 51 + .../Kint/Zval/SimpleXMLElementValue.php | 54 + system/ThirdParty/Kint/Zval/StreamValue.php | 56 + .../ThirdParty/Kint/Zval/ThrowableValue.php | 52 + .../ThirdParty/Kint/Zval/TraceFrameValue.php | 107 + system/ThirdParty/Kint/Zval/TraceValue.php | 47 + system/ThirdParty/Kint/Zval/Value.php | 266 + system/ThirdParty/Kint/init.php | 73 + system/ThirdParty/Kint/init_helpers.php | 88 + .../Kint/resources/compiled/aante-dark.css | 1 + .../Kint/resources/compiled/aante-light.css | 1 + .../Kint/resources/compiled/microtime.js | 1 + .../Kint/resources/compiled/original.css | 1 + .../Kint/resources/compiled/plain.css | 1 + .../Kint/resources/compiled/plain.js | 1 + .../Kint/resources/compiled/rich.js | 1 + .../Kint/resources/compiled/shared.js | 1 + .../resources/compiled/solarized-dark.css | 1 + .../Kint/resources/compiled/solarized.css | 1 + system/ThirdParty/PSR/Log/AbstractLogger.php | 15 + .../PSR/Log/InvalidArgumentException.php | 7 + system/ThirdParty/PSR/Log/LICENSE | 19 + system/ThirdParty/PSR/Log/LogLevel.php | 18 + .../PSR/Log/LoggerAwareInterface.php | 18 + .../ThirdParty/PSR/Log/LoggerAwareTrait.php | 26 + system/ThirdParty/PSR/Log/LoggerInterface.php | 125 + system/ThirdParty/PSR/Log/LoggerTrait.php | 142 + system/ThirdParty/PSR/Log/NullLogger.php | 30 + system/Throttle/Throttler.php | 187 + system/Throttle/ThrottlerInterface.php | 46 + system/Traits/ConditionalTrait.php | 63 + system/Traits/PropertiesTrait.php | 80 + system/Typography/Typography.php | 344 + system/Validation/CreditCardRules.php | 283 + system/Validation/DotArrayFilter.php | 114 + .../Exceptions/ValidationException.php | 69 + system/Validation/FileRules.php | 263 + system/Validation/FormatRules.php | 472 + system/Validation/Rules.php | 460 + .../StrictRules/CreditCardRules.php | 55 + system/Validation/StrictRules/FileRules.php | 25 + system/Validation/StrictRules/FormatRules.php | 411 + system/Validation/StrictRules/Rules.php | 431 + system/Validation/Validation.php | 996 + system/Validation/ValidationInterface.php | 165 + system/Validation/Views/list.php | 9 + system/Validation/Views/single.php | 1 + system/View/Cell.php | 313 + system/View/Cells/Cell.php | 155 + system/View/Exceptions/ViewException.php | 75 + system/View/Filters.php | 249 + system/View/Parser.php | 749 + system/View/Plugins.php | 135 + system/View/RendererInterface.php | 76 + system/View/Table.php | 547 + system/View/View.php | 525 + system/View/ViewDecoratorInterface.php | 28 + system/View/ViewDecoratorTrait.php | 39 + system/bootstrap.php | 163 + system/index.html | 11 + system/rewrite.php | 45 + tests/.htaccess | 6 + tests/README.md | 118 + .../2020-02-22-222222_example_migration.php | 37 + .../_support/Database/Seeds/ExampleSeeder.php | 41 + tests/_support/Libraries/ConfigReader.php | 17 + tests/_support/Models/ExampleModel.php | 24 + tests/database/ExampleDatabaseTest.php | 46 + tests/index.html | 11 + tests/session/ExampleSessionTest.php | 18 + tests/unit/HealthTest.php | 50 + writable/.htaccess | 6 + writable/cache/index.html | 11 + writable/index.html | 11 + writable/logs/index.html | 11 + writable/logs/log-2024-08-13.log | 28 + writable/session/index.html | 11 + writable/uploads/index.html | 11 + 904 files changed, 239087 insertions(+) create mode 100644 .env create mode 100644 .idea/.gitignore create mode 100644 .idea/MermsProviders2025.iml create mode 100644 .idea/modules.xml create mode 100644 .idea/php.xml create mode 100644 .idea/phpunit.xml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 apache_log/error.log create mode 100644 apache_log/other_vhosts_access.log create mode 100644 app/.htaccess create mode 100644 app/Common.php create mode 100644 app/Config/App.php create mode 100644 app/Config/Autoload.php create mode 100644 app/Config/Boot/development.php create mode 100644 app/Config/Boot/production.php create mode 100644 app/Config/Boot/testing.php create mode 100644 app/Config/CURLRequest.php create mode 100644 app/Config/Cache.php create mode 100644 app/Config/Constants.php create mode 100644 app/Config/ContentSecurityPolicy.php create mode 100644 app/Config/Cookie.php create mode 100644 app/Config/Cors.php create mode 100644 app/Config/Database.php create mode 100644 app/Config/DocTypes.php create mode 100644 app/Config/Email.php create mode 100644 app/Config/Encryption.php create mode 100644 app/Config/Events.php create mode 100644 app/Config/Exceptions.php create mode 100644 app/Config/Feature.php create mode 100644 app/Config/Filters.php create mode 100644 app/Config/ForeignCharacters.php create mode 100644 app/Config/Format.php create mode 100644 app/Config/Generators.php create mode 100644 app/Config/Honeypot.php create mode 100644 app/Config/Images.php create mode 100644 app/Config/Kint.php create mode 100644 app/Config/Logger.php create mode 100644 app/Config/Migrations.php create mode 100644 app/Config/Mimes.php create mode 100644 app/Config/Modules.php create mode 100644 app/Config/Optimize.php create mode 100644 app/Config/Pager.php create mode 100644 app/Config/Paths.php create mode 100644 app/Config/Publisher.php create mode 100644 app/Config/Routes.php create mode 100644 app/Config/Routing.php create mode 100644 app/Config/Security.php create mode 100644 app/Config/Services.php create mode 100644 app/Config/Session.php create mode 100644 app/Config/Toolbar.php create mode 100644 app/Config/UserAgents.php create mode 100644 app/Config/Validation.php create mode 100644 app/Config/View.php create mode 100644 app/Controllers/BaseController.php create mode 100644 app/Controllers/CoreController.php create mode 100644 app/Controllers/Home.php create mode 100644 app/Controllers/Login.php create mode 100644 app/Controllers/Logout.php create mode 100644 app/Controllers/Provider.php create mode 100644 app/Controllers/SecureBaseController.php create mode 100644 app/Database/Migrations/.gitkeep create mode 100644 app/Database/Seeds/.gitkeep create mode 100644 app/Filters/.gitkeep create mode 100644 app/Helpers/.gitkeep create mode 100644 app/Language/.gitkeep create mode 100644 app/Language/en/Validation.php create mode 100644 app/Libraries/.gitkeep create mode 100644 app/Models/.gitkeep create mode 100644 app/Models/Backend_model.php create mode 100644 app/Models/Chart_model.php create mode 100644 app/Models/Dash_model.php create mode 100644 app/Models/Encounter_model.php create mode 100644 app/Models/Patient_model.php create mode 100644 app/Models/Reminder_model.php create mode 100644 app/Models/index.html create mode 100644 app/ThirdParty/.gitkeep create mode 100644 app/Views/appstart/appstart.php create mode 100644 app/Views/errors/cli/error_404.php create mode 100644 app/Views/errors/cli/error_db.php create mode 100644 app/Views/errors/cli/error_exception.php create mode 100644 app/Views/errors/cli/error_general.php create mode 100644 app/Views/errors/cli/error_php.php create mode 100644 app/Views/errors/cli/index.html create mode 100644 app/Views/errors/cli/production.php create mode 100644 app/Views/errors/html/debug.css create mode 100644 app/Views/errors/html/debug.js create mode 100644 app/Views/errors/html/error_404.php create mode 100644 app/Views/errors/html/error_db.php create mode 100644 app/Views/errors/html/error_exception.php create mode 100644 app/Views/errors/html/error_general.php create mode 100644 app/Views/errors/html/error_php.php create mode 100644 app/Views/errors/html/index.html create mode 100644 app/Views/errors/html/production.php create mode 100644 app/Views/errors/index.html create mode 100644 app/Views/index.html create mode 100644 app/Views/provider/calendar.php create mode 100644 app/Views/provider/components/encounter_listing.php create mode 100644 app/Views/provider/components/large_patient_listing.php create mode 100644 app/Views/provider/components/patient_listing.php create mode 100644 app/Views/provider/dash.php create mode 100644 app/Views/provider/patient/chart/chart.php create mode 100644 app/Views/provider/patient/findpatient.php create mode 100644 app/Views/provider/patient/newpatient.php create mode 100644 app/Views/provider/patient/patientActions/view_chart_action.php create mode 100644 app/Views/provider/patient/patientActions/view_reminder_action.php create mode 100644 app/Views/provider/patient/patientActions/view_tracking_action.php create mode 100644 app/Views/provider/patient/patientreport.php create mode 100644 app/Views/provider/patient/thispatient.php create mode 100644 app/Views/provider/patient/thispatientChart.php create mode 100644 app/Views/provider/profile/inbox.php create mode 100644 app/Views/provider/profile/profile.php create mode 100644 app/Views/provider/profile/settings.php create mode 100644 app/Views/provider/reports/report.php create mode 100644 app/Views/register.php create mode 100644 app/Views/resetpass.php create mode 100644 app/Views/template/footer.php create mode 100644 app/Views/template/header.php create mode 100644 app/Views/template/menu/sidemain.php create mode 100644 app/Views/template/provider_footer.php create mode 100644 app/Views/template/provider_header.php create mode 100644 app/Views/template/topstrip.php create mode 100644 app/Views/template/topstrip2.php create mode 100644 app/Views/welcome_message.php create mode 100644 app/index.html create mode 100644 composer.json create mode 100644 docker-compose.yml create mode 100644 docker/apache/000-default.conf create mode 100644 docker/apache/Dockerfile create mode 100644 env create mode 100644 phpunit.xml.dist create mode 100644 preload.php create mode 100644 public/.htaccess create mode 100644 public/assets/app/css/bootstrap-grid.css create mode 100644 public/assets/app/css/bootstrap-grid.min.css create mode 100644 public/assets/app/css/bootstrap-reboot.css create mode 100644 public/assets/app/css/bootstrap-reboot.min.css create mode 100644 public/assets/app/css/bootstrap.css create mode 100644 public/assets/app/css/bootstrap.min.css create mode 100644 public/assets/app/js/bootstrap.bundle.js create mode 100644 public/assets/app/js/bootstrap.bundle.min.js create mode 100644 public/assets/app/js/bootstrap.js create mode 100644 public/assets/app/js/bootstrap.min.js create mode 100644 public/assets/css/main.css create mode 100644 public/assets/css/style.css create mode 100644 public/assets/css/vendors.css create mode 100644 public/assets/fonts/cryptocurrency-icons.eot create mode 100644 public/assets/fonts/cryptocurrency-icons.svg create mode 100644 public/assets/fonts/cryptocurrency-icons.ttf create mode 100644 public/assets/fonts/cryptocurrency-icons.woff create mode 100644 public/assets/fonts/cryptocurrency-icons.woff2 create mode 100644 public/assets/fonts/dashicons.eot create mode 100644 public/assets/fonts/dashicons.svg create mode 100644 public/assets/fonts/dashicons.ttf create mode 100644 public/assets/fonts/dashicons.woff create mode 100644 public/assets/fonts/dashicons.woff2 create mode 100644 public/assets/fonts/dripicons.eot create mode 100644 public/assets/fonts/dripicons.svg create mode 100644 public/assets/fonts/dripicons.ttf create mode 100644 public/assets/fonts/dripicons.woff create mode 100644 public/assets/fonts/feather.eot create mode 100644 public/assets/fonts/feather.svg create mode 100644 public/assets/fonts/feather.ttf create mode 100644 public/assets/fonts/feather.woff create mode 100644 public/assets/fonts/font-awesome.eot create mode 100644 public/assets/fonts/font-awesome.svg create mode 100644 public/assets/fonts/font-awesome.ttf create mode 100644 public/assets/fonts/font-awesome.woff create mode 100644 public/assets/fonts/font-awesome.woff2 create mode 100644 public/assets/fonts/ionicons.eot create mode 100644 public/assets/fonts/ionicons.svg create mode 100644 public/assets/fonts/ionicons.ttf create mode 100644 public/assets/fonts/ionicons.woff create mode 100644 public/assets/fonts/ionicons.woff2 create mode 100644 public/assets/fonts/linea-weather.eot create mode 100644 public/assets/fonts/linea-weather.svg create mode 100644 public/assets/fonts/linea-weather.ttf create mode 100644 public/assets/fonts/linea-weather.woff create mode 100644 public/assets/fonts/material-icons.eot create mode 100644 public/assets/fonts/material-icons.svg create mode 100644 public/assets/fonts/material-icons.ttf create mode 100644 public/assets/fonts/material-icons.woff create mode 100644 public/assets/fonts/material-icons.woff2 create mode 100644 public/assets/fonts/summernote.eot create mode 100644 public/assets/fonts/summernote.ttf create mode 100644 public/assets/fonts/summernote.woff create mode 100644 public/assets/fonts/themify-icons.eot create mode 100644 public/assets/fonts/themify-icons.svg create mode 100644 public/assets/fonts/themify-icons.ttf create mode 100644 public/assets/fonts/themify-icons.woff create mode 100644 public/assets/img/app-store-icons/apple-dark.svg create mode 100644 public/assets/img/app-store-icons/apple-white.svg create mode 100644 public/assets/img/app-store-icons/apple.svg create mode 100644 public/assets/img/app-store-icons/google-dark.svg create mode 100644 public/assets/img/app-store-icons/google-white.svg create mode 100644 public/assets/img/app-store-icons/google.svg create mode 100644 public/assets/img/avtar/01.jpg create mode 100644 public/assets/img/avtar/02.jpg create mode 100644 public/assets/img/avtar/03.jpg create mode 100644 public/assets/img/avtar/04.jpg create mode 100644 public/assets/img/avtar/05.jpg create mode 100644 public/assets/img/avtar/06.jpg create mode 100644 public/assets/img/avtar/07.jpg create mode 100644 public/assets/img/avtar/08.jpg create mode 100644 public/assets/img/avtar/09.jpg create mode 100644 public/assets/img/avtar/10.jpg create mode 100644 public/assets/img/bg/coming-soon-bg.svg create mode 100644 public/assets/img/bg/login.svg create mode 100644 public/assets/img/bg/provider-0.jpg create mode 100644 public/assets/img/bg/provider-1.0.jpg create mode 100644 public/assets/img/bg/provider-1.jpg create mode 100644 public/assets/img/bg/provider-1a.jpg create mode 100644 public/assets/img/bg/provider.jpg create mode 100644 public/assets/img/bg/provider2.jpg create mode 100644 public/assets/img/bg/provider3.jpg create mode 100644 public/assets/img/bg/provider4.jpg create mode 100644 public/assets/img/browser-logo/bootstrap.png create mode 100644 public/assets/img/browser-logo/envato.png create mode 100644 public/assets/img/browser-logo/google.png create mode 100644 public/assets/img/browser-logo/invision.png create mode 100644 public/assets/img/browser-logo/slack.png create mode 100644 public/assets/img/export/csv.svg create mode 100644 public/assets/img/export/icsv.png create mode 100644 public/assets/img/export/itxt.png create mode 100644 public/assets/img/export/ixls.png create mode 100644 public/assets/img/export/ixlsx.png create mode 100644 public/assets/img/export/txt.svg create mode 100644 public/assets/img/export/xls.svg create mode 100644 public/assets/img/export/xlsx.svg create mode 100644 public/assets/img/favicon.ico create mode 100644 public/assets/img/file-icon/ai.png create mode 100644 public/assets/img/file-icon/css.png create mode 100644 public/assets/img/file-icon/dbf.png create mode 100644 public/assets/img/file-icon/doc.png create mode 100644 public/assets/img/file-icon/dwg.png create mode 100644 public/assets/img/file-icon/exe.png create mode 100644 public/assets/img/file-icon/html.png create mode 100644 public/assets/img/file-icon/jpg.png create mode 100644 public/assets/img/file-icon/pdf.png create mode 100644 public/assets/img/file-icon/png.png create mode 100644 public/assets/img/file-icon/psd.png create mode 100644 public/assets/img/file-icon/rtf.png create mode 100644 public/assets/img/file-icon/svg.png create mode 100644 public/assets/img/file-icon/xls.png create mode 100644 public/assets/img/file-icon/xml.png create mode 100644 public/assets/img/file-icon/zip.png create mode 100644 public/assets/img/flags/au.png create mode 100644 public/assets/img/flags/bd.png create mode 100644 public/assets/img/flags/ca.png create mode 100644 public/assets/img/flags/cn.png create mode 100644 public/assets/img/flags/es.png create mode 100644 public/assets/img/flags/gb.png create mode 100644 public/assets/img/flags/in.png create mode 100644 public/assets/img/flags/iq.png create mode 100644 public/assets/img/flags/ir.png create mode 100644 public/assets/img/flags/jp.png create mode 100644 public/assets/img/flags/lk.png create mode 100644 public/assets/img/flags/nz.png create mode 100644 public/assets/img/flags/pk.png create mode 100644 public/assets/img/flags/ru.png create mode 100644 public/assets/img/flags/us.png create mode 100644 public/assets/img/flags/za.png create mode 100644 public/assets/img/loader/loader.svg create mode 100644 public/assets/img/logo-icon.png create mode 100644 public/assets/img/logo-light.png create mode 100644 public/assets/img/logo.png create mode 100644 public/assets/img/product.jpg create mode 100644 public/assets/img/real-estate/01.jpg create mode 100644 public/assets/img/real-estate/02.jpg create mode 100644 public/assets/img/real-estate/03.jpg create mode 100644 public/assets/img/real-estate/04.jpg create mode 100644 public/assets/img/real-estate/05.jpg create mode 100644 public/assets/img/real-estate/06.jpg create mode 100644 public/assets/img/slider/slide-1.jpg create mode 100644 public/assets/img/slider/slide-2.jpg create mode 100644 public/assets/img/slider/slide-3.jpg create mode 100644 public/assets/img/slider/slide-4.jpg create mode 100644 public/assets/img/widget/01.jpg create mode 100644 public/assets/img/widget/02.jpg create mode 100644 public/assets/img/widget/03.jpg create mode 100644 public/assets/img/widget/04.jpg create mode 100644 public/assets/img/widget/05.jpg create mode 100644 public/assets/img/widget/06.jpg create mode 100644 public/assets/img/widget/07.jpg create mode 100644 public/assets/img/widget/08.jpg create mode 100644 public/assets/img/widget/09.jpg create mode 100644 public/assets/img/widget/10.jpg create mode 100644 public/assets/js/app-min.js create mode 100644 public/assets/js/app.js create mode 100644 public/assets/js/vendors-min.js create mode 100644 public/assets/js/vendors.js create mode 100644 public/favicon.ico create mode 100644 public/index.php create mode 100644 public/robots.txt create mode 100644 spark create mode 100644 system/.htaccess create mode 100644 system/API/ResponseTrait.php create mode 100644 system/Autoloader/Autoloader.php create mode 100644 system/Autoloader/FileLocator.php create mode 100644 system/Autoloader/FileLocatorCached.php create mode 100644 system/Autoloader/FileLocatorInterface.php create mode 100644 system/BaseModel.php create mode 100644 system/Boot.php create mode 100644 system/CLI/BaseCommand.php create mode 100644 system/CLI/CLI.php create mode 100644 system/CLI/Commands.php create mode 100644 system/CLI/Console.php create mode 100644 system/CLI/Exceptions/CLIException.php create mode 100644 system/CLI/GeneratorTrait.php create mode 100644 system/CLI/InputOutput.php create mode 100644 system/Cache/CacheFactory.php create mode 100644 system/Cache/CacheInterface.php create mode 100644 system/Cache/Exceptions/CacheException.php create mode 100644 system/Cache/FactoriesCache.php create mode 100644 system/Cache/FactoriesCache/FileVarExportHandler.php create mode 100644 system/Cache/Handlers/BaseHandler.php create mode 100644 system/Cache/Handlers/DummyHandler.php create mode 100644 system/Cache/Handlers/FileHandler.php create mode 100644 system/Cache/Handlers/MemcachedHandler.php create mode 100644 system/Cache/Handlers/PredisHandler.php create mode 100644 system/Cache/Handlers/RedisHandler.php create mode 100644 system/Cache/Handlers/WincacheHandler.php create mode 100644 system/Cache/ResponseCache.php create mode 100644 system/CodeIgniter.php create mode 100644 system/Commands/Cache/ClearCache.php create mode 100644 system/Commands/Cache/InfoCache.php create mode 100644 system/Commands/Database/CreateDatabase.php create mode 100644 system/Commands/Database/Migrate.php create mode 100644 system/Commands/Database/MigrateRefresh.php create mode 100644 system/Commands/Database/MigrateRollback.php create mode 100644 system/Commands/Database/MigrateStatus.php create mode 100644 system/Commands/Database/Seed.php create mode 100644 system/Commands/Database/ShowTableInfo.php create mode 100644 system/Commands/Encryption/GenerateKey.php create mode 100644 system/Commands/Generators/CellGenerator.php create mode 100644 system/Commands/Generators/CommandGenerator.php create mode 100644 system/Commands/Generators/ConfigGenerator.php create mode 100644 system/Commands/Generators/ControllerGenerator.php create mode 100644 system/Commands/Generators/EntityGenerator.php create mode 100644 system/Commands/Generators/FilterGenerator.php create mode 100644 system/Commands/Generators/MigrationGenerator.php create mode 100644 system/Commands/Generators/ModelGenerator.php create mode 100644 system/Commands/Generators/ScaffoldGenerator.php create mode 100644 system/Commands/Generators/SeederGenerator.php create mode 100644 system/Commands/Generators/TestGenerator.php create mode 100644 system/Commands/Generators/ValidationGenerator.php create mode 100644 system/Commands/Generators/Views/cell.tpl.php create mode 100644 system/Commands/Generators/Views/cell_view.tpl.php create mode 100644 system/Commands/Generators/Views/command.tpl.php create mode 100644 system/Commands/Generators/Views/config.tpl.php create mode 100644 system/Commands/Generators/Views/controller.tpl.php create mode 100644 system/Commands/Generators/Views/entity.tpl.php create mode 100644 system/Commands/Generators/Views/filter.tpl.php create mode 100644 system/Commands/Generators/Views/migration.tpl.php create mode 100644 system/Commands/Generators/Views/model.tpl.php create mode 100644 system/Commands/Generators/Views/seeder.tpl.php create mode 100644 system/Commands/Generators/Views/test.tpl.php create mode 100644 system/Commands/Generators/Views/validation.tpl.php create mode 100644 system/Commands/Help.php create mode 100644 system/Commands/Housekeeping/ClearDebugbar.php create mode 100644 system/Commands/Housekeeping/ClearLogs.php create mode 100644 system/Commands/ListCommands.php create mode 100644 system/Commands/Server/Serve.php create mode 100644 system/Commands/Translation/LocalizationFinder.php create mode 100644 system/Commands/Utilities/ConfigCheck.php create mode 100644 system/Commands/Utilities/Environment.php create mode 100644 system/Commands/Utilities/FilterCheck.php create mode 100644 system/Commands/Utilities/Namespaces.php create mode 100644 system/Commands/Utilities/Optimize.php create mode 100644 system/Commands/Utilities/PhpIniCheck.php create mode 100644 system/Commands/Utilities/Publish.php create mode 100644 system/Commands/Utilities/Routes.php create mode 100644 system/Commands/Utilities/Routes/AutoRouteCollector.php create mode 100644 system/Commands/Utilities/Routes/AutoRouterImproved/AutoRouteCollector.php create mode 100644 system/Commands/Utilities/Routes/AutoRouterImproved/ControllerMethodReader.php create mode 100644 system/Commands/Utilities/Routes/ControllerFinder.php create mode 100644 system/Commands/Utilities/Routes/ControllerMethodReader.php create mode 100644 system/Commands/Utilities/Routes/FilterCollector.php create mode 100644 system/Commands/Utilities/Routes/FilterFinder.php create mode 100644 system/Commands/Utilities/Routes/SampleURIGenerator.php create mode 100644 system/Common.php create mode 100644 system/ComposerScripts.php create mode 100644 system/Config/AutoloadConfig.php create mode 100644 system/Config/BaseConfig.php create mode 100644 system/Config/BaseService.php create mode 100644 system/Config/DotEnv.php create mode 100644 system/Config/Factories.php create mode 100644 system/Config/Factory.php create mode 100644 system/Config/Filters.php create mode 100644 system/Config/ForeignCharacters.php create mode 100644 system/Config/Publisher.php create mode 100644 system/Config/Routing.php create mode 100644 system/Config/Services.php create mode 100644 system/Config/View.php create mode 100644 system/Controller.php create mode 100644 system/Cookie/CloneableCookieInterface.php create mode 100644 system/Cookie/Cookie.php create mode 100644 system/Cookie/CookieInterface.php create mode 100644 system/Cookie/CookieStore.php create mode 100644 system/Cookie/Exceptions/CookieException.php create mode 100644 system/DataCaster/Cast/ArrayCast.php create mode 100644 system/DataCaster/Cast/BaseCast.php create mode 100644 system/DataCaster/Cast/BooleanCast.php create mode 100644 system/DataCaster/Cast/CSVCast.php create mode 100644 system/DataCaster/Cast/CastInterface.php create mode 100644 system/DataCaster/Cast/DatetimeCast.php create mode 100644 system/DataCaster/Cast/FloatCast.php create mode 100644 system/DataCaster/Cast/IntBoolCast.php create mode 100644 system/DataCaster/Cast/IntegerCast.php create mode 100644 system/DataCaster/Cast/JsonCast.php create mode 100644 system/DataCaster/Cast/TimestampCast.php create mode 100644 system/DataCaster/Cast/URICast.php create mode 100644 system/DataCaster/DataCaster.php create mode 100644 system/DataCaster/Exceptions/CastException.php create mode 100644 system/DataConverter/DataConverter.php create mode 100644 system/Database/BaseBuilder.php create mode 100644 system/Database/BaseConnection.php create mode 100644 system/Database/BasePreparedQuery.php create mode 100644 system/Database/BaseResult.php create mode 100644 system/Database/BaseUtils.php create mode 100644 system/Database/Config.php create mode 100644 system/Database/ConnectionInterface.php create mode 100644 system/Database/Database.php create mode 100644 system/Database/Exceptions/DataException.php create mode 100644 system/Database/Exceptions/DatabaseException.php create mode 100644 system/Database/Exceptions/ExceptionInterface.php create mode 100644 system/Database/Forge.php create mode 100644 system/Database/Migration.php create mode 100644 system/Database/MigrationRunner.php create mode 100644 system/Database/MySQLi/Builder.php create mode 100644 system/Database/MySQLi/Connection.php create mode 100644 system/Database/MySQLi/Forge.php create mode 100644 system/Database/MySQLi/PreparedQuery.php create mode 100644 system/Database/MySQLi/Result.php create mode 100644 system/Database/MySQLi/Utils.php create mode 100644 system/Database/OCI8/Builder.php create mode 100644 system/Database/OCI8/Connection.php create mode 100644 system/Database/OCI8/Forge.php create mode 100644 system/Database/OCI8/PreparedQuery.php create mode 100644 system/Database/OCI8/Result.php create mode 100644 system/Database/OCI8/Utils.php create mode 100644 system/Database/Postgre/Builder.php create mode 100644 system/Database/Postgre/Connection.php create mode 100644 system/Database/Postgre/Forge.php create mode 100644 system/Database/Postgre/PreparedQuery.php create mode 100644 system/Database/Postgre/Result.php create mode 100644 system/Database/Postgre/Utils.php create mode 100644 system/Database/PreparedQueryInterface.php create mode 100644 system/Database/Query.php create mode 100644 system/Database/QueryInterface.php create mode 100644 system/Database/RawSql.php create mode 100644 system/Database/ResultInterface.php create mode 100644 system/Database/SQLSRV/Builder.php create mode 100644 system/Database/SQLSRV/Connection.php create mode 100644 system/Database/SQLSRV/Forge.php create mode 100644 system/Database/SQLSRV/PreparedQuery.php create mode 100644 system/Database/SQLSRV/Result.php create mode 100644 system/Database/SQLSRV/Utils.php create mode 100644 system/Database/SQLite3/Builder.php create mode 100644 system/Database/SQLite3/Connection.php create mode 100644 system/Database/SQLite3/Forge.php create mode 100644 system/Database/SQLite3/PreparedQuery.php create mode 100644 system/Database/SQLite3/Result.php create mode 100644 system/Database/SQLite3/Table.php create mode 100644 system/Database/SQLite3/Utils.php create mode 100644 system/Database/Seeder.php create mode 100644 system/Debug/BaseExceptionHandler.php create mode 100644 system/Debug/ExceptionHandler.php create mode 100644 system/Debug/ExceptionHandlerInterface.php create mode 100644 system/Debug/Exceptions.php create mode 100644 system/Debug/Iterator.php create mode 100644 system/Debug/Timer.php create mode 100644 system/Debug/Toolbar.php create mode 100644 system/Debug/Toolbar/Collectors/BaseCollector.php create mode 100644 system/Debug/Toolbar/Collectors/Config.php create mode 100644 system/Debug/Toolbar/Collectors/Database.php create mode 100644 system/Debug/Toolbar/Collectors/Events.php create mode 100644 system/Debug/Toolbar/Collectors/Files.php create mode 100644 system/Debug/Toolbar/Collectors/History.php create mode 100644 system/Debug/Toolbar/Collectors/Logs.php create mode 100644 system/Debug/Toolbar/Collectors/Routes.php create mode 100644 system/Debug/Toolbar/Collectors/Timers.php create mode 100644 system/Debug/Toolbar/Collectors/Views.php create mode 100644 system/Debug/Toolbar/Views/_config.tpl create mode 100644 system/Debug/Toolbar/Views/_database.tpl create mode 100644 system/Debug/Toolbar/Views/_events.tpl create mode 100644 system/Debug/Toolbar/Views/_files.tpl create mode 100644 system/Debug/Toolbar/Views/_history.tpl create mode 100644 system/Debug/Toolbar/Views/_logs.tpl create mode 100644 system/Debug/Toolbar/Views/_routes.tpl create mode 100644 system/Debug/Toolbar/Views/toolbar.css create mode 100644 system/Debug/Toolbar/Views/toolbar.js create mode 100644 system/Debug/Toolbar/Views/toolbar.tpl.php create mode 100644 system/Debug/Toolbar/Views/toolbarloader.js create mode 100644 system/Email/Email.php create mode 100644 system/Encryption/EncrypterInterface.php create mode 100644 system/Encryption/Encryption.php create mode 100644 system/Encryption/Exceptions/EncryptionException.php create mode 100644 system/Encryption/Handlers/BaseHandler.php create mode 100644 system/Encryption/Handlers/OpenSSLHandler.php create mode 100644 system/Encryption/Handlers/SodiumHandler.php create mode 100644 system/Entity/Cast/ArrayCast.php create mode 100644 system/Entity/Cast/BaseCast.php create mode 100644 system/Entity/Cast/BooleanCast.php create mode 100644 system/Entity/Cast/CSVCast.php create mode 100644 system/Entity/Cast/CastInterface.php create mode 100644 system/Entity/Cast/DatetimeCast.php create mode 100644 system/Entity/Cast/FloatCast.php create mode 100644 system/Entity/Cast/IntBoolCast.php create mode 100644 system/Entity/Cast/IntegerCast.php create mode 100644 system/Entity/Cast/JsonCast.php create mode 100644 system/Entity/Cast/ObjectCast.php create mode 100644 system/Entity/Cast/StringCast.php create mode 100644 system/Entity/Cast/TimestampCast.php create mode 100644 system/Entity/Cast/URICast.php create mode 100644 system/Entity/Entity.php create mode 100644 system/Entity/Exceptions/CastException.php create mode 100644 system/Events/Events.php create mode 100644 system/Exceptions/ConfigException.php create mode 100644 system/Exceptions/CriticalError.php create mode 100644 system/Exceptions/DebugTraceableTrait.php create mode 100644 system/Exceptions/DownloadException.php create mode 100644 system/Exceptions/ExceptionInterface.php create mode 100644 system/Exceptions/FrameworkException.php create mode 100644 system/Exceptions/HTTPExceptionInterface.php create mode 100644 system/Exceptions/HasExitCodeInterface.php create mode 100644 system/Exceptions/ModelException.php create mode 100644 system/Exceptions/PageNotFoundException.php create mode 100644 system/Exceptions/TestException.php create mode 100644 system/Files/Exceptions/FileException.php create mode 100644 system/Files/Exceptions/FileNotFoundException.php create mode 100644 system/Files/File.php create mode 100644 system/Files/FileCollection.php create mode 100644 system/Filters/CSRF.php create mode 100644 system/Filters/Cors.php create mode 100644 system/Filters/DebugToolbar.php create mode 100644 system/Filters/Exceptions/FilterException.php create mode 100644 system/Filters/FilterInterface.php create mode 100644 system/Filters/Filters.php create mode 100644 system/Filters/ForceHTTPS.php create mode 100644 system/Filters/Honeypot.php create mode 100644 system/Filters/InvalidChars.php create mode 100644 system/Filters/PageCache.php create mode 100644 system/Filters/PerformanceMetrics.php create mode 100644 system/Filters/SecureHeaders.php create mode 100644 system/Format/Exceptions/FormatException.php create mode 100644 system/Format/Format.php create mode 100644 system/Format/FormatterInterface.php create mode 100644 system/Format/JSONFormatter.php create mode 100644 system/Format/XMLFormatter.php create mode 100644 system/HTTP/CLIRequest.php create mode 100644 system/HTTP/CURLRequest.php create mode 100644 system/HTTP/ContentSecurityPolicy.php create mode 100644 system/HTTP/Cors.php create mode 100644 system/HTTP/DownloadResponse.php create mode 100644 system/HTTP/Exceptions/BadRequestException.php create mode 100644 system/HTTP/Exceptions/HTTPException.php create mode 100644 system/HTTP/Exceptions/RedirectException.php create mode 100644 system/HTTP/Files/FileCollection.php create mode 100644 system/HTTP/Files/UploadedFile.php create mode 100644 system/HTTP/Files/UploadedFileInterface.php create mode 100644 system/HTTP/Header.php create mode 100644 system/HTTP/IncomingRequest.php create mode 100644 system/HTTP/Message.php create mode 100644 system/HTTP/MessageInterface.php create mode 100644 system/HTTP/MessageTrait.php create mode 100644 system/HTTP/Method.php create mode 100644 system/HTTP/Negotiate.php create mode 100644 system/HTTP/OutgoingRequest.php create mode 100644 system/HTTP/OutgoingRequestInterface.php create mode 100644 system/HTTP/RedirectResponse.php create mode 100644 system/HTTP/Request.php create mode 100644 system/HTTP/RequestInterface.php create mode 100644 system/HTTP/RequestTrait.php create mode 100644 system/HTTP/ResponsableInterface.php create mode 100644 system/HTTP/Response.php create mode 100644 system/HTTP/ResponseInterface.php create mode 100644 system/HTTP/ResponseTrait.php create mode 100644 system/HTTP/SiteURI.php create mode 100644 system/HTTP/SiteURIFactory.php create mode 100644 system/HTTP/URI.php create mode 100644 system/HTTP/UserAgent.php create mode 100644 system/Helpers/Array/ArrayHelper.php create mode 100644 system/Helpers/array_helper.php create mode 100644 system/Helpers/cookie_helper.php create mode 100644 system/Helpers/date_helper.php create mode 100644 system/Helpers/filesystem_helper.php create mode 100644 system/Helpers/form_helper.php create mode 100644 system/Helpers/html_helper.php create mode 100644 system/Helpers/inflector_helper.php create mode 100644 system/Helpers/kint_helper.php create mode 100644 system/Helpers/number_helper.php create mode 100644 system/Helpers/security_helper.php create mode 100644 system/Helpers/test_helper.php create mode 100644 system/Helpers/text_helper.php create mode 100644 system/Helpers/url_helper.php create mode 100644 system/Helpers/xml_helper.php create mode 100644 system/Honeypot/Exceptions/HoneypotException.php create mode 100644 system/Honeypot/Honeypot.php create mode 100644 system/HotReloader/DirectoryHasher.php create mode 100644 system/HotReloader/HotReloader.php create mode 100644 system/HotReloader/IteratorFilter.php create mode 100644 system/I18n/Exceptions/I18nException.php create mode 100644 system/I18n/Time.php create mode 100644 system/I18n/TimeDifference.php create mode 100644 system/I18n/TimeLegacy.php create mode 100644 system/I18n/TimeTrait.php create mode 100644 system/Images/Exceptions/ImageException.php create mode 100644 system/Images/Handlers/BaseHandler.php create mode 100644 system/Images/Handlers/GDHandler.php create mode 100644 system/Images/Handlers/ImageMagickHandler.php create mode 100644 system/Images/Image.php create mode 100644 system/Images/ImageHandlerInterface.php create mode 100644 system/Language/Language.php create mode 100644 system/Language/en/CLI.php create mode 100644 system/Language/en/Cache.php create mode 100644 system/Language/en/Cast.php create mode 100644 system/Language/en/Cookie.php create mode 100644 system/Language/en/Core.php create mode 100644 system/Language/en/Database.php create mode 100644 system/Language/en/Email.php create mode 100644 system/Language/en/Encryption.php create mode 100644 system/Language/en/Errors.php create mode 100644 system/Language/en/Fabricator.php create mode 100644 system/Language/en/Files.php create mode 100644 system/Language/en/Filters.php create mode 100644 system/Language/en/Format.php create mode 100644 system/Language/en/HTTP.php create mode 100644 system/Language/en/Images.php create mode 100644 system/Language/en/Language.php create mode 100644 system/Language/en/Log.php create mode 100644 system/Language/en/Migrations.php create mode 100644 system/Language/en/Number.php create mode 100644 system/Language/en/Pager.php create mode 100644 system/Language/en/Publisher.php create mode 100644 system/Language/en/RESTful.php create mode 100644 system/Language/en/Router.php create mode 100644 system/Language/en/Security.php create mode 100644 system/Language/en/Session.php create mode 100644 system/Language/en/Test.php create mode 100644 system/Language/en/Time.php create mode 100644 system/Language/en/Validation.php create mode 100644 system/Language/en/View.php create mode 100644 system/Log/Exceptions/LogException.php create mode 100644 system/Log/Handlers/BaseHandler.php create mode 100644 system/Log/Handlers/ChromeLoggerHandler.php create mode 100644 system/Log/Handlers/ErrorlogHandler.php create mode 100644 system/Log/Handlers/FileHandler.php create mode 100644 system/Log/Handlers/HandlerInterface.php create mode 100644 system/Log/Logger.php create mode 100644 system/Model.php create mode 100644 system/Modules/Modules.php create mode 100644 system/Pager/Exceptions/PagerException.php create mode 100644 system/Pager/Pager.php create mode 100644 system/Pager/PagerInterface.php create mode 100644 system/Pager/PagerRenderer.php create mode 100644 system/Pager/Views/default_full.php create mode 100644 system/Pager/Views/default_head.php create mode 100644 system/Pager/Views/default_simple.php create mode 100644 system/Publisher/ContentReplacer.php create mode 100644 system/Publisher/Exceptions/PublisherException.php create mode 100644 system/Publisher/Publisher.php create mode 100644 system/RESTful/BaseResource.php create mode 100644 system/RESTful/ResourceController.php create mode 100644 system/RESTful/ResourcePresenter.php create mode 100644 system/Router/AutoRouter.php create mode 100644 system/Router/AutoRouterImproved.php create mode 100644 system/Router/AutoRouterInterface.php create mode 100644 system/Router/DefinedRouteCollector.php create mode 100644 system/Router/Exceptions/MethodNotFoundException.php create mode 100644 system/Router/Exceptions/RedirectException.php create mode 100644 system/Router/Exceptions/RouterException.php create mode 100644 system/Router/RouteCollection.php create mode 100644 system/Router/RouteCollectionInterface.php create mode 100644 system/Router/Router.php create mode 100644 system/Router/RouterInterface.php create mode 100644 system/Security/CheckPhpIni.php create mode 100644 system/Security/Exceptions/SecurityException.php create mode 100644 system/Security/Security.php create mode 100644 system/Security/SecurityInterface.php create mode 100644 system/Session/Exceptions/SessionException.php create mode 100644 system/Session/Handlers/ArrayHandler.php create mode 100644 system/Session/Handlers/BaseHandler.php create mode 100644 system/Session/Handlers/Database/MySQLiHandler.php create mode 100644 system/Session/Handlers/Database/PostgreHandler.php create mode 100644 system/Session/Handlers/DatabaseHandler.php create mode 100644 system/Session/Handlers/FileHandler.php create mode 100644 system/Session/Handlers/MemcachedHandler.php create mode 100644 system/Session/Handlers/RedisHandler.php create mode 100644 system/Session/Session.php create mode 100644 system/Session/SessionInterface.php create mode 100644 system/Superglobals.php create mode 100644 system/Test/CIUnitTestCase.php create mode 100644 system/Test/ConfigFromArrayTrait.php create mode 100644 system/Test/Constraints/SeeInDatabase.php create mode 100644 system/Test/ControllerTestTrait.php create mode 100644 system/Test/DOMParser.php create mode 100644 system/Test/DatabaseTestTrait.php create mode 100644 system/Test/Fabricator.php create mode 100644 system/Test/FeatureTestTrait.php create mode 100644 system/Test/FilterTestTrait.php create mode 100644 system/Test/Filters/CITestStreamFilter.php create mode 100644 system/Test/IniTestTrait.php create mode 100644 system/Test/Interfaces/FabricatorModel.php create mode 100644 system/Test/Mock/MockAppConfig.php create mode 100644 system/Test/Mock/MockAutoload.php create mode 100644 system/Test/Mock/MockBuilder.php create mode 100644 system/Test/Mock/MockCLIConfig.php create mode 100644 system/Test/Mock/MockCURLRequest.php create mode 100644 system/Test/Mock/MockCache.php create mode 100644 system/Test/Mock/MockCodeIgniter.php create mode 100644 system/Test/Mock/MockCommon.php create mode 100644 system/Test/Mock/MockConnection.php create mode 100644 system/Test/Mock/MockEmail.php create mode 100644 system/Test/Mock/MockEvents.php create mode 100644 system/Test/Mock/MockFileLogger.php create mode 100644 system/Test/Mock/MockIncomingRequest.php create mode 100644 system/Test/Mock/MockInputOutput.php create mode 100644 system/Test/Mock/MockLanguage.php create mode 100644 system/Test/Mock/MockLogger.php create mode 100644 system/Test/Mock/MockQuery.php create mode 100644 system/Test/Mock/MockResourceController.php create mode 100644 system/Test/Mock/MockResourcePresenter.php create mode 100644 system/Test/Mock/MockResponse.php create mode 100644 system/Test/Mock/MockResult.php create mode 100644 system/Test/Mock/MockSecurity.php create mode 100644 system/Test/Mock/MockServices.php create mode 100644 system/Test/Mock/MockSession.php create mode 100644 system/Test/Mock/MockTable.php create mode 100644 system/Test/PhpStreamWrapper.php create mode 100644 system/Test/ReflectionHelper.php create mode 100644 system/Test/StreamFilterTrait.php create mode 100644 system/Test/TestLogger.php create mode 100644 system/Test/TestResponse.php create mode 100644 system/Test/bootstrap.php create mode 100644 system/ThirdParty/Escaper/Escaper.php create mode 100644 system/ThirdParty/Escaper/Exception/ExceptionInterface.php create mode 100644 system/ThirdParty/Escaper/Exception/InvalidArgumentException.php create mode 100644 system/ThirdParty/Escaper/Exception/RuntimeException.php create mode 100644 system/ThirdParty/Escaper/LICENSE.md create mode 100644 system/ThirdParty/Kint/CallFinder.php create mode 100644 system/ThirdParty/Kint/FacadeInterface.php create mode 100644 system/ThirdParty/Kint/Kint.php create mode 100644 system/ThirdParty/Kint/LICENSE create mode 100644 system/ThirdParty/Kint/Parser/AbstractPlugin.php create mode 100644 system/ThirdParty/Kint/Parser/ArrayLimitPlugin.php create mode 100644 system/ThirdParty/Kint/Parser/ArrayObjectPlugin.php create mode 100644 system/ThirdParty/Kint/Parser/Base64Plugin.php create mode 100644 system/ThirdParty/Kint/Parser/BinaryPlugin.php create mode 100644 system/ThirdParty/Kint/Parser/BlacklistPlugin.php create mode 100644 system/ThirdParty/Kint/Parser/ClassMethodsPlugin.php create mode 100644 system/ThirdParty/Kint/Parser/ClassStaticsPlugin.php create mode 100644 system/ThirdParty/Kint/Parser/ClosurePlugin.php create mode 100644 system/ThirdParty/Kint/Parser/ColorPlugin.php create mode 100644 system/ThirdParty/Kint/Parser/ConstructablePluginInterface.php create mode 100644 system/ThirdParty/Kint/Parser/DOMDocumentPlugin.php create mode 100644 system/ThirdParty/Kint/Parser/DateTimePlugin.php create mode 100644 system/ThirdParty/Kint/Parser/EnumPlugin.php create mode 100644 system/ThirdParty/Kint/Parser/FsPathPlugin.php create mode 100644 system/ThirdParty/Kint/Parser/IteratorPlugin.php create mode 100644 system/ThirdParty/Kint/Parser/JsonPlugin.php create mode 100644 system/ThirdParty/Kint/Parser/MicrotimePlugin.php create mode 100644 system/ThirdParty/Kint/Parser/MysqliPlugin.php create mode 100644 system/ThirdParty/Kint/Parser/Parser.php create mode 100644 system/ThirdParty/Kint/Parser/PluginInterface.php create mode 100644 system/ThirdParty/Kint/Parser/ProxyPlugin.php create mode 100644 system/ThirdParty/Kint/Parser/SerializePlugin.php create mode 100644 system/ThirdParty/Kint/Parser/SimpleXMLElementPlugin.php create mode 100644 system/ThirdParty/Kint/Parser/SplFileInfoPlugin.php create mode 100644 system/ThirdParty/Kint/Parser/SplObjectStoragePlugin.php create mode 100644 system/ThirdParty/Kint/Parser/StreamPlugin.php create mode 100644 system/ThirdParty/Kint/Parser/TablePlugin.php create mode 100644 system/ThirdParty/Kint/Parser/ThrowablePlugin.php create mode 100644 system/ThirdParty/Kint/Parser/TimestampPlugin.php create mode 100644 system/ThirdParty/Kint/Parser/ToStringPlugin.php create mode 100644 system/ThirdParty/Kint/Parser/TracePlugin.php create mode 100644 system/ThirdParty/Kint/Parser/XmlPlugin.php create mode 100644 system/ThirdParty/Kint/Renderer/AbstractRenderer.php create mode 100644 system/ThirdParty/Kint/Renderer/CliRenderer.php create mode 100644 system/ThirdParty/Kint/Renderer/PlainRenderer.php create mode 100644 system/ThirdParty/Kint/Renderer/RendererInterface.php create mode 100644 system/ThirdParty/Kint/Renderer/Rich/AbstractPlugin.php create mode 100644 system/ThirdParty/Kint/Renderer/Rich/ArrayLimitPlugin.php create mode 100644 system/ThirdParty/Kint/Renderer/Rich/BinaryPlugin.php create mode 100644 system/ThirdParty/Kint/Renderer/Rich/BlacklistPlugin.php create mode 100644 system/ThirdParty/Kint/Renderer/Rich/CallablePlugin.php create mode 100644 system/ThirdParty/Kint/Renderer/Rich/ClosurePlugin.php create mode 100644 system/ThirdParty/Kint/Renderer/Rich/ColorPlugin.php create mode 100644 system/ThirdParty/Kint/Renderer/Rich/DepthLimitPlugin.php create mode 100644 system/ThirdParty/Kint/Renderer/Rich/MethodDefinitionPlugin.php create mode 100644 system/ThirdParty/Kint/Renderer/Rich/MicrotimePlugin.php create mode 100644 system/ThirdParty/Kint/Renderer/Rich/PluginInterface.php create mode 100644 system/ThirdParty/Kint/Renderer/Rich/RecursionPlugin.php create mode 100644 system/ThirdParty/Kint/Renderer/Rich/SimpleXMLElementPlugin.php create mode 100644 system/ThirdParty/Kint/Renderer/Rich/SourcePlugin.php create mode 100644 system/ThirdParty/Kint/Renderer/Rich/TabPluginInterface.php create mode 100644 system/ThirdParty/Kint/Renderer/Rich/TablePlugin.php create mode 100644 system/ThirdParty/Kint/Renderer/Rich/TimestampPlugin.php create mode 100644 system/ThirdParty/Kint/Renderer/Rich/TraceFramePlugin.php create mode 100644 system/ThirdParty/Kint/Renderer/Rich/ValuePluginInterface.php create mode 100644 system/ThirdParty/Kint/Renderer/RichRenderer.php create mode 100644 system/ThirdParty/Kint/Renderer/Text/AbstractPlugin.php create mode 100644 system/ThirdParty/Kint/Renderer/Text/ArrayLimitPlugin.php create mode 100644 system/ThirdParty/Kint/Renderer/Text/BlacklistPlugin.php create mode 100644 system/ThirdParty/Kint/Renderer/Text/DepthLimitPlugin.php create mode 100644 system/ThirdParty/Kint/Renderer/Text/EnumPlugin.php create mode 100644 system/ThirdParty/Kint/Renderer/Text/MicrotimePlugin.php create mode 100644 system/ThirdParty/Kint/Renderer/Text/PluginInterface.php create mode 100644 system/ThirdParty/Kint/Renderer/Text/RecursionPlugin.php create mode 100644 system/ThirdParty/Kint/Renderer/Text/TracePlugin.php create mode 100644 system/ThirdParty/Kint/Renderer/TextRenderer.php create mode 100644 system/ThirdParty/Kint/Utils.php create mode 100644 system/ThirdParty/Kint/Zval/BlobValue.php create mode 100644 system/ThirdParty/Kint/Zval/ClosureValue.php create mode 100644 system/ThirdParty/Kint/Zval/DateTimeValue.php create mode 100644 system/ThirdParty/Kint/Zval/EnumValue.php create mode 100644 system/ThirdParty/Kint/Zval/InstanceValue.php create mode 100644 system/ThirdParty/Kint/Zval/MethodValue.php create mode 100644 system/ThirdParty/Kint/Zval/ParameterHoldingTrait.php create mode 100644 system/ThirdParty/Kint/Zval/ParameterValue.php create mode 100644 system/ThirdParty/Kint/Zval/Representation/ColorRepresentation.php create mode 100644 system/ThirdParty/Kint/Zval/Representation/MethodDefinitionRepresentation.php create mode 100644 system/ThirdParty/Kint/Zval/Representation/MicrotimeRepresentation.php create mode 100644 system/ThirdParty/Kint/Zval/Representation/Representation.php create mode 100644 system/ThirdParty/Kint/Zval/Representation/SourceRepresentation.php create mode 100644 system/ThirdParty/Kint/Zval/Representation/SplFileInfoRepresentation.php create mode 100644 system/ThirdParty/Kint/Zval/ResourceValue.php create mode 100644 system/ThirdParty/Kint/Zval/SimpleXMLElementValue.php create mode 100644 system/ThirdParty/Kint/Zval/StreamValue.php create mode 100644 system/ThirdParty/Kint/Zval/ThrowableValue.php create mode 100644 system/ThirdParty/Kint/Zval/TraceFrameValue.php create mode 100644 system/ThirdParty/Kint/Zval/TraceValue.php create mode 100644 system/ThirdParty/Kint/Zval/Value.php create mode 100644 system/ThirdParty/Kint/init.php create mode 100644 system/ThirdParty/Kint/init_helpers.php create mode 100644 system/ThirdParty/Kint/resources/compiled/aante-dark.css create mode 100644 system/ThirdParty/Kint/resources/compiled/aante-light.css create mode 100644 system/ThirdParty/Kint/resources/compiled/microtime.js create mode 100644 system/ThirdParty/Kint/resources/compiled/original.css create mode 100644 system/ThirdParty/Kint/resources/compiled/plain.css create mode 100644 system/ThirdParty/Kint/resources/compiled/plain.js create mode 100644 system/ThirdParty/Kint/resources/compiled/rich.js create mode 100644 system/ThirdParty/Kint/resources/compiled/shared.js create mode 100644 system/ThirdParty/Kint/resources/compiled/solarized-dark.css create mode 100644 system/ThirdParty/Kint/resources/compiled/solarized.css create mode 100644 system/ThirdParty/PSR/Log/AbstractLogger.php create mode 100644 system/ThirdParty/PSR/Log/InvalidArgumentException.php create mode 100644 system/ThirdParty/PSR/Log/LICENSE create mode 100644 system/ThirdParty/PSR/Log/LogLevel.php create mode 100644 system/ThirdParty/PSR/Log/LoggerAwareInterface.php create mode 100644 system/ThirdParty/PSR/Log/LoggerAwareTrait.php create mode 100644 system/ThirdParty/PSR/Log/LoggerInterface.php create mode 100644 system/ThirdParty/PSR/Log/LoggerTrait.php create mode 100644 system/ThirdParty/PSR/Log/NullLogger.php create mode 100644 system/Throttle/Throttler.php create mode 100644 system/Throttle/ThrottlerInterface.php create mode 100644 system/Traits/ConditionalTrait.php create mode 100644 system/Traits/PropertiesTrait.php create mode 100644 system/Typography/Typography.php create mode 100644 system/Validation/CreditCardRules.php create mode 100644 system/Validation/DotArrayFilter.php create mode 100644 system/Validation/Exceptions/ValidationException.php create mode 100644 system/Validation/FileRules.php create mode 100644 system/Validation/FormatRules.php create mode 100644 system/Validation/Rules.php create mode 100644 system/Validation/StrictRules/CreditCardRules.php create mode 100644 system/Validation/StrictRules/FileRules.php create mode 100644 system/Validation/StrictRules/FormatRules.php create mode 100644 system/Validation/StrictRules/Rules.php create mode 100644 system/Validation/Validation.php create mode 100644 system/Validation/ValidationInterface.php create mode 100644 system/Validation/Views/list.php create mode 100644 system/Validation/Views/single.php create mode 100644 system/View/Cell.php create mode 100644 system/View/Cells/Cell.php create mode 100644 system/View/Exceptions/ViewException.php create mode 100644 system/View/Filters.php create mode 100644 system/View/Parser.php create mode 100644 system/View/Plugins.php create mode 100644 system/View/RendererInterface.php create mode 100644 system/View/Table.php create mode 100644 system/View/View.php create mode 100644 system/View/ViewDecoratorInterface.php create mode 100644 system/View/ViewDecoratorTrait.php create mode 100644 system/bootstrap.php create mode 100644 system/index.html create mode 100644 system/rewrite.php create mode 100644 tests/.htaccess create mode 100644 tests/README.md create mode 100644 tests/_support/Database/Migrations/2020-02-22-222222_example_migration.php create mode 100644 tests/_support/Database/Seeds/ExampleSeeder.php create mode 100644 tests/_support/Libraries/ConfigReader.php create mode 100644 tests/_support/Models/ExampleModel.php create mode 100644 tests/database/ExampleDatabaseTest.php create mode 100644 tests/index.html create mode 100644 tests/session/ExampleSessionTest.php create mode 100644 tests/unit/HealthTest.php create mode 100644 writable/.htaccess create mode 100644 writable/cache/index.html create mode 100644 writable/index.html create mode 100644 writable/logs/index.html create mode 100644 writable/logs/log-2024-08-13.log create mode 100644 writable/session/index.html create mode 100644 writable/uploads/index.html diff --git a/.env b/.env new file mode 100644 index 0000000..7dae6fc --- /dev/null +++ b/.env @@ -0,0 +1,71 @@ +CONTAINER_PORT=63101 +UID=1000 +#-------------------------------------------------------------------- +# Example Environment Configuration file +# +# This file can be used as a starting point for your own +# custom .env files, and contains most of the possible settings +# available in a default install. +# +# By default, all of the settings are commented out. If you want +# to override the setting, you must un-comment it by removing the '#' +# at the beginning of the line. +#-------------------------------------------------------------------- + +#-------------------------------------------------------------------- +# ENVIRONMENT +#-------------------------------------------------------------------- + +# CI_ENVIRONMENT = production + +#-------------------------------------------------------------------- +# APP +#-------------------------------------------------------------------- + +# app.baseURL = '' +# If you have trouble with `.`, you could also use `_`. +# app_baseURL = '' +# app.forceGlobalSecureRequests = false +# app.CSPEnabled = false + +#-------------------------------------------------------------------- +# DATABASE +#-------------------------------------------------------------------- + +# database.default.hostname = localhost +# database.default.database = ci4 +# database.default.username = root +# database.default.password = root +# database.default.DBDriver = MySQLi +# database.default.DBPrefix = +# database.default.port = 3306 + +# If you use MySQLi as tests, first update the values of Config\Database::$tests. +# database.tests.hostname = localhost +# database.tests.database = ci4_test +# database.tests.username = root +# database.tests.password = root +# database.tests.DBDriver = MySQLi +# database.tests.DBPrefix = +# database.tests.charset = utf8mb4 +# database.tests.DBCollat = utf8mb4_general_ci +# database.tests.port = 3306 + +#-------------------------------------------------------------------- +# ENCRYPTION +#-------------------------------------------------------------------- + +# encryption.key = + +#-------------------------------------------------------------------- +# SESSION +#-------------------------------------------------------------------- + +# session.driver = 'CodeIgniter\Session\Handlers\FileHandler' +# session.savePath = null + +#-------------------------------------------------------------------- +# LOGGER +#-------------------------------------------------------------------- + +# logger.threshold = 4 diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/MermsProviders2025.iml b/.idea/MermsProviders2025.iml new file mode 100644 index 0000000..c956989 --- /dev/null +++ b/.idea/MermsProviders2025.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..136d4ab --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/php.xml b/.idea/php.xml new file mode 100644 index 0000000..7d11e4f --- /dev/null +++ b/.idea/php.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/phpunit.xml b/.idea/phpunit.xml new file mode 100644 index 0000000..4f8104c --- /dev/null +++ b/.idea/phpunit.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..148e7f7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2014-2019 British Columbia Institute of Technology +Copyright (c) 2019-2024 CodeIgniter Foundation + +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/README.md b/README.md new file mode 100644 index 0000000..a23783a --- /dev/null +++ b/README.md @@ -0,0 +1,60 @@ +# CodeIgniter 4 Framework + +## What is CodeIgniter? + +CodeIgniter is a PHP full-stack web framework that is light, fast, flexible and secure. +More information can be found at the [official site](https://codeigniter.com). + +This repository holds the distributable version of the framework. +It has been built from the +[development repository](https://github.com/codeigniter4/CodeIgniter4). + +More information about the plans for version 4 can be found in [CodeIgniter 4](https://forum.codeigniter.com/forumdisplay.php?fid=28) on the forums. + +You can read the [user guide](https://codeigniter.com/user_guide/) +corresponding to the latest version of the framework. + +## Important Change with index.php + +`index.php` is no longer in the root of the project! It has been moved inside the *public* folder, +for better security and separation of components. + +This means that you should configure your web server to "point" to your project's *public* folder, and +not to the project root. A better practice would be to configure a virtual host to point there. A poor practice would be to point your web server to the project root and expect to enter *public/...*, as the rest of your logic and the +framework are exposed. + +**Please** read the user guide for a better explanation of how CI4 works! + +## Repository Management + +We use GitHub issues, in our main repository, to track **BUGS** and to track approved **DEVELOPMENT** work packages. +We use our [forum](http://forum.codeigniter.com) to provide SUPPORT and to discuss +FEATURE REQUESTS. + +This repository is a "distribution" one, built by our release preparation script. +Problems with it can be raised on our forum, or as issues in the main repository. + +## Contributing + +We welcome contributions from the community. + +Please read the [*Contributing to CodeIgniter*](https://github.com/codeigniter4/CodeIgniter4/blob/develop/CONTRIBUTING.md) section in the development repository. + +## Server Requirements + +PHP version 8.1 or higher is required, with the following extensions installed: + +- [intl](http://php.net/manual/en/intl.requirements.php) +- [mbstring](http://php.net/manual/en/mbstring.installation.php) + +> [!WARNING] +> - The end of life date for PHP 7.4 was November 28, 2022. +> - The end of life date for PHP 8.0 was November 26, 2023. +> - If you are still using PHP 7.4 or 8.0, you should upgrade immediately. +> - The end of life date for PHP 8.1 will be December 31, 2025. + +Additionally, make sure that the following extensions are enabled in your PHP: + +- json (enabled by default - don't turn it off) +- [mysqlnd](http://php.net/manual/en/mysqlnd.install.php) if you plan to use MySQL +- [libcurl](http://php.net/manual/en/curl.requirements.php) if you plan to use the HTTP\CURLRequest library diff --git a/apache_log/error.log b/apache_log/error.log new file mode 100644 index 0000000..c19e165 --- /dev/null +++ b/apache_log/error.log @@ -0,0 +1,4 @@ +[Tue Aug 13 15:21:53.990280 2024] [mpm_prefork:notice] [pid 1:tid 1] AH00163: Apache/2.4.61 (Debian) PHP/8.1.29 configured -- resuming normal operations +[Tue Aug 13 15:21:53.990816 2024] [core:notice] [pid 1:tid 1] AH00094: Command line: 'apache2 -D FOREGROUND' +[Thu Aug 15 00:59:26.087579 2024] [mpm_prefork:notice] [pid 1:tid 1] AH00163: Apache/2.4.61 (Debian) PHP/8.1.29 configured -- resuming normal operations +[Thu Aug 15 00:59:26.088145 2024] [core:notice] [pid 1:tid 1] AH00094: Command line: 'apache2 -D FOREGROUND' diff --git a/apache_log/other_vhosts_access.log b/apache_log/other_vhosts_access.log new file mode 100644 index 0000000..ad52e32 --- /dev/null +++ b/apache_log/other_vhosts_access.log @@ -0,0 +1,145 @@ +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:15:22:44 +0000] "GET / HTTP/1.1" 200 6394 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:15:22:46 +0000] "GET /favicon.ico HTTP/1.1" 200 5731 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:15:35:47 +0000] "GET / HTTP/1.1" 500 3648 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:15:35:48 +0000] "GET /favicon.ico HTTP/1.1" 304 250 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:15:37:22 +0000] "GET / HTTP/1.1" 500 3648 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:15:38:14 +0000] "-" 408 0 "-" "-" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:15:49:24 +0000] "GET / HTTP/1.1" 200 1997 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:15:49:26 +0000] "GET /assets/css/main.css HTTP/1.1" 200 4569 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:15:49:26 +0000] "GET /assets/img/loader/loader.svg HTTP/1.1" 200 1501 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:15:49:26 +0000] "GET /assets/css/vendors.css HTTP/1.1" 200 40148 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:15:49:26 +0000] "GET /assets/css/style.css HTTP/1.1" 200 79125 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:15:49:26 +0000] "GET /assets/js/app.js HTTP/1.1" 200 58191 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:15:49:26 +0000] "GET /assets/img/app-store-icons/google-dark.svg HTTP/1.1" 200 10890 "http://localhost:63101/assets/css/main.css" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:15:49:26 +0000] "GET /assets/img/app-store-icons/apple-dark.svg HTTP/1.1" 200 15045 "http://localhost:63101/assets/css/main.css" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:15:49:26 +0000] "GET /assets/img/bg/provider-1.jpg HTTP/1.1" 200 159364 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:15:49:26 +0000] "GET /assets/js/vendors.js HTTP/1.1" 200 2210568 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:15:49:26 +0000] "GET /assets/img/favicon.ico HTTP/1.1" 200 617 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 127.0.0.1 - - [13/Aug/2024:15:49:31 +0000] "OPTIONS * HTTP/1.0" 200 126 "-" "Apache/2.4.61 (Debian) PHP/8.1.29 (internal dummy connection)" +oameye.works.wrenchboard.com:80 127.0.0.1 - - [13/Aug/2024:15:49:32 +0000] "OPTIONS * HTTP/1.0" 200 126 "-" "Apache/2.4.61 (Debian) PHP/8.1.29 (internal dummy connection)" +oameye.works.wrenchboard.com:80 127.0.0.1 - - [13/Aug/2024:15:49:33 +0000] "OPTIONS * HTTP/1.0" 200 126 "-" "Apache/2.4.61 (Debian) PHP/8.1.29 (internal dummy connection)" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:15:58:13 +0000] "GET / HTTP/1.1" 200 1995 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:15:59:05 +0000] "-" 408 0 "-" "-" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:16:23:35 +0000] "GET / HTTP/1.1" 200 2027 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:16:23:39 +0000] "GET /assets/css/main.css HTTP/1.1" 200 4569 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:16:23:39 +0000] "GET /assets/img/loader/loader.svg HTTP/1.1" 304 249 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:16:23:39 +0000] "GET /assets/css/vendors.css HTTP/1.1" 200 40148 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:16:23:39 +0000] "GET /assets/css/style.css HTTP/1.1" 200 79125 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:16:23:39 +0000] "GET /assets/js/app.js HTTP/1.1" 200 58191 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:16:23:39 +0000] "GET /assets/img/app-store-icons/google-dark.svg HTTP/1.1" 304 249 "http://localhost:63101/assets/css/main.css" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:16:23:39 +0000] "GET /assets/img/app-store-icons/apple-dark.svg HTTP/1.1" 304 249 "http://localhost:63101/assets/css/main.css" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:16:23:39 +0000] "GET /assets/img/bg/provider-1.jpg HTTP/1.1" 304 250 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:16:23:39 +0000] "GET /assets/js/vendors.js HTTP/1.1" 200 2210568 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:16:23:40 +0000] "GET /assets/img/favicon.ico HTTP/1.1" 304 248 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 127.0.0.1 - - [13/Aug/2024:16:23:45 +0000] "OPTIONS * HTTP/1.0" 200 126 "-" "Apache/2.4.61 (Debian) PHP/8.1.29 (internal dummy connection)" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:16:24:08 +0000] "GET / HTTP/1.1" 200 2014 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:16:25:00 +0000] "-" 408 0 "-" "-" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:16:26:15 +0000] "GET / HTTP/1.1" 200 2063 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:16:27:07 +0000] "-" 408 0 "-" "-" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:16:27:26 +0000] "GET / HTTP/1.1" 200 2078 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:16:28:19 +0000] "-" 408 0 "-" "-" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:22:01:56 +0000] "GET / HTTP/1.1" 200 2078 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:22:02:02 +0000] "GET /assets/css/main.css HTTP/1.1" 200 4569 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:22:02:02 +0000] "GET /assets/img/loader/loader.svg HTTP/1.1" 304 249 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:22:02:02 +0000] "GET /assets/css/vendors.css HTTP/1.1" 200 40148 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:22:02:02 +0000] "GET /assets/css/style.css HTTP/1.1" 200 79125 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:22:02:02 +0000] "GET /assets/img/app-store-icons/apple-dark.svg HTTP/1.1" 304 249 "http://localhost:63101/assets/css/main.css" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:22:02:02 +0000] "GET /assets/img/bg/provider-1.jpg HTTP/1.1" 304 250 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:22:02:02 +0000] "GET /assets/img/app-store-icons/google-dark.svg HTTP/1.1" 304 249 "http://localhost:63101/assets/css/main.css" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:22:02:02 +0000] "GET /assets/js/app.js HTTP/1.1" 200 58190 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:22:02:02 +0000] "GET /assets/js/vendors.js HTTP/1.1" 200 2210568 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:22:02:04 +0000] "GET /assets/img/favicon.ico HTTP/1.1" 304 248 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:22:02:13 +0000] "POST /login HTTP/1.1" 404 268 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:22:02:15 +0000] "GET /favicon.ico HTTP/1.1" 304 249 "http://localhost:63101/login" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:22:03:05 +0000] "-" 408 0 "-" "-" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:22:03:42 +0000] "POST /login HTTP/1.1" 404 268 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:22:04:34 +0000] "-" 408 0 "-" "-" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:22:17:13 +0000] "POST /login HTTP/1.1" 404 268 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:22:18:06 +0000] "-" 408 0 "-" "-" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:22:26:14 +0000] "POST /login HTTP/1.1" 404 268 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:22:27:06 +0000] "-" 408 0 "-" "-" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:23:01:51 +0000] "GET /login HTTP/1.1" 404 268 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:23:02:27 +0000] "GET /login HTTP/1.1" 404 268 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:23:02:27 +0000] "GET /favicon.ico HTTP/1.1" 304 249 "http://localhost:63101/login" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:23:03:19 +0000] "-" 408 0 "-" "-" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:23:03:28 +0000] "GET /login HTTP/1.1" 404 268 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:23:03:33 +0000] "GET / HTTP/1.1" 200 2077 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:23:03:34 +0000] "GET /assets/css/main.css HTTP/1.1" 200 4569 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:23:03:34 +0000] "GET /assets/img/loader/loader.svg HTTP/1.1" 304 249 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:23:03:34 +0000] "GET /assets/css/vendors.css HTTP/1.1" 200 40148 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:23:03:34 +0000] "GET /assets/css/style.css HTTP/1.1" 200 79125 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:23:03:34 +0000] "GET /assets/js/app.js HTTP/1.1" 200 58191 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:23:03:34 +0000] "GET /assets/img/app-store-icons/google-dark.svg HTTP/1.1" 304 249 "http://localhost:63101/assets/css/main.css" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:23:03:34 +0000] "GET /assets/img/app-store-icons/apple-dark.svg HTTP/1.1" 304 249 "http://localhost:63101/assets/css/main.css" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:23:03:34 +0000] "GET /assets/img/bg/provider-1.jpg HTTP/1.1" 304 250 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:23:03:34 +0000] "GET /assets/js/vendors.js HTTP/1.1" 200 2210568 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:23:03:34 +0000] "GET /assets/img/favicon.ico HTTP/1.1" 304 248 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 127.0.0.1 - - [13/Aug/2024:23:03:40 +0000] "OPTIONS * HTTP/1.0" 200 126 "-" "Apache/2.4.61 (Debian) PHP/8.1.29 (internal dummy connection)" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:23:03:40 +0000] "POST /login HTTP/1.1" 404 268 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:23:04:32 +0000] "-" 408 0 "-" "-" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:23:29:48 +0000] "POST /login HTTP/1.1" 404 268 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:23:30:40 +0000] "-" 408 0 "-" "-" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:23:31:22 +0000] "POST /login HTTP/1.1" 404 268 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:23:31:30 +0000] "GET / HTTP/1.1" 200 2078 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:23:32:10 +0000] "POST /login HTTP/1.1" 404 268 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:23:33:02 +0000] "-" 408 0 "-" "-" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:23:34:23 +0000] "POST /login HTTP/1.1" 404 268 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:23:34:37 +0000] "GET /loginss HTTP/1.1" 404 268 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:23:34:47 +0000] "GET /login HTTP/1.1" 200 281 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [13/Aug/2024:23:35:39 +0000] "-" 408 0 "-" "-" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [14/Aug/2024:11:39:33 +0000] "GET /login HTTP/1.1" 200 281 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [14/Aug/2024:11:39:34 +0000] "GET /favicon.ico HTTP/1.1" 304 249 "http://localhost:63101/login" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [14/Aug/2024:12:36:19 +0000] "POST /login HTTP/1.1" 404 268 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [14/Aug/2024:12:36:17 +0000] "GET / HTTP/1.1" 200 2078 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [14/Aug/2024:12:36:25 +0000] "POST /login HTTP/1.1" 404 267 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [14/Aug/2024:12:36:42 +0000] "POST /login HTTP/1.1" 404 268 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [14/Aug/2024:12:36:45 +0000] "POST /login HTTP/1.1" 404 267 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [14/Aug/2024:12:36:48 +0000] "POST /login HTTP/1.1" 404 267 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [14/Aug/2024:12:36:52 +0000] "GET / HTTP/1.1" 200 2077 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [14/Aug/2024:12:36:54 +0000] "GET /assets/css/main.css HTTP/1.1" 200 4569 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [14/Aug/2024:12:36:54 +0000] "GET /assets/img/loader/loader.svg HTTP/1.1" 304 249 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [14/Aug/2024:12:36:54 +0000] "GET /assets/css/vendors.css HTTP/1.1" 200 40148 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [14/Aug/2024:12:36:54 +0000] "GET /assets/css/style.css HTTP/1.1" 200 79125 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [14/Aug/2024:12:36:54 +0000] "GET /assets/js/app.js HTTP/1.1" 200 58191 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [14/Aug/2024:12:36:54 +0000] "GET /assets/img/app-store-icons/apple-dark.svg HTTP/1.1" 304 249 "http://localhost:63101/assets/css/main.css" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [14/Aug/2024:12:36:54 +0000] "GET /assets/img/app-store-icons/google-dark.svg HTTP/1.1" 304 249 "http://localhost:63101/assets/css/main.css" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [14/Aug/2024:12:36:54 +0000] "GET /assets/img/bg/provider-1.jpg HTTP/1.1" 304 250 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [14/Aug/2024:12:36:54 +0000] "GET /assets/js/vendors.js HTTP/1.1" 200 2210568 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [14/Aug/2024:12:36:56 +0000] "GET /assets/img/favicon.ico HTTP/1.1" 304 248 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [14/Aug/2024:12:36:57 +0000] "POST /login HTTP/1.1" 404 267 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [14/Aug/2024:12:36:59 +0000] "POST /login HTTP/1.1" 404 267 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [14/Aug/2024:12:37:02 +0000] "GET / HTTP/1.1" 200 2077 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 127.0.0.1 - - [14/Aug/2024:12:37:09 +0000] "OPTIONS * HTTP/1.0" 200 126 "-" "Apache/2.4.61 (Debian) PHP/8.1.29 (internal dummy connection)" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [14/Aug/2024:12:37:31 +0000] "POST /login HTTP/1.1" 404 268 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [14/Aug/2024:12:37:45 +0000] "GET /login HTTP/1.1" 200 281 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [14/Aug/2024:12:37:46 +0000] "GET /favicon.ico HTTP/1.1" 200 5731 "http://localhost:63101/login" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [14/Aug/2024:12:37:57 +0000] "GET /login HTTP/1.1" 200 281 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [14/Aug/2024:12:38:37 +0000] "-" 408 0 "-" "-" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [14/Aug/2024:12:38:49 +0000] "-" 408 0 "-" "-" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [15/Aug/2024:15:14:05 +0000] "GET / HTTP/1.1" 200 2078 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [15/Aug/2024:15:14:09 +0000] "GET / HTTP/1.1" 200 2078 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [15/Aug/2024:15:14:11 +0000] "GET /assets/css/main.css HTTP/1.1" 200 4569 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [15/Aug/2024:15:14:11 +0000] "GET /assets/img/loader/loader.svg HTTP/1.1" 200 1501 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [15/Aug/2024:15:14:11 +0000] "GET /assets/css/vendors.css HTTP/1.1" 200 40148 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [15/Aug/2024:15:14:11 +0000] "GET /assets/css/style.css HTTP/1.1" 200 79125 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [15/Aug/2024:15:14:11 +0000] "GET /assets/js/app.js HTTP/1.1" 200 58191 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [15/Aug/2024:15:14:11 +0000] "GET /assets/img/app-store-icons/google-dark.svg HTTP/1.1" 200 10890 "http://localhost:63101/assets/css/main.css" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [15/Aug/2024:15:14:11 +0000] "GET /assets/img/app-store-icons/apple-dark.svg HTTP/1.1" 200 15045 "http://localhost:63101/assets/css/main.css" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [15/Aug/2024:15:14:11 +0000] "GET /assets/img/bg/provider-1.jpg HTTP/1.1" 200 159364 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [15/Aug/2024:15:14:11 +0000] "GET /assets/js/vendors.js HTTP/1.1" 200 2210568 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [15/Aug/2024:15:14:12 +0000] "GET /assets/img/favicon.ico HTTP/1.1" 200 617 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0" +oameye.works.wrenchboard.com:80 127.0.0.1 - - [15/Aug/2024:15:14:16 +0000] "OPTIONS * HTTP/1.0" 200 126 "-" "Apache/2.4.61 (Debian) PHP/8.1.29 (internal dummy connection)" +oameye.works.wrenchboard.com:80 127.0.0.1 - - [15/Aug/2024:15:14:17 +0000] "OPTIONS * HTTP/1.0" 200 126 "-" "Apache/2.4.61 (Debian) PHP/8.1.29 (internal dummy connection)" +oameye.works.wrenchboard.com:80 127.0.0.1 - - [15/Aug/2024:15:14:18 +0000] "OPTIONS * HTTP/1.0" 200 126 "-" "Apache/2.4.61 (Debian) PHP/8.1.29 (internal dummy connection)" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [16/Aug/2024:12:59:48 +0000] "GET / HTTP/1.1" 200 2078 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [16/Aug/2024:12:59:54 +0000] "GET /assets/img/loader/loader.svg HTTP/1.1" 200 1501 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [16/Aug/2024:12:59:54 +0000] "GET /assets/css/main.css HTTP/1.1" 200 4569 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [16/Aug/2024:12:59:54 +0000] "GET /assets/css/vendors.css HTTP/1.1" 200 40148 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [16/Aug/2024:12:59:54 +0000] "GET /assets/css/style.css HTTP/1.1" 200 79125 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [16/Aug/2024:12:59:55 +0000] "GET /assets/img/app-store-icons/google-dark.svg HTTP/1.1" 200 10890 "http://localhost:63101/assets/css/main.css" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [16/Aug/2024:12:59:55 +0000] "GET /assets/img/app-store-icons/apple-dark.svg HTTP/1.1" 200 15045 "http://localhost:63101/assets/css/main.css" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [16/Aug/2024:12:59:55 +0000] "GET /assets/img/bg/provider-1.jpg HTTP/1.1" 200 159364 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [16/Aug/2024:12:59:55 +0000] "GET /assets/js/app.js HTTP/1.1" 200 58190 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [16/Aug/2024:12:59:54 +0000] "GET /assets/js/vendors.js HTTP/1.1" 200 2210568 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [16/Aug/2024:12:59:57 +0000] "GET /assets/img/favicon.ico HTTP/1.1" 200 617 "http://localhost:63101/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" +oameye.works.wrenchboard.com:80 172.23.0.1 - - [16/Aug/2024:12:59:57 +0000] "GET /assets/js/toastr.js.map HTTP/1.1" 404 290 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" diff --git a/app/.htaccess b/app/.htaccess new file mode 100644 index 0000000..3462048 --- /dev/null +++ b/app/.htaccess @@ -0,0 +1,6 @@ + + Require all denied + + + Deny from all + diff --git a/app/Common.php b/app/Common.php new file mode 100644 index 0000000..95f5544 --- /dev/null +++ b/app/Common.php @@ -0,0 +1,15 @@ + + */ + public array $allowedHostnames = []; + + /** + * -------------------------------------------------------------------------- + * Index File + * -------------------------------------------------------------------------- + * + * Typically, this will be your `index.php` file, unless you've renamed it to + * something else. If you have configured your web server to remove this file + * from your site URIs, set this variable to an empty string. + */ + public string $indexPage = 'index.php'; + + /** + * -------------------------------------------------------------------------- + * URI PROTOCOL + * -------------------------------------------------------------------------- + * + * This item determines which server global should be used to retrieve the + * URI string. The default setting of 'REQUEST_URI' works for most servers. + * If your links do not seem to work, try one of the other delicious flavors: + * + * 'REQUEST_URI': Uses $_SERVER['REQUEST_URI'] + * 'QUERY_STRING': Uses $_SERVER['QUERY_STRING'] + * 'PATH_INFO': Uses $_SERVER['PATH_INFO'] + * + * WARNING: If you set this to 'PATH_INFO', URIs will always be URL-decoded! + */ + public string $uriProtocol = 'REQUEST_URI'; + + /* + |-------------------------------------------------------------------------- + | Allowed URL Characters + |-------------------------------------------------------------------------- + | + | This lets you specify which characters are permitted within your URLs. + | When someone tries to submit a URL with disallowed characters they will + | get a warning message. + | + | As a security measure you are STRONGLY encouraged to restrict URLs to + | as few characters as possible. + | + | By default, only these are allowed: `a-z 0-9~%.:_-` + | + | Set an empty string to allow all characters -- but only if you are insane. + | + | The configured value is actually a regular expression character group + | and it will be used as: '/\A[]+\z/iu' + | + | DO NOT CHANGE THIS UNLESS YOU FULLY UNDERSTAND THE REPERCUSSIONS!! + | + */ + public string $permittedURIChars = 'a-z 0-9~%.:_\-'; + + /** + * -------------------------------------------------------------------------- + * Default Locale + * -------------------------------------------------------------------------- + * + * The Locale roughly represents the language and location that your visitor + * is viewing the site from. It affects the language strings and other + * strings (like currency markers, numbers, etc), that your program + * should run under for this request. + */ + public string $defaultLocale = 'en'; + + /** + * -------------------------------------------------------------------------- + * Negotiate Locale + * -------------------------------------------------------------------------- + * + * If true, the current Request object will automatically determine the + * language to use based on the value of the Accept-Language header. + * + * If false, no automatic detection will be performed. + */ + public bool $negotiateLocale = false; + + /** + * -------------------------------------------------------------------------- + * Supported Locales + * -------------------------------------------------------------------------- + * + * If $negotiateLocale is true, this array lists the locales supported + * by the application in descending order of priority. If no match is + * found, the first locale will be used. + * + * IncomingRequest::setLocale() also uses this list. + * + * @var list + */ + public array $supportedLocales = ['en']; + + /** + * -------------------------------------------------------------------------- + * Application Timezone + * -------------------------------------------------------------------------- + * + * The default timezone that will be used in your application to display + * dates with the date helper, and can be retrieved through app_timezone() + * + * @see https://www.php.net/manual/en/timezones.php for list of timezones + * supported by PHP. + */ + public string $appTimezone = 'UTC'; + + /** + * -------------------------------------------------------------------------- + * Default Character Set + * -------------------------------------------------------------------------- + * + * This determines which character set is used by default in various methods + * that require a character set to be provided. + * + * @see http://php.net/htmlspecialchars for a list of supported charsets. + */ + public string $charset = 'UTF-8'; + + /** + * -------------------------------------------------------------------------- + * Force Global Secure Requests + * -------------------------------------------------------------------------- + * + * If true, this will force every request made to this application to be + * made via a secure connection (HTTPS). If the incoming request is not + * secure, the user will be redirected to a secure version of the page + * and the HTTP Strict Transport Security (HSTS) header will be set. + */ + public bool $forceGlobalSecureRequests = false; + + /** + * -------------------------------------------------------------------------- + * Reverse Proxy IPs + * -------------------------------------------------------------------------- + * + * If your server is behind a reverse proxy, you must whitelist the proxy + * IP addresses from which CodeIgniter should trust headers such as + * X-Forwarded-For or Client-IP in order to properly identify + * the visitor's IP address. + * + * You need to set a proxy IP address or IP address with subnets and + * the HTTP header for the client IP address. + * + * Here are some examples: + * [ + * '10.0.1.200' => 'X-Forwarded-For', + * '192.168.5.0/24' => 'X-Real-IP', + * ] + * + * @var array + */ + public array $proxyIPs = []; + + /** + * -------------------------------------------------------------------------- + * Content Security Policy + * -------------------------------------------------------------------------- + * + * Enables the Response's Content Secure Policy to restrict the sources that + * can be used for images, scripts, CSS files, audio, video, etc. If enabled, + * the Response object will populate default values for the policy from the + * `ContentSecurityPolicy.php` file. Controllers can always add to those + * restrictions at run time. + * + * For a better understanding of CSP, see these documents: + * + * @see http://www.html5rocks.com/en/tutorials/security/content-security-policy/ + * @see http://www.w3.org/TR/CSP/ + */ + public bool $CSPEnabled = false; +} diff --git a/app/Config/Autoload.php b/app/Config/Autoload.php new file mode 100644 index 0000000..76cd926 --- /dev/null +++ b/app/Config/Autoload.php @@ -0,0 +1,94 @@ +|string> + */ + public $psr4 = [ + APP_NAMESPACE => APPPATH, + ]; + + /** + * ------------------------------------------------------------------- + * Class Map + * ------------------------------------------------------------------- + * The class map provides a map of class names and their exact + * location on the drive. Classes loaded in this manner will have + * slightly faster performance because they will not have to be + * searched for within one or more directories as they would if they + * were being autoloaded through a namespace. + * + * Prototype: + * $classmap = [ + * 'MyClass' => '/path/to/class/file.php' + * ]; + * + * @var array + */ + public $classmap = []; + + /** + * ------------------------------------------------------------------- + * Files + * ------------------------------------------------------------------- + * The files array provides a list of paths to __non-class__ files + * that will be autoloaded. This can be useful for bootstrap operations + * or for loading functions. + * + * Prototype: + * $files = [ + * '/path/to/my/file.php', + * ]; + * + * @var list + */ + public $files = []; + + /** + * ------------------------------------------------------------------- + * Helpers + * ------------------------------------------------------------------- + * Prototype: + * $helpers = [ + * 'form', + * ]; + * + * @var list + */ + public $helpers = []; +} diff --git a/app/Config/Boot/development.php b/app/Config/Boot/development.php new file mode 100644 index 0000000..a868447 --- /dev/null +++ b/app/Config/Boot/development.php @@ -0,0 +1,34 @@ + + */ + public array $file = [ + 'storePath' => WRITEPATH . 'cache/', + 'mode' => 0640, + ]; + + /** + * ------------------------------------------------------------------------- + * Memcached settings + * ------------------------------------------------------------------------- + * Your Memcached servers can be specified below, if you are using + * the Memcached drivers. + * + * @see https://codeigniter.com/user_guide/libraries/caching.html#memcached + * + * @var array + */ + public array $memcached = [ + 'host' => '127.0.0.1', + 'port' => 11211, + 'weight' => 1, + 'raw' => false, + ]; + + /** + * ------------------------------------------------------------------------- + * Redis settings + * ------------------------------------------------------------------------- + * Your Redis server can be specified below, if you are using + * the Redis or Predis drivers. + * + * @var array + */ + public array $redis = [ + 'host' => '127.0.0.1', + 'password' => null, + 'port' => 6379, + 'timeout' => 0, + 'database' => 0, + ]; + + /** + * -------------------------------------------------------------------------- + * Available Cache Handlers + * -------------------------------------------------------------------------- + * + * This is an array of cache engine alias' and class names. Only engines + * that are listed here are allowed to be used. + * + * @var array> + */ + public array $validHandlers = [ + 'dummy' => DummyHandler::class, + 'file' => FileHandler::class, + 'memcached' => MemcachedHandler::class, + 'predis' => PredisHandler::class, + 'redis' => RedisHandler::class, + 'wincache' => WincacheHandler::class, + ]; + + /** + * -------------------------------------------------------------------------- + * Web Page Caching: Cache Include Query String + * -------------------------------------------------------------------------- + * + * Whether to take the URL query string into consideration when generating + * output cache files. Valid options are: + * + * false = Disabled + * true = Enabled, take all query parameters into account. + * Please be aware that this may result in numerous cache + * files generated for the same page over and over again. + * ['q'] = Enabled, but only take into account the specified list + * of query parameters. + * + * @var bool|list + */ + public $cacheQueryString = false; +} diff --git a/app/Config/Constants.php b/app/Config/Constants.php new file mode 100644 index 0000000..456d6e3 --- /dev/null +++ b/app/Config/Constants.php @@ -0,0 +1,185 @@ +|string|null + */ + public $defaultSrc; + + /** + * Lists allowed scripts' URLs. + * + * @var list|string + */ + public $scriptSrc = 'self'; + + /** + * Lists allowed stylesheets' URLs. + * + * @var list|string + */ + public $styleSrc = 'self'; + + /** + * Defines the origins from which images can be loaded. + * + * @var list|string + */ + public $imageSrc = 'self'; + + /** + * Restricts the URLs that can appear in a page's `` element. + * + * Will default to self if not overridden + * + * @var list|string|null + */ + public $baseURI; + + /** + * Lists the URLs for workers and embedded frame contents + * + * @var list|string + */ + public $childSrc = 'self'; + + /** + * Limits the origins that you can connect to (via XHR, + * WebSockets, and EventSource). + * + * @var list|string + */ + public $connectSrc = 'self'; + + /** + * Specifies the origins that can serve web fonts. + * + * @var list|string + */ + public $fontSrc; + + /** + * Lists valid endpoints for submission from `
` tags. + * + * @var list|string + */ + public $formAction = 'self'; + + /** + * Specifies the sources that can embed the current page. + * This directive applies to ``, `':"vimeo"===r.type?n='':"vzaar"===r.type&&(n=''),t('
'+n+"
").insertAfter(i.find(".owl-video")),this._playing=i.addClass("owl-video-playing"))},r.prototype.isInFullScreen=function(){var e=n.fullscreenElement||n.mozFullScreenElement||n.webkitFullscreenElement;return e&&t(e).parent().hasClass("owl-video-frame")},r.prototype.destroy=function(){var t,e;for(t in this._core.$element.off("click.owl.video"),this._handlers)this._core.$element.off(t,this._handlers[t]);for(e in Object.getOwnPropertyNames(this))"function"!=typeof this[e]&&(this[e]=null)},t.fn.owlCarousel.Constructor.Plugins.Video=r}(window.Zepto||window.jQuery,window,document),function(t,e,n,i){var r=function(e){this.core=e,this.core.options=t.extend({},r.Defaults,this.core.options),this.swapping=!0,this.previous=i,this.next=i,this.handlers={"change.owl.carousel":t.proxy(function(t){t.namespace&&"position"==t.property.name&&(this.previous=this.core.current(),this.next=t.property.value)},this),"drag.owl.carousel dragged.owl.carousel translated.owl.carousel":t.proxy(function(t){t.namespace&&(this.swapping="translated"==t.type)},this),"translate.owl.carousel":t.proxy(function(t){t.namespace&&this.swapping&&(this.core.options.animateOut||this.core.options.animateIn)&&this.swap()},this)},this.core.$element.on(this.handlers)};r.Defaults={animateOut:!1,animateIn:!1},r.prototype.swap=function(){if(1===this.core.settings.items&&t.support.animation&&t.support.transition){this.core.speed(0);var e,n=t.proxy(this.clear,this),i=this.core.$stage.children().eq(this.previous),r=this.core.$stage.children().eq(this.next),o=this.core.settings.animateIn,a=this.core.settings.animateOut;this.core.current()!==this.previous&&(a&&(e=this.core.coordinates(this.previous)-this.core.coordinates(this.next),i.one(t.support.animation.end,n).css({left:e+"px"}).addClass("animated owl-animated-out").addClass(a)),o&&r.one(t.support.animation.end,n).addClass("animated owl-animated-in").addClass(o))}},r.prototype.clear=function(e){t(e.target).css({left:""}).removeClass("animated owl-animated-out owl-animated-in").removeClass(this.core.settings.animateIn).removeClass(this.core.settings.animateOut),this.core.onTransitionEnd()},r.prototype.destroy=function(){var t,e;for(t in this.handlers)this.core.$element.off(t,this.handlers[t]);for(e in Object.getOwnPropertyNames(this))"function"!=typeof this[e]&&(this[e]=null)},t.fn.owlCarousel.Constructor.Plugins.Animate=r}(window.Zepto||window.jQuery,window,document),function(t,e,n,i){var r=function(e){this._core=e,this._timeout=null,this._paused=!1,this._handlers={"changed.owl.carousel":t.proxy(function(t){t.namespace&&"settings"===t.property.name?this._core.settings.autoplay?this.play():this.stop():t.namespace&&"position"===t.property.name&&this._core.settings.autoplay&&this._setAutoPlayInterval()},this),"initialized.owl.carousel":t.proxy(function(t){t.namespace&&this._core.settings.autoplay&&this.play()},this),"play.owl.autoplay":t.proxy(function(t,e,n){t.namespace&&this.play(e,n)},this),"stop.owl.autoplay":t.proxy(function(t){t.namespace&&this.stop()},this),"mouseover.owl.autoplay":t.proxy(function(){this._core.settings.autoplayHoverPause&&this._core.is("rotating")&&this.pause()},this),"mouseleave.owl.autoplay":t.proxy(function(){this._core.settings.autoplayHoverPause&&this._core.is("rotating")&&this.play()},this),"touchstart.owl.core":t.proxy(function(){this._core.settings.autoplayHoverPause&&this._core.is("rotating")&&this.pause()},this),"touchend.owl.core":t.proxy(function(){this._core.settings.autoplayHoverPause&&this.play()},this)},this._core.$element.on(this._handlers),this._core.options=t.extend({},r.Defaults,this._core.options)};r.Defaults={autoplay:!1,autoplayTimeout:5e3,autoplayHoverPause:!1,autoplaySpeed:!1},r.prototype.play=function(t,e){this._paused=!1,this._core.is("rotating")||(this._core.enter("rotating"),this._setAutoPlayInterval())},r.prototype._getNextTimeout=function(i,r){return this._timeout&&e.clearTimeout(this._timeout),e.setTimeout(t.proxy(function(){this._paused||this._core.is("busy")||this._core.is("interacting")||n.hidden||this._core.next(r||this._core.settings.autoplaySpeed)},this),i||this._core.settings.autoplayTimeout)},r.prototype._setAutoPlayInterval=function(){this._timeout=this._getNextTimeout()},r.prototype.stop=function(){this._core.is("rotating")&&(e.clearTimeout(this._timeout),this._core.leave("rotating"))},r.prototype.pause=function(){this._core.is("rotating")&&(this._paused=!0)},r.prototype.destroy=function(){var t,e;for(t in this.stop(),this._handlers)this._core.$element.off(t,this._handlers[t]);for(e in Object.getOwnPropertyNames(this))"function"!=typeof this[e]&&(this[e]=null)},t.fn.owlCarousel.Constructor.Plugins.autoplay=r}(window.Zepto||window.jQuery,window,document),function(t,e,n,i){"use strict";var r=function(e){this._core=e,this._initialized=!1,this._pages=[],this._controls={},this._templates=[],this.$element=this._core.$element,this._overrides={next:this._core.next,prev:this._core.prev,to:this._core.to},this._handlers={"prepared.owl.carousel":t.proxy(function(e){e.namespace&&this._core.settings.dotsData&&this._templates.push('
'+t(e.content).find("[data-dot]").addBack("[data-dot]").attr("data-dot")+"
")},this),"added.owl.carousel":t.proxy(function(t){t.namespace&&this._core.settings.dotsData&&this._templates.splice(t.position,0,this._templates.pop())},this),"remove.owl.carousel":t.proxy(function(t){t.namespace&&this._core.settings.dotsData&&this._templates.splice(t.position,1)},this),"changed.owl.carousel":t.proxy(function(t){t.namespace&&"position"==t.property.name&&this.draw()},this),"initialized.owl.carousel":t.proxy(function(t){t.namespace&&!this._initialized&&(this._core.trigger("initialize",null,"navigation"),this.initialize(),this.update(),this.draw(),this._initialized=!0,this._core.trigger("initialized",null,"navigation"))},this),"refreshed.owl.carousel":t.proxy(function(t){t.namespace&&this._initialized&&(this._core.trigger("refresh",null,"navigation"),this.update(),this.draw(),this._core.trigger("refreshed",null,"navigation"))},this)},this._core.options=t.extend({},r.Defaults,this._core.options),this.$element.on(this._handlers)};r.Defaults={nav:!1,navText:["prev","next"],navSpeed:!1,navElement:"div",navContainer:!1,navContainerClass:"owl-nav",navClass:["owl-prev","owl-next"],slideBy:1,dotClass:"owl-dot",dotsClass:"owl-dots",dots:!0,dotsEach:!1,dotsData:!1,dotsSpeed:!1,dotsContainer:!1},r.prototype.initialize=function(){var e,n=this._core.settings;for(e in this._controls.$relative=(n.navContainer?t(n.navContainer):t("
").addClass(n.navContainerClass).appendTo(this.$element)).addClass("disabled"),this._controls.$previous=t("<"+n.navElement+">").addClass(n.navClass[0]).html(n.navText[0]).prependTo(this._controls.$relative).on("click",t.proxy(function(t){this.prev(n.navSpeed)},this)),this._controls.$next=t("<"+n.navElement+">").addClass(n.navClass[1]).html(n.navText[1]).appendTo(this._controls.$relative).on("click",t.proxy(function(t){this.next(n.navSpeed)},this)),n.dotsData||(this._templates=[t("
").addClass(n.dotClass).append(t("")).prop("outerHTML")]),this._controls.$absolute=(n.dotsContainer?t(n.dotsContainer):t("
").addClass(n.dotsClass).appendTo(this.$element)).addClass("disabled"),this._controls.$absolute.on("click","div",t.proxy(function(e){var i=t(e.target).parent().is(this._controls.$absolute)?t(e.target).index():t(e.target).parent().index();e.preventDefault(),this.to(i,n.dotsSpeed)},this)),this._overrides)this._core[e]=t.proxy(this[e],this)},r.prototype.destroy=function(){var t,e,n,i;for(t in this._handlers)this.$element.off(t,this._handlers[t]);for(e in this._controls)this._controls[e].remove();for(i in this.overides)this._core[i]=this._overrides[i];for(n in Object.getOwnPropertyNames(this))"function"!=typeof this[n]&&(this[n]=null)},r.prototype.update=function(){var t,e,n=this._core.clones().length/2,i=n+this._core.items().length,r=this._core.maximum(!0),o=this._core.settings,a=o.center||o.autoWidth||o.dotsData?1:o.dotsEach||o.items;if("page"!==o.slideBy&&(o.slideBy=Math.min(o.slideBy,o.items)),o.dots||"page"==o.slideBy)for(this._pages=[],t=n,e=0,0;t=a||0===e){if(this._pages.push({start:Math.min(r,t-n),end:t-n+a-1}),Math.min(r,t-n)===r)break;e=0,0}e+=this._core.mergers(this._core.relative(t))}},r.prototype.draw=function(){var e,n=this._core.settings,i=this._core.items().length<=n.items,r=this._core.relative(this._core.current()),o=n.loop||n.rewind;this._controls.$relative.toggleClass("disabled",!n.nav||i),n.nav&&(this._controls.$previous.toggleClass("disabled",!o&&r<=this._core.minimum(!0)),this._controls.$next.toggleClass("disabled",!o&&r>=this._core.maximum(!0))),this._controls.$absolute.toggleClass("disabled",!n.dots||i),n.dots&&(e=this._pages.length-this._controls.$absolute.children().length,n.dotsData&&0!==e?this._controls.$absolute.html(this._templates.join("")):e>0?this._controls.$absolute.append(new Array(e+1).join(this._templates[0])):e<0&&this._controls.$absolute.children().slice(e).remove(),this._controls.$absolute.find(".active").removeClass("active"),this._controls.$absolute.children().eq(t.inArray(this.current(),this._pages)).addClass("active"))},r.prototype.onTrigger=function(e){var n=this._core.settings;e.page={index:t.inArray(this.current(),this._pages),count:this._pages.length,size:n&&(n.center||n.autoWidth||n.dotsData?1:n.dotsEach||n.items)}},r.prototype.current=function(){var e=this._core.relative(this._core.current());return t.grep(this._pages,t.proxy(function(t,n){return t.start<=e&&t.end>=e},this)).pop()},r.prototype.getPosition=function(e){var n,i,r=this._core.settings;return"page"==r.slideBy?(n=t.inArray(this.current(),this._pages),i=this._pages.length,e?++n:--n,n=this._pages[(n%i+i)%i].start):(n=this._core.relative(this._core.current()),i=this._core.items().length,e?n+=r.slideBy:n-=r.slideBy),n},r.prototype.next=function(e){t.proxy(this._overrides.to,this._core)(this.getPosition(!0),e)},r.prototype.prev=function(e){t.proxy(this._overrides.to,this._core)(this.getPosition(!1),e)},r.prototype.to=function(e,n,i){var r;!i&&this._pages.length?(r=this._pages.length,t.proxy(this._overrides.to,this._core)(this._pages[(e%r+r)%r].start,n)):t.proxy(this._overrides.to,this._core)(e,n)},t.fn.owlCarousel.Constructor.Plugins.Navigation=r}(window.Zepto||window.jQuery,window,document),function(t,e,n,i){"use strict";var r=function(n){this._core=n,this._hashes={},this.$element=this._core.$element,this._handlers={"initialized.owl.carousel":t.proxy(function(n){n.namespace&&"URLHash"===this._core.settings.startPosition&&t(e).trigger("hashchange.owl.navigation")},this),"prepared.owl.carousel":t.proxy(function(e){if(e.namespace){var n=t(e.content).find("[data-hash]").addBack("[data-hash]").attr("data-hash");if(!n)return;this._hashes[n]=e.content}},this),"changed.owl.carousel":t.proxy(function(n){if(n.namespace&&"position"===n.property.name){var i=this._core.items(this._core.relative(this._core.current())),r=t.map(this._hashes,function(t,e){return t===i?e:null}).join();if(!r||e.location.hash.slice(1)===r)return;e.location.hash=r}},this)},this._core.options=t.extend({},r.Defaults,this._core.options),this.$element.on(this._handlers),t(e).on("hashchange.owl.navigation",t.proxy(function(t){var n=e.location.hash.substring(1),i=this._core.$stage.children(),r=this._hashes[n]&&i.index(this._hashes[n]);void 0!==r&&r!==this._core.current()&&this._core.to(this._core.relative(r),!1,!0)},this))};r.Defaults={URLhashListener:!1},r.prototype.destroy=function(){var n,i;for(n in t(e).off("hashchange.owl.navigation"),this._handlers)this._core.$element.off(n,this._handlers[n]);for(i in Object.getOwnPropertyNames(this))"function"!=typeof this[i]&&(this[i]=null)},t.fn.owlCarousel.Constructor.Plugins.Hash=r}(window.Zepto||window.jQuery,window,document),function(t,e,n,i){function r(e,n){var r=!1,o=e.charAt(0).toUpperCase()+e.slice(1);return t.each((e+" "+s.join(o+" ")+o).split(" "),function(t,e){if(a[e]!==i)return r=!n||e,!1}),r}function o(t){return r(t,!0)}var a=t("").get(0).style,s="Webkit Moz O ms".split(" "),l={transition:{end:{WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd",transition:"transitionend"}},animation:{end:{WebkitAnimation:"webkitAnimationEnd",MozAnimation:"animationend",OAnimation:"oAnimationEnd",animation:"animationend"}}},c=function(){return!!r("transform")},u=function(){return!!r("perspective")},h=function(){return!!r("animation")};(function(){return!!r("transition")})()&&(t.support.transition=new String(o("transition")),t.support.transition.end=l.transition.end[t.support.transition]),h()&&(t.support.animation=new String(o("animation")),t.support.animation.end=l.animation.end[t.support.animation]),c()&&(t.support.transform=new String(o("transform")),t.support.transform3d=u())}(window.Zepto||window.jQuery,window,document),function(t){"function"==typeof define&&define.amd?define(["jquery"],t):t("object"==typeof exports?require("jquery"):window.jQuery||window.Zepto)}(function(t){var e,n,i,r,o,a,s="Close",l="BeforeClose",c="MarkupParse",u="Open",h="Change",d="mfp",C="."+d,f="mfp-ready",p="mfp-removing",g="mfp-prevent-close",m=function(){},v=!!window.jQuery,y=t(window),b=function(t,n){e.ev.on(d+t+C,n)},x=function(e,n,i,r){var o=document.createElement("div");return o.className="mfp-"+e,i&&(o.innerHTML=i),r?n&&n.appendChild(o):(o=t(o),n&&o.appendTo(n)),o},w=function(n,i){e.ev.triggerHandler(d+n,i),e.st.callbacks&&(n=n.charAt(0).toLowerCase()+n.slice(1),e.st.callbacks[n]&&e.st.callbacks[n].apply(e,t.isArray(i)?i:[i]))},_=function(n){return n===a&&e.currTemplate.closeBtn||(e.currTemplate.closeBtn=t(e.st.closeMarkup.replace("%title%",e.st.tClose)),a=n),e.currTemplate.closeBtn},S=function(){t.magnificPopup.instance||((e=new m).init(),t.magnificPopup.instance=e)};m.prototype={constructor:m,init:function(){var n=navigator.appVersion;e.isLowIE=e.isIE8=document.all&&!document.addEventListener,e.isAndroid=/android/gi.test(n),e.isIOS=/iphone|ipad|ipod/gi.test(n),e.supportsTransition=function(){var t=document.createElement("p").style,e=["ms","O","Moz","Webkit"];if(void 0!==t.transition)return!0;for(;e.length;)if(e.pop()+"Transition"in t)return!0;return!1}(),e.probablyMobile=e.isAndroid||e.isIOS||/(Opera Mini)|Kindle|webOS|BlackBerry|(Opera Mobi)|(Windows Phone)|IEMobile/i.test(navigator.userAgent),i=t(document),e.popupsCache={}},open:function(n){var r;if(!1===n.isObj){e.items=n.items.toArray(),e.index=0;var a,s=n.items;for(r=0;r(t||y.height())},_setFocus:function(){(e.st.focus?e.content.find(e.st.focus).eq(0):e.wrap).focus()},_onFocusIn:function(n){return n.target===e.wrap[0]||t.contains(e.wrap[0],n.target)?void 0:(e._setFocus(),!1)},_parseMarkup:function(e,n,i){var r;i.data&&(n=t.extend(i.data,n)),w(c,[e,n,i]),t.each(n,function(n,i){if(void 0===i||!1===i)return!0;if((r=n.split("_")).length>1){var o=e.find(C+"-"+r[0]);if(o.length>0){var a=r[1];"replaceWith"===a?o[0]!==i[0]&&o.replaceWith(i):"img"===a?o.is("img")?o.attr("src",i):o.replaceWith(t("").attr("src",i).attr("class",o.attr("class"))):o.attr(r[1],i)}}else e.find(C+"-"+n).html(i)})},_getScrollbarSize:function(){if(void 0===e.scrollbarSize){var t=document.createElement("div");t.style.cssText="width: 99px; height: 99px; overflow: scroll; position: absolute; top: -9999px;",document.body.appendChild(t),e.scrollbarSize=t.offsetWidth-t.clientWidth,document.body.removeChild(t)}return e.scrollbarSize}},t.magnificPopup={instance:null,proto:m.prototype,modules:[],open:function(e,n){return S(),(e=e?t.extend(!0,{},e):{}).isObj=!0,e.index=n||0,this.instance.open(e)},close:function(){return t.magnificPopup.instance&&t.magnificPopup.instance.close()},registerModule:function(e,n){n.options&&(t.magnificPopup.defaults[e]=n.options),t.extend(this.proto,n.proto),this.modules.push(e)},defaults:{disableOn:0,key:null,midClick:!1,mainClass:"",preloader:!0,focus:"",closeOnContentClick:!1,closeOnBgClick:!0,closeBtnInside:!0,showCloseBtn:!0,enableEscapeKey:!0,modal:!1,alignTop:!1,removalDelay:0,prependTo:null,fixedContentPos:"auto",fixedBgPos:"auto",overflowY:"auto",closeMarkup:'',tClose:"Close (Esc)",tLoading:"Loading...",autoFocusLast:!0}},t.fn.magnificPopup=function(n){S();var i=t(this);if("string"==typeof n)if("open"===n){var r,o=v?i.data("magnificPopup"):i[0].magnificPopup,a=parseInt(arguments[1],10)||0;o.items?r=o.items[a]:(r=i,o.delegate&&(r=r.find(o.delegate)),r=r.eq(a)),e._openClick({mfpEl:r},i,o)}else e.isOpen&&e[n].apply(e,Array.prototype.slice.call(arguments,1));else n=t.extend(!0,{},n),v?i.data("magnificPopup",n):i[0].magnificPopup=n,e.addGroup(i,n);return i};var k,T,E,A="inline",M=function(){E&&(T.after(E.addClass(k)).detach(),E=null)};t.magnificPopup.registerModule(A,{options:{hiddenClass:"hide",markup:"",tNotFound:"Content not found"},proto:{initInline:function(){e.types.push(A),b(s+"."+A,function(){M()})},getInline:function(n,i){if(M(),n.src){var r=e.st.inline,o=t(n.src);if(o.length){var a=o[0].parentNode;a&&a.tagName&&(T||(k=r.hiddenClass,T=x(k),k="mfp-"+k),E=o.after(T).detach().removeClass(k)),e.updateStatus("ready")}else e.updateStatus("error",r.tNotFound),o=t("
");return n.inlineElement=o,o}return e.updateStatus("ready"),e._parseMarkup(i,{},n),i}}});var D,P="ajax",O=function(){D&&t(document.body).removeClass(D)},L=function(){O(),e.req&&e.req.abort()};t.magnificPopup.registerModule(P,{options:{settings:null,cursor:"mfp-ajax-cur",tError:'The content could not be loaded.'},proto:{initAjax:function(){e.types.push(P),D=e.st.ajax.cursor,b(s+"."+P,L),b("BeforeChange."+P,L)},getAjax:function(n){D&&t(document.body).addClass(D),e.updateStatus("loading");var i=t.extend({url:n.src,success:function(i,r,o){var a={data:i,xhr:o};w("ParseAjax",a),e.appendContent(t(a.data),P),n.finished=!0,O(),e._setFocus(),setTimeout(function(){e.wrap.addClass(f)},16),e.updateStatus("ready"),w("AjaxContentAdded")},error:function(){O(),n.finished=n.loadError=!0,e.updateStatus("error",e.st.ajax.tError.replace("%url%",n.src))}},e.st.ajax.settings);return e.req=t.ajax(i),""}}});var I,N=function(n){if(n.data&&void 0!==n.data.title)return n.data.title;var i=e.st.image.titleSrc;if(i){if(t.isFunction(i))return i.call(e,n);if(n.el)return n.el.attr(i)||""}return""};t.magnificPopup.registerModule("image",{options:{markup:'
',cursor:"mfp-zoom-out-cur",titleSrc:"title",verticalFit:!0,tError:'The image could not be loaded.'},proto:{initImage:function(){var n=e.st.image,i=".image";e.types.push("image"),b(u+i,function(){"image"===e.currItem.type&&n.cursor&&t(document.body).addClass(n.cursor)}),b(s+i,function(){n.cursor&&t(document.body).removeClass(n.cursor),y.off("resize"+C)}),b("Resize"+i,e.resizeImage),e.isLowIE&&b("AfterChange",e.resizeImage)},resizeImage:function(){var t=e.currItem;if(t&&t.img&&e.st.image.verticalFit){var n=0;e.isLowIE&&(n=parseInt(t.img.css("padding-top"),10)+parseInt(t.img.css("padding-bottom"),10)),t.img.css("max-height",e.wH-n)}},_onImageHasSize:function(t){t.img&&(t.hasSize=!0,I&&clearInterval(I),t.isCheckingImgSize=!1,w("ImageHasSize",t),t.imgHidden&&(e.content&&e.content.removeClass("mfp-loading"),t.imgHidden=!1))},findImageSize:function(t){var n=0,i=t.img[0],r=function(o){I&&clearInterval(I),I=setInterval(function(){return i.naturalWidth>0?void e._onImageHasSize(t):(n>200&&clearInterval(I),void(3===++n?r(10):40===n?r(50):100===n&&r(500)))},o)};r(1)},getImage:function(n,i){var r=0,o=function(){n&&(n.img[0].complete?(n.img.off(".mfploader"),n===e.currItem&&(e._onImageHasSize(n),e.updateStatus("ready")),n.hasSize=!0,n.loaded=!0,w("ImageLoadComplete")):200>++r?setTimeout(o,100):a())},a=function(){n&&(n.img.off(".mfploader"),n===e.currItem&&(e._onImageHasSize(n),e.updateStatus("error",s.tError.replace("%url%",n.src))),n.hasSize=!0,n.loaded=!0,n.loadError=!0)},s=e.st.image,l=i.find(".mfp-img");if(l.length){var c=document.createElement("img");c.className="mfp-img",n.el&&n.el.find("img").length&&(c.alt=n.el.find("img").attr("alt")),n.img=t(c).on("load.mfploader",o).on("error.mfploader",a),c.src=n.src,l.is("img")&&(n.img=n.img.clone()),(c=n.img[0]).naturalWidth>0?n.hasSize=!0:c.width||(n.hasSize=!1)}return e._parseMarkup(i,{title:N(n),img_replaceWith:n.img},n),e.resizeImage(),n.hasSize?(I&&clearInterval(I),n.loadError?(i.addClass("mfp-loading"),e.updateStatus("error",s.tError.replace("%url%",n.src))):(i.removeClass("mfp-loading"),e.updateStatus("ready")),i):(e.updateStatus("loading"),n.loading=!0,n.hasSize||(n.imgHidden=!0,i.addClass("mfp-loading"),e.findImageSize(n)),i)}}});var R;t.magnificPopup.registerModule("zoom",{options:{enabled:!1,easing:"ease-in-out",duration:300,opener:function(t){return t.is("img")?t:t.find("img")}},proto:{initZoom:function(){var t,n=e.st.zoom,i=".zoom";if(n.enabled&&e.supportsTransition){var r,o,a=n.duration,c=function(t){var e=t.clone().removeAttr("style").removeAttr("class").addClass("mfp-animated-image"),i="all "+n.duration/1e3+"s "+n.easing,r={position:"fixed",zIndex:9999,left:0,top:0,"-webkit-backface-visibility":"hidden"},o="transition";return r["-webkit-"+o]=r["-moz-"+o]=r["-o-"+o]=r[o]=i,e.css(r),e},u=function(){e.content.css("visibility","visible")};b("BuildControls"+i,function(){if(e._allowZoom()){if(clearTimeout(r),e.content.css("visibility","hidden"),!(t=e._getItemToZoom()))return void u();(o=c(t)).css(e._getOffset()),e.wrap.append(o),r=setTimeout(function(){o.css(e._getOffset(!0)),r=setTimeout(function(){u(),setTimeout(function(){o.remove(),t=o=null,w("ZoomAnimationEnded")},16)},a)},16)}}),b(l+i,function(){if(e._allowZoom()){if(clearTimeout(r),e.st.removalDelay=a,!t){if(!(t=e._getItemToZoom()))return;o=c(t)}o.css(e._getOffset(!0)),e.wrap.append(o),e.content.css("visibility","hidden"),setTimeout(function(){o.css(e._getOffset())},16)}}),b(s+i,function(){e._allowZoom()&&(u(),o&&o.remove(),t=null)})}},_allowZoom:function(){return"image"===e.currItem.type},_getItemToZoom:function(){return!!e.currItem.hasSize&&e.currItem.img},_getOffset:function(n){var i,r=(i=n?e.currItem.img:e.st.zoom.opener(e.currItem.el||e.currItem)).offset(),o=parseInt(i.css("padding-top"),10),a=parseInt(i.css("padding-bottom"),10);r.top-=t(window).scrollTop()-o;var s={width:i.width(),height:(v?i.innerHeight():i[0].offsetHeight)-a-o};return void 0===R&&(R=void 0!==document.createElement("p").style.MozTransform),R?s["-moz-transform"]=s.transform="translate("+r.left+"px,"+r.top+"px)":(s.left=r.left,s.top=r.top),s}}});var B="iframe",F=function(t){if(e.currTemplate[B]){var n=e.currTemplate[B].find("iframe");n.length&&(t||(n[0].src="//about:blank"),e.isIE8&&n.css("display",t?"block":"none"))}};t.magnificPopup.registerModule(B,{options:{markup:'
',srcAction:"iframe_src",patterns:{youtube:{index:"youtube.com",id:"v=",src:"//www.youtube.com/embed/%id%?autoplay=1"},vimeo:{index:"vimeo.com/",id:"/",src:"//player.vimeo.com/video/%id%?autoplay=1"},gmaps:{index:"//maps.google.",src:"%id%&output=embed"}}},proto:{initIframe:function(){e.types.push(B),b("BeforeChange",function(t,e,n){e!==n&&(e===B?F():n===B&&F(!0))}),b(s+"."+B,function(){F()})},getIframe:function(n,i){var r=n.src,o=e.st.iframe;t.each(o.patterns,function(){return r.indexOf(this.index)>-1?(this.id&&(r="string"==typeof this.id?r.substr(r.lastIndexOf(this.id)+this.id.length,r.length):this.id.call(this,r)),r=this.src.replace("%id%",r),!1):void 0});var a={};return o.srcAction&&(a[o.srcAction]=r),e._parseMarkup(i,a,n),e.updateStatus("ready"),i}}});var z=function(t){var n=e.items.length;return t>n-1?t-n:0>t?n+t:t},H=function(t,e,n){return t.replace(/%curr%/gi,e+1).replace(/%total%/gi,n)};t.magnificPopup.registerModule("gallery",{options:{enabled:!1,arrowMarkup:'',preload:[0,2],navigateByImgClick:!0,arrows:!0,tPrev:"Previous (Left arrow key)",tNext:"Next (Right arrow key)",tCounter:"%curr% of %total%"},proto:{initGallery:function(){var n=e.st.gallery,r=".mfp-gallery";return e.direction=!0,!(!n||!n.enabled)&&(o+=" mfp-gallery",b(u+r,function(){n.navigateByImgClick&&e.wrap.on("click"+r,".mfp-img",function(){return e.items.length>1?(e.next(),!1):void 0}),i.on("keydown"+r,function(t){37===t.keyCode?e.prev():39===t.keyCode&&e.next()})}),b("UpdateStatus"+r,function(t,n){n.text&&(n.text=H(n.text,e.currItem.index,e.items.length))}),b(c+r,function(t,i,r,o){var a=e.items.length;r.counter=a>1?H(n.tCounter,o.index,a):""}),b("BuildControls"+r,function(){if(e.items.length>1&&n.arrows&&!e.arrowLeft){var i=n.arrowMarkup,r=e.arrowLeft=t(i.replace(/%title%/gi,n.tPrev).replace(/%dir%/gi,"left")).addClass(g),o=e.arrowRight=t(i.replace(/%title%/gi,n.tNext).replace(/%dir%/gi,"right")).addClass(g);r.click(function(){e.prev()}),o.click(function(){e.next()}),e.container.append(r.add(o))}}),b(h+r,function(){e._preloadTimeout&&clearTimeout(e._preloadTimeout),e._preloadTimeout=setTimeout(function(){e.preloadNearbyImages(),e._preloadTimeout=null},16)}),void b(s+r,function(){i.off(r),e.wrap.off("click"+r),e.arrowRight=e.arrowLeft=null}))},next:function(){e.direction=!0,e.index=z(e.index+1),e.updateItemHTML()},prev:function(){e.direction=!1,e.index=z(e.index-1),e.updateItemHTML()},goTo:function(t){e.direction=t>=e.index,e.index=t,e.updateItemHTML()},preloadNearbyImages:function(){var t,n=e.st.gallery.preload,i=Math.min(n[0],e.items.length),r=Math.min(n[1],e.items.length);for(t=1;t<=(e.direction?r:i);t++)e._preloadItem(e.index+t);for(t=1;t<=(e.direction?i:r);t++)e._preloadItem(e.index-t)},_preloadItem:function(n){if(n=z(n),!e.items[n].preloaded){var i=e.items[n];i.parsed||(i=e.parseEl(n)),w("LazyLoad",i),"image"===i.type&&(i.img=t('').on("load.mfploader",function(){i.hasSize=!0}).on("error.mfploader",function(){i.hasSize=!0,i.loadError=!0,w("LazyLoadError",i)}).attr("src",i.src)),i.preloaded=!0}}}});var V="retina";t.magnificPopup.registerModule(V,{options:{replaceSrc:function(t){return t.src.replace(/\.\w+$/,function(t){return"@2x"+t})},ratio:1},proto:{initRetina:function(){if(window.devicePixelRatio>1){var t=e.st.retina,n=t.ratio;(n=isNaN(n)?n():n)>1&&(b("ImageHasSize."+V,function(t,e){e.img.css({"max-width":e.img[0].naturalWidth/n,width:"100%"})}),b("ElementParse."+V,function(e,i){i.src=t.replaceSrc(i,n)}))}}}}),S()}),function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.Sweetalert2=e()}(this,function(){"use strict";function t(e){return(t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(e)}function e(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function n(t,e){for(var n=0;n(e=parseInt(e.getAttribute("tabindex")))?1:t\n
\n
    \n
    \n \n
    \n
    \n ?\n
    \n
    \n !\n
    \n
    \n i\n
    \n
    \n
    \n \n
    \n
    \n
    \n \n

    \n \n
    \n
    \n
    \n \n \n
    \n \n \n
    \n \n
    \n \n \n
    \n
    \n
    \n \n \n
    \n
    \n
    \n
    \n').replace(/(^|\n)\s*/g,""),Q=function(t){var e=L();if(e&&(e.parentNode.removeChild(e),A([document.documentElement,document.body],[x["no-backdrop"],x["toast-shown"],x["has-column"]])),!Z()){var n=document.createElement("div");n.className=x.container,n.innerHTML=K;var i="string"==typeof t.target?document.querySelector(t.target):t.target;i.appendChild(n);var r,o=N(),a=F(),s=M(a,x.input),l=M(a,x.file),c=a.querySelector(".".concat(x.range," input")),u=a.querySelector(".".concat(x.range," output")),h=M(a,x.select),d=a.querySelector(".".concat(x.checkbox," input")),C=M(a,x.textarea);o.setAttribute("role",t.toast?"alert":"dialog"),o.setAttribute("aria-live",t.toast?"polite":"assertive"),t.toast||o.setAttribute("aria-modal","true"),"rtl"===window.getComputedStyle(i).direction&&E(L(),x.rtl);var p=function(t){zt.isVisible()&&r!==t.target.value&&zt.resetValidationMessage(),r=t.target.value};return s.oninput=p,l.onchange=p,h.onchange=p,d.onchange=p,C.oninput=p,c.oninput=function(t){p(t),u.value=c.value},c.onchange=function(t){p(t),c.nextSibling.value=c.value},o}f("SweetAlert2 requires document to initialize")},J=function(e,n){if(!e)return P(n);if(e instanceof HTMLElement)n.appendChild(e);else if("object"===t(e))if(n.innerHTML="",0 in e)for(var i=0;i in e;i++)n.appendChild(e[i].cloneNode(!0));else n.appendChild(e.cloneNode(!0));else e&&(n.innerHTML=e);D(n)},tt=function(){if(Z())return!1;var t=document.createElement("div"),e={WebkitAnimation:"webkitAnimationEnd",OAnimation:"oAnimationEnd oanimationend",animation:"animationend"};for(var n in e)if(e.hasOwnProperty(n)&&void 0!==t.style[n])return e[n];return!1}(),et=function(t){var e=U(),n=j(),i=W();if(t.showConfirmButton||t.showCancelButton?D(e):P(e),t.showCancelButton?i.style.display="inline-block":P(i),t.showConfirmButton?n.style.removeProperty("display"):P(n),n.innerHTML=t.confirmButtonText,i.innerHTML=t.cancelButtonText,n.setAttribute("aria-label",t.confirmButtonAriaLabel),i.setAttribute("aria-label",t.cancelButtonAriaLabel),n.className=x.confirm,E(n,t.confirmButtonClass),i.className=x.cancel,E(i,t.cancelButtonClass),t.buttonsStyling){E([n,i],x.styled),t.confirmButtonColor&&(n.style.backgroundColor=t.confirmButtonColor),t.cancelButtonColor&&(i.style.backgroundColor=t.cancelButtonColor);var r=window.getComputedStyle(n).getPropertyValue("background-color");n.style.borderLeftColor=r,n.style.borderRightColor=r}else A([n,i],x.styled),n.style.backgroundColor=n.style.borderLeftColor=n.style.borderRightColor="",i.style.backgroundColor=i.style.borderLeftColor=i.style.borderRightColor=""},nt=function(t){var e=F().querySelector("#"+x.content);t.html?J(t.html,e):t.text?(e.textContent=t.text,D(e)):P(e)},it=function(t){for(var e=R(),n=0;n=t.progressSteps.length&&C("Invalid currentProgressStep parameter, it should be less than progressSteps.length (currentProgressStep like JS arrays starts from 0)"),t.progressSteps.forEach(function(i,r){var o=document.createElement("li");if(E(o,x.progresscircle),o.innerHTML=i,r===n&&E(o,x.activeprogressstep),e.appendChild(o),r!==t.progressSteps.length-1){var a=document.createElement("li");E(a,x.progressline),t.progressStepsDistance&&(a.style.width=t.progressStepsDistance),e.appendChild(a)}})):P(e)},at=function(t){var e=B();t.titleText?e.innerText=t.titleText:t.title&&("string"==typeof t.title&&(t.title=t.title.split("\n").join("
    ")),J(t.title,e))},st=function(){null===_.previousBodyPadding&&document.body.scrollHeight>window.innerHeight&&(_.previousBodyPadding=parseInt(window.getComputedStyle(document.body).getPropertyValue("padding-right")),document.body.style.paddingRight=_.previousBodyPadding+function(){if("ontouchstart"in window||navigator.msMaxTouchPoints)return 0;var t=document.createElement("div");t.style.width="50px",t.style.height="50px",t.style.overflow="scroll",document.body.appendChild(t);var e=t.offsetWidth-t.clientWidth;return document.body.removeChild(t),e}()+"px")},lt=function(){return!!window.MSInputMethodContext&&!!document.documentMode},ct=function(){var t=L(),e=N();t.style.removeProperty("align-items"),e.offsetTop<0&&(t.style.alignItems="flex-start")},ut={},ht=function(t,e){var n=L(),i=N();if(i){null!==t&&"function"==typeof t&&t(i),A(i,x.show),E(i,x.hide);var r=function(){$()?dt(e):(new Promise(function(t){var e=window.scrollX,n=window.scrollY;ut.restoreFocusTimeout=setTimeout(function(){ut.previousActiveElement&&ut.previousActiveElement.focus?(ut.previousActiveElement.focus(),ut.previousActiveElement=null):document.body&&document.body.focus(),t()},100),void 0!==e&&void 0!==n&&window.scrollTo(e,n)}).then(function(){return dt(e)}),ut.keydownTarget.removeEventListener("keydown",ut.keydownHandler,{capture:ut.keydownListenerCapture}),ut.keydownHandlerAdded=!1),n.parentNode&&n.parentNode.removeChild(n),A([document.documentElement,document.body],[x.shown,x["height-auto"],x["no-backdrop"],x["toast-shown"],x["toast-column"]]),X()&&(null!==_.previousBodyPadding&&(document.body.style.paddingRight=_.previousBodyPadding,_.previousBodyPadding=null),function(){if(S(document.body,x.iosfix)){var t=parseInt(document.body.style.top,10);A(document.body,x.iosfix),document.body.style.top="",document.body.scrollTop=-1*t}}(),"undefined"!=typeof window&<()&&window.removeEventListener("resize",ct),h(document.body.children).forEach(function(t){t.hasAttribute("data-previous-aria-hidden")?(t.setAttribute("aria-hidden",t.getAttribute("data-previous-aria-hidden")),t.removeAttribute("data-previous-aria-hidden")):t.removeAttribute("aria-hidden")}))};tt&&!S(i,x.noanimation)?i.addEventListener(tt,function t(){i.removeEventListener(tt,t),S(i,x.hide)&&r()}):r()}},dt=function(t){null!==t&&"function"==typeof t&&setTimeout(function(){t()})};function Ct(t){var e=function t(){for(var e=arguments.length,n=new Array(e),i=0;i.swal2-modal{box-shadow:0 0 10px rgba(0,0,0,.4)}body.swal2-no-backdrop .swal2-shown.swal2-top{top:0;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%)}body.swal2-no-backdrop .swal2-shown.swal2-top-left,body.swal2-no-backdrop .swal2-shown.swal2-top-start{top:0;left:0}body.swal2-no-backdrop .swal2-shown.swal2-top-end,body.swal2-no-backdrop .swal2-shown.swal2-top-right{top:0;right:0}body.swal2-no-backdrop .swal2-shown.swal2-center{top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}body.swal2-no-backdrop .swal2-shown.swal2-center-left,body.swal2-no-backdrop .swal2-shown.swal2-center-start{top:50%;left:0;-webkit-transform:translateY(-50%);transform:translateY(-50%)}body.swal2-no-backdrop .swal2-shown.swal2-center-end,body.swal2-no-backdrop .swal2-shown.swal2-center-right{top:50%;right:0;-webkit-transform:translateY(-50%);transform:translateY(-50%)}body.swal2-no-backdrop .swal2-shown.swal2-bottom{bottom:0;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%)}body.swal2-no-backdrop .swal2-shown.swal2-bottom-left,body.swal2-no-backdrop .swal2-shown.swal2-bottom-start{bottom:0;left:0}body.swal2-no-backdrop .swal2-shown.swal2-bottom-end,body.swal2-no-backdrop .swal2-shown.swal2-bottom-right{right:0;bottom:0}.swal2-container{display:flex;position:fixed;top:0;right:0;bottom:0;left:0;flex-direction:row;align-items:center;justify-content:center;padding:10px;background-color:transparent;z-index:1060;overflow-x:hidden;-webkit-overflow-scrolling:touch}.swal2-container.swal2-top{align-items:flex-start}.swal2-container.swal2-top-left,.swal2-container.swal2-top-start{align-items:flex-start;justify-content:flex-start}.swal2-container.swal2-top-end,.swal2-container.swal2-top-right{align-items:flex-start;justify-content:flex-end}.swal2-container.swal2-center{align-items:center}.swal2-container.swal2-center-left,.swal2-container.swal2-center-start{align-items:center;justify-content:flex-start}.swal2-container.swal2-center-end,.swal2-container.swal2-center-right{align-items:center;justify-content:flex-end}.swal2-container.swal2-bottom{align-items:flex-end}.swal2-container.swal2-bottom-left,.swal2-container.swal2-bottom-start{align-items:flex-end;justify-content:flex-start}.swal2-container.swal2-bottom-end,.swal2-container.swal2-bottom-right{align-items:flex-end;justify-content:flex-end}.swal2-container.swal2-grow-fullscreen>.swal2-modal{display:flex!important;flex:1;align-self:stretch;justify-content:center}.swal2-container.swal2-grow-row>.swal2-modal{display:flex!important;flex:1;align-content:center;justify-content:center}.swal2-container.swal2-grow-column{flex:1;flex-direction:column}.swal2-container.swal2-grow-column.swal2-bottom,.swal2-container.swal2-grow-column.swal2-center,.swal2-container.swal2-grow-column.swal2-top{align-items:center}.swal2-container.swal2-grow-column.swal2-bottom-left,.swal2-container.swal2-grow-column.swal2-bottom-start,.swal2-container.swal2-grow-column.swal2-center-left,.swal2-container.swal2-grow-column.swal2-center-start,.swal2-container.swal2-grow-column.swal2-top-left,.swal2-container.swal2-grow-column.swal2-top-start{align-items:flex-start}.swal2-container.swal2-grow-column.swal2-bottom-end,.swal2-container.swal2-grow-column.swal2-bottom-right,.swal2-container.swal2-grow-column.swal2-center-end,.swal2-container.swal2-grow-column.swal2-center-right,.swal2-container.swal2-grow-column.swal2-top-end,.swal2-container.swal2-grow-column.swal2-top-right{align-items:flex-end}.swal2-container.swal2-grow-column>.swal2-modal{display:flex!important;flex:1;align-content:center;justify-content:center}.swal2-container:not(.swal2-top):not(.swal2-top-start):not(.swal2-top-end):not(.swal2-top-left):not(.swal2-top-right):not(.swal2-center-start):not(.swal2-center-end):not(.swal2-center-left):not(.swal2-center-right):not(.swal2-bottom):not(.swal2-bottom-start):not(.swal2-bottom-end):not(.swal2-bottom-left):not(.swal2-bottom-right):not(.swal2-grow-fullscreen)>.swal2-modal{margin:auto}@media all and (-ms-high-contrast:none),(-ms-high-contrast:active){.swal2-container .swal2-modal{margin:0!important}}.swal2-container.swal2-fade{transition:background-color .1s}.swal2-container.swal2-shown{background-color:rgba(0,0,0,.4)}.swal2-popup{display:none;position:relative;flex-direction:column;justify-content:center;width:32em;max-width:100%;padding:1.25em;border-radius:.3125em;background:#fff;font-family:inherit;font-size:1rem;box-sizing:border-box}.swal2-popup:focus{outline:0}.swal2-popup.swal2-loading{overflow-y:hidden}.swal2-popup .swal2-header{display:flex;flex-direction:column;align-items:center}.swal2-popup .swal2-title{display:block;position:relative;max-width:100%;margin:0 0 .4em;padding:0;color:#595959;font-size:1.875em;font-weight:600;text-align:center;text-transform:none;word-wrap:break-word}.swal2-popup .swal2-actions{flex-wrap:wrap;align-items:center;justify-content:center;margin:1.25em auto 0;z-index:1}.swal2-popup .swal2-actions:not(.swal2-loading) .swal2-styled[disabled]{opacity:.4}.swal2-popup .swal2-actions:not(.swal2-loading) .swal2-styled:hover{background-image:linear-gradient(rgba(0,0,0,.1),rgba(0,0,0,.1))}.swal2-popup .swal2-actions:not(.swal2-loading) .swal2-styled:active{background-image:linear-gradient(rgba(0,0,0,.2),rgba(0,0,0,.2))}.swal2-popup .swal2-actions.swal2-loading .swal2-styled.swal2-confirm{width:2.5em;height:2.5em;margin:.46875em;padding:0;border:.25em solid transparent;border-radius:100%;border-color:transparent;background-color:transparent!important;color:transparent;cursor:default;box-sizing:border-box;-webkit-animation:swal2-rotate-loading 1.5s linear 0s infinite normal;animation:swal2-rotate-loading 1.5s linear 0s infinite normal;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.swal2-popup .swal2-actions.swal2-loading .swal2-styled.swal2-cancel{margin-right:30px;margin-left:30px}.swal2-popup .swal2-actions.swal2-loading :not(.swal2-styled).swal2-confirm::after{display:inline-block;width:15px;height:15px;margin-left:5px;border:3px solid #999;border-radius:50%;border-right-color:transparent;box-shadow:1px 1px 1px #fff;content:'';-webkit-animation:swal2-rotate-loading 1.5s linear 0s infinite normal;animation:swal2-rotate-loading 1.5s linear 0s infinite normal}.swal2-popup .swal2-styled{margin:.3125em;padding:.625em 2em;font-weight:500;box-shadow:none}.swal2-popup .swal2-styled:not([disabled]){cursor:pointer}.swal2-popup .swal2-styled.swal2-confirm{border:0;border-radius:.25em;background:initial;background-color:#3085d6;color:#fff;font-size:1.0625em}.swal2-popup .swal2-styled.swal2-cancel{border:0;border-radius:.25em;background:initial;background-color:#aaa;color:#fff;font-size:1.0625em}.swal2-popup .swal2-styled:focus{outline:0;box-shadow:0 0 0 2px #fff,0 0 0 4px rgba(50,100,150,.4)}.swal2-popup .swal2-styled::-moz-focus-inner{border:0}.swal2-popup .swal2-footer{justify-content:center;margin:1.25em 0 0;padding:1em 0 0;border-top:1px solid #eee;color:#545454;font-size:1em}.swal2-popup .swal2-image{max-width:100%;margin:1.25em auto}.swal2-popup .swal2-close{position:absolute;top:0;right:0;justify-content:center;width:1.2em;height:1.2em;padding:0;transition:color .1s ease-out;border:none;border-radius:0;outline:initial;background:0 0;color:#ccc;font-family:serif;font-size:2.5em;line-height:1.2;cursor:pointer;overflow:hidden}.swal2-popup .swal2-close:hover{-webkit-transform:none;transform:none;color:#f27474}.swal2-popup>.swal2-checkbox,.swal2-popup>.swal2-file,.swal2-popup>.swal2-input,.swal2-popup>.swal2-radio,.swal2-popup>.swal2-select,.swal2-popup>.swal2-textarea{display:none}.swal2-popup .swal2-content{justify-content:center;margin:0;padding:0;color:#545454;font-size:1.125em;font-weight:300;line-height:normal;z-index:1;word-wrap:break-word}.swal2-popup #swal2-content{text-align:center}.swal2-popup .swal2-checkbox,.swal2-popup .swal2-file,.swal2-popup .swal2-input,.swal2-popup .swal2-radio,.swal2-popup .swal2-select,.swal2-popup .swal2-textarea{margin:1em auto}.swal2-popup .swal2-file,.swal2-popup .swal2-input,.swal2-popup .swal2-textarea{width:100%;transition:border-color .3s,box-shadow .3s;border:1px solid #d9d9d9;border-radius:.1875em;font-size:1.125em;box-shadow:inset 0 1px 1px rgba(0,0,0,.06);box-sizing:border-box}.swal2-popup .swal2-file.swal2-inputerror,.swal2-popup .swal2-input.swal2-inputerror,.swal2-popup .swal2-textarea.swal2-inputerror{border-color:#f27474!important;box-shadow:0 0 2px #f27474!important}.swal2-popup .swal2-file:focus,.swal2-popup .swal2-input:focus,.swal2-popup .swal2-textarea:focus{border:1px solid #b4dbed;outline:0;box-shadow:0 0 3px #c4e6f5}.swal2-popup .swal2-file::-webkit-input-placeholder,.swal2-popup .swal2-input::-webkit-input-placeholder,.swal2-popup .swal2-textarea::-webkit-input-placeholder{color:#ccc}.swal2-popup .swal2-file:-ms-input-placeholder,.swal2-popup .swal2-input:-ms-input-placeholder,.swal2-popup .swal2-textarea:-ms-input-placeholder{color:#ccc}.swal2-popup .swal2-file::-ms-input-placeholder,.swal2-popup .swal2-input::-ms-input-placeholder,.swal2-popup .swal2-textarea::-ms-input-placeholder{color:#ccc}.swal2-popup .swal2-file::placeholder,.swal2-popup .swal2-input::placeholder,.swal2-popup .swal2-textarea::placeholder{color:#ccc}.swal2-popup .swal2-range input{width:80%}.swal2-popup .swal2-range output{width:20%;font-weight:600;text-align:center}.swal2-popup .swal2-range input,.swal2-popup .swal2-range output{height:2.625em;margin:1em auto;padding:0;font-size:1.125em;line-height:2.625em}.swal2-popup .swal2-input{height:2.625em;padding:0 .75em}.swal2-popup .swal2-input[type=number]{max-width:10em}.swal2-popup .swal2-file{font-size:1.125em}.swal2-popup .swal2-textarea{height:6.75em;padding:.75em}.swal2-popup .swal2-select{min-width:50%;max-width:100%;padding:.375em .625em;color:#545454;font-size:1.125em}.swal2-popup .swal2-checkbox,.swal2-popup .swal2-radio{align-items:center;justify-content:center}.swal2-popup .swal2-checkbox label,.swal2-popup .swal2-radio label{margin:0 .6em;font-size:1.125em}.swal2-popup .swal2-checkbox input,.swal2-popup .swal2-radio input{margin:0 .4em}.swal2-popup .swal2-validation-message{display:none;align-items:center;justify-content:center;padding:.625em;background:#f0f0f0;color:#666;font-size:1em;font-weight:300;overflow:hidden}.swal2-popup .swal2-validation-message::before{display:inline-block;width:1.5em;min-width:1.5em;height:1.5em;margin:0 .625em;border-radius:50%;background-color:#f27474;color:#fff;font-weight:600;line-height:1.5em;text-align:center;content:'!';zoom:normal}@supports (-ms-accelerator:true){.swal2-range input{width:100%!important}.swal2-range output{display:none}}@media all and (-ms-high-contrast:none),(-ms-high-contrast:active){.swal2-range input{width:100%!important}.swal2-range output{display:none}}@-moz-document url-prefix(){.swal2-close:focus{outline:2px solid rgba(50,100,150,.4)}}.swal2-icon{position:relative;justify-content:center;width:5em;height:5em;margin:1.25em auto 1.875em;border:.25em solid transparent;border-radius:50%;line-height:5em;cursor:default;box-sizing:content-box;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;zoom:normal}.swal2-icon-text{font-size:3.75em}.swal2-icon.swal2-error{border-color:#f27474}.swal2-icon.swal2-error .swal2-x-mark{position:relative;flex-grow:1}.swal2-icon.swal2-error [class^=swal2-x-mark-line]{display:block;position:absolute;top:2.3125em;width:2.9375em;height:.3125em;border-radius:.125em;background-color:#f27474}.swal2-icon.swal2-error [class^=swal2-x-mark-line][class$=left]{left:1.0625em;-webkit-transform:rotate(45deg);transform:rotate(45deg)}.swal2-icon.swal2-error [class^=swal2-x-mark-line][class$=right]{right:1em;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}.swal2-icon.swal2-warning{border-color:#facea8;color:#f8bb86}.swal2-icon.swal2-info{border-color:#9de0f6;color:#3fc3ee}.swal2-icon.swal2-question{border-color:#c9dae1;color:#87adbd}.swal2-icon.swal2-success{border-color:#a5dc86}.swal2-icon.swal2-success [class^=swal2-success-circular-line]{position:absolute;width:3.75em;height:7.5em;-webkit-transform:rotate(45deg);transform:rotate(45deg);border-radius:50%}.swal2-icon.swal2-success [class^=swal2-success-circular-line][class$=left]{top:-.4375em;left:-2.0635em;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);-webkit-transform-origin:3.75em 3.75em;transform-origin:3.75em 3.75em;border-radius:7.5em 0 0 7.5em}.swal2-icon.swal2-success [class^=swal2-success-circular-line][class$=right]{top:-.6875em;left:1.875em;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);-webkit-transform-origin:0 3.75em;transform-origin:0 3.75em;border-radius:0 7.5em 7.5em 0}.swal2-icon.swal2-success .swal2-success-ring{position:absolute;top:-.25em;left:-.25em;width:100%;height:100%;border:.25em solid rgba(165,220,134,.3);border-radius:50%;z-index:2;box-sizing:content-box}.swal2-icon.swal2-success .swal2-success-fix{position:absolute;top:.5em;left:1.625em;width:.4375em;height:5.625em;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);z-index:1}.swal2-icon.swal2-success [class^=swal2-success-line]{display:block;position:absolute;height:.3125em;border-radius:.125em;background-color:#a5dc86;z-index:2}.swal2-icon.swal2-success [class^=swal2-success-line][class$=tip]{top:2.875em;left:.875em;width:1.5625em;-webkit-transform:rotate(45deg);transform:rotate(45deg)}.swal2-icon.swal2-success [class^=swal2-success-line][class$=long]{top:2.375em;right:.5em;width:2.9375em;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}.swal2-progresssteps{align-items:center;margin:0 0 1.25em;padding:0;font-weight:600}.swal2-progresssteps li{display:inline-block;position:relative}.swal2-progresssteps .swal2-progresscircle{width:2em;height:2em;border-radius:2em;background:#3085d6;color:#fff;line-height:2em;text-align:center;z-index:20}.swal2-progresssteps .swal2-progresscircle:first-child{margin-left:0}.swal2-progresssteps .swal2-progresscircle:last-child{margin-right:0}.swal2-progresssteps .swal2-progresscircle.swal2-activeprogressstep{background:#3085d6}.swal2-progresssteps .swal2-progresscircle.swal2-activeprogressstep~.swal2-progresscircle{background:#add8e6}.swal2-progresssteps .swal2-progresscircle.swal2-activeprogressstep~.swal2-progressline{background:#add8e6}.swal2-progresssteps .swal2-progressline{width:2.5em;height:.4em;margin:0 -1px;background:#3085d6;z-index:10}[class^=swal2]{-webkit-tap-highlight-color:transparent}.swal2-show{-webkit-animation:swal2-show .3s;animation:swal2-show .3s}.swal2-show.swal2-noanimation{-webkit-animation:none;animation:none}.swal2-hide{-webkit-animation:swal2-hide .15s forwards;animation:swal2-hide .15s forwards}.swal2-hide.swal2-noanimation{-webkit-animation:none;animation:none}.swal2-rtl .swal2-close{right:auto;left:0}.swal2-animate-success-icon .swal2-success-line-tip{-webkit-animation:swal2-animate-success-line-tip .75s;animation:swal2-animate-success-line-tip .75s}.swal2-animate-success-icon .swal2-success-line-long{-webkit-animation:swal2-animate-success-line-long .75s;animation:swal2-animate-success-line-long .75s}.swal2-animate-success-icon .swal2-success-circular-line-right{-webkit-animation:swal2-rotate-success-circular-line 4.25s ease-in;animation:swal2-rotate-success-circular-line 4.25s ease-in}.swal2-animate-error-icon{-webkit-animation:swal2-animate-error-icon .5s;animation:swal2-animate-error-icon .5s}.swal2-animate-error-icon .swal2-x-mark{-webkit-animation:swal2-animate-error-x-mark .5s;animation:swal2-animate-error-x-mark .5s}@-webkit-keyframes swal2-rotate-loading{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes swal2-rotate-loading{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@media print{body.swal2-shown:not(.swal2-no-backdrop):not(.swal2-toast-shown){overflow-y:scroll!important}body.swal2-shown:not(.swal2-no-backdrop):not(.swal2-toast-shown)>[aria-hidden=true]{display:none}body.swal2-shown:not(.swal2-no-backdrop):not(.swal2-toast-shown) .swal2-container{position:initial!important}}"),("function"==typeof define&&define.amd?define:function(t,e){"undefined"!=typeof module&&module.exports?module.exports=e(require("jquery")):window.toastr=e(window.jQuery)})(["jquery"],function(t){return function(){function e(e,n){return e||(e=a()),(l=t("#"+e.containerId)).length?l:(n&&(l=function(e){return(l=t("
    ").attr("id",e.containerId).addClass(e.positionClass)).appendTo(t(e.target)),l}(e)),l)}function n(e){for(var n=l.children(),r=n.length-1;r>=0;r--)i(t(n[r]),e)}function i(e,n,i){var r=!(!i||!i.force)&&i.force;return!(!e||!r&&0!==t(":focus",e).length||(e[n.hideMethod]({duration:n.hideDuration,easing:n.hideEasing,complete:function(){s(e)}}),0))}function r(t){c&&c(t)}function o(n){function i(t){return null==t&&(t=""),t.replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(//g,">")}function o(e){var n=e&&!1!==f.closeMethod?f.closeMethod:f.hideMethod,i=e&&!1!==f.closeDuration?f.closeDuration:f.hideDuration,o=e&&!1!==f.closeEasing?f.closeEasing:f.hideEasing;if(!t(":focus",m).length||e)return clearTimeout(w.intervalId),m[n]({duration:i,easing:o,complete:function(){s(m),clearTimeout(g),f.onHidden&&"hidden"!==_.state&&f.onHidden(),_.state="hidden",_.endTime=new Date,r(_)}})}function c(){(f.timeOut>0||f.extendedTimeOut>0)&&(g=setTimeout(o,f.extendedTimeOut),w.maxHideTime=parseFloat(f.extendedTimeOut),w.hideEta=(new Date).getTime()+w.maxHideTime)}function d(){clearTimeout(g),w.hideEta=0,m.stop(!0,!0)[f.showMethod]({duration:f.showDuration,easing:f.showEasing})}function C(){var t=(w.hideEta-(new Date).getTime())/w.maxHideTime*100;b.width(t+"%")}var f=a(),p=n.iconClass||f.iconClass;if(void 0!==n.optionsOverride&&(f=t.extend(f,n.optionsOverride),p=n.optionsOverride.iconClass||p),!function(t,e){if(t.preventDuplicates){if(e.message===u)return!0;u=e.message}return!1}(f,n)){h++,l=e(f,!0);var g=null,m=t("
    "),v=t("
    "),y=t("
    "),b=t("
    "),x=t(f.closeHtml),w={intervalId:null,hideEta:null,maxHideTime:null},_={toastId:h,state:"visible",startTime:new Date,options:f,map:n};return n.iconClass&&m.addClass(f.toastClass).addClass(p),function(){if(n.title){var t=n.title;f.escapeHtml&&(t=i(n.title)),v.append(t).addClass(f.titleClass),m.append(v)}}(),function(){if(n.message){var t=n.message;f.escapeHtml&&(t=i(n.message)),y.append(t).addClass(f.messageClass),m.append(y)}}(),f.closeButton&&(x.addClass(f.closeClass).attr("role","button"),m.prepend(x)),f.progressBar&&(b.addClass(f.progressClass),m.prepend(b)),f.rtl&&m.addClass("rtl"),f.newestOnTop?l.prepend(m):l.append(m),function(){var t="";switch(n.iconClass){case"toast-success":case"toast-info":t="polite";break;default:t="assertive"}m.attr("aria-live",t)}(),m.hide(),m[f.showMethod]({duration:f.showDuration,easing:f.showEasing,complete:f.onShown}),f.timeOut>0&&(g=setTimeout(o,f.timeOut),w.maxHideTime=parseFloat(f.timeOut),w.hideEta=(new Date).getTime()+w.maxHideTime,f.progressBar&&(w.intervalId=setInterval(C,10))),f.closeOnHover&&m.hover(d,c),!f.onclick&&f.tapToDismiss&&m.click(o),f.closeButton&&x&&x.click(function(t){t.stopPropagation?t.stopPropagation():void 0!==t.cancelBubble&&!0!==t.cancelBubble&&(t.cancelBubble=!0),f.onCloseClick&&f.onCloseClick(t),o(!0)}),f.onclick&&m.click(function(t){f.onclick(t),o()}),r(_),f.debug&&console&&console.log(_),m}}function a(){return t.extend({},{tapToDismiss:!0,toastClass:"toast",containerId:"toast-container",debug:!1,showMethod:"fadeIn",showDuration:300,showEasing:"swing",onShown:void 0,hideMethod:"fadeOut",hideDuration:1e3,hideEasing:"swing",onHidden:void 0,closeMethod:!1,closeDuration:!1,closeEasing:!1,closeOnHover:!0,extendedTimeOut:1e3,iconClasses:{error:"toast-error",info:"toast-info",success:"toast-success",warning:"toast-warning"},iconClass:"toast-info",positionClass:"toast-top-right",timeOut:5e3,titleClass:"toast-title",messageClass:"toast-message",escapeHtml:!1,target:"body",closeHtml:'',closeClass:"toast-close-button",newestOnTop:!0,preventDuplicates:!1,progressBar:!1,progressClass:"toast-progress",rtl:!1},C.options)}function s(t){l||(l=e()),t.is(":visible")||(t.remove(),t=null,0===l.children().length&&(l.remove(),u=void 0))}var l,c,u,h=0,d={error:"error",info:"info",success:"success",warning:"warning"},C={clear:function(t,r){var o=a();l||e(o),i(t,o,r)||n(o)},remove:function(n){var i=a();return l||e(i),n&&0===t(":focus",n).length?void s(n):void(l.children().length&&l.remove())},error:function(t,e,n){return o({type:d.error,iconClass:a().iconClasses.error,message:t,optionsOverride:n,title:e})},getContainer:e,info:function(t,e,n){return o({type:d.info,iconClass:a().iconClasses.info,message:t,optionsOverride:n,title:e})},options:{},subscribe:function(t){c=t},success:function(t,e,n){return o({type:d.success,iconClass:a().iconClasses.success,message:t,optionsOverride:n,title:e})},version:"2.1.4",warning:function(t,e,n){return o({type:d.warning,iconClass:a().iconClasses.warning,message:t,optionsOverride:n,title:e})}};return C}()}),function(t){"function"==typeof define&&define.amd?define(["jquery"],t):"object"==typeof exports?t(require("jquery")):t(jQuery)}(function(t,e){function n(){return new Date(Date.UTC.apply(Date,arguments))}function i(){var t=new Date;return n(t.getFullYear(),t.getMonth(),t.getDate())}function r(t,e){return t.getUTCFullYear()===e.getUTCFullYear()&&t.getUTCMonth()===e.getUTCMonth()&&t.getUTCDate()===e.getUTCDate()}function o(n,i){return function(){return i!==e&&t.fn.datepicker.deprecated(i),this[n].apply(this,arguments)}}var a,s=(a={get:function(t){return this.slice(t)[0]},contains:function(t){for(var e=t&&t.valueOf(),n=0,i=this.length;n]/g)||[]).length<=0||t(n).length>0)}catch(t){return!1}},_process_options:function(e){this._o=t.extend({},this._o,e);var r=this.o=t.extend({},this._o),o=r.language;f[o]||(o=o.split("-")[0],f[o]||(o=d.language)),r.language=o,r.startView=this._resolveViewName(r.startView),r.minViewMode=this._resolveViewName(r.minViewMode),r.maxViewMode=this._resolveViewName(r.maxViewMode),r.startView=Math.max(this.o.minViewMode,Math.min(this.o.maxViewMode,r.startView)),!0!==r.multidate&&(r.multidate=Number(r.multidate)||!1,!1!==r.multidate&&(r.multidate=Math.max(0,r.multidate))),r.multidateSeparator=String(r.multidateSeparator),r.weekStart%=7,r.weekEnd=(r.weekStart+6)%7;var a=p.parseFormat(r.format);r.startDate!==-1/0&&(r.startDate?r.startDate instanceof Date?r.startDate=this._local_to_utc(this._zero_time(r.startDate)):r.startDate=p.parseDate(r.startDate,a,r.language,r.assumeNearbyYear):r.startDate=-1/0),r.endDate!==1/0&&(r.endDate?r.endDate instanceof Date?r.endDate=this._local_to_utc(this._zero_time(r.endDate)):r.endDate=p.parseDate(r.endDate,a,r.language,r.assumeNearbyYear):r.endDate=1/0),r.daysOfWeekDisabled=this._resolveDaysOfWeek(r.daysOfWeekDisabled||[]),r.daysOfWeekHighlighted=this._resolveDaysOfWeek(r.daysOfWeekHighlighted||[]),r.datesDisabled=r.datesDisabled||[],t.isArray(r.datesDisabled)||(r.datesDisabled=r.datesDisabled.split(",")),r.datesDisabled=t.map(r.datesDisabled,function(t){return p.parseDate(t,a,r.language,r.assumeNearbyYear)});var s=String(r.orientation).toLowerCase().split(/\s+/g),l=r.orientation.toLowerCase();if(s=t.grep(s,function(t){return/^auto|left|right|top|bottom$/.test(t)}),r.orientation={x:"auto",y:"auto"},l&&"auto"!==l)if(1===s.length)switch(s[0]){case"top":case"bottom":r.orientation.y=s[0];break;case"left":case"right":r.orientation.x=s[0]}else l=t.grep(s,function(t){return/^left|right$/.test(t)}),r.orientation.x=l[0]||"auto",l=t.grep(s,function(t){return/^top|bottom$/.test(t)}),r.orientation.y=l[0]||"auto";else;if(r.defaultViewDate instanceof Date||"string"==typeof r.defaultViewDate)r.defaultViewDate=p.parseDate(r.defaultViewDate,a,r.language,r.assumeNearbyYear);else if(r.defaultViewDate){var c=r.defaultViewDate.year||(new Date).getFullYear(),u=r.defaultViewDate.month||0,h=r.defaultViewDate.day||1;r.defaultViewDate=n(c,u,h)}else r.defaultViewDate=i()},_events:[],_secondaryEvents:[],_applyEvents:function(t){for(var n,i,r,o=0;or?(this.picker.addClass("datepicker-orient-right"),d+=h-e):this.o.rtl?this.picker.addClass("datepicker-orient-right"):this.picker.addClass("datepicker-orient-left");var f=this.o.orientation.y;if("auto"===f&&(f=-o+C-n<0?"bottom":"top"),this.picker.addClass("datepicker-orient-"+f),"top"===f?C-=n+parseInt(this.picker.css("padding-top")):C+=u,this.o.rtl){var p=r-(d+h);this.picker.css({top:C,right:p,zIndex:l})}else this.picker.css({top:C,left:d,zIndex:l});return this},_allow_update:!0,update:function(){if(!this._allow_update)return this;var e=this.dates.copy(),n=[],i=!1;return arguments.length?(t.each(arguments,t.proxy(function(t,e){e instanceof Date&&(e=this._local_to_utc(e)),n.push(e)},this)),i=!0):(n=(n=this.isInput?this.element.val():this.element.data("date")||this.inputField.val())&&this.o.multidate?n.split(this.o.multidateSeparator):[n],delete this.element.data().date),n=t.map(n,t.proxy(function(t){return p.parseDate(t,this.o.format,this.o.language,this.o.assumeNearbyYear)},this)),n=t.grep(n,t.proxy(function(t){return!this.dateWithinRange(t)||!t},this),!0),this.dates.replace(n),this.o.updateViewDate&&(this.dates.length?this.viewDate=new Date(this.dates.get(-1)):this.viewDatethis.o.endDate?this.viewDate=new Date(this.o.endDate):this.viewDate=this.o.defaultViewDate),i?(this.setValue(),this.element.change()):this.dates.length&&String(e)!==String(this.dates)&&i&&(this._trigger("changeDate"),this.element.change()),!this.dates.length&&e.length&&(this._trigger("clearDate"),this.element.change()),this.fill(),this},fillDow:function(){if(this.o.showWeekDays){var e=this.o.weekStart,n="";for(this.o.calendarWeeks&&(n+=' ');e";n+="",this.picker.find(".datepicker-days thead").append(n)}},fillMonths:function(){for(var t=this._utc_to_local(this.viewDate),e="",n=0;n<12;n++)e+=''+f[this.o.language].monthsShort[n]+"";this.picker.find(".datepicker-months td").html(e)},setRange:function(e){e&&e.length?this.range=t.map(e,function(t){return t.valueOf()}):delete this.range,this.fill()},getClassNames:function(e){var n=[],o=this.viewDate.getUTCFullYear(),a=this.viewDate.getUTCMonth(),s=i();return e.getUTCFullYear()o||e.getUTCFullYear()===o&&e.getUTCMonth()>a)&&n.push("new"),this.focusDate&&e.valueOf()===this.focusDate.valueOf()&&n.push("focused"),this.o.todayHighlight&&r(e,s)&&n.push("today"),-1!==this.dates.contains(e)&&n.push("active"),this.dateWithinRange(e)||n.push("disabled"),this.dateIsDisabled(e)&&n.push("disabled","disabled-date"),-1!==t.inArray(e.getUTCDay(),this.o.daysOfWeekHighlighted)&&n.push("highlighted"),this.range&&(e>this.range[0]&&es)&&c.push("disabled"),y===m&&c.push("focused"),l!==t.noop&&((h=l(new Date(y,0,1)))===e?h={}:"boolean"==typeof h?h={enabled:h}:"string"==typeof h&&(h={classes:h}),!1===h.enabled&&c.push("disabled"),h.classes&&(c=c.concat(h.classes.split(/\s+/))),h.tooltip&&(u=h.tooltip)),d+='"+y+"";f.find(".datepicker-switch").text(p+"-"+g),f.find("td").html(d)},fill:function(){var i,r,o=new Date(this.viewDate),a=o.getUTCFullYear(),s=o.getUTCMonth(),l=this.o.startDate!==-1/0?this.o.startDate.getUTCFullYear():-1/0,c=this.o.startDate!==-1/0?this.o.startDate.getUTCMonth():-1/0,u=this.o.endDate!==1/0?this.o.endDate.getUTCFullYear():1/0,h=this.o.endDate!==1/0?this.o.endDate.getUTCMonth():1/0,d=f[this.o.language].today||f.en.today||"",C=f[this.o.language].clear||f.en.clear||"",g=f[this.o.language].titleFormat||f.en.titleFormat;if(!isNaN(a)&&!isNaN(s)){this.picker.find(".datepicker-days .datepicker-switch").text(p.formatDate(o,g,this.o.language)),this.picker.find("tfoot .today").text(d).css("display",!0===this.o.todayBtn||"linked"===this.o.todayBtn?"table-cell":"none"),this.picker.find("tfoot .clear").text(C).css("display",!0===this.o.clearBtn?"table-cell":"none"),this.picker.find("thead .datepicker-title").text(this.o.title).css("display","string"==typeof this.o.title&&""!==this.o.title?"table-cell":"none"),this.updateNavArrows(),this.fillMonths();var m=n(a,s,0),v=m.getUTCDate();m.setUTCDate(v-(m.getUTCDay()-this.o.weekStart+7)%7);var y=new Date(m);m.getUTCFullYear()<100&&y.setUTCFullYear(m.getUTCFullYear()),y.setUTCDate(y.getUTCDate()+42),y=y.valueOf();for(var b,x,w=[];m.valueOf()"),this.o.calendarWeeks)){var _=new Date(+m+(this.o.weekStart-b-7)%7*864e5),S=new Date(Number(_)+(11-_.getUTCDay())%7*864e5),k=new Date(Number(k=n(S.getUTCFullYear(),0,1))+(11-k.getUTCDay())%7*864e5),T=(S-k)/864e5/7+1;w.push(''+T+"")}(x=this.getClassNames(m)).push("day");var E=m.getUTCDate();this.o.beforeShowDay!==t.noop&&((r=this.o.beforeShowDay(this._utc_to_local(m)))===e?r={}:"boolean"==typeof r?r={enabled:r}:"string"==typeof r&&(r={classes:r}),!1===r.enabled&&x.push("disabled"),r.classes&&(x=x.concat(r.classes.split(/\s+/))),r.tooltip&&(i=r.tooltip),r.content&&(E=r.content)),x=t.isFunction(t.uniqueSort)?t.uniqueSort(x):t.unique(x),w.push(''+E+""),i=null,b===this.o.weekEnd&&w.push(""),m.setUTCDate(m.getUTCDate()+1)}this.picker.find(".datepicker-days tbody").html(w.join(""));var A=f[this.o.language].monthsTitle||f.en.monthsTitle||"Months",M=this.picker.find(".datepicker-months").find(".datepicker-switch").text(this.o.maxViewMode<2?A:a).end().find("tbody span").removeClass("active");if(t.each(this.dates,function(t,e){e.getUTCFullYear()===a&&M.eq(e.getUTCMonth()).addClass("active")}),(au)&&M.addClass("disabled"),a===l&&M.slice(0,c).addClass("disabled"),a===u&&M.slice(h+1).addClass("disabled"),this.o.beforeShowMonth!==t.noop){var D=this;t.each(M,function(n,i){var r=new Date(a,n,1),o=D.o.beforeShowMonth(r);o===e?o={}:"boolean"==typeof o?o={enabled:o}:"string"==typeof o&&(o={classes:o}),!1!==o.enabled||t(i).hasClass("disabled")||t(i).addClass("disabled"),o.classes&&t(i).addClass(o.classes),o.tooltip&&t(i).prop("title",o.tooltip)})}this._fill_yearsView(".datepicker-years","year",10,a,l,u,this.o.beforeShowYear),this._fill_yearsView(".datepicker-decades","decade",100,a,l,u,this.o.beforeShowDecade),this._fill_yearsView(".datepicker-centuries","century",1e3,a,l,u,this.o.beforeShowCentury)}},updateNavArrows:function(){if(this._allow_update){var t,e,n=new Date(this.viewDate),i=n.getUTCFullYear(),r=n.getUTCMonth(),o=this.o.startDate!==-1/0?this.o.startDate.getUTCFullYear():-1/0,a=this.o.startDate!==-1/0?this.o.startDate.getUTCMonth():-1/0,s=this.o.endDate!==1/0?this.o.endDate.getUTCFullYear():1/0,l=this.o.endDate!==1/0?this.o.endDate.getUTCMonth():1/0,c=1;switch(this.viewMode){case 4:c*=10;case 3:c*=10;case 2:c*=10;case 1:t=Math.floor(i/c)*cs;break;case 0:t=i<=o&&r=s&&r>l}this.picker.find(".prev").toggleClass("disabled",t),this.picker.find(".next").toggleClass("disabled",e)}},click:function(e){var r,o,a;e.preventDefault(),e.stopPropagation(),(r=t(e.target)).hasClass("datepicker-switch")&&this.viewMode!==this.o.maxViewMode&&this.setViewMode(this.viewMode+1),r.hasClass("today")&&!r.hasClass("day")&&(this.setViewMode(0),this._setDate(i(),"linked"===this.o.todayBtn?null:"view")),r.hasClass("clear")&&this.clearDates(),r.hasClass("disabled")||(r.hasClass("month")||r.hasClass("year")||r.hasClass("decade")||r.hasClass("century"))&&(this.viewDate.setUTCDate(1),1,1===this.viewMode?(a=r.parent().find("span").index(r),o=this.viewDate.getUTCFullYear(),this.viewDate.setUTCMonth(a)):(a=0,o=Number(r.text()),this.viewDate.setUTCFullYear(o)),this._trigger(p.viewModes[this.viewMode-1].e,this.viewDate),this.viewMode===this.o.minViewMode?this._setDate(n(o,a,1)):(this.setViewMode(this.viewMode-1),this.fill())),this.picker.is(":visible")&&this._focused_from&&this._focused_from.focus(),delete this._focused_from},dayCellClick:function(e){var n=t(e.currentTarget).data("date"),i=new Date(n);this.o.updateViewDate&&(i.getUTCFullYear()!==this.viewDate.getUTCFullYear()&&this._trigger("changeYear",this.viewDate),i.getUTCMonth()!==this.viewDate.getUTCMonth()&&this._trigger("changeMonth",this.viewDate)),this._setDate(i)},navArrowsClick:function(e){var n=t(e.currentTarget).hasClass("prev")?-1:1;0!==this.viewMode&&(n*=12*p.viewModes[this.viewMode].navStep),this.viewDate=this.moveMonth(this.viewDate,n),this._trigger(p.viewModes[this.viewMode].e,this.viewDate),this.fill()},_toggle_multidate:function(t){var e=this.dates.contains(t);if(t||this.dates.clear(),-1!==e?(!0===this.o.multidate||this.o.multidate>1||this.o.toggleActive)&&this.dates.remove(e):!1===this.o.multidate?(this.dates.clear(),this.dates.push(t)):this.dates.push(t),"number"==typeof this.o.multidate)for(;this.dates.length>this.o.multidate;)this.dates.remove(0)},_setDate:function(t,e){e&&"date"!==e||this._toggle_multidate(t&&new Date(t)),(!e&&this.o.updateViewDate||"view"===e)&&(this.viewDate=t&&new Date(t)),this.fill(),this.setValue(),e&&"view"===e||this._trigger("changeDate"),this.inputField.trigger("change"),!this.o.autoclose||e&&"date"!==e||this.hide()},moveDay:function(t,e){var n=new Date(t);return n.setUTCDate(t.getUTCDate()+e),n},moveWeek:function(t,e){return this.moveDay(t,7*e)},moveMonth:function(t,e){if(!(n=t)||isNaN(n.getTime()))return this.o.defaultViewDate;var n;if(!e)return t;var i,r,o=new Date(t.valueOf()),a=o.getUTCDate(),s=o.getUTCMonth(),l=Math.abs(e);if(e=e>0?1:-1,1===l)r=-1===e?function(){return o.getUTCMonth()===s}:function(){return o.getUTCMonth()!==i},i=s+e,o.setUTCMonth(i),i=(i+12)%12;else{for(var c=0;c0},dateWithinRange:function(t){return t>=this.o.startDate&&t<=this.o.endDate},keydown:function(t){if(this.picker.is(":visible")){var e,n,i=!1,r=this.focusDate||this.viewDate;switch(t.keyCode){case 27:this.focusDate?(this.focusDate=null,this.viewDate=this.dates.get(-1)||this.viewDate,this.fill()):this.hide(),t.preventDefault(),t.stopPropagation();break;case 37:case 38:case 39:case 40:if(!this.o.keyboardNavigation||7===this.o.daysOfWeekDisabled.length)break;e=37===t.keyCode||38===t.keyCode?-1:1,0===this.viewMode?t.ctrlKey?(n=this.moveAvailableDate(r,e,"moveYear"))&&this._trigger("changeYear",this.viewDate):t.shiftKey?(n=this.moveAvailableDate(r,e,"moveMonth"))&&this._trigger("changeMonth",this.viewDate):37===t.keyCode||39===t.keyCode?n=this.moveAvailableDate(r,e,"moveDay"):this.weekOfDateIsDisabled(r)||(n=this.moveAvailableDate(r,e,"moveWeek")):1===this.viewMode?(38!==t.keyCode&&40!==t.keyCode||(e*=4),n=this.moveAvailableDate(r,e,"moveMonth")):2===this.viewMode&&(38!==t.keyCode&&40!==t.keyCode||(e*=4),n=this.moveAvailableDate(r,e,"moveYear")),n&&(this.focusDate=this.viewDate=n,this.setValue(),this.fill(),t.preventDefault());break;case 13:if(!this.o.forceParse)break;r=this.focusDate||this.dates.get(-1)||this.viewDate,this.o.keyboardNavigation&&(this._toggle_multidate(r),i=!0),this.focusDate=null,this.viewDate=this.dates.get(-1)||this.viewDate,this.setValue(),this.fill(),this.picker.is(":visible")&&(t.preventDefault(),t.stopPropagation(),this.o.autoclose&&this.hide());break;case 9:this.focusDate=null,this.viewDate=this.dates.get(-1)||this.viewDate,this.fill(),this.hide()}i&&(this.dates.length?this._trigger("changeDate"):this._trigger("clearDate"),this.inputField.trigger("change"))}else 40!==t.keyCode&&27!==t.keyCode||(this.show(),t.stopPropagation())},setViewMode:function(t){this.viewMode=t,this.picker.children("div").hide().filter(".datepicker-"+p.viewModes[this.viewMode].clsName).show(),this.updateNavArrows(),this._trigger("changeViewMode",new Date(this.viewDate))}};var c=function(e,n){t.data(e,"datepicker",this),this.element=t(e),this.inputs=t.map(n.inputs,function(t){return t.jquery?t[0]:t}),delete n.inputs,this.keepEmptyValues=n.keepEmptyValues,delete n.keepEmptyValues,h.call(t(this.inputs),n).on("changeDate",t.proxy(this.dateUpdated,this)),this.pickers=t.map(this.inputs,function(e){return t.data(e,"datepicker")}),this.updateDates()};c.prototype={updateDates:function(){this.dates=t.map(this.pickers,function(t){return t.getUTCDate()}),this.updateRanges()},updateRanges:function(){var e=t.map(this.dates,function(t){return t.valueOf()});t.each(this.pickers,function(t,n){n.setRange(e)})},clearDates:function(){t.each(this.pickers,function(t,e){e.clearDates()})},dateUpdated:function(n){if(!this.updating){this.updating=!0;var i=t.data(n.target,"datepicker");if(i!==e){var r=i.getUTCDate(),o=this.keepEmptyValues,a=t.inArray(n.target,this.inputs),s=a-1,l=a+1,c=this.inputs.length;if(-1!==a){if(t.each(this.pickers,function(t,e){e.getUTCDate()||e!==i&&o||e.setUTCDate(r)}),r=0&&rthis.dates[l])for(;lthis.dates[l];)this.pickers[l++].setUTCDate(r);this.updateDates(),delete this.updating}}}},destroy:function(){t.map(this.pickers,function(t){t.destroy()}),t(this.inputs).off("changeDate",this.dateUpdated),delete this.element.data().datepicker},remove:o("destroy","Method `remove` is deprecated and will be removed in version 2.0. Use `destroy` instead")};var u=t.fn.datepicker,h=function(n){var i,r=Array.apply(null,arguments);if(r.shift(),this.each(function(){var e=t(this),o=e.data("datepicker"),a="object"==typeof n&&n;if(!o){var s=function(e,n){var i=t(e).data(),r={},o=new RegExp("^"+n.toLowerCase()+"([A-Z])");function a(t,e){return e.toLowerCase()}for(var s in n=new RegExp("^"+n.toLowerCase()),i)n.test(s)&&(r[s.replace(o,a)]=i[s]);return r}(this,"date"),u=function(e){var n={};if(f[e]||(e=e.split("-")[0],f[e])){var i=f[e];return t.each(C,function(t,e){e in i&&(n[e]=i[e])}),n}}(t.extend({},d,s,a).language),h=t.extend({},d,u,s,a);e.hasClass("input-daterange")||h.inputs?(t.extend(h,{inputs:h.inputs||e.find("input").toArray()}),o=new c(this,h)):o=new l(this,h),e.data("datepicker",o)}"string"==typeof n&&"function"==typeof o[n]&&(i=o[n].apply(o,r))}),i===e||i instanceof l||i instanceof c)return this;if(this.length>1)throw new Error("Using only allowed for the collection of a single element ("+n+" function)");return i};t.fn.datepicker=h;var d=t.fn.datepicker.defaults={assumeNearbyYear:!1,autoclose:!1,beforeShowDay:t.noop,beforeShowMonth:t.noop,beforeShowYear:t.noop,beforeShowDecade:t.noop,beforeShowCentury:t.noop,calendarWeeks:!1,clearBtn:!1,toggleActive:!1,daysOfWeekDisabled:[],daysOfWeekHighlighted:[],datesDisabled:[],endDate:1/0,forceParse:!0,format:"mm/dd/yyyy",keepEmptyValues:!1,keyboardNavigation:!0,language:"en",minViewMode:0,maxViewMode:4,multidate:!1,multidateSeparator:",",orientation:"auto",rtl:!1,startDate:-1/0,startView:0,todayBtn:!1,todayHighlight:!1,updateViewDate:!0,weekStart:0,disableTouchKeyboard:!1,enableOnReadonly:!0,showOnFocus:!0,zIndexOffset:10,container:"body",immediateUpdates:!1,title:"",templates:{leftArrow:"«",rightArrow:"»"},showWeekDays:!0},C=t.fn.datepicker.locale_opts=["format","rtl","weekStart"];t.fn.datepicker.Constructor=l;var f=t.fn.datepicker.dates={en:{days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],daysShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],daysMin:["Su","Mo","Tu","We","Th","Fr","Sa"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],monthsShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],today:"Today",clear:"Clear",titleFormat:"MM yyyy"}},p={viewModes:[{names:["days","month"],clsName:"days",e:"changeMonth"},{names:["months","year"],clsName:"months",e:"changeYear",navStep:1},{names:["years","decade"],clsName:"years",e:"changeDecade",navStep:10},{names:["decades","century"],clsName:"decades",e:"changeCentury",navStep:100},{names:["centuries","millennium"],clsName:"centuries",e:"changeMillennium",navStep:1e3}],validParts:/dd?|DD?|mm?|MM?|yy(?:yy)?/g,nonpunctuation:/[^ -\/:-@\u5e74\u6708\u65e5\[-`{-~\t\n\r]+/g,parseFormat:function(t){if("function"==typeof t.toValue&&"function"==typeof t.toDisplay)return t;var e=t.replace(this.validParts,"\0").split("\0"),n=t.match(this.validParts);if(!e||!e.length||!n||0===n.length)throw new Error("Invalid date format.");return{separators:e,parts:n}},parseDate:function(n,r,o,a){if(!n)return e;if(n instanceof Date)return n;if("string"==typeof r&&(r=p.parseFormat(r)),r.toValue)return r.toValue(n,r,o);var s,c,u,h,d,C={d:"moveDay",m:"moveMonth",w:"moveWeek",y:"moveYear"},g={yesterday:"-1d",today:"+0d",tomorrow:"+1d"};if(n in g&&(n=g[n]),/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/i.test(n)){for(s=n.match(/([\-+]\d+)([dmwy])/gi),n=new Date,h=0;h(new Date).getFullYear()+i&&(n-=100),n):e);var n,i},m:function(t,e){if(isNaN(t))return t;for(e-=1;e<0;)e+=12;for(e%=12,t.setUTCMonth(e);t.getUTCMonth()!==e;)t.setUTCDate(t.getUTCDate()-1);return t},d:function(t,e){return t.setUTCDate(e)}};x.yy=x.yyyy,x.M=x.MM=x.mm=x.m,x.dd=x.d,n=i();var w=r.parts.slice();function _(){var t=this.slice(0,s[h].length),e=s[h].slice(0,t.length);return t.toLowerCase()===e.toLowerCase()}if(s.length!==w.length&&(w=t(w).filter(function(e,n){return-1!==t.inArray(n,b)}).toArray()),s.length===w.length){var S,k,T;for(h=0,S=w.length;h'+d.templates.leftArrow+''+d.templates.rightArrow+"",contTemplate:'',footTemplate:''};p.template='
    '+p.headTemplate+""+p.footTemplate+'
    '+p.headTemplate+p.contTemplate+p.footTemplate+'
    '+p.headTemplate+p.contTemplate+p.footTemplate+'
    '+p.headTemplate+p.contTemplate+p.footTemplate+'
    '+p.headTemplate+p.contTemplate+p.footTemplate+"
    ",t.fn.datepicker.DPGlobal=p,t.fn.datepicker.noConflict=function(){return t.fn.datepicker=u,this},t.fn.datepicker.version="1.8.0",t.fn.datepicker.deprecated=function(t){var e=window.console;e&&e.warn&&e.warn("DEPRECATED: "+t)},t(document).on("focus.datepicker.data-api click.datepicker.data-api",'[data-provide="datepicker"]',function(e){var n=t(this);n.data("datepicker")||(e.preventDefault(),h.call(n,"show"))}),t(function(){h.call(t('[data-provide="datepicker-inline"]'))})}),function(t){"function"==typeof define&&define.amd?define(["jquery"],t):"object"==typeof module&&module.exports?module.exports=function(e,n){return void 0===n&&(n="undefined"!=typeof window?require("jquery"):require("jquery")(e)),t(n),n}:t(jQuery)}(function(t){var e=function(){if(t&&t.fn&&t.fn.select2&&t.fn.select2.amd)var e=t.fn.select2.amd;var n,i,r,o;return e&&e.requirejs||(e?i=e:e={},function(t){var e,o,a,s,l={},c={},u={},h={},d=Object.prototype.hasOwnProperty,C=[].slice,f=/\.js$/;function p(t,e){return d.call(t,e)}function g(t,e){var n,i,r,o,a,s,l,c,h,d,C,p=e&&e.split("/"),g=u.map,m=g&&g["*"]||{};if(t){for(a=(t=t.split("/")).length-1,u.nodeIdCompat&&f.test(t[a])&&(t[a]=t[a].replace(f,"")),"."===t[0].charAt(0)&&p&&(t=p.slice(0,p.length-1).concat(t)),h=0;h0&&(t.splice(h-1,2),h-=2)}t=t.join("/")}if((p||m)&&g){for(h=(n=t.split("/")).length;h>0;h-=1){if(i=n.slice(0,h).join("/"),p)for(d=p.length;d>0;d-=1)if((r=g[p.slice(0,d).join("/")])&&(r=r[i])){o=r,s=h;break}if(o)break;!l&&m&&m[i]&&(l=m[i],c=h)}!o&&l&&(o=l,s=c),o&&(n.splice(0,s,o),t=n.join("/"))}return t}function m(e,n){return function(){var i=C.call(arguments,0);return"string"!=typeof i[0]&&1===i.length&&i.push(null),o.apply(t,i.concat([e,n]))}}function v(t){return function(e){l[t]=e}}function y(n){if(p(c,n)){var i=c[n];delete c[n],h[n]=!0,e.apply(t,i)}if(!p(l,n)&&!p(h,n))throw new Error("No "+n);return l[n]}function b(t){var e,n=t?t.indexOf("!"):-1;return n>-1&&(e=t.substring(0,n),t=t.substring(n+1,t.length)),[e,t]}function x(t){return t?b(t):[]}function w(t){return function(){return u&&u.config&&u.config[t]||{}}}a=function(t,e){var n,i,r=b(t),o=r[0],a=e[1];return t=r[1],o&&(n=y(o=g(o,a))),o?t=n&&n.normalize?n.normalize(t,(i=a,function(t){return g(t,i)})):g(t,a):(o=(r=b(t=g(t,a)))[0],t=r[1],o&&(n=y(o))),{f:o?o+"!"+t:t,n:t,pr:o,p:n}},s={require:function(t){return m(t)},exports:function(t){var e=l[t];return void 0!==e?e:l[t]={}},module:function(t){return{id:t,uri:"",exports:l[t],config:w(t)}}},e=function(e,n,i,r){var o,u,d,C,f,g,b,w=[],_=typeof i;if(g=x(r=r||e),"undefined"===_||"function"===_){for(n=!n.length&&i.length?["require","exports","module"]:n,f=0;f0&&(n.call(arguments,t.prototype.constructor),r=e.prototype.constructor),r.apply(this,arguments)}e.displayName=t.displayName,o.prototype=new function(){this.constructor=o};for(var a=0;a":">",'"':""","'":"'","/":"/"};return"string"!=typeof t?t:String(t).replace(/[&<>"'\/\\]/g,function(t){return e[t]})},e.appendMany=function(e,n){if("1.7"===t.fn.jquery.substr(0,3)){var i=t();t.map(n,function(t){i=i.add(t)}),n=i}e.append(n)},e.__cache={};var r=0;return e.GetUniqueElementId=function(t){var e=t.getAttribute("data-select2-id");return null==e&&(t.id?(e=t.id,t.setAttribute("data-select2-id",e)):(t.setAttribute("data-select2-id",++r),e=r.toString())),e},e.StoreData=function(t,n,i){var r=e.GetUniqueElementId(t);e.__cache[r]||(e.__cache[r]={}),e.__cache[r][n]=i},e.GetData=function(n,i){var r=e.GetUniqueElementId(n);return i?e.__cache[r]&&null!=e.__cache[r][i]?e.__cache[r][i]:t(n).data(i):e.__cache[r]},e.RemoveData=function(t){var n=e.GetUniqueElementId(t);null!=e.__cache[n]&&delete e.__cache[n]},e}),e.define("select2/results",["jquery","./utils"],function(t,e){function n(t,e,i){this.$element=t,this.data=i,this.options=e,n.__super__.constructor.call(this)}return e.Extend(n,e.Observable),n.prototype.render=function(){var e=t('
      ');return this.options.get("multiple")&&e.attr("aria-multiselectable","true"),this.$results=e,e},n.prototype.clear=function(){this.$results.empty()},n.prototype.displayMessage=function(e){var n=this.options.get("escapeMarkup");this.clear(),this.hideLoading();var i=t('
    • '),r=this.options.get("translations").get(e.message);i.append(n(r(e.args))),i[0].className+=" select2-results__message",this.$results.append(i)},n.prototype.hideMessages=function(){this.$results.find(".select2-results__message").remove()},n.prototype.append=function(t){this.hideLoading();var e=[];if(null!=t.results&&0!==t.results.length){t.results=this.sort(t.results);for(var n=0;n0?e.first().trigger("mouseenter"):t.first().trigger("mouseenter"),this.ensureHighlightVisible()},n.prototype.setClasses=function(){var n=this;this.data.current(function(i){var r=t.map(i,function(t){return t.id.toString()});n.$results.find(".select2-results__option[aria-selected]").each(function(){var n=t(this),i=e.GetData(this,"data"),o=""+i.id;null!=i.element&&i.element.selected||null==i.element&&t.inArray(o,r)>-1?n.attr("aria-selected","true"):n.attr("aria-selected","false")})})},n.prototype.showLoading=function(t){this.hideLoading();var e={disabled:!0,loading:!0,text:this.options.get("translations").get("searching")(t)},n=this.option(e);n.className+=" loading-results",this.$results.prepend(n)},n.prototype.hideLoading=function(){this.$results.find(".loading-results").remove()},n.prototype.option=function(n){var i=document.createElement("li");i.className="select2-results__option";var r={role:"treeitem","aria-selected":"false"};for(var o in n.disabled&&(delete r["aria-selected"],r["aria-disabled"]="true"),null==n.id&&delete r["aria-selected"],null!=n._resultId&&(i.id=n._resultId),n.title&&(i.title=n.title),n.children&&(r.role="group",r["aria-label"]=n.text,delete r["aria-selected"]),r){var a=r[o];i.setAttribute(o,a)}if(n.children){var s=t(i),l=document.createElement("strong");l.className="select2-results__group";t(l);this.template(n,l);for(var c=[],u=0;u",{class:"select2-results__options select2-results__options--nested"});C.append(c),s.append(l),s.append(C)}else this.template(n,i);return e.StoreData(i,"data",n),i},n.prototype.bind=function(n,i){var r=this,o=n.id+"-results";this.$results.attr("id",o),n.on("results:all",function(t){r.clear(),r.append(t.data),n.isOpen()&&(r.setClasses(),r.highlightFirstItem())}),n.on("results:append",function(t){r.append(t.data),n.isOpen()&&r.setClasses()}),n.on("query",function(t){r.hideMessages(),r.showLoading(t)}),n.on("select",function(){n.isOpen()&&(r.setClasses(),r.highlightFirstItem())}),n.on("unselect",function(){n.isOpen()&&(r.setClasses(),r.highlightFirstItem())}),n.on("open",function(){r.$results.attr("aria-expanded","true"),r.$results.attr("aria-hidden","false"),r.setClasses(),r.ensureHighlightVisible()}),n.on("close",function(){r.$results.attr("aria-expanded","false"),r.$results.attr("aria-hidden","true"),r.$results.removeAttr("aria-activedescendant")}),n.on("results:toggle",function(){var t=r.getHighlightedResults();0!==t.length&&t.trigger("mouseup")}),n.on("results:select",function(){var t=r.getHighlightedResults();if(0!==t.length){var n=e.GetData(t[0],"data");"true"==t.attr("aria-selected")?r.trigger("close",{}):r.trigger("select",{data:n})}}),n.on("results:previous",function(){var t=r.getHighlightedResults(),e=r.$results.find("[aria-selected]"),n=e.index(t);if(!(n<=0)){var i=n-1;0===t.length&&(i=0);var o=e.eq(i);o.trigger("mouseenter");var a=r.$results.offset().top,s=o.offset().top,l=r.$results.scrollTop()+(s-a);0===i?r.$results.scrollTop(0):s-a<0&&r.$results.scrollTop(l)}}),n.on("results:next",function(){var t=r.getHighlightedResults(),e=r.$results.find("[aria-selected]"),n=e.index(t)+1;if(!(n>=e.length)){var i=e.eq(n);i.trigger("mouseenter");var o=r.$results.offset().top+r.$results.outerHeight(!1),a=i.offset().top+i.outerHeight(!1),s=r.$results.scrollTop()+a-o;0===n?r.$results.scrollTop(0):a>o&&r.$results.scrollTop(s)}}),n.on("results:focus",function(t){t.element.addClass("select2-results__option--highlighted")}),n.on("results:message",function(t){r.displayMessage(t)}),t.fn.mousewheel&&this.$results.on("mousewheel",function(t){var e=r.$results.scrollTop(),n=r.$results.get(0).scrollHeight-e+t.deltaY,i=t.deltaY>0&&e-t.deltaY<=0,o=t.deltaY<0&&n<=r.$results.height();i?(r.$results.scrollTop(0),t.preventDefault(),t.stopPropagation()):o&&(r.$results.scrollTop(r.$results.get(0).scrollHeight-r.$results.height()),t.preventDefault(),t.stopPropagation())}),this.$results.on("mouseup",".select2-results__option[aria-selected]",function(n){var i=t(this),o=e.GetData(this,"data");"true"!==i.attr("aria-selected")?r.trigger("select",{originalEvent:n,data:o}):r.options.get("multiple")?r.trigger("unselect",{originalEvent:n,data:o}):r.trigger("close",{})}),this.$results.on("mouseenter",".select2-results__option[aria-selected]",function(n){var i=e.GetData(this,"data");r.getHighlightedResults().removeClass("select2-results__option--highlighted"),r.trigger("results:focus",{data:i,element:t(this)})})},n.prototype.getHighlightedResults=function(){return this.$results.find(".select2-results__option--highlighted")},n.prototype.destroy=function(){this.$results.remove()},n.prototype.ensureHighlightVisible=function(){var t=this.getHighlightedResults();if(0!==t.length){var e=this.$results.find("[aria-selected]").index(t),n=this.$results.offset().top,i=t.offset().top,r=this.$results.scrollTop()+(i-n),o=i-n;r-=2*t.outerHeight(!1),e<=2?this.$results.scrollTop(0):(o>this.$results.outerHeight()||o<0)&&this.$results.scrollTop(r)}},n.prototype.template=function(e,n){var i=this.options.get("templateResult"),r=this.options.get("escapeMarkup"),o=i(e,n);null==o?n.style.display="none":"string"==typeof o?n.innerHTML=r(o):t(n).append(o)},n}),e.define("select2/keys",[],function(){return{BACKSPACE:8,TAB:9,ENTER:13,SHIFT:16,CTRL:17,ALT:18,ESC:27,SPACE:32,PAGE_UP:33,PAGE_DOWN:34,END:35,HOME:36,LEFT:37,UP:38,RIGHT:39,DOWN:40,DELETE:46}}),e.define("select2/selection/base",["jquery","../utils","../keys"],function(t,e,n){function i(t,e){this.$element=t,this.options=e,i.__super__.constructor.call(this)}return e.Extend(i,e.Observable),i.prototype.render=function(){var n=t('');return this._tabindex=0,null!=e.GetData(this.$element[0],"old-tabindex")?this._tabindex=e.GetData(this.$element[0],"old-tabindex"):null!=this.$element.attr("tabindex")&&(this._tabindex=this.$element.attr("tabindex")),n.attr("title",this.$element.attr("title")),n.attr("tabindex",this._tabindex),this.$selection=n,n},i.prototype.bind=function(t,e){var i=this,r=(t.id,t.id+"-results");this.container=t,this.$selection.on("focus",function(t){i.trigger("focus",t)}),this.$selection.on("blur",function(t){i._handleBlur(t)}),this.$selection.on("keydown",function(t){i.trigger("keypress",t),t.which===n.SPACE&&t.preventDefault()}),t.on("results:focus",function(t){i.$selection.attr("aria-activedescendant",t.data._resultId)}),t.on("selection:update",function(t){i.update(t.data)}),t.on("open",function(){i.$selection.attr("aria-expanded","true"),i.$selection.attr("aria-owns",r),i._attachCloseHandler(t)}),t.on("close",function(){i.$selection.attr("aria-expanded","false"),i.$selection.removeAttr("aria-activedescendant"),i.$selection.removeAttr("aria-owns"),i.$selection.focus(),window.setTimeout(function(){i.$selection.focus()},0),i._detachCloseHandler(t)}),t.on("enable",function(){i.$selection.attr("tabindex",i._tabindex)}),t.on("disable",function(){i.$selection.attr("tabindex","-1")})},i.prototype._handleBlur=function(e){var n=this;window.setTimeout(function(){document.activeElement==n.$selection[0]||t.contains(n.$selection[0],document.activeElement)||n.trigger("blur",e)},1)},i.prototype._attachCloseHandler=function(n){t(document.body).on("mousedown.select2."+n.id,function(n){var i=t(n.target).closest(".select2");t(".select2.select2-container--open").each(function(){t(this);this!=i[0]&&e.GetData(this,"element").select2("close")})})},i.prototype._detachCloseHandler=function(e){t(document.body).off("mousedown.select2."+e.id)},i.prototype.position=function(t,e){e.find(".selection").append(t)},i.prototype.destroy=function(){this._detachCloseHandler(this.container)},i.prototype.update=function(t){throw new Error("The `update` method must be defined in child classes.")},i}),e.define("select2/selection/single",["jquery","./base","../utils","../keys"],function(t,e,n,i){function r(){r.__super__.constructor.apply(this,arguments)}return n.Extend(r,e),r.prototype.render=function(){var t=r.__super__.render.call(this);return t.addClass("select2-selection--single"),t.html(''),t},r.prototype.bind=function(t,e){var n=this;r.__super__.bind.apply(this,arguments);var i=t.id+"-container";this.$selection.find(".select2-selection__rendered").attr("id",i).attr("role","textbox").attr("aria-readonly","true"),this.$selection.attr("aria-labelledby",i),this.$selection.on("mousedown",function(t){1===t.which&&n.trigger("toggle",{originalEvent:t})}),this.$selection.on("focus",function(t){}),this.$selection.on("blur",function(t){}),t.on("focus",function(e){t.isOpen()||n.$selection.focus()})},r.prototype.clear=function(){var t=this.$selection.find(".select2-selection__rendered");t.empty(),t.removeAttr("title")},r.prototype.display=function(t,e){var n=this.options.get("templateSelection");return this.options.get("escapeMarkup")(n(t,e))},r.prototype.selectionContainer=function(){return t("")},r.prototype.update=function(t){if(0!==t.length){var e=t[0],n=this.$selection.find(".select2-selection__rendered"),i=this.display(e,n);n.empty().append(i),n.attr("title",e.title||e.text)}else this.clear()},r}),e.define("select2/selection/multiple",["jquery","./base","../utils"],function(t,e,n){function i(t,e){i.__super__.constructor.apply(this,arguments)}return n.Extend(i,e),i.prototype.render=function(){var t=i.__super__.render.call(this);return t.addClass("select2-selection--multiple"),t.html('
        '),t},i.prototype.bind=function(e,r){var o=this;i.__super__.bind.apply(this,arguments),this.$selection.on("click",function(t){o.trigger("toggle",{originalEvent:t})}),this.$selection.on("click",".select2-selection__choice__remove",function(e){if(!o.options.get("disabled")){var i=t(this).parent(),r=n.GetData(i[0],"data");o.trigger("unselect",{originalEvent:e,data:r})}})},i.prototype.clear=function(){var t=this.$selection.find(".select2-selection__rendered");t.empty(),t.removeAttr("title")},i.prototype.display=function(t,e){var n=this.options.get("templateSelection");return this.options.get("escapeMarkup")(n(t,e))},i.prototype.selectionContainer=function(){return t('
      • ×
      • ')},i.prototype.update=function(t){if(this.clear(),0!==t.length){for(var e=[],i=0;i1||n)return t.call(this,e);this.clear();var i=this.createPlaceholder(this.placeholder);this.$selection.find(".select2-selection__rendered").append(i)},e}),e.define("select2/selection/allowClear",["jquery","../keys","../utils"],function(t,e,n){function i(){}return i.prototype.bind=function(t,e,n){var i=this;t.call(this,e,n),null==this.placeholder&&this.options.get("debug")&&window.console&&console.error&&console.error("Select2: The `allowClear` option should be used in combination with the `placeholder` option."),this.$selection.on("mousedown",".select2-selection__clear",function(t){i._handleClear(t)}),e.on("keypress",function(t){i._handleKeyboardClear(t,e)})},i.prototype._handleClear=function(t,e){if(!this.options.get("disabled")){var i=this.$selection.find(".select2-selection__clear");if(0!==i.length){e.stopPropagation();var r=n.GetData(i[0],"data"),o=this.$element.val();this.$element.val(this.placeholder.id);var a={data:r};if(this.trigger("clear",a),a.prevented)this.$element.val(o);else{for(var s=0;s0||0===i.length)){var r=t('×');n.StoreData(r[0],"data",i),this.$selection.find(".select2-selection__rendered").prepend(r)}},i}),e.define("select2/selection/search",["jquery","../utils","../keys"],function(t,e,n){function i(t,e,n){t.call(this,e,n)}return i.prototype.render=function(e){var n=t('');this.$searchContainer=n,this.$search=n.find("input");var i=e.call(this);return this._transferTabIndex(),i},i.prototype.bind=function(t,i,r){var o=this;t.call(this,i,r),i.on("open",function(){o.$search.trigger("focus")}),i.on("close",function(){o.$search.val(""),o.$search.removeAttr("aria-activedescendant"),o.$search.trigger("focus")}),i.on("enable",function(){o.$search.prop("disabled",!1),o._transferTabIndex()}),i.on("disable",function(){o.$search.prop("disabled",!0)}),i.on("focus",function(t){o.$search.trigger("focus")}),i.on("results:focus",function(t){o.$search.attr("aria-activedescendant",t.id)}),this.$selection.on("focusin",".select2-search--inline",function(t){o.trigger("focus",t)}),this.$selection.on("focusout",".select2-search--inline",function(t){o._handleBlur(t)}),this.$selection.on("keydown",".select2-search--inline",function(t){if(t.stopPropagation(),o.trigger("keypress",t),o._keyUpPrevented=t.isDefaultPrevented(),t.which===n.BACKSPACE&&""===o.$search.val()){var i=o.$searchContainer.prev(".select2-selection__choice");if(i.length>0){var r=e.GetData(i[0],"data");o.searchRemoveChoice(r),t.preventDefault()}}});var a=document.documentMode,s=a&&a<=11;this.$selection.on("input.searchcheck",".select2-search--inline",function(t){s?o.$selection.off("input.search input.searchcheck"):o.$selection.off("keyup.search")}),this.$selection.on("keyup.search input.search",".select2-search--inline",function(t){if(s&&"input"===t.type)o.$selection.off("input.search input.searchcheck");else{var e=t.which;e!=n.SHIFT&&e!=n.CTRL&&e!=n.ALT&&e!=n.TAB&&o.handleSearch(t)}})},i.prototype._transferTabIndex=function(t){this.$search.attr("tabindex",this.$selection.attr("tabindex")),this.$selection.attr("tabindex","-1")},i.prototype.createPlaceholder=function(t,e){this.$search.attr("placeholder",e.text)},i.prototype.update=function(t,e){var n=this.$search[0]==document.activeElement;(this.$search.attr("placeholder",""),t.call(this,e),this.$selection.find(".select2-selection__rendered").append(this.$searchContainer),this.resizeSearch(),n)&&(this.$element.find("[data-select2-tag]").length?this.$element.focus():this.$search.focus())},i.prototype.handleSearch=function(){if(this.resizeSearch(),!this._keyUpPrevented){var t=this.$search.val();this.trigger("query",{term:t})}this._keyUpPrevented=!1},i.prototype.searchRemoveChoice=function(t,e){this.trigger("unselect",{data:e}),this.$search.val(e.text),this.handleSearch()},i.prototype.resizeSearch=function(){this.$search.css("width","25px");var t="";""!==this.$search.attr("placeholder")?t=this.$selection.find(".select2-selection__rendered").innerWidth():t=.75*(this.$search.val().length+1)+"em";this.$search.css("width",t)},i}),e.define("select2/selection/eventRelay",["jquery"],function(t){function e(){}return e.prototype.bind=function(e,n,i){var r=this,o=["open","opening","close","closing","select","selecting","unselect","unselecting","clear","clearing"],a=["opening","closing","selecting","unselecting","clearing"];e.call(this,n,i),n.on("*",function(e,n){if(-1!==t.inArray(e,o)){n=n||{};var i=t.Event("select2:"+e,{params:n});r.$element.trigger(i),-1!==t.inArray(e,a)&&(n.prevented=i.isDefaultPrevented())}})},e}),e.define("select2/translation",["jquery","require"],function(t,e){function n(t){this.dict=t||{}}return n.prototype.all=function(){return this.dict},n.prototype.get=function(t){return this.dict[t]},n.prototype.extend=function(e){this.dict=t.extend({},e.all(),this.dict)},n._cache={},n.loadPath=function(t){if(!(t in n._cache)){var i=e(t);n._cache[t]=i}return new n(n._cache[t])},n}),e.define("select2/diacritics",[],function(){return{"â’¶":"A","A":"A","À":"A","Ã":"A","Â":"A","Ầ":"A","Ấ":"A","Ẫ":"A","Ẩ":"A","Ã":"A","Ä€":"A","Ä‚":"A","Ằ":"A","Ắ":"A","Ẵ":"A","Ẳ":"A","Ȧ":"A","Ç ":"A","Ä":"A","Çž":"A","Ả":"A","Ã…":"A","Ǻ":"A","Ç":"A","È€":"A","È‚":"A","Ạ":"A","Ậ":"A","Ặ":"A","Ḁ":"A","Ä„":"A","Ⱥ":"A","Ɐ":"A","Ꜳ":"AA","Æ":"AE","Ǽ":"AE","Ç¢":"AE","Ꜵ":"AO","Ꜷ":"AU","Ꜹ":"AV","Ꜻ":"AV","Ꜽ":"AY","â’·":"B","ï¼¢":"B","Ḃ":"B","Ḅ":"B","Ḇ":"B","Ƀ":"B","Æ‚":"B","Æ":"B","â’¸":"C","ï¼£":"C","Ć":"C","Ĉ":"C","ÄŠ":"C","ÄŒ":"C","Ç":"C","Ḉ":"C","Ƈ":"C","È»":"C","Ꜿ":"C","â’¹":"D","D":"D","Ḋ":"D","ÄŽ":"D","Ḍ":"D","á¸":"D","Ḓ":"D","Ḏ":"D","Ä":"D","Æ‹":"D","ÆŠ":"D","Ɖ":"D","ê¹":"D","DZ":"DZ","Ç„":"DZ","Dz":"Dz","Ç…":"Dz","â’º":"E","ï¼¥":"E","È":"E","É":"E","Ê":"E","Ề":"E","Ế":"E","Ễ":"E","Ể":"E","Ẽ":"E","Ä’":"E","Ḕ":"E","Ḗ":"E","Ä”":"E","Ä–":"E","Ë":"E","Ẻ":"E","Äš":"E","È„":"E","Ȇ":"E","Ẹ":"E","Ệ":"E","Ȩ":"E","Ḝ":"E","Ę":"E","Ḙ":"E","Ḛ":"E","Æ":"E","ÆŽ":"E","â’»":"F","F":"F","Ḟ":"F","Æ‘":"F","ê»":"F","â’¼":"G","ï¼§":"G","Ç´":"G","Äœ":"G","Ḡ":"G","Äž":"G","Ä ":"G","Ǧ":"G","Ä¢":"G","Ǥ":"G","Æ“":"G","êž ":"G","ê½":"G","ê¾":"G","â’½":"H","H":"H","Ĥ":"H","Ḣ":"H","Ḧ":"H","Èž":"H","Ḥ":"H","Ḩ":"H","Ḫ":"H","Ħ":"H","â±§":"H","â±µ":"H","êž":"H","â’¾":"I","I":"I","ÃŒ":"I","Ã":"I","ÃŽ":"I","Ĩ":"I","Ī":"I","Ĭ":"I","İ":"I","Ã":"I","Ḯ":"I","Ỉ":"I","Ç":"I","Ȉ":"I","ÈŠ":"I","Ị":"I","Ä®":"I","Ḭ":"I","Æ—":"I","â’¿":"J","J":"J","Ä´":"J","Ɉ":"J","â“€":"K","K":"K","Ḱ":"K","Ǩ":"K","Ḳ":"K","Ķ":"K","Ḵ":"K","Ƙ":"K","Ⱪ":"K","ê€":"K","ê‚":"K","ê„":"K","Ꞣ":"K","â“":"L","L":"L","Ä¿":"L","Ĺ":"L","Ľ":"L","Ḷ":"L","Ḹ":"L","Ä»":"L","Ḽ":"L","Ḻ":"L","Å":"L","Ƚ":"L","â±¢":"L","â± ":"L","êˆ":"L","ê†":"L","Ꞁ":"L","LJ":"LJ","Lj":"Lj","â“‚":"M","ï¼­":"M","Ḿ":"M","á¹€":"M","Ṃ":"M","â±®":"M","Æœ":"M","Ⓝ":"N","ï¼®":"N","Ǹ":"N","Ń":"N","Ñ":"N","Ṅ":"N","Ň":"N","Ṇ":"N","Å…":"N","Ṋ":"N","Ṉ":"N","È ":"N","Æ":"N","êž":"N","Ꞥ":"N","ÇŠ":"NJ","Ç‹":"Nj","â“„":"O","O":"O","Ã’":"O","Ó":"O","Ô":"O","á»’":"O","á»":"O","á»–":"O","á»”":"O","Õ":"O","Ṍ":"O","Ȭ":"O","Ṏ":"O","ÅŒ":"O","á¹":"O","á¹’":"O","ÅŽ":"O","È®":"O","Ȱ":"O","Ö":"O","Ȫ":"O","Ỏ":"O","Å":"O","Ç‘":"O","ÈŒ":"O","ÈŽ":"O","Æ ":"O","Ờ":"O","Ớ":"O","á» ":"O","Ở":"O","Ợ":"O","Ọ":"O","Ộ":"O","Ǫ":"O","Ǭ":"O","Ø":"O","Ǿ":"O","Ɔ":"O","ÆŸ":"O","êŠ":"O","êŒ":"O","Æ¢":"OI","êŽ":"OO","È¢":"OU","â“…":"P","ï¼°":"P","á¹”":"P","á¹–":"P","Ƥ":"P","â±£":"P","ê":"P","ê’":"P","ê”":"P","Ⓠ":"Q","ï¼±":"Q","ê–":"Q","ê˜":"Q","ÉŠ":"Q","Ⓡ":"R","ï¼²":"R","Å”":"R","Ṙ":"R","Ř":"R","È":"R","È’":"R","Ṛ":"R","Ṝ":"R","Å–":"R","Ṟ":"R","ÉŒ":"R","Ɽ":"R","êš":"R","Ꞧ":"R","êž‚":"R","Ⓢ":"S","ï¼³":"S","ẞ":"S","Åš":"S","Ṥ":"S","Åœ":"S","á¹ ":"S","Å ":"S","Ṧ":"S","á¹¢":"S","Ṩ":"S","Ș":"S","Åž":"S","â±¾":"S","Ꞩ":"S","êž„":"S","Ⓣ":"T","ï¼´":"T","Ṫ":"T","Ť":"T","Ṭ":"T","Èš":"T","Å¢":"T","á¹°":"T","á¹®":"T","Ŧ":"T","Ƭ":"T","Æ®":"T","Ⱦ":"T","Ꞇ":"T","Ꜩ":"TZ","Ⓤ":"U","ï¼µ":"U","Ù":"U","Ú":"U","Û":"U","Ũ":"U","Ṹ":"U","Ū":"U","Ṻ":"U","Ŭ":"U","Ü":"U","Ç›":"U","Ç—":"U","Ç•":"U","Ç™":"U","Ủ":"U","Å®":"U","Ű":"U","Ç“":"U","È”":"U","È–":"U","Ư":"U","Ừ":"U","Ứ":"U","á»®":"U","Ử":"U","á»°":"U","Ụ":"U","á¹²":"U","Ų":"U","á¹¶":"U","á¹´":"U","É„":"U","â“‹":"V","ï¼¶":"V","á¹¼":"V","á¹¾":"V","Ʋ":"V","êž":"V","É…":"V","ê ":"VY","Ⓦ":"W","ï¼·":"W","Ẁ":"W","Ẃ":"W","Å´":"W","Ẇ":"W","Ẅ":"W","Ẉ":"W","â±²":"W","â“":"X","X":"X","Ẋ":"X","Ẍ":"X","Ⓨ":"Y","ï¼¹":"Y","Ỳ":"Y","Ã":"Y","Ŷ":"Y","Ỹ":"Y","Ȳ":"Y","Ẏ":"Y","Ÿ":"Y","á»¶":"Y","á»´":"Y","Ƴ":"Y","ÉŽ":"Y","Ỿ":"Y","â“":"Z","Z":"Z","Ź":"Z","áº":"Z","Å»":"Z","Ž":"Z","Ẓ":"Z","Ẕ":"Z","Ƶ":"Z","Ȥ":"Z","Ɀ":"Z","Ⱬ":"Z","ê¢":"Z","â“":"a","ï½":"a","ẚ":"a","à":"a","á":"a","â":"a","ầ":"a","ấ":"a","ẫ":"a","ẩ":"a","ã":"a","Ä":"a","ă":"a","ằ":"a","ắ":"a","ẵ":"a","ẳ":"a","ȧ":"a","Ç¡":"a","ä":"a","ÇŸ":"a","ả":"a","Ã¥":"a","Ç»":"a","ÇŽ":"a","È":"a","ȃ":"a","ạ":"a","ậ":"a","ặ":"a","á¸":"a","Ä…":"a","â±¥":"a","É":"a","ꜳ":"aa","æ":"ae","ǽ":"ae","Ç£":"ae","ꜵ":"ao","ꜷ":"au","ꜹ":"av","ꜻ":"av","ꜽ":"ay","â“‘":"b","b":"b","ḃ":"b","ḅ":"b","ḇ":"b","Æ€":"b","ƃ":"b","É“":"b","â“’":"c","c":"c","ć":"c","ĉ":"c","Ä‹":"c","Ä":"c","ç":"c","ḉ":"c","ƈ":"c","ȼ":"c","ꜿ":"c","ↄ":"c","â““":"d","d":"d","ḋ":"d","Ä":"d","á¸":"d","ḑ":"d","ḓ":"d","á¸":"d","Ä‘":"d","ÆŒ":"d","É–":"d","É—":"d","êº":"d","dz":"dz","dž":"dz","â“”":"e","ï½…":"e","è":"e","é":"e","ê":"e","á»":"e","ế":"e","á»…":"e","ể":"e","ẽ":"e","Ä“":"e","ḕ":"e","ḗ":"e","Ä•":"e","Ä—":"e","ë":"e","ẻ":"e","Ä›":"e","È…":"e","ȇ":"e","ẹ":"e","ệ":"e","È©":"e","á¸":"e","Ä™":"e","ḙ":"e","ḛ":"e","ɇ":"e","É›":"e","Ç":"e","â“•":"f","f":"f","ḟ":"f","Æ’":"f","ê¼":"f","â“–":"g","g":"g","ǵ":"g","Ä":"g","ḡ":"g","ÄŸ":"g","Ä¡":"g","ǧ":"g","Ä£":"g","Ç¥":"g","É ":"g","êž¡":"g","áµ¹":"g","ê¿":"g","â“—":"h","h":"h","Ä¥":"h","ḣ":"h","ḧ":"h","ÈŸ":"h","ḥ":"h","ḩ":"h","ḫ":"h","ẖ":"h","ħ":"h","ⱨ":"h","â±¶":"h","É¥":"h","Æ•":"hv","ⓘ":"i","i":"i","ì":"i","í":"i","î":"i","Ä©":"i","Ä«":"i","Ä­":"i","ï":"i","ḯ":"i","ỉ":"i","Ç":"i","ȉ":"i","È‹":"i","ị":"i","į":"i","ḭ":"i","ɨ":"i","ı":"i","â“™":"j","j":"j","ĵ":"j","ǰ":"j","ɉ":"j","ⓚ":"k","k":"k","ḱ":"k","Ç©":"k","ḳ":"k","Ä·":"k","ḵ":"k","Æ™":"k","ⱪ":"k","ê":"k","êƒ":"k","ê…":"k","ꞣ":"k","â“›":"l","l":"l","Å€":"l","ĺ":"l","ľ":"l","ḷ":"l","ḹ":"l","ļ":"l","ḽ":"l","ḻ":"l","Å¿":"l","Å‚":"l","Æš":"l","É«":"l","ⱡ":"l","ê‰":"l","êž":"l","ê‡":"l","lj":"lj","ⓜ":"m","ï½":"m","ḿ":"m","á¹":"m","ṃ":"m","ɱ":"m","ɯ":"m","â“":"n","n":"n","ǹ":"n","Å„":"n","ñ":"n","á¹…":"n","ň":"n","ṇ":"n","ņ":"n","ṋ":"n","ṉ":"n","Æž":"n","ɲ":"n","ʼn":"n","êž‘":"n","ꞥ":"n","ÇŒ":"nj","ⓞ":"o","ï½":"o","ò":"o","ó":"o","ô":"o","ồ":"o","ố":"o","á»—":"o","ổ":"o","õ":"o","á¹":"o","È­":"o","á¹":"o","Å":"o","ṑ":"o","ṓ":"o","Å":"o","ȯ":"o","ȱ":"o","ö":"o","È«":"o","á»":"o","Å‘":"o","Ç’":"o","È":"o","È":"o","Æ¡":"o","á»":"o","á»›":"o","ỡ":"o","ở":"o","ợ":"o","á»":"o","á»™":"o","Ç«":"o","Ç­":"o","ø":"o","Ç¿":"o","É”":"o","ê‹":"o","ê":"o","ɵ":"o","Æ£":"oi","È£":"ou","ê":"oo","ⓟ":"p","ï½":"p","ṕ":"p","á¹—":"p","Æ¥":"p","áµ½":"p","ê‘":"p","ê“":"p","ê•":"p","â“ ":"q","q":"q","É‹":"q","ê—":"q","ê™":"q","â“¡":"r","ï½’":"r","Å•":"r","á¹™":"r","Å™":"r","È‘":"r","È“":"r","á¹›":"r","á¹":"r","Å—":"r","ṟ":"r","É":"r","ɽ":"r","ê›":"r","êž§":"r","ꞃ":"r","â“¢":"s","s":"s","ß":"s","Å›":"s","á¹¥":"s","Å":"s","ṡ":"s","Å¡":"s","á¹§":"s","á¹£":"s","ṩ":"s","È™":"s","ÅŸ":"s","È¿":"s","êž©":"s","êž…":"s","ẛ":"s","â“£":"t","ï½”":"t","ṫ":"t","ẗ":"t","Å¥":"t","á¹­":"t","È›":"t","Å£":"t","á¹±":"t","ṯ":"t","ŧ":"t","Æ­":"t","ʈ":"t","ⱦ":"t","ꞇ":"t","ꜩ":"tz","ⓤ":"u","u":"u","ù":"u","ú":"u","û":"u","Å©":"u","á¹¹":"u","Å«":"u","á¹»":"u","Å­":"u","ü":"u","Çœ":"u","ǘ":"u","Ç–":"u","Çš":"u","á»§":"u","ů":"u","ű":"u","Ç”":"u","È•":"u","È—":"u","ư":"u","ừ":"u","ứ":"u","ữ":"u","á»­":"u","á»±":"u","ụ":"u","á¹³":"u","ų":"u","á¹·":"u","á¹µ":"u","ʉ":"u","â“¥":"v","ï½–":"v","á¹½":"v","ṿ":"v","Ê‹":"v","êŸ":"v","ÊŒ":"v","ê¡":"vy","ⓦ":"w","ï½—":"w","áº":"w","ẃ":"w","ŵ":"w","ẇ":"w","ẅ":"w","ẘ":"w","ẉ":"w","â±³":"w","â“§":"x","x":"x","ẋ":"x","áº":"x","ⓨ":"y","ï½™":"y","ỳ":"y","ý":"y","Å·":"y","ỹ":"y","ȳ":"y","áº":"y","ÿ":"y","á»·":"y","ẙ":"y","ỵ":"y","Æ´":"y","É":"y","ỿ":"y","â“©":"z","z":"z","ź":"z","ẑ":"z","ż":"z","ž":"z","ẓ":"z","ẕ":"z","ƶ":"z","È¥":"z","É€":"z","ⱬ":"z","ê£":"z","Ά":"Α","Έ":"Ε","Ή":"Η","Ί":"Ι","Ϊ":"Ι","ÎŒ":"Ο","ÎŽ":"Î¥","Ϋ":"Î¥","Î":"Ω","ά":"α","έ":"ε","ή":"η","ί":"ι","ÏŠ":"ι","Î":"ι","ÏŒ":"ο","Ï":"Ï…","Ï‹":"Ï…","ΰ":"Ï…","ω":"ω","Ï‚":"σ"}}),e.define("select2/data/base",["../utils"],function(t){function e(t,n){e.__super__.constructor.call(this)}return t.Extend(e,t.Observable),e.prototype.current=function(t){throw new Error("The `current` method must be defined in child classes.")},e.prototype.query=function(t,e){throw new Error("The `query` method must be defined in child classes.")},e.prototype.bind=function(t,e){},e.prototype.destroy=function(){},e.prototype.generateResultId=function(e,n){var i=e.id+"-result-";return i+=t.generateChars(4),null!=n.id?i+="-"+n.id.toString():i+="-"+t.generateChars(4),i},e}),e.define("select2/data/select",["./base","../utils","jquery"],function(t,e,n){function i(t,e){this.$element=t,this.options=e,i.__super__.constructor.call(this)}return e.Extend(i,t),i.prototype.current=function(t){var e=[],i=this;this.$element.find(":selected").each(function(){var t=n(this),r=i.item(t);e.push(r)}),t(e)},i.prototype.select=function(t){var e=this;if(t.selected=!0,n(t.element).is("option"))return t.element.selected=!0,void this.$element.trigger("change");if(this.$element.prop("multiple"))this.current(function(i){var r=[];(t=[t]).push.apply(t,i);for(var o=0;o=0){var u=r.filter(s(c)),h=this.item(u),d=n.extend(!0,{},c,h),C=this.option(d);u.replaceWith(C)}else{var f=this.option(c);if(c.children){var p=this.convertToOptions(c.children);e.appendMany(f,p)}a.push(f)}}return a},i}),e.define("select2/data/ajax",["./array","../utils","jquery"],function(t,e,n){function i(t,e){this.ajaxOptions=this._applyDefaults(e.get("ajax")),null!=this.ajaxOptions.processResults&&(this.processResults=this.ajaxOptions.processResults),i.__super__.constructor.call(this,t,e)}return e.Extend(i,t),i.prototype._applyDefaults=function(t){var e={data:function(t){return n.extend({},t,{q:t.term})},transport:function(t,e,i){var r=n.ajax(t);return r.then(e),r.fail(i),r}};return n.extend({},e,t,!0)},i.prototype.processResults=function(t){return t},i.prototype.query=function(t,e){var i=this;null!=this._request&&(n.isFunction(this._request.abort)&&this._request.abort(),this._request=null);var r=n.extend({type:"GET"},this.ajaxOptions);function o(){var o=r.transport(r,function(r){var o=i.processResults(r,t);i.options.get("debug")&&window.console&&console.error&&(o&&o.results&&n.isArray(o.results)||console.error("Select2: The AJAX results did not return an array in the `results` key of the response.")),e(o)},function(){"status"in o&&(0===o.status||"0"===o.status)||i.trigger("results:message",{message:"errorLoading"})});i._request=o}"function"==typeof r.url&&(r.url=r.url.call(this.$element,t)),"function"==typeof r.data&&(r.data=r.data.call(this.$element,t)),this.ajaxOptions.delay&&null!=t.term?(this._queryTimeout&&window.clearTimeout(this._queryTimeout),this._queryTimeout=window.setTimeout(o,this.ajaxOptions.delay)):o()},i}),e.define("select2/data/tags",["jquery"],function(t){function e(e,n,i){var r=i.get("tags"),o=i.get("createTag");void 0!==o&&(this.createTag=o);var a=i.get("insertTag");if(void 0!==a&&(this.insertTag=a),e.call(this,n,i),t.isArray(r))for(var s=0;s0&&e.term.length>this.maximumInputLength?this.trigger("results:message",{message:"inputTooLong",args:{maximum:this.maximumInputLength,input:e.term,params:e}}):t.call(this,e,n)},t}),e.define("select2/data/maximumSelectionLength",[],function(){function t(t,e,n){this.maximumSelectionLength=n.get("maximumSelectionLength"),t.call(this,e,n)}return t.prototype.query=function(t,e,n){var i=this;this.current(function(r){var o=null!=r?r.length:0;i.maximumSelectionLength>0&&o>=i.maximumSelectionLength?i.trigger("results:message",{message:"maximumSelected",args:{maximum:i.maximumSelectionLength}}):t.call(i,e,n)})},t}),e.define("select2/dropdown",["jquery","./utils"],function(t,e){function n(t,e){this.$element=t,this.options=e,n.__super__.constructor.call(this)}return e.Extend(n,e.Observable),n.prototype.render=function(){var e=t('');return e.attr("dir",this.options.get("dir")),this.$dropdown=e,e},n.prototype.bind=function(){},n.prototype.position=function(t,e){},n.prototype.destroy=function(){this.$dropdown.remove()},n}),e.define("select2/dropdown/search",["jquery","../utils"],function(t,e){function n(){}return n.prototype.render=function(e){var n=e.call(this),i=t('');return this.$searchContainer=i,this.$search=i.find("input"),n.prepend(i),n},n.prototype.bind=function(e,n,i){var r=this;e.call(this,n,i),this.$search.on("keydown",function(t){r.trigger("keypress",t),r._keyUpPrevented=t.isDefaultPrevented()}),this.$search.on("input",function(e){t(this).off("keyup")}),this.$search.on("keyup input",function(t){r.handleSearch(t)}),n.on("open",function(){r.$search.attr("tabindex",0),r.$search.focus(),window.setTimeout(function(){r.$search.focus()},0)}),n.on("close",function(){r.$search.attr("tabindex",-1),r.$search.val(""),r.$search.blur()}),n.on("focus",function(){n.isOpen()||r.$search.focus()}),n.on("results:all",function(t){null!=t.query.term&&""!==t.query.term||(r.showSearch(t)?r.$searchContainer.removeClass("select2-search--hide"):r.$searchContainer.addClass("select2-search--hide"))})},n.prototype.handleSearch=function(t){if(!this._keyUpPrevented){var e=this.$search.val();this.trigger("query",{term:e})}this._keyUpPrevented=!1},n.prototype.showSearch=function(t,e){return!0},n}),e.define("select2/dropdown/hidePlaceholder",[],function(){function t(t,e,n,i){this.placeholder=this.normalizePlaceholder(n.get("placeholder")),t.call(this,e,n,i)}return t.prototype.append=function(t,e){e.results=this.removePlaceholder(e.results),t.call(this,e)},t.prototype.normalizePlaceholder=function(t,e){return"string"==typeof e&&(e={id:"",text:e}),e},t.prototype.removePlaceholder=function(t,e){for(var n=e.slice(0),i=e.length-1;i>=0;i--){var r=e[i];this.placeholder.id===r.id&&n.splice(i,1)}return n},t}),e.define("select2/dropdown/infiniteScroll",["jquery"],function(t){function e(t,e,n,i){this.lastParams={},t.call(this,e,n,i),this.$loadingMore=this.createLoadingMore(),this.loading=!1}return e.prototype.append=function(t,e){this.$loadingMore.remove(),this.loading=!1,t.call(this,e),this.showLoadingMore(e)&&this.$results.append(this.$loadingMore)},e.prototype.bind=function(e,n,i){var r=this;e.call(this,n,i),n.on("query",function(t){r.lastParams=t,r.loading=!0}),n.on("query:append",function(t){r.lastParams=t,r.loading=!0}),this.$results.on("scroll",function(){var e=t.contains(document.documentElement,r.$loadingMore[0]);!r.loading&&e&&(r.$results.offset().top+r.$results.outerHeight(!1)+50>=r.$loadingMore.offset().top+r.$loadingMore.outerHeight(!1)&&r.loadMore())})},e.prototype.loadMore=function(){this.loading=!0;var e=t.extend({},{page:1},this.lastParams);e.page++,this.trigger("query:append",e)},e.prototype.showLoadingMore=function(t,e){return e.pagination&&e.pagination.more},e.prototype.createLoadingMore=function(){var e=t('
      • '),n=this.options.get("translations").get("loadingMore");return e.html(n(this.lastParams)),e},e}),e.define("select2/dropdown/attachBody",["jquery","../utils"],function(t,e){function n(e,n,i){this.$dropdownParent=i.get("dropdownParent")||t(document.body),e.call(this,n,i)}return n.prototype.bind=function(t,e,n){var i=this,r=!1;t.call(this,e,n),e.on("open",function(){i._showDropdown(),i._attachPositioningHandler(e),r||(r=!0,e.on("results:all",function(){i._positionDropdown(),i._resizeDropdown()}),e.on("results:append",function(){i._positionDropdown(),i._resizeDropdown()}))}),e.on("close",function(){i._hideDropdown(),i._detachPositioningHandler(e)}),this.$dropdownContainer.on("mousedown",function(t){t.stopPropagation()})},n.prototype.destroy=function(t){t.call(this),this.$dropdownContainer.remove()},n.prototype.position=function(t,e,n){e.attr("class",n.attr("class")),e.removeClass("select2"),e.addClass("select2-container--open"),e.css({position:"absolute",top:-999999}),this.$container=n},n.prototype.render=function(e){var n=t(""),i=e.call(this);return n.append(i),this.$dropdownContainer=n,n},n.prototype._hideDropdown=function(t){this.$dropdownContainer.detach()},n.prototype._attachPositioningHandler=function(n,i){var r=this,o="scroll.select2."+i.id,a="resize.select2."+i.id,s="orientationchange.select2."+i.id,l=this.$container.parents().filter(e.hasScroll);l.each(function(){e.StoreData(this,"select2-scroll-position",{x:t(this).scrollLeft(),y:t(this).scrollTop()})}),l.on(o,function(n){var i=e.GetData(this,"select2-scroll-position");t(this).scrollTop(i.y)}),t(window).on(o+" "+a+" "+s,function(t){r._positionDropdown(),r._resizeDropdown()})},n.prototype._detachPositioningHandler=function(n,i){var r="scroll.select2."+i.id,o="resize.select2."+i.id,a="orientationchange.select2."+i.id;this.$container.parents().filter(e.hasScroll).off(r),t(window).off(r+" "+o+" "+a)},n.prototype._positionDropdown=function(){var e=t(window),n=this.$dropdown.hasClass("select2-dropdown--above"),i=this.$dropdown.hasClass("select2-dropdown--below"),r=null,o=this.$container.offset();o.bottom=o.top+this.$container.outerHeight(!1);var a={height:this.$container.outerHeight(!1)};a.top=o.top,a.bottom=o.top+a.height;var s=this.$dropdown.outerHeight(!1),l=e.scrollTop(),c=e.scrollTop()+e.height(),u=lo.bottom+s,d={left:o.left,top:a.bottom},C=this.$dropdownParent;"static"===C.css("position")&&(C=C.offsetParent());var f=C.offset();d.top-=f.top,d.left-=f.left,n||i||(r="below"),h||!u||n?!u&&h&&n&&(r="below"):r="above",("above"==r||n&&"below"!==r)&&(d.top=a.top-f.top-s),null!=r&&(this.$dropdown.removeClass("select2-dropdown--below select2-dropdown--above").addClass("select2-dropdown--"+r),this.$container.removeClass("select2-container--below select2-container--above").addClass("select2-container--"+r)),this.$dropdownContainer.css(d)},n.prototype._resizeDropdown=function(){var t={width:this.$container.outerWidth(!1)+"px"};this.options.get("dropdownAutoWidth")&&(t.minWidth=t.width,t.position="relative",t.width="auto"),this.$dropdown.css(t)},n.prototype._showDropdown=function(t){this.$dropdownContainer.appendTo(this.$dropdownParent),this._positionDropdown(),this._resizeDropdown()},n}),e.define("select2/dropdown/minimumResultsForSearch",[],function(){function t(t,e,n,i){this.minimumResultsForSearch=n.get("minimumResultsForSearch"),this.minimumResultsForSearch<0&&(this.minimumResultsForSearch=1/0),t.call(this,e,n,i)}return t.prototype.showSearch=function(t,e){return!(function t(e){for(var n=0,i=0;i0&&(h.dataAdapter=c.Decorate(h.dataAdapter,m)),h.maximumInputLength>0&&(h.dataAdapter=c.Decorate(h.dataAdapter,v)),h.maximumSelectionLength>0&&(h.dataAdapter=c.Decorate(h.dataAdapter,y)),h.tags&&(h.dataAdapter=c.Decorate(h.dataAdapter,p)),null==h.tokenSeparators&&null==h.tokenizer||(h.dataAdapter=c.Decorate(h.dataAdapter,g)),null!=h.query){var A=e(h.amdBase+"compat/query");h.dataAdapter=c.Decorate(h.dataAdapter,A)}if(null!=h.initSelection){var M=e(h.amdBase+"compat/initSelection");h.dataAdapter=c.Decorate(h.dataAdapter,M)}}if(null==h.resultsAdapter&&(h.resultsAdapter=n,null!=h.ajax&&(h.resultsAdapter=c.Decorate(h.resultsAdapter,_)),null!=h.placeholder&&(h.resultsAdapter=c.Decorate(h.resultsAdapter,w)),h.selectOnClose&&(h.resultsAdapter=c.Decorate(h.resultsAdapter,T))),null==h.dropdownAdapter){if(h.multiple)h.dropdownAdapter=b;else{var D=c.Decorate(b,x);h.dropdownAdapter=D}if(0!==h.minimumResultsForSearch&&(h.dropdownAdapter=c.Decorate(h.dropdownAdapter,k)),h.closeOnSelect&&(h.dropdownAdapter=c.Decorate(h.dropdownAdapter,E)),null!=h.dropdownCssClass||null!=h.dropdownCss||null!=h.adaptDropdownCssClass){var P=e(h.amdBase+"compat/dropdownCss");h.dropdownAdapter=c.Decorate(h.dropdownAdapter,P)}h.dropdownAdapter=c.Decorate(h.dropdownAdapter,S)}if(null==h.selectionAdapter){if(h.multiple?h.selectionAdapter=r:h.selectionAdapter=i,null!=h.placeholder&&(h.selectionAdapter=c.Decorate(h.selectionAdapter,o)),h.allowClear&&(h.selectionAdapter=c.Decorate(h.selectionAdapter,a)),h.multiple&&(h.selectionAdapter=c.Decorate(h.selectionAdapter,s)),null!=h.containerCssClass||null!=h.containerCss||null!=h.adaptContainerCssClass){var O=e(h.amdBase+"compat/containerCss");h.selectionAdapter=c.Decorate(h.selectionAdapter,O)}h.selectionAdapter=c.Decorate(h.selectionAdapter,l)}if("string"==typeof h.language)if(h.language.indexOf("-")>0){var L=h.language.split("-")[0];h.language=[h.language,L]}else h.language=[h.language];if(t.isArray(h.language)){var I=new u;h.language.push("en");for(var N=h.language,R=0;R0){for(var o=t.extend(!0,{},r),a=r.children.length-1;a>=0;a--)null==n(i,r.children[a])&&o.children.splice(a,1);return o.children.length>0?o:n(i,o)}var s=e(r.text).toUpperCase(),l=e(i.term).toUpperCase();return s.indexOf(l)>-1?r:null},minimumInputLength:0,maximumInputLength:0,maximumSelectionLength:0,minimumResultsForSearch:0,selectOnClose:!1,sorter:function(t){return t},templateResult:function(t){return t.text},templateSelection:function(t){return t.text},theme:"default",width:"resolve"}},M.prototype.set=function(e,n){var i={};i[t.camelCase(e)]=n;var r=c._convertData(i);t.extend(!0,this.defaults,r)},new M}),e.define("select2/options",["require","jquery","./defaults","./utils"],function(t,e,n,i){function r(e,r){if(this.options=e,null!=r&&this.fromElement(r),this.options=n.apply(this.options),r&&r.is("input")){var o=t(this.get("amdBase")+"compat/inputData");this.options.dataAdapter=i.Decorate(this.options.dataAdapter,o)}}return r.prototype.fromElement=function(t){var n=["select2"];null==this.options.multiple&&(this.options.multiple=t.prop("multiple")),null==this.options.disabled&&(this.options.disabled=t.prop("disabled")),null==this.options.language&&(t.prop("lang")?this.options.language=t.prop("lang").toLowerCase():t.closest("[lang]").prop("lang")&&(this.options.language=t.closest("[lang]").prop("lang"))),null==this.options.dir&&(t.prop("dir")?this.options.dir=t.prop("dir"):t.closest("[dir]").prop("dir")?this.options.dir=t.closest("[dir]").prop("dir"):this.options.dir="ltr"),t.prop("disabled",this.options.disabled),t.prop("multiple",this.options.multiple),i.GetData(t[0],"select2Tags")&&(this.options.debug&&window.console&&console.warn&&console.warn('Select2: The `data-select2-tags` attribute has been changed to use the `data-data` and `data-tags="true"` attributes and will be removed in future versions of Select2.'),i.StoreData(t[0],"data",i.GetData(t[0],"select2Tags")),i.StoreData(t[0],"tags",!0)),i.GetData(t[0],"ajaxUrl")&&(this.options.debug&&window.console&&console.warn&&console.warn("Select2: The `data-ajax-url` attribute has been changed to `data-ajax--url` and support for the old attribute will be removed in future versions of Select2."),t.attr("ajax--url",i.GetData(t[0],"ajaxUrl")),i.StoreData(t[0],"ajax-Url",i.GetData(t[0],"ajaxUrl")));var r={};r=e.fn.jquery&&"1."==e.fn.jquery.substr(0,2)&&t[0].dataset?e.extend(!0,{},t[0].dataset,i.GetData(t[0])):i.GetData(t[0]);var o=e.extend(!0,{},r);for(var a in o=i._convertData(o))e.inArray(a,n)>-1||(e.isPlainObject(this.options[a])?e.extend(this.options[a],o[a]):this.options[a]=o[a]);return this},r.prototype.get=function(t){return this.options[t]},r.prototype.set=function(t,e){this.options[t]=e},r}),e.define("select2/core",["jquery","./options","./utils","./keys"],function(t,e,n,i){var r=function(t,i){null!=n.GetData(t[0],"select2")&&n.GetData(t[0],"select2").destroy(),this.$element=t,this.id=this._generateId(t),i=i||{},this.options=new e(i,t),r.__super__.constructor.call(this);var o=t.attr("tabindex")||0;n.StoreData(t[0],"old-tabindex",o),t.attr("tabindex","-1");var a=this.options.get("dataAdapter");this.dataAdapter=new a(t,this.options);var s=this.render();this._placeContainer(s);var l=this.options.get("selectionAdapter");this.selection=new l(t,this.options),this.$selection=this.selection.render(),this.selection.position(this.$selection,s);var c=this.options.get("dropdownAdapter");this.dropdown=new c(t,this.options),this.$dropdown=this.dropdown.render(),this.dropdown.position(this.$dropdown,s);var u=this.options.get("resultsAdapter");this.results=new u(t,this.options,this.dataAdapter),this.$results=this.results.render(),this.results.position(this.$results,this.$dropdown);var h=this;this._bindAdapters(),this._registerDomEvents(),this._registerDataEvents(),this._registerSelectionEvents(),this._registerDropdownEvents(),this._registerResultsEvents(),this._registerEvents(),this.dataAdapter.current(function(t){h.trigger("selection:update",{data:t})}),t.addClass("select2-hidden-accessible"),t.attr("aria-hidden","true"),this._syncAttributes(),n.StoreData(t[0],"select2",this),t.data("select2",this)};return n.Extend(r,n.Observable),r.prototype._generateId=function(t){return"select2-"+(null!=t.attr("id")?t.attr("id"):null!=t.attr("name")?t.attr("name")+"-"+n.generateChars(2):n.generateChars(4)).replace(/(:|\.|\[|\]|,)/g,"")},r.prototype._placeContainer=function(t){t.insertAfter(this.$element);var e=this._resolveWidth(this.$element,this.options.get("width"));null!=e&&t.css("width",e)},r.prototype._resolveWidth=function(t,e){var n=/^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i;if("resolve"==e){var i=this._resolveWidth(t,"style");return null!=i?i:this._resolveWidth(t,"element")}if("element"==e){var r=t.outerWidth(!1);return r<=0?"auto":r+"px"}if("style"==e){var o=t.attr("style");if("string"!=typeof o)return null;for(var a=o.split(";"),s=0,l=a.length;s=1)return c[1]}return null}return e},r.prototype._bindAdapters=function(){this.dataAdapter.bind(this,this.$container),this.selection.bind(this,this.$container),this.dropdown.bind(this,this.$container),this.results.bind(this,this.$container)},r.prototype._registerDomEvents=function(){var e=this;this.$element.on("change.select2",function(){e.dataAdapter.current(function(t){e.trigger("selection:update",{data:t})})}),this.$element.on("focus.select2",function(t){e.trigger("focus",t)}),this._syncA=n.bind(this._syncAttributes,this),this._syncS=n.bind(this._syncSubtree,this),this.$element[0].attachEvent&&this.$element[0].attachEvent("onpropertychange",this._syncA);var i=window.MutationObserver||window.WebKitMutationObserver||window.MozMutationObserver;null!=i?(this._observer=new i(function(n){t.each(n,e._syncA),t.each(n,e._syncS)}),this._observer.observe(this.$element[0],{attributes:!0,childList:!0,subtree:!1})):this.$element[0].addEventListener&&(this.$element[0].addEventListener("DOMAttrModified",e._syncA,!1),this.$element[0].addEventListener("DOMNodeInserted",e._syncS,!1),this.$element[0].addEventListener("DOMNodeRemoved",e._syncS,!1))},r.prototype._registerDataEvents=function(){var t=this;this.dataAdapter.on("*",function(e,n){t.trigger(e,n)})},r.prototype._registerSelectionEvents=function(){var e=this,n=["toggle","focus"];this.selection.on("toggle",function(){e.toggleDropdown()}),this.selection.on("focus",function(t){e.focus(t)}),this.selection.on("*",function(i,r){-1===t.inArray(i,n)&&e.trigger(i,r)})},r.prototype._registerDropdownEvents=function(){var t=this;this.dropdown.on("*",function(e,n){t.trigger(e,n)})},r.prototype._registerResultsEvents=function(){var t=this;this.results.on("*",function(e,n){t.trigger(e,n)})},r.prototype._registerEvents=function(){var t=this;this.on("open",function(){t.$container.addClass("select2-container--open")}),this.on("close",function(){t.$container.removeClass("select2-container--open")}),this.on("enable",function(){t.$container.removeClass("select2-container--disabled")}),this.on("disable",function(){t.$container.addClass("select2-container--disabled")}),this.on("blur",function(){t.$container.removeClass("select2-container--focus")}),this.on("query",function(e){t.isOpen()||t.trigger("open",{}),this.dataAdapter.query(e,function(n){t.trigger("results:all",{data:n,query:e})})}),this.on("query:append",function(e){this.dataAdapter.query(e,function(n){t.trigger("results:append",{data:n,query:e})})}),this.on("keypress",function(e){var n=e.which;t.isOpen()?n===i.ESC||n===i.TAB||n===i.UP&&e.altKey?(t.close(),e.preventDefault()):n===i.ENTER?(t.trigger("results:select",{}),e.preventDefault()):n===i.SPACE&&e.ctrlKey?(t.trigger("results:toggle",{}),e.preventDefault()):n===i.UP?(t.trigger("results:previous",{}),e.preventDefault()):n===i.DOWN&&(t.trigger("results:next",{}),e.preventDefault()):(n===i.ENTER||n===i.SPACE||n===i.DOWN&&e.altKey)&&(t.open(),e.preventDefault())})},r.prototype._syncAttributes=function(){this.options.set("disabled",this.$element.prop("disabled")),this.options.get("disabled")?(this.isOpen()&&this.close(),this.trigger("disable",{})):this.trigger("enable",{})},r.prototype._syncSubtree=function(t,e){var n=!1,i=this;if(!t||!t.target||"OPTION"===t.target.nodeName||"OPTGROUP"===t.target.nodeName){if(e)if(e.addedNodes&&e.addedNodes.length>0)for(var r=0;r0&&(n=!0);else n=!0;n&&this.dataAdapter.current(function(t){i.trigger("selection:update",{data:t})})}},r.prototype.trigger=function(t,e){var n=r.__super__.trigger,i={open:"opening",close:"closing",select:"selecting",unselect:"unselecting",clear:"clearing"};if(void 0===e&&(e={}),t in i){var o=i[t],a={prevented:!1,name:t,args:e};if(n.call(this,o,a),a.prevented)return void(e.prevented=!0)}n.call(this,t,e)},r.prototype.toggleDropdown=function(){this.options.get("disabled")||(this.isOpen()?this.close():this.open())},r.prototype.open=function(){this.isOpen()||this.trigger("query",{})},r.prototype.close=function(){this.isOpen()&&this.trigger("close",{})},r.prototype.isOpen=function(){return this.$container.hasClass("select2-container--open")},r.prototype.hasFocus=function(){return this.$container.hasClass("select2-container--focus")},r.prototype.focus=function(t){this.hasFocus()||(this.$container.addClass("select2-container--focus"),this.trigger("focus",{}))},r.prototype.enable=function(t){this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("enable")` method has been deprecated and will be removed in later Select2 versions. Use $element.prop("disabled") instead.'),null!=t&&0!==t.length||(t=[!0]);var e=!t[0];this.$element.prop("disabled",e)},r.prototype.data=function(){this.options.get("debug")&&arguments.length>0&&window.console&&console.warn&&console.warn('Select2: Data can no longer be set using `select2("data")`. You should consider setting the value instead using `$element.val()`.');var t=[];return this.dataAdapter.current(function(e){t=e}),t},r.prototype.val=function(e){if(this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("val")` method has been deprecated and will be removed in later Select2 versions. Use $element.val() instead.'),null==e||0===e.length)return this.$element.val();var n=e[0];t.isArray(n)&&(n=t.map(n,function(t){return t.toString()})),this.$element.val(n).trigger("change")},r.prototype.destroy=function(){this.$container.remove(),this.$element[0].detachEvent&&this.$element[0].detachEvent("onpropertychange",this._syncA),null!=this._observer?(this._observer.disconnect(),this._observer=null):this.$element[0].removeEventListener&&(this.$element[0].removeEventListener("DOMAttrModified",this._syncA,!1),this.$element[0].removeEventListener("DOMNodeInserted",this._syncS,!1),this.$element[0].removeEventListener("DOMNodeRemoved",this._syncS,!1)),this._syncA=null,this._syncS=null,this.$element.off(".select2"),this.$element.attr("tabindex",n.GetData(this.$element[0],"old-tabindex")),this.$element.removeClass("select2-hidden-accessible"),this.$element.attr("aria-hidden","false"),n.RemoveData(this.$element[0]),this.$element.removeData("select2"),this.dataAdapter.destroy(),this.selection.destroy(),this.dropdown.destroy(),this.results.destroy(),this.dataAdapter=null,this.selection=null,this.dropdown=null,this.results=null},r.prototype.render=function(){var e=t('');return e.attr("dir",this.options.get("dir")),this.$container=e,this.$container.addClass("select2-container--"+this.options.get("theme")),n.StoreData(e[0],"element",this.$element),e},r}),e.define("select2/compat/utils",["jquery"],function(t){return{syncCssClasses:function(e,n,i){var r,o,a=[];(r=t.trim(e.attr("class")))&&t((r=""+r).split(/\s+/)).each(function(){0===this.indexOf("select2-")&&a.push(this)}),(r=t.trim(n.attr("class")))&&t((r=""+r).split(/\s+/)).each(function(){0!==this.indexOf("select2-")&&null!=(o=i(this))&&a.push(o)}),e.attr("class",a.join(" "))}}}),e.define("select2/compat/containerCss",["jquery","./utils"],function(t,e){function n(t){return null}function i(){}return i.prototype.render=function(i){var r=i.call(this),o=this.options.get("containerCssClass")||"";t.isFunction(o)&&(o=o(this.$element));var a=this.options.get("adaptContainerCssClass");if(a=a||n,-1!==o.indexOf(":all:")){o=o.replace(":all:","");var s=a;a=function(t){var e=s(t);return null!=e?e+" "+t:t}}var l=this.options.get("containerCss")||{};return t.isFunction(l)&&(l=l(this.$element)),e.syncCssClasses(r,this.$element,a),r.css(l),r.addClass(o),r},i}),e.define("select2/compat/dropdownCss",["jquery","./utils"],function(t,e){function n(t){return null}function i(){}return i.prototype.render=function(i){var r=i.call(this),o=this.options.get("dropdownCssClass")||"";t.isFunction(o)&&(o=o(this.$element));var a=this.options.get("adaptDropdownCssClass");if(a=a||n,-1!==o.indexOf(":all:")){o=o.replace(":all:","");var s=a;a=function(t){var e=s(t);return null!=e?e+" "+t:t}}var l=this.options.get("dropdownCss")||{};return t.isFunction(l)&&(l=l(this.$element)),e.syncCssClasses(r,this.$element,a),r.css(l),r.addClass(o),r},i}),e.define("select2/compat/initSelection",["jquery"],function(t){function e(t,e,n){n.get("debug")&&window.console&&console.warn&&console.warn("Select2: The `initSelection` option has been deprecated in favor of a custom data adapter that overrides the `current` method. This method is now called multiple times instead of a single time when the instance is initialized. Support will be removed for the `initSelection` option in future versions of Select2"),this.initSelection=n.get("initSelection"),this._isInitialized=!1,t.call(this,e,n)}return e.prototype.current=function(e,n){var i=this;this._isInitialized?e.call(this,n):this.initSelection.call(null,this.$element,function(e){i._isInitialized=!0,t.isArray(e)||(e=[e]),n(e)})},e}),e.define("select2/compat/inputData",["jquery","../utils"],function(t,e){function n(t,e,n){this._currentData=[],this._valueSeparator=n.get("valueSeparator")||",","hidden"===e.prop("type")&&n.get("debug")&&console&&console.warn&&console.warn("Select2: Using a hidden input with Select2 is no longer supported and may stop working in the future. It is recommended to use a `').appendTo(this.$container),this.$element.before(this.$container),this.build(n),this.isInit=!1}function i(t,e){if("function"!=typeof t[e]){var n=t[e];t[e]=function(t){return t[n]}}}function r(t,e){if("function"!=typeof t[e]){var n=t[e];t[e]=function(){return n}}}n.prototype={constructor:n,add:function(e,n,i){var r=this;if(!(r.options.maxTags&&r.itemsArray.length>=r.options.maxTags)&&(!1===e||e)){if("string"==typeof e&&r.options.trimValue&&(e=t.trim(e)),"object"==typeof e&&!r.objectItems)throw"Can't add objects when itemValue option is not set";if(!e.toString().match(/^\s*$/)){if(r.isSelect&&!r.multiple&&r.itemsArray.length>0&&r.remove(r.itemsArray[0]),"string"==typeof e&&"INPUT"===this.$element[0].tagName){var o=r.options.delimiterRegex?r.options.delimiterRegex:r.options.delimiter,s=e.split(o);if(s.length>1){for(var l=0;lr.options.maxInputLength)){var f=t.Event("beforeItemAdd",{item:e,cancel:!1,options:i});if(r.$element.trigger(f),!f.cancel){r.itemsArray.push(e);var p=t(''+a(u)+'');p.data("item",e),r.findInputWrapper().before(p),p.after(" ");var g=t('option[value="'+encodeURIComponent(c)+'"]',r.$element).length||t('option[value="'+a(c)+'"]',r.$element).length;if(r.isSelect&&!g){var m=t("");m.data("item",e),m.attr("value",c),r.$element.append(m)}n||r.pushVal(),r.options.maxTags!==r.itemsArray.length&&r.items().toString().length!==r.options.maxInputLength||r.$container.addClass("bootstrap-tagsinput-max"),t(".typeahead, .twitter-typeahead",r.$container).length&&r.$input.typeahead("val",""),this.isInit?r.$element.trigger(t.Event("itemAddedOnInit",{item:e,options:i})):r.$element.trigger(t.Event("itemAdded",{item:e,options:i}))}}}else if(r.options.onTagExists){var v=t(".tag",r.$container).filter(function(){return t(this).data("item")===C});r.options.onTagExists(e,v)}}}},remove:function(e,n,i){var r=this;if(r.objectItems&&(e=(e="object"==typeof e?t.grep(r.itemsArray,function(t){return r.options.itemValue(t)==r.options.itemValue(e)}):t.grep(r.itemsArray,function(t){return r.options.itemValue(t)==e}))[e.length-1]),e){var o=t.Event("beforeItemRemove",{item:e,cancel:!1,options:i});if(r.$element.trigger(o),o.cancel)return;t(".tag",r.$container).filter(function(){return t(this).data("item")===e}).remove(),t("option",r.$element).filter(function(){return t(this).data("item")===e}).remove(),-1!==t.inArray(e,r.itemsArray)&&r.itemsArray.splice(t.inArray(e,r.itemsArray),1)}n||r.pushVal(),r.options.maxTags>r.itemsArray.length&&r.$container.removeClass("bootstrap-tagsinput-max"),r.$element.trigger(t.Event("itemRemoved",{item:e,options:i}))},removeAll:function(){for(t(".tag",this.$container).remove(),t("option",this.$element).remove();this.itemsArray.length>0;)this.itemsArray.pop();this.pushVal()},refresh:function(){var e=this;t(".tag",e.$container).each(function(){var n=t(this),i=n.data("item"),r=e.options.itemValue(i),o=e.options.itemText(i),s=e.options.tagClass(i);(n.attr("class",null),n.addClass("tag "+a(s)),n.contents().filter(function(){return 3==this.nodeType})[0].nodeValue=a(o),e.isSelect)&&t("option",e.$element).filter(function(){return t(this).data("item")===i}).attr("value",r)})},items:function(){return this.itemsArray},pushVal:function(){var e=this,n=t.map(e.items(),function(t){return e.options.itemValue(t).toString()});e.$element.val(n,!0).trigger("change")},build:function(n){var o=this;if(o.options=t.extend({},e,n),o.objectItems&&(o.options.freeInput=!1),i(o.options,"itemValue"),i(o.options,"itemText"),r(o.options,"tagClass"),o.options.typeahead){var a=o.options.typeahead||{};r(a,"source"),o.$input.typeahead(t.extend({},a,{source:function(e,n){function i(t){for(var e=[],i=0;i$1")}}))}if(o.options.typeaheadjs){var l=null,c={},u=o.options.typeaheadjs;t.isArray(u)?(l=u[0],c=u[1]):c=u,o.$input.typeahead(l,c).on("typeahead:selected",t.proxy(function(t,e){c.valueKey?o.add(e[c.valueKey]):o.add(e),o.$input.typeahead("val","")},o))}o.$container.on("click",t.proxy(function(t){o.$element.attr("disabled")||o.$input.removeAttr("disabled"),o.$input.focus()},o)),o.options.addOnBlur&&o.options.freeInput&&o.$input.on("focusout",t.proxy(function(e){0===t(".typeahead, .twitter-typeahead",o.$container).length&&(o.add(o.$input.val()),o.$input.val(""))},o)),o.$container.on("keydown","input",t.proxy(function(e){var n=t(e.target),i=o.findInputWrapper();if(o.$element.attr("disabled"))o.$input.attr("disabled","disabled");else{switch(e.which){case 8:if(0===s(n[0])){var r=i.prev();r.length&&o.remove(r.data("item"))}break;case 46:if(0===s(n[0])){var a=i.next();a.length&&o.remove(a.data("item"))}break;case 37:var l=i.prev();0===n.val().length&&l[0]&&(l.before(i),n.focus());break;case 39:var c=i.next();0===n.val().length&&c[0]&&(c.after(i),n.focus())}var u=n.val().length;Math.ceil(u/5);n.attr("size",Math.max(this.inputSize,n.val().length))}},o)),o.$container.on("keypress","input",t.proxy(function(e){var n=t(e.target);if(o.$element.attr("disabled"))o.$input.attr("disabled","disabled");else{var i,r,a,s=n.val(),l=o.options.maxChars&&s.length>=o.options.maxChars;o.options.freeInput&&(i=e,r=o.options.confirmKeys,a=!1,t.each(r,function(t,e){if("number"==typeof e&&i.which===e)return a=!0,!1;if(i.which===e.which){var n=!e.hasOwnProperty("altKey")||i.altKey===e.altKey,r=!e.hasOwnProperty("shiftKey")||i.shiftKey===e.shiftKey,o=!e.hasOwnProperty("ctrlKey")||i.ctrlKey===e.ctrlKey;if(n&&r&&o)return a=!0,!1}}),a||l)&&(0!==s.length&&(o.add(l?s.substr(0,o.options.maxChars):s),n.val("")),!1===o.options.cancelConfirmKeysOnEmpty&&e.preventDefault());var c=n.val().length;Math.ceil(c/5);n.attr("size",Math.max(this.inputSize,n.val().length))}},o)),o.$container.on("click","[data-role=remove]",t.proxy(function(e){o.$element.attr("disabled")||o.remove(t(e.target).closest(".tag").data("item"))},o)),o.options.itemValue===e.itemValue&&("INPUT"===o.$element[0].tagName?o.add(o.$element.val()):t("option",o.$element).each(function(){o.add(t(this).attr("value"),!0)}))},destroy:function(){this.$container.off("keypress","input"),this.$container.off("click","[role=remove]"),this.$container.remove(),this.$element.removeData("tagsinput"),this.$element.show()},focus:function(){this.$input.focus()},input:function(){return this.$input},findInputWrapper:function(){for(var e=this.$input[0],n=this.$container[0];e&&e.parentNode!==n;)e=e.parentNode;return t(e)}},t.fn.tagsinput=function(e,i,r){var o=[];return this.each(function(){var a=t(this).data("tagsinput");if(a)if(e||i){if(void 0!==a[e]){if(3===a[e].length&&void 0!==r)var s=a[e](i,null,r);else s=a[e](i);void 0!==s&&o.push(s)}}else o.push(a);else a=new n(this,e),t(this).data("tagsinput",a),o.push(a),"SELECT"===this.tagName&&t("option",t(this)).attr("selected","selected"),t(this).val(t(this).val())}),"string"==typeof e?o.length>1?o:o[0]:o},t.fn.tagsinput.Constructor=n;var o=t("
        ");function a(t){return t?o.text(t).html():""}function s(t){var e=0;if(document.selection){t.focus();var n=document.selection.createRange();n.moveStart("character",-t.value.length),e=n.text.length}else(t.selectionStart||"0"==t.selectionStart)&&(e=t.selectionStart);return e}t(function(){t("input[data-role=tagsinput], select[multiple][data-role=tagsinput]").tagsinput()})}(window.jQuery),function(t){!jQuery&&"function"==typeof define&&define.amd?define(["jquery"],function(e){return t(e,document,window,navigator)}):jQuery||"object"!=typeof exports?t(jQuery,document,window,navigator):t(require("jquery"),document,window,navigator)}(function(t,e,n,i,r){"use strict";var o,a,s=0,l=(a=/msie\s\d+/i,0<(o=i.userAgent).search(a)&&a.exec(o).toString().split(" ")[1]<9&&(t("html").addClass("lt-ie9"),!0));Function.prototype.bind||(Function.prototype.bind=function(t){var e=this,n=[].slice;if("function"!=typeof e)throw new TypeError;var i=n.call(arguments,1),r=function(){if(this instanceof r){var o=function(){};o.prototype=e.prototype;var a=new o,s=e.apply(a,i.concat(n.call(arguments)));return Object(s)===s?s:a}return e.apply(t,i.concat(n.call(arguments)))};return r}),Array.prototype.indexOf||(Array.prototype.indexOf=function(t,e){var n;if(null==this)throw new TypeError('"this" is null or not defined');var i=Object(this),r=i.length>>>0;if(0===r)return-1;var o=+e||0;if(Math.abs(o)===1/0&&(o=0),r<=o)return-1;for(n=Math.max(0<=o?o:r-Math.abs(o),0);n!",u[0]),(l={skin:u.data("skin"),type:u.data("type"),min:u.data("min"),max:u.data("max"),from:u.data("from"),to:u.data("to"),step:u.data("step"),min_interval:u.data("minInterval"),max_interval:u.data("maxInterval"),drag_interval:u.data("dragInterval"),values:u.data("values"),from_fixed:u.data("fromFixed"),from_min:u.data("fromMin"),from_max:u.data("fromMax"),from_shadow:u.data("fromShadow"),to_fixed:u.data("toFixed"),to_min:u.data("toMin"),to_max:u.data("toMax"),to_shadow:u.data("toShadow"),prettify_enabled:u.data("prettifyEnabled"),prettify_separator:u.data("prettifySeparator"),force_edges:u.data("forceEdges"),keyboard:u.data("keyboard"),grid:u.data("grid"),grid_margin:u.data("gridMargin"),grid_num:u.data("gridNum"),grid_snap:u.data("gridSnap"),hide_min_max:u.data("hideMinMax"),hide_from_to:u.data("hideFromTo"),prefix:u.data("prefix"),postfix:u.data("postfix"),max_postfix:u.data("maxPostfix"),decorate_both:u.data("decorateBoth"),values_separator:u.data("valuesSeparator"),input_values_separator:u.data("inputValuesSeparator"),disable:u.data("disable"),block:u.data("block"),extra_classes:u.data("extraClasses")}).values=l.values&&l.values.split(","),l)l.hasOwnProperty(c)&&(l[c]!==r&&""!==l[c]||delete l[c]);h!==r&&""!==h&&((h=h.split(l.input_values_separator||o.input_values_separator||";"))[0]&&h[0]==+h[0]&&(h[0]=+h[0]),h[1]&&h[1]==+h[1]&&(h[1]=+h[1]),o&&o.values&&o.values.length?(s.from=h[0]&&o.values.indexOf(h[0]),s.to=h[1]&&o.values.indexOf(h[1])):(s.from=h[0]&&+h[0],s.to=h[1]&&+h[1])),t.extend(s,o),t.extend(s,l),this.options=s,this.update_check={},this.validate(),this.result={input:this.$cache.input,slider:null,min:this.options.min,max:this.options.max,from:this.options.from,from_percent:0,from_value:null,to:this.options.to,to_percent:0,to_value:null},this.init()};c.prototype={init:function(t){this.no_diapason=!1,this.coords.p_step=this.convertToPercent(this.options.step,!0),this.target="base",this.toggleInput(),this.append(),this.setMinMax(),t?(this.force_redraw=!0,this.calc(!0),this.callOnUpdate()):(this.force_redraw=!0,this.calc(!0),this.callOnStart()),this.updateScene()},append:function(){var t='';this.$cache.input.before(t),this.$cache.input.prop("readonly",!0),this.$cache.cont=this.$cache.input.prev(),this.result.slider=this.$cache.cont,this.$cache.cont.html('01000'),this.$cache.rs=this.$cache.cont.find(".irs"),this.$cache.min=this.$cache.cont.find(".irs-min"),this.$cache.max=this.$cache.cont.find(".irs-max"),this.$cache.from=this.$cache.cont.find(".irs-from"),this.$cache.to=this.$cache.cont.find(".irs-to"),this.$cache.single=this.$cache.cont.find(".irs-single"),this.$cache.line=this.$cache.cont.find(".irs-line"),this.$cache.grid=this.$cache.cont.find(".irs-grid"),"single"===this.options.type?(this.$cache.cont.append(''),this.$cache.bar=this.$cache.cont.find(".irs-bar"),this.$cache.edge=this.$cache.cont.find(".irs-bar-edge"),this.$cache.s_single=this.$cache.cont.find(".single"),this.$cache.from[0].style.visibility="hidden",this.$cache.to[0].style.visibility="hidden",this.$cache.shad_single=this.$cache.cont.find(".shadow-single")):(this.$cache.cont.append(''),this.$cache.bar=this.$cache.cont.find(".irs-bar"),this.$cache.s_from=this.$cache.cont.find(".from"),this.$cache.s_to=this.$cache.cont.find(".to"),this.$cache.shad_from=this.$cache.cont.find(".shadow-from"),this.$cache.shad_to=this.$cache.cont.find(".shadow-to"),this.setTopHandler()),this.options.hide_from_to&&(this.$cache.from[0].style.display="none",this.$cache.to[0].style.display="none",this.$cache.single[0].style.display="none"),this.appendGrid(),this.options.disable?(this.appendDisableMask(),this.$cache.input[0].disabled=!0):(this.$cache.input[0].disabled=!1,this.removeDisableMask(),this.bindEvents()),this.options.disable||(this.options.block?this.appendDisableMask():this.removeDisableMask()),this.options.drag_interval&&(this.$cache.bar[0].style.cursor="ew-resize")},setTopHandler:function(){var t=this.options.min,e=this.options.max,n=this.options.from,i=this.options.to;t'),this.$cache.cont.addClass("irs-disabled")},removeDisableMask:function(){this.$cache.cont.remove(".irs-disable-mask"),this.$cache.cont.removeClass("irs-disabled")},remove:function(){this.$cache.cont.remove(),this.$cache.cont=null,this.$cache.line.off("keydown.irs_"+this.plugin_count),this.$cache.body.off("touchmove.irs_"+this.plugin_count),this.$cache.body.off("mousemove.irs_"+this.plugin_count),this.$cache.win.off("touchend.irs_"+this.plugin_count),this.$cache.win.off("mouseup.irs_"+this.plugin_count),l&&(this.$cache.body.off("mouseup.irs_"+this.plugin_count),this.$cache.body.off("mouseleave.irs_"+this.plugin_count)),this.$cache.grid_labels=[],this.coords.big=[],this.coords.big_w=[],this.coords.big_p=[],this.coords.big_x=[],cancelAnimationFrame(this.raf_id)},bindEvents:function(){this.no_diapason||(this.$cache.body.on("touchmove.irs_"+this.plugin_count,this.pointerMove.bind(this)),this.$cache.body.on("mousemove.irs_"+this.plugin_count,this.pointerMove.bind(this)),this.$cache.win.on("touchend.irs_"+this.plugin_count,this.pointerUp.bind(this)),this.$cache.win.on("mouseup.irs_"+this.plugin_count,this.pointerUp.bind(this)),this.$cache.line.on("touchstart.irs_"+this.plugin_count,this.pointerClick.bind(this,"click")),this.$cache.line.on("mousedown.irs_"+this.plugin_count,this.pointerClick.bind(this,"click")),this.$cache.line.on("focus.irs_"+this.plugin_count,this.pointerFocus.bind(this)),this.options.drag_interval&&"double"===this.options.type?(this.$cache.bar.on("touchstart.irs_"+this.plugin_count,this.pointerDown.bind(this,"both")),this.$cache.bar.on("mousedown.irs_"+this.plugin_count,this.pointerDown.bind(this,"both"))):(this.$cache.bar.on("touchstart.irs_"+this.plugin_count,this.pointerClick.bind(this,"click")),this.$cache.bar.on("mousedown.irs_"+this.plugin_count,this.pointerClick.bind(this,"click"))),"single"===this.options.type?(this.$cache.single.on("touchstart.irs_"+this.plugin_count,this.pointerDown.bind(this,"single")),this.$cache.s_single.on("touchstart.irs_"+this.plugin_count,this.pointerDown.bind(this,"single")),this.$cache.shad_single.on("touchstart.irs_"+this.plugin_count,this.pointerClick.bind(this,"click")),this.$cache.single.on("mousedown.irs_"+this.plugin_count,this.pointerDown.bind(this,"single")),this.$cache.s_single.on("mousedown.irs_"+this.plugin_count,this.pointerDown.bind(this,"single")),this.$cache.edge.on("mousedown.irs_"+this.plugin_count,this.pointerClick.bind(this,"click")),this.$cache.shad_single.on("mousedown.irs_"+this.plugin_count,this.pointerClick.bind(this,"click"))):(this.$cache.single.on("touchstart.irs_"+this.plugin_count,this.pointerDown.bind(this,null)),this.$cache.single.on("mousedown.irs_"+this.plugin_count,this.pointerDown.bind(this,null)),this.$cache.from.on("touchstart.irs_"+this.plugin_count,this.pointerDown.bind(this,"from")),this.$cache.s_from.on("touchstart.irs_"+this.plugin_count,this.pointerDown.bind(this,"from")),this.$cache.to.on("touchstart.irs_"+this.plugin_count,this.pointerDown.bind(this,"to")),this.$cache.s_to.on("touchstart.irs_"+this.plugin_count,this.pointerDown.bind(this,"to")),this.$cache.shad_from.on("touchstart.irs_"+this.plugin_count,this.pointerClick.bind(this,"click")),this.$cache.shad_to.on("touchstart.irs_"+this.plugin_count,this.pointerClick.bind(this,"click")),this.$cache.from.on("mousedown.irs_"+this.plugin_count,this.pointerDown.bind(this,"from")),this.$cache.s_from.on("mousedown.irs_"+this.plugin_count,this.pointerDown.bind(this,"from")),this.$cache.to.on("mousedown.irs_"+this.plugin_count,this.pointerDown.bind(this,"to")),this.$cache.s_to.on("mousedown.irs_"+this.plugin_count,this.pointerDown.bind(this,"to")),this.$cache.shad_from.on("mousedown.irs_"+this.plugin_count,this.pointerClick.bind(this,"click")),this.$cache.shad_to.on("mousedown.irs_"+this.plugin_count,this.pointerClick.bind(this,"click"))),this.options.keyboard&&this.$cache.line.on("keydown.irs_"+this.plugin_count,this.key.bind(this,"keyboard")),l&&(this.$cache.body.on("mouseup.irs_"+this.plugin_count,this.pointerUp.bind(this)),this.$cache.body.on("mouseleave.irs_"+this.plugin_count,this.pointerUp.bind(this))))},pointerFocus:function(t){var e,n;this.target||(e=(n="single"===this.options.type?this.$cache.single:this.$cache.from).offset().left,e+=n.width()/2-1,this.pointerClick("single",{preventDefault:function(){},pageX:e}))},pointerMove:function(t){if(this.dragging){var e=t.pageX||t.originalEvent.touches&&t.originalEvent.touches[0].pageX;this.coords.x_pointer=e-this.coords.x_gap,this.calc()}},pointerUp:function(e){this.current_plugin===this.plugin_count&&this.is_active&&(this.is_active=!1,this.$cache.cont.find(".state_hover").removeClass("state_hover"),this.force_redraw=!0,l&&t("*").prop("unselectable",!1),this.updateScene(),this.restoreOriginalMinInterval(),(t.contains(this.$cache.cont[0],e.target)||this.dragging)&&this.callOnFinish(),this.dragging=!1)},pointerDown:function(e,n){n.preventDefault();var i=n.pageX||n.originalEvent.touches&&n.originalEvent.touches[0].pageX;2!==n.button&&("both"===e&&this.setTempMinInterval(),e||(e=this.target||"from"),this.current_plugin=this.plugin_count,this.target=e,this.is_active=!0,this.dragging=!0,this.coords.x_gap=this.$cache.rs.offset().left,this.coords.x_pointer=i-this.coords.x_gap,this.calcPointerPercent(),this.changeLevel(e),l&&t("*").prop("unselectable",!0),this.$cache.line.trigger("focus"),this.updateScene())},pointerClick:function(t,e){e.preventDefault();var n=e.pageX||e.originalEvent.touches&&e.originalEvent.touches[0].pageX;2!==e.button&&(this.current_plugin=this.plugin_count,this.target=t,this.is_click=!0,this.coords.x_gap=this.$cache.rs.offset().left,this.coords.x_pointer=+(n-this.coords.x_gap).toFixed(),this.force_redraw=!0,this.calc(),this.$cache.line.trigger("focus"))},key:function(t,e){if(!(this.current_plugin!==this.plugin_count||e.altKey||e.ctrlKey||e.shiftKey||e.metaKey)){switch(e.which){case 83:case 65:case 40:case 37:e.preventDefault(),this.moveByKey(!1);break;case 87:case 68:case 38:case 39:e.preventDefault(),this.moveByKey(!0)}return!0}},moveByKey:function(t){var e=this.coords.p_pointer,n=(this.options.max-this.options.min)/100;n=this.options.step/n,t?e+=n:e-=n,this.coords.x_pointer=this.toFixed(this.coords.w_rs/100*e),this.is_key=!0,this.calc()},setMinMax:function(){if(this.options){if(this.options.hide_min_max)return this.$cache.min[0].style.display="none",void(this.$cache.max[0].style.display="none");if(this.options.values.length)this.$cache.min.html(this.decorate(this.options.p_values[this.options.min])),this.$cache.max.html(this.decorate(this.options.p_values[this.options.max]));else{var t=this._prettify(this.options.min),e=this._prettify(this.options.max);this.result.min_pretty=t,this.result.max_pretty=e,this.$cache.min.html(this.decorate(t,this.options.min)),this.$cache.max.html(this.decorate(e,this.options.max))}this.labels.w_min=this.$cache.min.outerWidth(!1),this.labels.w_max=this.$cache.max.outerWidth(!1)}},setTempMinInterval:function(){var t=this.result.to-this.result.from;null===this.old_min_interval&&(this.old_min_interval=this.options.min_interval),this.options.min_interval=t},restoreOriginalMinInterval:function(){null!==this.old_min_interval&&(this.options.min_interval=this.old_min_interval,this.old_min_interval=null)},calc:function(t){if(this.options&&(this.calc_count++,(10===this.calc_count||t)&&(this.calc_count=0,this.coords.w_rs=this.$cache.rs.outerWidth(!1),this.calcHandlePercent()),this.coords.w_rs)){this.calcPointerPercent();var e=this.getHandleX();switch("both"===this.target&&(this.coords.p_gap=0,e=this.getHandleX()),"click"===this.target&&(this.coords.p_gap=this.coords.p_handle/2,e=this.getHandleX(),this.options.drag_interval?this.target="both_one":this.target=this.chooseHandle(e)),this.target){case"base":var n=(this.options.max-this.options.min)/100,i=(this.result.from-this.options.min)/n,r=(this.result.to-this.options.min)/n;this.coords.p_single_real=this.toFixed(i),this.coords.p_from_real=this.toFixed(i),this.coords.p_to_real=this.toFixed(r),this.coords.p_single_real=this.checkDiapason(this.coords.p_single_real,this.options.from_min,this.options.from_max),this.coords.p_from_real=this.checkDiapason(this.coords.p_from_real,this.options.from_min,this.options.from_max),this.coords.p_to_real=this.checkDiapason(this.coords.p_to_real,this.options.to_min,this.options.to_max),this.coords.p_single_fake=this.convertToFakePercent(this.coords.p_single_real),this.coords.p_from_fake=this.convertToFakePercent(this.coords.p_from_real),this.coords.p_to_fake=this.convertToFakePercent(this.coords.p_to_real),this.target=null;break;case"single":if(this.options.from_fixed)break;this.coords.p_single_real=this.convertToRealPercent(e),this.coords.p_single_real=this.calcWithStep(this.coords.p_single_real),this.coords.p_single_real=this.checkDiapason(this.coords.p_single_real,this.options.from_min,this.options.from_max),this.coords.p_single_fake=this.convertToFakePercent(this.coords.p_single_real);break;case"from":if(this.options.from_fixed)break;this.coords.p_from_real=this.convertToRealPercent(e),this.coords.p_from_real=this.calcWithStep(this.coords.p_from_real),this.coords.p_from_real>this.coords.p_to_real&&(this.coords.p_from_real=this.coords.p_to_real),this.coords.p_from_real=this.checkDiapason(this.coords.p_from_real,this.options.from_min,this.options.from_max),this.coords.p_from_real=this.checkMinInterval(this.coords.p_from_real,this.coords.p_to_real,"from"),this.coords.p_from_real=this.checkMaxInterval(this.coords.p_from_real,this.coords.p_to_real,"from"),this.coords.p_from_fake=this.convertToFakePercent(this.coords.p_from_real);break;case"to":if(this.options.to_fixed)break;this.coords.p_to_real=this.convertToRealPercent(e),this.coords.p_to_real=this.calcWithStep(this.coords.p_to_real),this.coords.p_to_realthis.coords.w_rs&&(this.coords.x_pointer=this.coords.w_rs),this.coords.p_pointer=this.toFixed(this.coords.x_pointer/this.coords.w_rs*100)):this.coords.p_pointer=0},convertToRealPercent:function(t){return t/(100-this.coords.p_handle)*100},convertToFakePercent:function(t){return t/100*(100-this.coords.p_handle)},getHandleX:function(){var t=100-this.coords.p_handle,e=this.toFixed(this.coords.p_pointer-this.coords.p_gap);return e<0?e=0:t100-this.labels.p_max-1?this.$cache.max[0].style.visibility="hidden":this.$cache.max[0].style.visibility="visible";else{n=o?(this.options.decorate_both?(t=this.decorate(a[this.result.from]),t+=this.options.values_separator,t+=this.decorate(a[this.result.to])):t=this.decorate(a[this.result.from]+this.options.values_separator+a[this.result.to]),e=this.decorate(a[this.result.from]),this.decorate(a[this.result.to])):(i=this._prettify(this.result.from),r=this._prettify(this.result.to),this.options.decorate_both?(t=this.decorate(i,this.result.from),t+=this.options.values_separator,t+=this.decorate(r,this.result.to)):t=this.decorate(i+this.options.values_separator+r,this.result.to),e=this.decorate(i,this.result.from),this.decorate(r,this.result.to)),this.$cache.single.html(t),this.$cache.from.html(e),this.$cache.to.html(n),this.calcLabels();var s=Math.min(this.labels.p_single_left,this.labels.p_from_left),l=this.labels.p_single_left+this.labels.p_single_fake,c=this.labels.p_to_left+this.labels.p_to_fake,u=Math.max(l,c);this.labels.p_from_left+this.labels.p_from_fake>=this.labels.p_to_left?(this.$cache.from[0].style.visibility="hidden",this.$cache.to[0].style.visibility="hidden",this.$cache.single[0].style.visibility="visible",u=this.result.from===this.result.to?("from"===this.target?this.$cache.from[0].style.visibility="visible":"to"===this.target?this.$cache.to[0].style.visibility="visible":this.target||(this.$cache.from[0].style.visibility="visible"),this.$cache.single[0].style.visibility="hidden",c):(this.$cache.from[0].style.visibility="hidden",this.$cache.to[0].style.visibility="hidden",this.$cache.single[0].style.visibility="visible",Math.max(l,c))):(this.$cache.from[0].style.visibility="visible",this.$cache.to[0].style.visibility="visible",this.$cache.single[0].style.visibility="hidden"),s100-this.labels.p_max-1?this.$cache.max[0].style.visibility="hidden":this.$cache.max[0].style.visibility="visible"}}},drawShadow:function(){var t,e,n,i,r=this.options,o=this.$cache,a="number"==typeof r.from_min&&!isNaN(r.from_min),s="number"==typeof r.from_max&&!isNaN(r.from_max),l="number"==typeof r.to_min&&!isNaN(r.to_min),c="number"==typeof r.to_max&&!isNaN(r.to_max);"single"===r.type?r.from_shadow&&(a||s)?(t=this.convertToPercent(a?r.from_min:r.min),e=this.convertToPercent(s?r.from_max:r.max)-t,t=this.toFixed(t-this.coords.p_handle/100*t),e=this.toFixed(e-this.coords.p_handle/100*e),t+=this.coords.p_handle/2,o.shad_single[0].style.display="block",o.shad_single[0].style.left=t+"%",o.shad_single[0].style.width=e+"%"):o.shad_single[0].style.display="none":(r.from_shadow&&(a||s)?(t=this.convertToPercent(a?r.from_min:r.min),e=this.convertToPercent(s?r.from_max:r.max)-t,t=this.toFixed(t-this.coords.p_handle/100*t),e=this.toFixed(e-this.coords.p_handle/100*e),t+=this.coords.p_handle/2,o.shad_from[0].style.display="block",o.shad_from[0].style.left=t+"%",o.shad_from[0].style.width=e+"%"):o.shad_from[0].style.display="none",r.to_shadow&&(l||c)?(n=this.convertToPercent(l?r.to_min:r.min),i=this.convertToPercent(c?r.to_max:r.max)-n,n=this.toFixed(n-this.coords.p_handle/100*n),i=this.toFixed(i-this.coords.p_handle/100*i),n+=this.coords.p_handle/2,o.shad_to[0].style.display="block",o.shad_to[0].style.left=n+"%",o.shad_to[0].style.width=i+"%"):o.shad_to[0].style.display="none")},writeToInput:function(){"single"===this.options.type?(this.options.values.length?this.$cache.input.prop("value",this.result.from_value):this.$cache.input.prop("value",this.result.from),this.$cache.input.data("from",this.result.from)):(this.options.values.length?this.$cache.input.prop("value",this.result.from_value+this.options.input_values_separator+this.result.to_value):this.$cache.input.prop("value",this.result.from+this.options.input_values_separator+this.result.to),this.$cache.input.data("from",this.result.from),this.$cache.input.data("to",this.result.to))},callOnStart:function(){this.writeToInput(),this.options.onStart&&"function"==typeof this.options.onStart&&(this.options.scope?this.options.onStart.call(this.options.scope,this.result):this.options.onStart(this.result))},callOnChange:function(){this.writeToInput(),this.options.onChange&&"function"==typeof this.options.onChange&&(this.options.scope?this.options.onChange.call(this.options.scope,this.result):this.options.onChange(this.result))},callOnFinish:function(){this.writeToInput(),this.options.onFinish&&"function"==typeof this.options.onFinish&&(this.options.scope?this.options.onFinish.call(this.options.scope,this.result):this.options.onFinish(this.result))},callOnUpdate:function(){this.writeToInput(),this.options.onUpdate&&"function"==typeof this.options.onUpdate&&(this.options.scope?this.options.onUpdate.call(this.options.scope,this.result):this.options.onUpdate(this.result))},toggleInput:function(){this.$cache.input.toggleClass("irs-hidden-input"),this.has_tab_index?this.$cache.input.prop("tabindex",-1):this.$cache.input.removeProp("tabindex"),this.has_tab_index=!this.has_tab_index},convertToPercent:function(t,e){var n,i=this.options.max-this.options.min,r=i/100;return i?(n=(e?t:t-this.options.min)/r,this.toFixed(n)):(this.no_diapason=!0,0)},convertToValue:function(t){var e,n,i=this.options.min,r=this.options.max,o=i.toString().split(".")[1],a=r.toString().split(".")[1],s=0,l=0;if(0===t)return this.options.min;if(100===t)return this.options.max;o&&(s=e=o.length),a&&(s=n=a.length),e&&n&&(s=n<=e?e:n),i<0&&(i=+(i+(l=Math.abs(i))).toFixed(s),r=+(r+l).toFixed(s));var c,u=(r-i)/100*t+i,h=this.options.step.toString().split(".")[1];return u=h?+u.toFixed(h.length):(u/=this.options.step,+(u*=this.options.step).toFixed(0)),l&&(u-=l),(c=h?+u.toFixed(h.length):this.toFixed(u))this.options.max&&(c=this.options.max),c},calcWithStep:function(t){var e=Math.round(t/this.coords.p_step)*this.coords.p_step;return 100o.max_interval&&(i=r-o.max_interval):i-r>o.max_interval&&(i=r+o.max_interval),this.convertToPercent(i)):t},checkDiapason:function(t,e,n){var i=this.convertToValue(t),r=this.options;return"number"!=typeof e&&(e=r.min),"number"!=typeof n&&(n=r.max),in.max&&(n.from=n.max)):(n.fromn.max&&(n.from=n.max),n.ton.max&&(n.to=n.max),this.update_check.from&&(this.update_check.from!==n.from&&n.from>n.to&&(n.from=n.to),this.update_check.to!==n.to&&n.ton.to&&(n.from=n.to),n.ton.from_max&&(n.from=n.from_max),"number"==typeof n.to_min&&n.ton.to_max&&(n.to=n.to_max),i&&(i.min!==n.min&&(i.min=n.min),i.max!==n.max&&(i.max=n.max),(i.fromi.max)&&(i.from=n.from),(i.toi.max)&&(i.to=n.to)),("number"!=typeof n.min_interval||isNaN(n.min_interval)||!n.min_interval||n.min_interval<0)&&(n.min_interval=0),("number"!=typeof n.max_interval||isNaN(n.max_interval)||!n.max_interval||n.max_interval<0)&&(n.max_interval=0),n.min_interval&&n.min_interval>n.max-n.min&&(n.min_interval=n.max-n.min),n.max_interval&&n.max_interval>n.max-n.min&&(n.max_interval=n.max-n.min)},decorate:function(t,e){var n="",i=this.options;return i.prefix&&(n+=i.prefix),n+=t,i.max_postfix&&(i.values.length&&t===i.p_values[i.max]?(n+=i.max_postfix,i.postfix&&(n+=" ")):e===i.max&&(n+=i.max_postfix,i.postfix&&(n+=" "))),i.postfix&&(n+=i.postfix),n},updateFrom:function(){this.result.from=this.options.from,this.result.from_percent=this.convertToPercent(this.result.from),this.result.from_pretty=this._prettify(this.result.from),this.options.values&&(this.result.from_value=this.options.values[this.result.from])},updateTo:function(){this.result.to=this.options.to,this.result.to_percent=this.convertToPercent(this.result.to),this.result.to_pretty=this._prettify(this.result.to),this.options.values&&(this.result.to_value=this.options.values[this.result.to])},updateResult:function(){this.result.min=this.options.min,this.result.max=this.options.max,this.updateFrom(),this.updateTo()},appendGrid:function(){if(this.options.grid){var t,e,n,i,r,o,a=this.options,s=a.max-a.min,l=a.grid_num,c=0,u=4,h="";for(this.calcGridMargin(),a.grid_snap&&(l=s/a.step),50';h+='',o=this.convertToValue(c),h+=''+(o=a.values.length?a.p_values[o]:this._prettify(o))+""}this.coords.big_num=Math.ceil(l+1),this.$cache.cont.addClass("irs-with-grid"),this.$cache.grid.html(h),this.cacheGridLabels()}},cacheGridLabels:function(){var t,e,n=this.coords.big_num;for(e=0;e100+this.coords.grid_gap&&(i[r-1]=100+this.coords.grid_gap,n[r-1]=this.toFixed(i[r-1]-this.coords.big_p[r-1]),this.coords.big_x[r-1]=this.toFixed(this.coords.big_p[r-1]-this.coords.grid_gap))),this.calcGridCollision(2,n,i),this.calcGridCollision(4,n,i),t=0;t").attr("name",n.submitButton.name).val(t(n.submitButton).val()).appendTo(n.currentForm)),!(n.settings.submitHandler&&!n.settings.debug)||(r=n.settings.submitHandler.call(n,n.currentForm,e),i&&i.remove(),void 0!==r&&r)}return n.settings.debug&&e.preventDefault(),n.cancelSubmit?(n.cancelSubmit=!1,i()):n.form()?n.pendingRequest?(n.formSubmitted=!0,!1):i():(n.focusInvalid(),!1)})),n)}e&&e.debug&&window.console&&console.warn("Nothing selected, can't validate, returning nothing.")},valid:function(){var e,n,i;return t(this[0]).is("form")?e=this.validate().form():(i=[],e=!0,n=t(this[0].form).validate(),this.each(function(){(e=n.element(this)&&e)||(i=i.concat(n.errorList))}),n.errorList=i),e},rules:function(e,n){var i,r,o,a,s,l,c=this[0],u=void 0!==this.attr("contenteditable")&&"false"!==this.attr("contenteditable");if(null!=c&&(!c.form&&u&&(c.form=this.closest("form")[0],c.name=this.attr("name")),null!=c.form)){if(e)switch(r=(i=t.data(c.form,"validator").settings).rules,o=t.validator.staticRules(c),e){case"add":t.extend(o,t.validator.normalizeRule(n)),delete o.messages,r[c.name]=o,n.messages&&(i.messages[c.name]=t.extend(i.messages[c.name],n.messages));break;case"remove":return n?(l={},t.each(n.split(/\s/),function(t,e){l[e]=o[e],delete o[e]}),l):(delete r[c.name],o)}return(a=t.validator.normalizeRules(t.extend({},t.validator.classRules(c),t.validator.attributeRules(c),t.validator.dataRules(c),t.validator.staticRules(c)),c)).required&&(s=a.required,delete a.required,a=t.extend({required:s},a)),a.remote&&(s=a.remote,delete a.remote,a=t.extend(a,{remote:s})),a}}}),t.extend(t.expr.pseudos||t.expr[":"],{blank:function(e){return!t.trim(""+t(e).val())},filled:function(e){var n=t(e).val();return null!==n&&!!t.trim(""+n)},unchecked:function(e){return!t(e).prop("checked")}}),t.validator=function(e,n){this.settings=t.extend(!0,{},t.validator.defaults,e),this.currentForm=n,this.init()},t.validator.format=function(e,n){return 1===arguments.length?function(){var n=t.makeArray(arguments);return n.unshift(e),t.validator.format.apply(this,n)}:void 0===n?e:(arguments.length>2&&n.constructor!==Array&&(n=t.makeArray(arguments).slice(1)),n.constructor!==Array&&(n=[n]),t.each(n,function(t,n){e=e.replace(new RegExp("\\{"+t+"\\}","g"),function(){return n})}),e)},t.extend(t.validator,{defaults:{messages:{},groups:{},rules:{},errorClass:"error",pendingClass:"pending",validClass:"valid",errorElement:"label",focusCleanup:!1,focusInvalid:!0,errorContainer:t([]),errorLabelContainer:t([]),onsubmit:!0,ignore:":hidden",ignoreTitle:!1,onfocusin:function(t){this.lastActive=t,this.settings.focusCleanup&&(this.settings.unhighlight&&this.settings.unhighlight.call(this,t,this.settings.errorClass,this.settings.validClass),this.hideThese(this.errorsFor(t)))},onfocusout:function(t){this.checkable(t)||!(t.name in this.submitted)&&this.optional(t)||this.element(t)},onkeyup:function(e,n){9===n.which&&""===this.elementValue(e)||-1!==t.inArray(n.keyCode,[16,17,18,20,35,36,37,38,39,40,45,144,225])||(e.name in this.submitted||e.name in this.invalid)&&this.element(e)},onclick:function(t){t.name in this.submitted?this.element(t):t.parentNode.name in this.submitted&&this.element(t.parentNode)},highlight:function(e,n,i){"radio"===e.type?this.findByName(e.name).addClass(n).removeClass(i):t(e).addClass(n).removeClass(i)},unhighlight:function(e,n,i){"radio"===e.type?this.findByName(e.name).removeClass(n).addClass(i):t(e).removeClass(n).addClass(i)}},setDefaults:function(e){t.extend(t.validator.defaults,e)},messages:{required:"This field is required.",remote:"Please fix this field.",email:"Please enter a valid email address.",url:"Please enter a valid URL.",date:"Please enter a valid date.",dateISO:"Please enter a valid date (ISO).",number:"Please enter a valid number.",digits:"Please enter only digits.",equalTo:"Please enter the same value again.",maxlength:t.validator.format("Please enter no more than {0} characters."),minlength:t.validator.format("Please enter at least {0} characters."),rangelength:t.validator.format("Please enter a value between {0} and {1} characters long."),range:t.validator.format("Please enter a value between {0} and {1}."),max:t.validator.format("Please enter a value less than or equal to {0}."),min:t.validator.format("Please enter a value greater than or equal to {0}."),step:t.validator.format("Please enter a multiple of {0}.")},autoCreateRanges:!1,prototype:{init:function(){this.labelContainer=t(this.settings.errorLabelContainer),this.errorContext=this.labelContainer.length&&this.labelContainer||t(this.currentForm),this.containers=t(this.settings.errorContainer).add(this.settings.errorLabelContainer),this.submitted={},this.valueCache={},this.pendingRequest=0,this.pending={},this.invalid={},this.reset();var e,n=this.currentForm,i=this.groups={};function r(e){var i=void 0!==t(this).attr("contenteditable")&&"false"!==t(this).attr("contenteditable");if(!this.form&&i&&(this.form=t(this).closest("form")[0],this.name=t(this).attr("name")),n===this.form){var r=t.data(this.form,"validator"),o="on"+e.type.replace(/^validate/,""),a=r.settings;a[o]&&!t(this).is(a.ignore)&&a[o].call(r,this,e)}}t.each(this.settings.groups,function(e,n){"string"==typeof n&&(n=n.split(/\s/)),t.each(n,function(t,n){i[n]=e})}),e=this.settings.rules,t.each(e,function(n,i){e[n]=t.validator.normalizeRule(i)}),t(this.currentForm).on("focusin.validate focusout.validate keyup.validate",":text, [type='password'], [type='file'], select, textarea, [type='number'], [type='search'], [type='tel'], [type='url'], [type='email'], [type='datetime'], [type='date'], [type='month'], [type='week'], [type='time'], [type='datetime-local'], [type='range'], [type='color'], [type='radio'], [type='checkbox'], [contenteditable], [type='button']",r).on("click.validate","select, option, [type='radio'], [type='checkbox']",r),this.settings.invalidHandler&&t(this.currentForm).on("invalid-form.validate",this.settings.invalidHandler)},form:function(){return this.checkForm(),t.extend(this.submitted,this.errorMap),this.invalid=t.extend({},this.errorMap),this.valid()||t(this.currentForm).triggerHandler("invalid-form",[this]),this.showErrors(),this.valid()},checkForm:function(){this.prepareForm();for(var t=0,e=this.currentElements=this.elements();e[t];t++)this.check(e[t]);return this.valid()},element:function(e){var n,i,r=this.clean(e),o=this.validationTargetFor(r),a=this,s=!0;return void 0===o?delete this.invalid[r.name]:(this.prepareElement(o),this.currentElements=t(o),(i=this.groups[o.name])&&t.each(this.groups,function(t,e){e===i&&t!==o.name&&(r=a.validationTargetFor(a.clean(a.findByName(t))))&&r.name in a.invalid&&(a.currentElements.push(r),s=a.check(r)&&s)}),n=!1!==this.check(o),s=s&&n,this.invalid[o.name]=!n,this.numberOfInvalids()||(this.toHide=this.toHide.add(this.containers)),this.showErrors(),t(e).attr("aria-invalid",!n)),s},showErrors:function(e){if(e){var n=this;t.extend(this.errorMap,e),this.errorList=t.map(this.errorMap,function(t,e){return{message:t,element:n.findByName(e)[0]}}),this.successList=t.grep(this.successList,function(t){return!(t.name in e)})}this.settings.showErrors?this.settings.showErrors.call(this,this.errorMap,this.errorList):this.defaultShowErrors()},resetForm:function(){t.fn.resetForm&&t(this.currentForm).resetForm(),this.invalid={},this.submitted={},this.prepareForm(),this.hideErrors();var e=this.elements().removeData("previousValue").removeAttr("aria-invalid");this.resetElements(e)},resetElements:function(t){var e;if(this.settings.unhighlight)for(e=0;t[e];e++)this.settings.unhighlight.call(this,t[e],this.settings.errorClass,""),this.findByName(t[e].name).removeClass(this.settings.validClass);else t.removeClass(this.settings.errorClass).removeClass(this.settings.validClass)},numberOfInvalids:function(){return this.objectLength(this.invalid)},objectLength:function(t){var e,n=0;for(e in t)void 0!==t[e]&&null!==t[e]&&!1!==t[e]&&n++;return n},hideErrors:function(){this.hideThese(this.toHide)},hideThese:function(t){t.not(this.containers).text(""),this.addWrapper(t).hide()},valid:function(){return 0===this.size()},size:function(){return this.errorList.length},focusInvalid:function(){if(this.settings.focusInvalid)try{t(this.findLastActive()||this.errorList.length&&this.errorList[0].element||[]).filter(":visible").focus().trigger("focusin")}catch(t){}},findLastActive:function(){var e=this.lastActive;return e&&1===t.grep(this.errorList,function(t){return t.element.name===e.name}).length&&e},elements:function(){var e=this,n={};return t(this.currentForm).find("input, select, textarea, [contenteditable]").not(":submit, :reset, :image, :disabled").not(this.settings.ignore).filter(function(){var i=this.name||t(this).attr("name"),r=void 0!==t(this).attr("contenteditable")&&"false"!==t(this).attr("contenteditable");return!i&&e.settings.debug&&window.console&&console.error("%o has no name assigned",this),r&&(this.form=t(this).closest("form")[0],this.name=i),this.form===e.currentForm&&(!(i in n||!e.objectLength(t(this).rules()))&&(n[i]=!0,!0))})},clean:function(e){return t(e)[0]},errors:function(){var e=this.settings.errorClass.split(" ").join(".");return t(this.settings.errorElement+"."+e,this.errorContext)},resetInternals:function(){this.successList=[],this.errorList=[],this.errorMap={},this.toShow=t([]),this.toHide=t([])},reset:function(){this.resetInternals(),this.currentElements=t([])},prepareForm:function(){this.reset(),this.toHide=this.errors().add(this.containers)},prepareElement:function(t){this.reset(),this.toHide=this.errorsFor(t)},elementValue:function(e){var n,i,r=t(e),o=e.type,a=void 0!==r.attr("contenteditable")&&"false"!==r.attr("contenteditable");return"radio"===o||"checkbox"===o?this.findByName(e.name).filter(":checked").val():"number"===o&&void 0!==e.validity?e.validity.badInput?"NaN":r.val():(n=a?r.text():r.val(),"file"===o?"C:\\fakepath\\"===n.substr(0,12)?n.substr(12):(i=n.lastIndexOf("/"))>=0?n.substr(i+1):(i=n.lastIndexOf("\\"))>=0?n.substr(i+1):n:"string"==typeof n?n.replace(/\r/g,""):n)},check:function(e){e=this.validationTargetFor(this.clean(e));var n,i,r,o,a=t(e).rules(),s=t.map(a,function(t,e){return e}).length,l=!1,c=this.elementValue(e);for(i in"function"==typeof a.normalizer?o=a.normalizer:"function"==typeof this.settings.normalizer&&(o=this.settings.normalizer),o&&(c=o.call(e,c),delete a.normalizer),a){r={method:i,parameters:a[i]};try{if("dependency-mismatch"===(n=t.validator.methods[i].call(this,c,e,r.parameters))&&1===s){l=!0;continue}if(l=!1,"pending"===n)return void(this.toHide=this.toHide.not(this.errorsFor(e)));if(!n)return this.formatAndAdd(e,r),!1}catch(t){throw this.settings.debug&&window.console&&console.log("Exception occurred when checking element "+e.id+", check the '"+r.method+"' method.",t),t instanceof TypeError&&(t.message+=". Exception occurred when checking element "+e.id+", check the '"+r.method+"' method."),t}}if(!l)return this.objectLength(a)&&this.successList.push(e),!0},customDataMessage:function(e,n){return t(e).data("msg"+n.charAt(0).toUpperCase()+n.substring(1).toLowerCase())||t(e).data("msg")},customMessage:function(t,e){var n=this.settings.messages[t];return n&&(n.constructor===String?n:n[e])},findDefined:function(){for(var t=0;tWarning: No message defined for "+e.name+""),r=/\$?\{(\d+)\}/g;return"function"==typeof i?i=i.call(this,n.parameters,e):r.test(i)&&(i=t.validator.format(i.replace(r,"{$1}"),n.parameters)),i},formatAndAdd:function(t,e){var n=this.defaultMessage(t,e);this.errorList.push({message:n,element:t,method:e.method}),this.errorMap[t.name]=n,this.submitted[t.name]=n},addWrapper:function(t){return this.settings.wrapper&&(t=t.add(t.parent(this.settings.wrapper))),t},defaultShowErrors:function(){var t,e,n;for(t=0;this.errorList[t];t++)n=this.errorList[t],this.settings.highlight&&this.settings.highlight.call(this,n.element,this.settings.errorClass,this.settings.validClass),this.showLabel(n.element,n.message);if(this.errorList.length&&(this.toShow=this.toShow.add(this.containers)),this.settings.success)for(t=0;this.successList[t];t++)this.showLabel(this.successList[t]);if(this.settings.unhighlight)for(t=0,e=this.validElements();e[t];t++)this.settings.unhighlight.call(this,e[t],this.settings.errorClass,this.settings.validClass);this.toHide=this.toHide.not(this.toShow),this.hideErrors(),this.addWrapper(this.toShow).show()},validElements:function(){return this.currentElements.not(this.invalidElements())},invalidElements:function(){return t(this.errorList).map(function(){return this.element})},showLabel:function(e,n){var i,r,o,a,s=this.errorsFor(e),l=this.idOrName(e),c=t(e).attr("aria-describedby");s.length?(s.removeClass(this.settings.validClass).addClass(this.settings.errorClass),s.html(n)):(i=s=t("<"+this.settings.errorElement+">").attr("id",l+"-error").addClass(this.settings.errorClass).html(n||""),this.settings.wrapper&&(i=s.hide().show().wrap("<"+this.settings.wrapper+"/>").parent()),this.labelContainer.length?this.labelContainer.append(i):this.settings.errorPlacement?this.settings.errorPlacement.call(this,i,t(e)):i.insertAfter(e),s.is("label")?s.attr("for",l):0===s.parents("label[for='"+this.escapeCssMeta(l)+"']").length&&(o=s.attr("id"),c?c.match(new RegExp("\\b"+this.escapeCssMeta(o)+"\\b"))||(c+=" "+o):c=o,t(e).attr("aria-describedby",c),(r=this.groups[e.name])&&(a=this,t.each(a.groups,function(e,n){n===r&&t("[name='"+a.escapeCssMeta(e)+"']",a.currentForm).attr("aria-describedby",s.attr("id"))})))),!n&&this.settings.success&&(s.text(""),"string"==typeof this.settings.success?s.addClass(this.settings.success):this.settings.success(s,e)),this.toShow=this.toShow.add(s)},errorsFor:function(e){var n=this.escapeCssMeta(this.idOrName(e)),i=t(e).attr("aria-describedby"),r="label[for='"+n+"'], label[for='"+n+"'] *";return i&&(r=r+", #"+this.escapeCssMeta(i).replace(/\s+/g,", #")),this.errors().filter(r)},escapeCssMeta:function(t){return t.replace(/([\\!"#$%&'()*+,.\/:;<=>?@\[\]^`{|}~])/g,"\\$1")},idOrName:function(t){return this.groups[t.name]||(this.checkable(t)?t.name:t.id||t.name)},validationTargetFor:function(e){return this.checkable(e)&&(e=this.findByName(e.name)),t(e).not(this.settings.ignore)[0]},checkable:function(t){return/radio|checkbox/i.test(t.type)},findByName:function(e){return t(this.currentForm).find("[name='"+this.escapeCssMeta(e)+"']")},getLength:function(e,n){switch(n.nodeName.toLowerCase()){case"select":return t("option:selected",n).length;case"input":if(this.checkable(n))return this.findByName(n.name).filter(":checked").length}return e.length},depend:function(t,e){return!this.dependTypes[typeof t]||this.dependTypes[typeof t](t,e)},dependTypes:{boolean:function(t){return t},string:function(e,n){return!!t(e,n.form).length},function:function(t,e){return t(e)}},optional:function(e){var n=this.elementValue(e);return!t.validator.methods.required.call(this,n,e)&&"dependency-mismatch"},startRequest:function(e){this.pending[e.name]||(this.pendingRequest++,t(e).addClass(this.settings.pendingClass),this.pending[e.name]=!0)},stopRequest:function(e,n){this.pendingRequest--,this.pendingRequest<0&&(this.pendingRequest=0),delete this.pending[e.name],t(e).removeClass(this.settings.pendingClass),n&&0===this.pendingRequest&&this.formSubmitted&&this.form()?(t(this.currentForm).submit(),this.submitButton&&t("input:hidden[name='"+this.submitButton.name+"']",this.currentForm).remove(),this.formSubmitted=!1):!n&&0===this.pendingRequest&&this.formSubmitted&&(t(this.currentForm).triggerHandler("invalid-form",[this]),this.formSubmitted=!1)},previousValue:function(e,n){return n="string"==typeof n&&n||"remote",t.data(e,"previousValue")||t.data(e,"previousValue",{old:null,valid:!0,message:this.defaultMessage(e,{method:n})})},destroy:function(){this.resetForm(),t(this.currentForm).off(".validate").removeData("validator").find(".validate-equalTo-blur").off(".validate-equalTo").removeClass("validate-equalTo-blur").find(".validate-lessThan-blur").off(".validate-lessThan").removeClass("validate-lessThan-blur").find(".validate-lessThanEqual-blur").off(".validate-lessThanEqual").removeClass("validate-lessThanEqual-blur").find(".validate-greaterThanEqual-blur").off(".validate-greaterThanEqual").removeClass("validate-greaterThanEqual-blur").find(".validate-greaterThan-blur").off(".validate-greaterThan").removeClass("validate-greaterThan-blur")}},classRuleSettings:{required:{required:!0},email:{email:!0},url:{url:!0},date:{date:!0},dateISO:{dateISO:!0},number:{number:!0},digits:{digits:!0},creditcard:{creditcard:!0}},addClassRules:function(e,n){e.constructor===String?this.classRuleSettings[e]=n:t.extend(this.classRuleSettings,e)},classRules:function(e){var n={},i=t(e).attr("class");return i&&t.each(i.split(" "),function(){this in t.validator.classRuleSettings&&t.extend(n,t.validator.classRuleSettings[this])}),n},normalizeAttributeRule:function(t,e,n,i){/min|max|step/.test(n)&&(null===e||/number|range|text/.test(e))&&(i=Number(i),isNaN(i)&&(i=void 0)),i||0===i?t[n]=i:e===n&&"range"!==e&&(t[n]=!0)},attributeRules:function(e){var n,i,r={},o=t(e),a=e.getAttribute("type");for(n in t.validator.methods)"required"===n?(""===(i=e.getAttribute(n))&&(i=!0),i=!!i):i=o.attr(n),this.normalizeAttributeRule(r,a,n,i);return r.maxlength&&/-1|2147483647|524288/.test(r.maxlength)&&delete r.maxlength,r},dataRules:function(e){var n,i,r={},o=t(e),a=e.getAttribute("type");for(n in t.validator.methods)""===(i=o.data("rule"+n.charAt(0).toUpperCase()+n.substring(1).toLowerCase()))&&(i=!0),this.normalizeAttributeRule(r,a,n,i);return r},staticRules:function(e){var n={},i=t.data(e.form,"validator");return i.settings.rules&&(n=t.validator.normalizeRule(i.settings.rules[e.name])||{}),n},normalizeRules:function(e,n){return t.each(e,function(i,r){if(!1!==r){if(r.param||r.depends){var o=!0;switch(typeof r.depends){case"string":o=!!t(r.depends,n.form).length;break;case"function":o=r.depends.call(n,n)}o?e[i]=void 0===r.param||r.param:(t.data(n.form,"validator").resetElements(t(n)),delete e[i])}}else delete e[i]}),t.each(e,function(i,r){e[i]=t.isFunction(r)&&"normalizer"!==i?r(n):r}),t.each(["minlength","maxlength"],function(){e[this]&&(e[this]=Number(e[this]))}),t.each(["rangelength","range"],function(){var n;e[this]&&(t.isArray(e[this])?e[this]=[Number(e[this][0]),Number(e[this][1])]:"string"==typeof e[this]&&(n=e[this].replace(/[\[\]]/g,"").split(/[\s,]+/),e[this]=[Number(n[0]),Number(n[1])]))}),t.validator.autoCreateRanges&&(null!=e.min&&null!=e.max&&(e.range=[e.min,e.max],delete e.min,delete e.max),null!=e.minlength&&null!=e.maxlength&&(e.rangelength=[e.minlength,e.maxlength],delete e.minlength,delete e.maxlength)),e},normalizeRule:function(e){if("string"==typeof e){var n={};t.each(e.split(/\s/),function(){n[this]=!0}),e=n}return e},addMethod:function(e,n,i){t.validator.methods[e]=n,t.validator.messages[e]=void 0!==i?i:t.validator.messages[e],n.length<3&&t.validator.addClassRules(e,t.validator.normalizeRule(e))},methods:{required:function(e,n,i){if(!this.depend(i,n))return"dependency-mismatch";if("select"===n.nodeName.toLowerCase()){var r=t(n).val();return r&&r.length>0}return this.checkable(n)?this.getLength(e,n)>0:null!=e&&e.length>0},email:function(t,e){return this.optional(e)||/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test(t)},url:function(t,e){return this.optional(e)||/^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[\/?#]\S*)?$/i.test(t)},date:(e=!1,function(t,n){return e||(e=!0,this.settings.debug&&window.console&&console.warn("The `date` method is deprecated and will be removed in version '2.0.0'.\nPlease don't use it, since it relies on the Date constructor, which\nbehaves very differently across browsers and locales. Use `dateISO`\ninstead or one of the locale specific methods in `localizations/`\nand `additional-methods.js`.")),this.optional(n)||!/Invalid|NaN/.test(new Date(t).toString())}),dateISO:function(t,e){return this.optional(e)||/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test(t)},number:function(t,e){return this.optional(e)||/^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(t)},digits:function(t,e){return this.optional(e)||/^\d+$/.test(t)},minlength:function(e,n,i){var r=t.isArray(e)?e.length:this.getLength(e,n);return this.optional(n)||r>=i},maxlength:function(e,n,i){var r=t.isArray(e)?e.length:this.getLength(e,n);return this.optional(n)||r<=i},rangelength:function(e,n,i){var r=t.isArray(e)?e.length:this.getLength(e,n);return this.optional(n)||r>=i[0]&&r<=i[1]},min:function(t,e,n){return this.optional(e)||t>=n},max:function(t,e,n){return this.optional(e)||t<=n},range:function(t,e,n){return this.optional(e)||t>=n[0]&&t<=n[1]},step:function(e,n,i){var r,o=t(n).attr("type"),a="Step attribute on input type "+o+" is not supported.",s=new RegExp("\\b"+o+"\\b"),l=function(t){var e=(""+t).match(/(?:\.(\d+))?$/);return e&&e[1]?e[1].length:0},c=function(t){return Math.round(t*Math.pow(10,r))},u=!0;if(o&&!s.test(["text","number","range"].join()))throw new Error(a);return r=l(i),(l(e)>r||c(e)%c(i)!=0)&&(u=!1),this.optional(n)||u},equalTo:function(e,n,i){var r=t(i);return this.settings.onfocusout&&r.not(".validate-equalTo-blur").length&&r.addClass("validate-equalTo-blur").on("blur.validate-equalTo",function(){t(n).valid()}),e===r.val()},remote:function(e,n,i,r){if(this.optional(n))return"dependency-mismatch";r="string"==typeof r&&r||"remote";var o,a,s,l=this.previousValue(n,r);return this.settings.messages[n.name]||(this.settings.messages[n.name]={}),l.originalMessage=l.originalMessage||this.settings.messages[n.name][r],this.settings.messages[n.name][r]=l.message,i="string"==typeof i&&{url:i}||i,s=t.param(t.extend({data:e},i.data)),l.old===s?l.valid:(l.old=s,o=this,this.startRequest(n),(a={})[n.name]=e,t.ajax(t.extend(!0,{mode:"abort",port:"validate"+n.name,dataType:"json",data:a,context:o.currentForm,success:function(t){var i,a,s,c=!0===t||"true"===t;o.settings.messages[n.name][r]=l.originalMessage,c?(s=o.formSubmitted,o.resetInternals(),o.toHide=o.errorsFor(n),o.formSubmitted=s,o.successList.push(n),o.invalid[n.name]=!1,o.showErrors()):(i={},a=t||o.defaultMessage(n,{method:r,parameters:e}),i[n.name]=l.message=a,o.invalid[n.name]=!0,o.showErrors(i)),l.valid=c,o.stopRequest(n,c)}},i)),"pending")}}});var n,i={};return t.ajaxPrefilter?t.ajaxPrefilter(function(t,e,n){var r=t.port;"abort"===t.mode&&(i[r]&&i[r].abort(),i[r]=n)}):(n=t.ajax,t.ajax=function(e){var r=("mode"in e?e:t.ajaxSettings).mode,o=("port"in e?e:t.ajaxSettings).port;return"abort"===r?(i[o]&&i[o].abort(),i[o]=n.apply(this,arguments),i[o]):n.apply(this,arguments)}),t}); \ No newline at end of file diff --git a/public/assets/js/vendors.js b/public/assets/js/vendors.js new file mode 100644 index 0000000..af722c6 --- /dev/null +++ b/public/assets/js/vendors.js @@ -0,0 +1,62617 @@ +/*! jQuery v3.3.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){"use strict";var n=[],r=e.document,i=Object.getPrototypeOf,o=n.slice,a=n.concat,s=n.push,u=n.indexOf,l={},c=l.toString,f=l.hasOwnProperty,p=f.toString,d=p.call(Object),h={},g=function e(t){return"function"==typeof t&&"number"!=typeof t.nodeType},y=function e(t){return null!=t&&t===t.window},v={type:!0,src:!0,noModule:!0};function m(e,t,n){var i,o=(t=t||r).createElement("script");if(o.text=e,n)for(i in v)n[i]&&(o[i]=n[i]);t.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[c.call(e)]||"object":typeof e}var b="3.3.1",w=function(e,t){return new w.fn.init(e,t)},T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;w.fn=w.prototype={jquery:"3.3.1",constructor:w,length:0,toArray:function(){return o.call(this)},get:function(e){return null==e?o.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=w.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return w.each(this,e)},map:function(e){return this.pushStack(w.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(o.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n0&&t-1 in e)}var E=function(e){var t,n,r,i,o,a,s,u,l,c,f,p,d,h,g,y,v,m,x,b="sizzle"+1*new Date,w=e.document,T=0,C=0,E=ae(),k=ae(),S=ae(),D=function(e,t){return e===t&&(f=!0),0},N={}.hasOwnProperty,A=[],j=A.pop,q=A.push,L=A.push,H=A.slice,O=function(e,t){for(var n=0,r=e.length;n+~]|"+M+")"+M+"*"),z=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),X=new RegExp(W),U=new RegExp("^"+R+"$"),V={ID:new RegExp("^#("+R+")"),CLASS:new RegExp("^\\.("+R+")"),TAG:new RegExp("^("+R+"|[*])"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+W),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+P+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},G=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Q=/^[^{]+\{\s*\[native \w/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,K=/[+~]/,Z=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ee=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},te=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ne=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},re=function(){p()},ie=me(function(e){return!0===e.disabled&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{L.apply(A=H.call(w.childNodes),w.childNodes),A[w.childNodes.length].nodeType}catch(e){L={apply:A.length?function(e,t){q.apply(e,H.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function oe(e,t,r,i){var o,s,l,c,f,h,v,m=t&&t.ownerDocument,T=t?t.nodeType:9;if(r=r||[],"string"!=typeof e||!e||1!==T&&9!==T&&11!==T)return r;if(!i&&((t?t.ownerDocument||t:w)!==d&&p(t),t=t||d,g)){if(11!==T&&(f=J.exec(e)))if(o=f[1]){if(9===T){if(!(l=t.getElementById(o)))return r;if(l.id===o)return r.push(l),r}else if(m&&(l=m.getElementById(o))&&x(t,l)&&l.id===o)return r.push(l),r}else{if(f[2])return L.apply(r,t.getElementsByTagName(e)),r;if((o=f[3])&&n.getElementsByClassName&&t.getElementsByClassName)return L.apply(r,t.getElementsByClassName(o)),r}if(n.qsa&&!S[e+" "]&&(!y||!y.test(e))){if(1!==T)m=t,v=e;else if("object"!==t.nodeName.toLowerCase()){(c=t.getAttribute("id"))?c=c.replace(te,ne):t.setAttribute("id",c=b),s=(h=a(e)).length;while(s--)h[s]="#"+c+" "+ve(h[s]);v=h.join(","),m=K.test(e)&&ge(t.parentNode)||t}if(v)try{return L.apply(r,m.querySelectorAll(v)),r}catch(e){}finally{c===b&&t.removeAttribute("id")}}}return u(e.replace(B,"$1"),t,r,i)}function ae(){var e=[];function t(n,i){return e.push(n+" ")>r.cacheLength&&delete t[e.shift()],t[n+" "]=i}return t}function se(e){return e[b]=!0,e}function ue(e){var t=d.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function le(e,t){var n=e.split("|"),i=n.length;while(i--)r.attrHandle[n[i]]=t}function ce(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function fe(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function pe(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function de(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&ie(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function he(e){return se(function(t){return t=+t,se(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function ge(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}n=oe.support={},o=oe.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},p=oe.setDocument=function(e){var t,i,a=e?e.ownerDocument||e:w;return a!==d&&9===a.nodeType&&a.documentElement?(d=a,h=d.documentElement,g=!o(d),w!==d&&(i=d.defaultView)&&i.top!==i&&(i.addEventListener?i.addEventListener("unload",re,!1):i.attachEvent&&i.attachEvent("onunload",re)),n.attributes=ue(function(e){return e.className="i",!e.getAttribute("className")}),n.getElementsByTagName=ue(function(e){return e.appendChild(d.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=Q.test(d.getElementsByClassName),n.getById=ue(function(e){return h.appendChild(e).id=b,!d.getElementsByName||!d.getElementsByName(b).length}),n.getById?(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){return e.getAttribute("id")===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n=t.getElementById(e);return n?[n]:[]}}):(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),r.find.TAG=n.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):n.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},r.find.CLASS=n.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&g)return t.getElementsByClassName(e)},v=[],y=[],(n.qsa=Q.test(d.querySelectorAll))&&(ue(function(e){h.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&y.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||y.push("\\["+M+"*(?:value|"+P+")"),e.querySelectorAll("[id~="+b+"-]").length||y.push("~="),e.querySelectorAll(":checked").length||y.push(":checked"),e.querySelectorAll("a#"+b+"+*").length||y.push(".#.+[+~]")}),ue(function(e){e.innerHTML="";var t=d.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&y.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&y.push(":enabled",":disabled"),h.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&y.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),y.push(",.*:")})),(n.matchesSelector=Q.test(m=h.matches||h.webkitMatchesSelector||h.mozMatchesSelector||h.oMatchesSelector||h.msMatchesSelector))&&ue(function(e){n.disconnectedMatch=m.call(e,"*"),m.call(e,"[s!='']:x"),v.push("!=",W)}),y=y.length&&new RegExp(y.join("|")),v=v.length&&new RegExp(v.join("|")),t=Q.test(h.compareDocumentPosition),x=t||Q.test(h.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return f=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r||(1&(r=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!n.sortDetached&&t.compareDocumentPosition(e)===r?e===d||e.ownerDocument===w&&x(w,e)?-1:t===d||t.ownerDocument===w&&x(w,t)?1:c?O(c,e)-O(c,t):0:4&r?-1:1)}:function(e,t){if(e===t)return f=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===d?-1:t===d?1:i?-1:o?1:c?O(c,e)-O(c,t):0;if(i===o)return ce(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?ce(a[r],s[r]):a[r]===w?-1:s[r]===w?1:0},d):d},oe.matches=function(e,t){return oe(e,null,null,t)},oe.matchesSelector=function(e,t){if((e.ownerDocument||e)!==d&&p(e),t=t.replace(z,"='$1']"),n.matchesSelector&&g&&!S[t+" "]&&(!v||!v.test(t))&&(!y||!y.test(t)))try{var r=m.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){}return oe(t,d,null,[e]).length>0},oe.contains=function(e,t){return(e.ownerDocument||e)!==d&&p(e),x(e,t)},oe.attr=function(e,t){(e.ownerDocument||e)!==d&&p(e);var i=r.attrHandle[t.toLowerCase()],o=i&&N.call(r.attrHandle,t.toLowerCase())?i(e,t,!g):void 0;return void 0!==o?o:n.attributes||!g?e.getAttribute(t):(o=e.getAttributeNode(t))&&o.specified?o.value:null},oe.escape=function(e){return(e+"").replace(te,ne)},oe.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},oe.uniqueSort=function(e){var t,r=[],i=0,o=0;if(f=!n.detectDuplicates,c=!n.sortStable&&e.slice(0),e.sort(D),f){while(t=e[o++])t===e[o]&&(i=r.push(o));while(i--)e.splice(r[i],1)}return c=null,e},i=oe.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=i(e)}else if(3===o||4===o)return e.nodeValue}else while(t=e[r++])n+=i(t);return n},(r=oe.selectors={cacheLength:50,createPseudo:se,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(Z,ee),e[3]=(e[3]||e[4]||e[5]||"").replace(Z,ee),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||oe.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&oe.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return V.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=a(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(Z,ee).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=E[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&E(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=oe.attr(r,e);return null==i?"!="===t:!t||(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i.replace($," ")+" ").indexOf(n)>-1:"|="===t&&(i===n||i.slice(0,n.length+1)===n+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h,g=o!==a?"nextSibling":"previousSibling",y=t.parentNode,v=s&&t.nodeName.toLowerCase(),m=!u&&!s,x=!1;if(y){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===v:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?y.firstChild:y.lastChild],a&&m){x=(d=(l=(c=(f=(p=y)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1])&&l[2],p=d&&y.childNodes[d];while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if(1===p.nodeType&&++x&&p===t){c[e]=[T,d,x];break}}else if(m&&(x=d=(l=(c=(f=(p=t)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1]),!1===x)while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===v:1===p.nodeType)&&++x&&(m&&((c=(f=p[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]=[T,x]),p===t))break;return(x-=i)===r||x%r==0&&x/r>=0}}},PSEUDO:function(e,t){var n,i=r.pseudos[e]||r.setFilters[e.toLowerCase()]||oe.error("unsupported pseudo: "+e);return i[b]?i(t):i.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?se(function(e,n){var r,o=i(e,t),a=o.length;while(a--)e[r=O(e,o[a])]=!(n[r]=o[a])}):function(e){return i(e,0,n)}):i}},pseudos:{not:se(function(e){var t=[],n=[],r=s(e.replace(B,"$1"));return r[b]?se(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),t[0]=null,!n.pop()}}),has:se(function(e){return function(t){return oe(e,t).length>0}}),contains:se(function(e){return e=e.replace(Z,ee),function(t){return(t.textContent||t.innerText||i(t)).indexOf(e)>-1}}),lang:se(function(e){return U.test(e||"")||oe.error("unsupported lang: "+e),e=e.replace(Z,ee).toLowerCase(),function(t){var n;do{if(n=g?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===h},focus:function(e){return e===d.activeElement&&(!d.hasFocus||d.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:de(!1),disabled:de(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return Y.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:he(function(){return[0]}),last:he(function(e,t){return[t-1]}),eq:he(function(e,t,n){return[n<0?n+t:n]}),even:he(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:he(function(e,t,n){for(var r=n<0?n+t:n;++r1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function be(e,t,n){for(var r=0,i=t.length;r-1&&(o[l]=!(a[l]=f))}}else v=we(v===a?v.splice(h,v.length):v),i?i(null,a,v,u):L.apply(a,v)})}function Ce(e){for(var t,n,i,o=e.length,a=r.relative[e[0].type],s=a||r.relative[" "],u=a?1:0,c=me(function(e){return e===t},s,!0),f=me(function(e){return O(t,e)>-1},s,!0),p=[function(e,n,r){var i=!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):f(e,n,r));return t=null,i}];u1&&xe(p),u>1&&ve(e.slice(0,u-1).concat({value:" "===e[u-2].type?"*":""})).replace(B,"$1"),n,u0,i=e.length>0,o=function(o,a,s,u,c){var f,h,y,v=0,m="0",x=o&&[],b=[],w=l,C=o||i&&r.find.TAG("*",c),E=T+=null==w?1:Math.random()||.1,k=C.length;for(c&&(l=a===d||a||c);m!==k&&null!=(f=C[m]);m++){if(i&&f){h=0,a||f.ownerDocument===d||(p(f),s=!g);while(y=e[h++])if(y(f,a||d,s)){u.push(f);break}c&&(T=E)}n&&((f=!y&&f)&&v--,o&&x.push(f))}if(v+=m,n&&m!==v){h=0;while(y=t[h++])y(x,b,a,s);if(o){if(v>0)while(m--)x[m]||b[m]||(b[m]=j.call(u));b=we(b)}L.apply(u,b),c&&!o&&b.length>0&&v+t.length>1&&oe.uniqueSort(u)}return c&&(T=E,l=w),x};return n?se(o):o}return s=oe.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=a(e)),n=t.length;while(n--)(o=Ce(t[n]))[b]?r.push(o):i.push(o);(o=S(e,Ee(i,r))).selector=e}return o},u=oe.select=function(e,t,n,i){var o,u,l,c,f,p="function"==typeof e&&e,d=!i&&a(e=p.selector||e);if(n=n||[],1===d.length){if((u=d[0]=d[0].slice(0)).length>2&&"ID"===(l=u[0]).type&&9===t.nodeType&&g&&r.relative[u[1].type]){if(!(t=(r.find.ID(l.matches[0].replace(Z,ee),t)||[])[0]))return n;p&&(t=t.parentNode),e=e.slice(u.shift().value.length)}o=V.needsContext.test(e)?0:u.length;while(o--){if(l=u[o],r.relative[c=l.type])break;if((f=r.find[c])&&(i=f(l.matches[0].replace(Z,ee),K.test(u[0].type)&&ge(t.parentNode)||t))){if(u.splice(o,1),!(e=i.length&&ve(u)))return L.apply(n,i),n;break}}}return(p||s(e,d))(i,t,!g,n,!t||K.test(e)&&ge(t.parentNode)||t),n},n.sortStable=b.split("").sort(D).join("")===b,n.detectDuplicates=!!f,p(),n.sortDetached=ue(function(e){return 1&e.compareDocumentPosition(d.createElement("fieldset"))}),ue(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||le("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),n.attributes&&ue(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||le("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ue(function(e){return null==e.getAttribute("disabled")})||le(P,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),oe}(e);w.find=E,w.expr=E.selectors,w.expr[":"]=w.expr.pseudos,w.uniqueSort=w.unique=E.uniqueSort,w.text=E.getText,w.isXMLDoc=E.isXML,w.contains=E.contains,w.escapeSelector=E.escape;var k=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&w(e).is(n))break;r.push(e)}return r},S=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},D=w.expr.match.needsContext;function N(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var A=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,t,n){return g(t)?w.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?w.grep(e,function(e){return e===t!==n}):"string"!=typeof t?w.grep(e,function(e){return u.call(t,e)>-1!==n}):w.filter(t,e,n)}w.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?w.find.matchesSelector(r,e)?[r]:[]:w.find.matches(e,w.grep(t,function(e){return 1===e.nodeType}))},w.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(w(e).filter(function(){for(t=0;t1?w.uniqueSort(n):n},filter:function(e){return this.pushStack(j(this,e||[],!1))},not:function(e){return this.pushStack(j(this,e||[],!0))},is:function(e){return!!j(this,"string"==typeof e&&D.test(e)?w(e):e||[],!1).length}});var q,L=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(w.fn.init=function(e,t,n){var i,o;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(i="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:L.exec(e))||!i[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(i[1]){if(t=t instanceof w?t[0]:t,w.merge(this,w.parseHTML(i[1],t&&t.nodeType?t.ownerDocument||t:r,!0)),A.test(i[1])&&w.isPlainObject(t))for(i in t)g(this[i])?this[i](t[i]):this.attr(i,t[i]);return this}return(o=r.getElementById(i[2]))&&(this[0]=o,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):g(e)?void 0!==n.ready?n.ready(e):e(w):w.makeArray(e,this)}).prototype=w.fn,q=w(r);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};w.fn.extend({has:function(e){var t=w(e,this),n=t.length;return this.filter(function(){for(var e=0;e-1:1===n.nodeType&&w.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?w.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?u.call(w(e),this[0]):u.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(w.uniqueSort(w.merge(this.get(),w(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}w.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return k(e,"parentNode")},parentsUntil:function(e,t,n){return k(e,"parentNode",n)},next:function(e){return P(e,"nextSibling")},prev:function(e){return P(e,"previousSibling")},nextAll:function(e){return k(e,"nextSibling")},prevAll:function(e){return k(e,"previousSibling")},nextUntil:function(e,t,n){return k(e,"nextSibling",n)},prevUntil:function(e,t,n){return k(e,"previousSibling",n)},siblings:function(e){return S((e.parentNode||{}).firstChild,e)},children:function(e){return S(e.firstChild)},contents:function(e){return N(e,"iframe")?e.contentDocument:(N(e,"template")&&(e=e.content||e),w.merge([],e.childNodes))}},function(e,t){w.fn[e]=function(n,r){var i=w.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=w.filter(r,i)),this.length>1&&(O[e]||w.uniqueSort(i),H.test(e)&&i.reverse()),this.pushStack(i)}});var M=/[^\x20\t\r\n\f]+/g;function R(e){var t={};return w.each(e.match(M)||[],function(e,n){t[n]=!0}),t}w.Callbacks=function(e){e="string"==typeof e?R(e):w.extend({},e);var t,n,r,i,o=[],a=[],s=-1,u=function(){for(i=i||e.once,r=t=!0;a.length;s=-1){n=a.shift();while(++s-1)o.splice(n,1),n<=s&&s--}),this},has:function(e){return e?w.inArray(e,o)>-1:o.length>0},empty:function(){return o&&(o=[]),this},disable:function(){return i=a=[],o=n="",this},disabled:function(){return!o},lock:function(){return i=a=[],n||t||(o=n=""),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=[e,(n=n||[]).slice?n.slice():n],a.push(n),t||u()),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!r}};return l};function I(e){return e}function W(e){throw e}function $(e,t,n,r){var i;try{e&&g(i=e.promise)?i.call(e).done(t).fail(n):e&&g(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}w.extend({Deferred:function(t){var n=[["notify","progress",w.Callbacks("memory"),w.Callbacks("memory"),2],["resolve","done",w.Callbacks("once memory"),w.Callbacks("once memory"),0,"resolved"],["reject","fail",w.Callbacks("once memory"),w.Callbacks("once memory"),1,"rejected"]],r="pending",i={state:function(){return r},always:function(){return o.done(arguments).fail(arguments),this},"catch":function(e){return i.then(null,e)},pipe:function(){var e=arguments;return w.Deferred(function(t){w.each(n,function(n,r){var i=g(e[r[4]])&&e[r[4]];o[r[1]](function(){var e=i&&i.apply(this,arguments);e&&g(e.promise)?e.promise().progress(t.notify).done(t.resolve).fail(t.reject):t[r[0]+"With"](this,i?[e]:arguments)})}),e=null}).promise()},then:function(t,r,i){var o=0;function a(t,n,r,i){return function(){var s=this,u=arguments,l=function(){var e,l;if(!(t=o&&(r!==W&&(s=void 0,u=[e]),n.rejectWith(s,u))}};t?c():(w.Deferred.getStackHook&&(c.stackTrace=w.Deferred.getStackHook()),e.setTimeout(c))}}return w.Deferred(function(e){n[0][3].add(a(0,e,g(i)?i:I,e.notifyWith)),n[1][3].add(a(0,e,g(t)?t:I)),n[2][3].add(a(0,e,g(r)?r:W))}).promise()},promise:function(e){return null!=e?w.extend(e,i):i}},o={};return w.each(n,function(e,t){var a=t[2],s=t[5];i[t[1]]=a.add,s&&a.add(function(){r=s},n[3-e][2].disable,n[3-e][3].disable,n[0][2].lock,n[0][3].lock),a.add(t[3].fire),o[t[0]]=function(){return o[t[0]+"With"](this===o?void 0:this,arguments),this},o[t[0]+"With"]=a.fireWith}),i.promise(o),t&&t.call(o,o),o},when:function(e){var t=arguments.length,n=t,r=Array(n),i=o.call(arguments),a=w.Deferred(),s=function(e){return function(n){r[e]=this,i[e]=arguments.length>1?o.call(arguments):n,--t||a.resolveWith(r,i)}};if(t<=1&&($(e,a.done(s(n)).resolve,a.reject,!t),"pending"===a.state()||g(i[n]&&i[n].then)))return a.then();while(n--)$(i[n],s(n),a.reject);return a.promise()}});var B=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;w.Deferred.exceptionHook=function(t,n){e.console&&e.console.warn&&t&&B.test(t.name)&&e.console.warn("jQuery.Deferred exception: "+t.message,t.stack,n)},w.readyException=function(t){e.setTimeout(function(){throw t})};var F=w.Deferred();w.fn.ready=function(e){return F.then(e)["catch"](function(e){w.readyException(e)}),this},w.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--w.readyWait:w.isReady)||(w.isReady=!0,!0!==e&&--w.readyWait>0||F.resolveWith(r,[w]))}}),w.ready.then=F.then;function _(){r.removeEventListener("DOMContentLoaded",_),e.removeEventListener("load",_),w.ready()}"complete"===r.readyState||"loading"!==r.readyState&&!r.documentElement.doScroll?e.setTimeout(w.ready):(r.addEventListener("DOMContentLoaded",_),e.addEventListener("load",_));var z=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===x(n)){i=!0;for(s in n)z(e,t,s,n[s],!0,o,a)}else if(void 0!==r&&(i=!0,g(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(w(e),n)})),t))for(;s1,null,!0)},removeData:function(e){return this.each(function(){K.remove(this,e)})}}),w.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=J.get(e,t),n&&(!r||Array.isArray(n)?r=J.access(e,t,w.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=w.queue(e,t),r=n.length,i=n.shift(),o=w._queueHooks(e,t),a=function(){w.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return J.get(e,n)||J.access(e,n,{empty:w.Callbacks("once memory").add(function(){J.remove(e,[t+"queue",n])})})}}),w.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length\x20\t\r\n\f]+)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
        "],col:[2,"","
        "],tr:[2,"","
        "],td:[3,"","
        "],_default:[0,"",""]};ge.optgroup=ge.option,ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td;function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&N(e,t)?w.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n-1)i&&i.push(o);else if(l=w.contains(o.ownerDocument,o),a=ye(f.appendChild(o),"script"),l&&ve(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}!function(){var e=r.createDocumentFragment().appendChild(r.createElement("div")),t=r.createElement("input");t.setAttribute("type","radio"),t.setAttribute("checked","checked"),t.setAttribute("name","t"),e.appendChild(t),h.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,e.innerHTML="",h.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue}();var be=r.documentElement,we=/^key/,Te=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ce=/^([^.]*)(?:\.(.+)|)/;function Ee(){return!0}function ke(){return!1}function Se(){try{return r.activeElement}catch(e){}}function De(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)De(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=ke;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return w().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=w.guid++)),e.each(function(){w.event.add(this,t,i,r,n)})}w.event={global:{},add:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.get(e);if(y){n.handler&&(n=(o=n).handler,i=o.selector),i&&w.find.matchesSelector(be,i),n.guid||(n.guid=w.guid++),(u=y.events)||(u=y.events={}),(a=y.handle)||(a=y.handle=function(t){return"undefined"!=typeof w&&w.event.triggered!==t.type?w.event.dispatch.apply(e,arguments):void 0}),l=(t=(t||"").match(M)||[""]).length;while(l--)d=g=(s=Ce.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=w.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=w.event.special[d]||{},c=w.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&w.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(d,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),w.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.hasData(e)&&J.get(e);if(y&&(u=y.events)){l=(t=(t||"").match(M)||[""]).length;while(l--)if(s=Ce.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){f=w.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,y.handle)||w.removeEvent(e,d,y.handle),delete u[d])}else for(d in u)w.event.remove(e,d+t[l],n,r,!0);w.isEmptyObject(u)&&J.remove(e,"handle events")}},dispatch:function(e){var t=w.event.fix(e),n,r,i,o,a,s,u=new Array(arguments.length),l=(J.get(this,"events")||{})[t.type]||[],c=w.event.special[t.type]||{};for(u[0]=t,n=1;n=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n-1:w.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u\x20\t\r\n\f]*)[^>]*)\/>/gi,Ae=/\s*$/g;function Le(e,t){return N(e,"table")&&N(11!==t.nodeType?t:t.firstChild,"tr")?w(e).children("tbody")[0]||e:e}function He(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Oe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Pe(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(J.hasData(e)&&(o=J.access(e),a=J.set(t,o),l=o.events)){delete a.handle,a.events={};for(i in l)for(n=0,r=l[i].length;n1&&"string"==typeof y&&!h.checkClone&&je.test(y))return e.each(function(i){var o=e.eq(i);v&&(t[0]=y.call(this,i,o.html())),Re(o,t,n,r)});if(p&&(i=xe(t,e[0].ownerDocument,!1,e,r),o=i.firstChild,1===i.childNodes.length&&(i=o),o||r)){for(u=(s=w.map(ye(i,"script"),He)).length;f")},clone:function(e,t,n){var r,i,o,a,s=e.cloneNode(!0),u=w.contains(e.ownerDocument,e);if(!(h.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||w.isXMLDoc(e)))for(a=ye(s),r=0,i=(o=ye(e)).length;r0&&ve(a,!u&&ye(e,"script")),s},cleanData:function(e){for(var t,n,r,i=w.event.special,o=0;void 0!==(n=e[o]);o++)if(Y(n)){if(t=n[J.expando]){if(t.events)for(r in t.events)i[r]?w.event.remove(n,r):w.removeEvent(n,r,t.handle);n[J.expando]=void 0}n[K.expando]&&(n[K.expando]=void 0)}}}),w.fn.extend({detach:function(e){return Ie(this,e,!0)},remove:function(e){return Ie(this,e)},text:function(e){return z(this,function(e){return void 0===e?w.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Re(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Le(this,e).appendChild(e)})},prepend:function(){return Re(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Le(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(w.cleanData(ye(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return w.clone(this,e,t)})},html:function(e){return z(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ae.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=w.htmlPrefilter(e);try{for(;n=0&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))),u}function et(e,t,n){var r=$e(e),i=Fe(e,t,r),o="border-box"===w.css(e,"boxSizing",!1,r),a=o;if(We.test(i)){if(!n)return i;i="auto"}return a=a&&(h.boxSizingReliable()||i===e.style[t]),("auto"===i||!parseFloat(i)&&"inline"===w.css(e,"display",!1,r))&&(i=e["offset"+t[0].toUpperCase()+t.slice(1)],a=!0),(i=parseFloat(i)||0)+Ze(e,t,n||(o?"border":"content"),a,r,i)+"px"}w.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Fe(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=G(t),u=Xe.test(t),l=e.style;if(u||(t=Je(s)),a=w.cssHooks[t]||w.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"==(o=typeof n)&&(i=ie.exec(n))&&i[1]&&(n=ue(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(w.cssNumber[s]?"":"px")),h.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=G(t);return Xe.test(t)||(t=Je(s)),(a=w.cssHooks[t]||w.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Fe(e,t,r)),"normal"===i&&t in Ve&&(i=Ve[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),w.each(["height","width"],function(e,t){w.cssHooks[t]={get:function(e,n,r){if(n)return!ze.test(w.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?et(e,t,r):se(e,Ue,function(){return et(e,t,r)})},set:function(e,n,r){var i,o=$e(e),a="border-box"===w.css(e,"boxSizing",!1,o),s=r&&Ze(e,t,r,a,o);return a&&h.scrollboxSize()===o.position&&(s-=Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(o[t])-Ze(e,t,"border",!1,o)-.5)),s&&(i=ie.exec(n))&&"px"!==(i[3]||"px")&&(e.style[t]=n,n=w.css(e,t)),Ke(e,n,s)}}}),w.cssHooks.marginLeft=_e(h.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Fe(e,"marginLeft"))||e.getBoundingClientRect().left-se(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),w.each({margin:"",padding:"",border:"Width"},function(e,t){w.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+oe[r]+t]=o[r]||o[r-2]||o[0];return i}},"margin"!==e&&(w.cssHooks[e+t].set=Ke)}),w.fn.extend({css:function(e,t){return z(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=$e(e),i=t.length;a1)}});function tt(e,t,n,r,i){return new tt.prototype.init(e,t,n,r,i)}w.Tween=tt,tt.prototype={constructor:tt,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||w.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(w.cssNumber[n]?"":"px")},cur:function(){var e=tt.propHooks[this.prop];return e&&e.get?e.get(this):tt.propHooks._default.get(this)},run:function(e){var t,n=tt.propHooks[this.prop];return this.options.duration?this.pos=t=w.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):tt.propHooks._default.set(this),this}},tt.prototype.init.prototype=tt.prototype,tt.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=w.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){w.fx.step[e.prop]?w.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[w.cssProps[e.prop]]&&!w.cssHooks[e.prop]?e.elem[e.prop]=e.now:w.style(e.elem,e.prop,e.now+e.unit)}}},tt.propHooks.scrollTop=tt.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},w.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},w.fx=tt.prototype.init,w.fx.step={};var nt,rt,it=/^(?:toggle|show|hide)$/,ot=/queueHooks$/;function at(){rt&&(!1===r.hidden&&e.requestAnimationFrame?e.requestAnimationFrame(at):e.setTimeout(at,w.fx.interval),w.fx.tick())}function st(){return e.setTimeout(function(){nt=void 0}),nt=Date.now()}function ut(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=oe[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function lt(e,t,n){for(var r,i=(pt.tweeners[t]||[]).concat(pt.tweeners["*"]),o=0,a=i.length;o1)},removeAttr:function(e){return this.each(function(){w.removeAttr(this,e)})}}),w.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?w.prop(e,t,n):(1===o&&w.isXMLDoc(e)||(i=w.attrHooks[t.toLowerCase()]||(w.expr.match.bool.test(t)?dt:void 0)),void 0!==n?null===n?void w.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=w.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!h.radioValue&&"radio"===t&&N(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(M);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),dt={set:function(e,t,n){return!1===t?w.removeAttr(e,n):e.setAttribute(n,n),n}},w.each(w.expr.match.bool.source.match(/\w+/g),function(e,t){var n=ht[t]||w.find.attr;ht[t]=function(e,t,r){var i,o,a=t.toLowerCase();return r||(o=ht[a],ht[a]=i,i=null!=n(e,t,r)?a:null,ht[a]=o),i}});var gt=/^(?:input|select|textarea|button)$/i,yt=/^(?:a|area)$/i;w.fn.extend({prop:function(e,t){return z(this,w.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[w.propFix[e]||e]})}}),w.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&w.isXMLDoc(e)||(t=w.propFix[t]||t,i=w.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=w.find.attr(e,"tabindex");return t?parseInt(t,10):gt.test(e.nodeName)||yt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),h.optSelected||(w.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),w.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){w.propFix[this.toLowerCase()]=this});function vt(e){return(e.match(M)||[]).join(" ")}function mt(e){return e.getAttribute&&e.getAttribute("class")||""}function xt(e){return Array.isArray(e)?e:"string"==typeof e?e.match(M)||[]:[]}w.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).addClass(e.call(this,t,mt(this)))});if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).removeClass(e.call(this,t,mt(this)))});if(!arguments.length)return this.attr("class","");if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])while(r.indexOf(" "+o+" ")>-1)r=r.replace(" "+o+" "," ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(e,t){var n=typeof e,r="string"===n||Array.isArray(e);return"boolean"==typeof t&&r?t?this.addClass(e):this.removeClass(e):g(e)?this.each(function(n){w(this).toggleClass(e.call(this,n,mt(this),t),t)}):this.each(function(){var t,i,o,a;if(r){i=0,o=w(this),a=xt(e);while(t=a[i++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else void 0!==e&&"boolean"!==n||((t=mt(this))&&J.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":J.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&(" "+vt(mt(n))+" ").indexOf(t)>-1)return!0;return!1}});var bt=/\r/g;w.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=g(e),this.each(function(n){var i;1===this.nodeType&&(null==(i=r?e.call(this,n,w(this).val()):e)?i="":"number"==typeof i?i+="":Array.isArray(i)&&(i=w.map(i,function(e){return null==e?"":e+""})),(t=w.valHooks[this.type]||w.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return(t=w.valHooks[i.type]||w.valHooks[i.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:"string"==typeof(n=i.value)?n.replace(bt,""):null==n?"":n}}}),w.extend({valHooks:{option:{get:function(e){var t=w.find.attr(e,"value");return null!=t?t:vt(w.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r-1)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),w.each(["radio","checkbox"],function(){w.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=w.inArray(w(e).val(),t)>-1}},h.checkOn||(w.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),h.focusin="onfocusin"in e;var wt=/^(?:focusinfocus|focusoutblur)$/,Tt=function(e){e.stopPropagation()};w.extend(w.event,{trigger:function(t,n,i,o){var a,s,u,l,c,p,d,h,v=[i||r],m=f.call(t,"type")?t.type:t,x=f.call(t,"namespace")?t.namespace.split("."):[];if(s=h=u=i=i||r,3!==i.nodeType&&8!==i.nodeType&&!wt.test(m+w.event.triggered)&&(m.indexOf(".")>-1&&(m=(x=m.split(".")).shift(),x.sort()),c=m.indexOf(":")<0&&"on"+m,t=t[w.expando]?t:new w.Event(m,"object"==typeof t&&t),t.isTrigger=o?2:3,t.namespace=x.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+x.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=i),n=null==n?[t]:w.makeArray(n,[t]),d=w.event.special[m]||{},o||!d.trigger||!1!==d.trigger.apply(i,n))){if(!o&&!d.noBubble&&!y(i)){for(l=d.delegateType||m,wt.test(l+m)||(s=s.parentNode);s;s=s.parentNode)v.push(s),u=s;u===(i.ownerDocument||r)&&v.push(u.defaultView||u.parentWindow||e)}a=0;while((s=v[a++])&&!t.isPropagationStopped())h=s,t.type=a>1?l:d.bindType||m,(p=(J.get(s,"events")||{})[t.type]&&J.get(s,"handle"))&&p.apply(s,n),(p=c&&s[c])&&p.apply&&Y(s)&&(t.result=p.apply(s,n),!1===t.result&&t.preventDefault());return t.type=m,o||t.isDefaultPrevented()||d._default&&!1!==d._default.apply(v.pop(),n)||!Y(i)||c&&g(i[m])&&!y(i)&&((u=i[c])&&(i[c]=null),w.event.triggered=m,t.isPropagationStopped()&&h.addEventListener(m,Tt),i[m](),t.isPropagationStopped()&&h.removeEventListener(m,Tt),w.event.triggered=void 0,u&&(i[c]=u)),t.result}},simulate:function(e,t,n){var r=w.extend(new w.Event,n,{type:e,isSimulated:!0});w.event.trigger(r,null,t)}}),w.fn.extend({trigger:function(e,t){return this.each(function(){w.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return w.event.trigger(e,t,n,!0)}}),h.focusin||w.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){w.event.simulate(t,e.target,w.event.fix(e))};w.event.special[t]={setup:function(){var r=this.ownerDocument||this,i=J.access(r,t);i||r.addEventListener(e,n,!0),J.access(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this,i=J.access(r,t)-1;i?J.access(r,t,i):(r.removeEventListener(e,n,!0),J.remove(r,t))}}});var Ct=e.location,Et=Date.now(),kt=/\?/;w.parseXML=function(t){var n;if(!t||"string"!=typeof t)return null;try{n=(new e.DOMParser).parseFromString(t,"text/xml")}catch(e){n=void 0}return n&&!n.getElementsByTagName("parsererror").length||w.error("Invalid XML: "+t),n};var St=/\[\]$/,Dt=/\r?\n/g,Nt=/^(?:submit|button|image|reset|file)$/i,At=/^(?:input|select|textarea|keygen)/i;function jt(e,t,n,r){var i;if(Array.isArray(t))w.each(t,function(t,i){n||St.test(e)?r(e,i):jt(e+"["+("object"==typeof i&&null!=i?t:"")+"]",i,n,r)});else if(n||"object"!==x(t))r(e,t);else for(i in t)jt(e+"["+i+"]",t[i],n,r)}w.param=function(e,t){var n,r=[],i=function(e,t){var n=g(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(Array.isArray(e)||e.jquery&&!w.isPlainObject(e))w.each(e,function(){i(this.name,this.value)});else for(n in e)jt(n,e[n],t,i);return r.join("&")},w.fn.extend({serialize:function(){return w.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=w.prop(this,"elements");return e?w.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!w(this).is(":disabled")&&At.test(this.nodeName)&&!Nt.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=w(this).val();return null==n?null:Array.isArray(n)?w.map(n,function(e){return{name:t.name,value:e.replace(Dt,"\r\n")}}):{name:t.name,value:n.replace(Dt,"\r\n")}}).get()}});var qt=/%20/g,Lt=/#.*$/,Ht=/([?&])_=[^&]*/,Ot=/^(.*?):[ \t]*([^\r\n]*)$/gm,Pt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Mt=/^(?:GET|HEAD)$/,Rt=/^\/\//,It={},Wt={},$t="*/".concat("*"),Bt=r.createElement("a");Bt.href=Ct.href;function Ft(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(M)||[];if(g(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function _t(e,t,n,r){var i={},o=e===Wt;function a(s){var u;return i[s]=!0,w.each(e[s]||[],function(e,s){var l=s(t,n,r);return"string"!=typeof l||o||i[l]?o?!(u=l):void 0:(t.dataTypes.unshift(l),a(l),!1)}),u}return a(t.dataTypes[0])||!i["*"]&&a("*")}function zt(e,t){var n,r,i=w.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&w.extend(!0,e,r),e}function Xt(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}function Ut(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}w.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Ct.href,type:"GET",isLocal:Pt.test(Ct.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":$t,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":w.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?zt(zt(e,w.ajaxSettings),t):zt(w.ajaxSettings,e)},ajaxPrefilter:Ft(It),ajaxTransport:Ft(Wt),ajax:function(t,n){"object"==typeof t&&(n=t,t=void 0),n=n||{};var i,o,a,s,u,l,c,f,p,d,h=w.ajaxSetup({},n),g=h.context||h,y=h.context&&(g.nodeType||g.jquery)?w(g):w.event,v=w.Deferred(),m=w.Callbacks("once memory"),x=h.statusCode||{},b={},T={},C="canceled",E={readyState:0,getResponseHeader:function(e){var t;if(c){if(!s){s={};while(t=Ot.exec(a))s[t[1].toLowerCase()]=t[2]}t=s[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return c?a:null},setRequestHeader:function(e,t){return null==c&&(e=T[e.toLowerCase()]=T[e.toLowerCase()]||e,b[e]=t),this},overrideMimeType:function(e){return null==c&&(h.mimeType=e),this},statusCode:function(e){var t;if(e)if(c)E.always(e[E.status]);else for(t in e)x[t]=[x[t],e[t]];return this},abort:function(e){var t=e||C;return i&&i.abort(t),k(0,t),this}};if(v.promise(E),h.url=((t||h.url||Ct.href)+"").replace(Rt,Ct.protocol+"//"),h.type=n.method||n.type||h.method||h.type,h.dataTypes=(h.dataType||"*").toLowerCase().match(M)||[""],null==h.crossDomain){l=r.createElement("a");try{l.href=h.url,l.href=l.href,h.crossDomain=Bt.protocol+"//"+Bt.host!=l.protocol+"//"+l.host}catch(e){h.crossDomain=!0}}if(h.data&&h.processData&&"string"!=typeof h.data&&(h.data=w.param(h.data,h.traditional)),_t(It,h,n,E),c)return E;(f=w.event&&h.global)&&0==w.active++&&w.event.trigger("ajaxStart"),h.type=h.type.toUpperCase(),h.hasContent=!Mt.test(h.type),o=h.url.replace(Lt,""),h.hasContent?h.data&&h.processData&&0===(h.contentType||"").indexOf("application/x-www-form-urlencoded")&&(h.data=h.data.replace(qt,"+")):(d=h.url.slice(o.length),h.data&&(h.processData||"string"==typeof h.data)&&(o+=(kt.test(o)?"&":"?")+h.data,delete h.data),!1===h.cache&&(o=o.replace(Ht,"$1"),d=(kt.test(o)?"&":"?")+"_="+Et+++d),h.url=o+d),h.ifModified&&(w.lastModified[o]&&E.setRequestHeader("If-Modified-Since",w.lastModified[o]),w.etag[o]&&E.setRequestHeader("If-None-Match",w.etag[o])),(h.data&&h.hasContent&&!1!==h.contentType||n.contentType)&&E.setRequestHeader("Content-Type",h.contentType),E.setRequestHeader("Accept",h.dataTypes[0]&&h.accepts[h.dataTypes[0]]?h.accepts[h.dataTypes[0]]+("*"!==h.dataTypes[0]?", "+$t+"; q=0.01":""):h.accepts["*"]);for(p in h.headers)E.setRequestHeader(p,h.headers[p]);if(h.beforeSend&&(!1===h.beforeSend.call(g,E,h)||c))return E.abort();if(C="abort",m.add(h.complete),E.done(h.success),E.fail(h.error),i=_t(Wt,h,n,E)){if(E.readyState=1,f&&y.trigger("ajaxSend",[E,h]),c)return E;h.async&&h.timeout>0&&(u=e.setTimeout(function(){E.abort("timeout")},h.timeout));try{c=!1,i.send(b,k)}catch(e){if(c)throw e;k(-1,e)}}else k(-1,"No Transport");function k(t,n,r,s){var l,p,d,b,T,C=n;c||(c=!0,u&&e.clearTimeout(u),i=void 0,a=s||"",E.readyState=t>0?4:0,l=t>=200&&t<300||304===t,r&&(b=Xt(h,E,r)),b=Ut(h,b,E,l),l?(h.ifModified&&((T=E.getResponseHeader("Last-Modified"))&&(w.lastModified[o]=T),(T=E.getResponseHeader("etag"))&&(w.etag[o]=T)),204===t||"HEAD"===h.type?C="nocontent":304===t?C="notmodified":(C=b.state,p=b.data,l=!(d=b.error))):(d=C,!t&&C||(C="error",t<0&&(t=0))),E.status=t,E.statusText=(n||C)+"",l?v.resolveWith(g,[p,C,E]):v.rejectWith(g,[E,C,d]),E.statusCode(x),x=void 0,f&&y.trigger(l?"ajaxSuccess":"ajaxError",[E,h,l?p:d]),m.fireWith(g,[E,C]),f&&(y.trigger("ajaxComplete",[E,h]),--w.active||w.event.trigger("ajaxStop")))}return E},getJSON:function(e,t,n){return w.get(e,t,n,"json")},getScript:function(e,t){return w.get(e,void 0,t,"script")}}),w.each(["get","post"],function(e,t){w[t]=function(e,n,r,i){return g(n)&&(i=i||r,r=n,n=void 0),w.ajax(w.extend({url:e,type:t,dataType:i,data:n,success:r},w.isPlainObject(e)&&e))}}),w._evalUrl=function(e){return w.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},w.fn.extend({wrapAll:function(e){var t;return this[0]&&(g(e)&&(e=e.call(this[0])),t=w(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(e){return g(e)?this.each(function(t){w(this).wrapInner(e.call(this,t))}):this.each(function(){var t=w(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=g(e);return this.each(function(n){w(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not("body").each(function(){w(this).replaceWith(this.childNodes)}),this}}),w.expr.pseudos.hidden=function(e){return!w.expr.pseudos.visible(e)},w.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},w.ajaxSettings.xhr=function(){try{return new e.XMLHttpRequest}catch(e){}};var Vt={0:200,1223:204},Gt=w.ajaxSettings.xhr();h.cors=!!Gt&&"withCredentials"in Gt,h.ajax=Gt=!!Gt,w.ajaxTransport(function(t){var n,r;if(h.cors||Gt&&!t.crossDomain)return{send:function(i,o){var a,s=t.xhr();if(s.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(a in t.xhrFields)s[a]=t.xhrFields[a];t.mimeType&&s.overrideMimeType&&s.overrideMimeType(t.mimeType),t.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");for(a in i)s.setRequestHeader(a,i[a]);n=function(e){return function(){n&&(n=r=s.onload=s.onerror=s.onabort=s.ontimeout=s.onreadystatechange=null,"abort"===e?s.abort():"error"===e?"number"!=typeof s.status?o(0,"error"):o(s.status,s.statusText):o(Vt[s.status]||s.status,s.statusText,"text"!==(s.responseType||"text")||"string"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=n(),r=s.onerror=s.ontimeout=n("error"),void 0!==s.onabort?s.onabort=r:s.onreadystatechange=function(){4===s.readyState&&e.setTimeout(function(){n&&r()})},n=n("abort");try{s.send(t.hasContent&&t.data||null)}catch(e){if(n)throw e}},abort:function(){n&&n()}}}),w.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),w.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return w.globalEval(e),e}}}),w.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),w.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(i,o){t=w("' + . '' + . '' + . $kintScript + . PHP_EOL; + + if (str_contains((string) $response->getBody(), '')) { + $response->setBody( + preg_replace( + '//', + '' . $script, + $response->getBody(), + 1 + ) + ); + + return; + } + + $response->appendBody($script); + } + } + + /** + * Inject debug toolbar into the response. + * + * @codeCoverageIgnore + * + * @return void + */ + public function respond() + { + if (ENVIRONMENT === 'testing') { + return; + } + + $request = service('request'); + + // If the request contains '?debugbar then we're + // simply returning the loading script + if ($request->getGet('debugbar') !== null) { + header('Content-Type: application/javascript'); + + ob_start(); + include $this->config->viewsPath . 'toolbarloader.js'; + $output = ob_get_clean(); + $output = str_replace('{url}', rtrim(site_url(), '/'), $output); + echo $output; + + exit; + } + + // Otherwise, if it includes ?debugbar_time, then + // we should return the entire debugbar. + if ($request->getGet('debugbar_time')) { + helper('security'); + + // Negotiate the content-type to format the output + $format = $request->negotiate('media', ['text/html', 'application/json', 'application/xml']); + $format = explode('/', $format)[1]; + + $filename = sanitize_filename('debugbar_' . $request->getGet('debugbar_time')); + $filename = WRITEPATH . 'debugbar/' . $filename . '.json'; + + if (is_file($filename)) { + // Show the toolbar if it exists + echo $this->format(file_get_contents($filename), $format); + + exit; + } + + // Filename not found + http_response_code(404); + + exit; // Exit here is needed to avoid loading the index page + } + } + + /** + * Format output + */ + protected function format(string $data, string $format = 'html'): string + { + $data = json_decode($data, true); + + if ($this->config->maxHistory !== 0 && preg_match('/\d+\.\d{6}/s', (string) service('request')->getGet('debugbar_time'), $debugbarTime)) { + $history = new History(); + $history->setFiles( + $debugbarTime[0], + $this->config->maxHistory + ); + + $data['collectors'][] = $history->getAsArray(); + } + + $output = ''; + + switch ($format) { + case 'html': + $data['styles'] = []; + extract($data); + $parser = Services::parser($this->config->viewsPath, null, false); + ob_start(); + include $this->config->viewsPath . 'toolbar.tpl.php'; + $output = ob_get_clean(); + break; + + case 'json': + $formatter = new JSONFormatter(); + $output = $formatter->format($data); + break; + + case 'xml': + $formatter = new XMLFormatter(); + $output = $formatter->format($data); + break; + } + + return $output; + } +} diff --git a/system/Debug/Toolbar/Collectors/BaseCollector.php b/system/Debug/Toolbar/Collectors/BaseCollector.php new file mode 100644 index 0000000..81cc631 --- /dev/null +++ b/system/Debug/Toolbar/Collectors/BaseCollector.php @@ -0,0 +1,238 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Debug\Toolbar\Collectors; + +/** + * Base Toolbar collector + */ +class BaseCollector +{ + /** + * Whether this collector has data that can + * be displayed in the Timeline. + * + * @var bool + */ + protected $hasTimeline = false; + + /** + * Whether this collector needs to display + * content in a tab or not. + * + * @var bool + */ + protected $hasTabContent = false; + + /** + * Whether this collector needs to display + * a label or not. + * + * @var bool + */ + protected $hasLabel = false; + + /** + * Whether this collector has data that + * should be shown in the Vars tab. + * + * @var bool + */ + protected $hasVarData = false; + + /** + * The 'title' of this Collector. + * Used to name things in the toolbar HTML. + * + * @var string + */ + protected $title = ''; + + /** + * Gets the Collector's title. + */ + public function getTitle(bool $safe = false): string + { + if ($safe) { + return str_replace(' ', '-', strtolower($this->title)); + } + + return $this->title; + } + + /** + * Returns any information that should be shown next to the title. + */ + public function getTitleDetails(): string + { + return ''; + } + + /** + * Does this collector need it's own tab? + */ + public function hasTabContent(): bool + { + return (bool) $this->hasTabContent; + } + + /** + * Does this collector have a label? + */ + public function hasLabel(): bool + { + return (bool) $this->hasLabel; + } + + /** + * Does this collector have information for the timeline? + */ + public function hasTimelineData(): bool + { + return (bool) $this->hasTimeline; + } + + /** + * Grabs the data for the timeline, properly formatted, + * or returns an empty array. + */ + public function timelineData(): array + { + if (! $this->hasTimeline) { + return []; + } + + return $this->formatTimelineData(); + } + + /** + * Does this Collector have data that should be shown in the + * 'Vars' tab? + */ + public function hasVarData(): bool + { + return (bool) $this->hasVarData; + } + + /** + * Gets a collection of data that should be shown in the 'Vars' tab. + * The format is an array of sections, each with their own array + * of key/value pairs: + * + * $data = [ + * 'section 1' => [ + * 'foo' => 'bar, + * 'bar' => 'baz' + * ], + * 'section 2' => [ + * 'foo' => 'bar, + * 'bar' => 'baz' + * ], + * ]; + * + * @return array|null + */ + public function getVarData() + { + return null; + } + + /** + * Child classes should implement this to return the timeline data + * formatted for correct usage. + * + * Timeline data should be formatted into arrays that look like: + * + * [ + * 'name' => 'Database::Query', + * 'component' => 'Database', + * 'start' => 10 // milliseconds + * 'duration' => 15 // milliseconds + * ] + */ + protected function formatTimelineData(): array + { + return []; + } + + /** + * Returns the data of this collector to be formatted in the toolbar + * + * @return array|string + */ + public function display() + { + return []; + } + + /** + * This makes nicer looking paths for the error output. + * + * @deprecated Use the dedicated `clean_path()` function. + */ + public function cleanPath(string $file): string + { + return clean_path($file); + } + + /** + * Gets the "badge" value for the button. + * + * @return int|null + */ + public function getBadgeValue() + { + return null; + } + + /** + * Does this collector have any data collected? + * + * If not, then the toolbar button won't get shown. + */ + public function isEmpty(): bool + { + return false; + } + + /** + * Returns the HTML to display the icon. Should either + * be SVG, or a base-64 encoded. + * + * Recommended dimensions are 24px x 24px + */ + public function icon(): string + { + return ''; + } + + /** + * Return settings as an array. + */ + public function getAsArray(): array + { + return [ + 'title' => $this->getTitle(), + 'titleSafe' => $this->getTitle(true), + 'titleDetails' => $this->getTitleDetails(), + 'display' => $this->display(), + 'badgeValue' => $this->getBadgeValue(), + 'isEmpty' => $this->isEmpty(), + 'hasTabContent' => $this->hasTabContent(), + 'hasLabel' => $this->hasLabel(), + 'icon' => $this->icon(), + 'hasTimelineData' => $this->hasTimelineData(), + 'timelineData' => $this->timelineData(), + ]; + } +} diff --git a/system/Debug/Toolbar/Collectors/Config.php b/system/Debug/Toolbar/Collectors/Config.php new file mode 100644 index 0000000..80673f9 --- /dev/null +++ b/system/Debug/Toolbar/Collectors/Config.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Debug\Toolbar\Collectors; + +use CodeIgniter\CodeIgniter; +use Config\App; + +/** + * Debug toolbar configuration + */ +class Config +{ + /** + * Return toolbar config values as an array. + */ + public static function display(): array + { + $config = config(App::class); + + return [ + 'ciVersion' => CodeIgniter::CI_VERSION, + 'phpVersion' => PHP_VERSION, + 'phpSAPI' => PHP_SAPI, + 'environment' => ENVIRONMENT, + 'baseURL' => $config->baseURL, + 'timezone' => app_timezone(), + 'locale' => service('request')->getLocale(), + 'cspEnabled' => $config->CSPEnabled, + ]; + } +} diff --git a/system/Debug/Toolbar/Collectors/Database.php b/system/Debug/Toolbar/Collectors/Database.php new file mode 100644 index 0000000..ee9f829 --- /dev/null +++ b/system/Debug/Toolbar/Collectors/Database.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 CodeIgniter\Debug\Toolbar\Collectors; + +use CodeIgniter\Database\Query; +use CodeIgniter\I18n\Time; +use Config\Toolbar; + +/** + * Collector for the Database tab of the Debug Toolbar. + * + * @see \CodeIgniter\Debug\Toolbar\Collectors\DatabaseTest + */ +class Database extends BaseCollector +{ + /** + * Whether this collector has timeline data. + * + * @var bool + */ + protected $hasTimeline = true; + + /** + * Whether this collector should display its own tab. + * + * @var bool + */ + protected $hasTabContent = true; + + /** + * Whether this collector has data for the Vars tab. + * + * @var bool + */ + protected $hasVarData = false; + + /** + * The name used to reference this collector in the toolbar. + * + * @var string + */ + protected $title = 'Database'; + + /** + * Array of database connections. + * + * @var array + */ + protected $connections; + + /** + * The query instances that have been collected + * through the DBQuery Event. + * + * @var array + */ + protected static $queries = []; + + /** + * Constructor + */ + public function __construct() + { + $this->getConnections(); + } + + /** + * The static method used during Events to collect + * data. + * + * @internal + * + * @return void + */ + public static function collect(Query $query) + { + $config = config(Toolbar::class); + + // Provide default in case it's not set + $max = $config->maxQueries ?: 100; + + if (count(static::$queries) < $max) { + $queryString = $query->getQuery(); + + $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + + if (! is_cli()) { + // when called in the browser, the first two trace arrays + // are from the DB event trigger, which are unneeded + $backtrace = array_slice($backtrace, 2); + } + + static::$queries[] = [ + 'query' => $query, + 'string' => $queryString, + 'duplicate' => in_array($queryString, array_column(static::$queries, 'string', null), true), + 'trace' => $backtrace, + ]; + } + } + + /** + * Returns timeline data formatted for the toolbar. + * + * @return array The formatted data or an empty array. + */ + protected function formatTimelineData(): array + { + $data = []; + + foreach ($this->connections as $alias => $connection) { + // Connection Time + $data[] = [ + 'name' => 'Connecting to Database: "' . $alias . '"', + 'component' => 'Database', + 'start' => $connection->getConnectStart(), + 'duration' => $connection->getConnectDuration(), + ]; + } + + foreach (static::$queries as $query) { + $data[] = [ + 'name' => 'Query', + 'component' => 'Database', + 'start' => $query['query']->getStartTime(true), + 'duration' => $query['query']->getDuration(), + 'query' => $query['query']->debugToolbarDisplay(), + ]; + } + + return $data; + } + + /** + * Returns the data of this collector to be formatted in the toolbar + */ + public function display(): array + { + $data = []; + $data['queries'] = array_map(static function (array $query) { + $isDuplicate = $query['duplicate'] === true; + + $firstNonSystemLine = ''; + + foreach ($query['trace'] as $index => &$line) { + // simplify file and line + if (isset($line['file'])) { + $line['file'] = clean_path($line['file']) . ':' . $line['line']; + unset($line['line']); + } else { + $line['file'] = '[internal function]'; + } + + // find the first trace line that does not originate from `system/` + if ($firstNonSystemLine === '' && ! str_contains($line['file'], 'SYSTEMPATH')) { + $firstNonSystemLine = $line['file']; + } + + // simplify function call + if (isset($line['class'])) { + $line['function'] = $line['class'] . $line['type'] . $line['function']; + unset($line['class'], $line['type']); + } + + if (strrpos($line['function'], '{closure}') === false) { + $line['function'] .= '()'; + } + + $line['function'] = str_repeat(chr(0xC2) . chr(0xA0), 8) . $line['function']; + + // add index numbering padded with nonbreaking space + $indexPadded = str_pad(sprintf('%d', $index + 1), 3, ' ', STR_PAD_LEFT); + $indexPadded = preg_replace('/\s/', chr(0xC2) . chr(0xA0), $indexPadded); + + $line['index'] = $indexPadded . str_repeat(chr(0xC2) . chr(0xA0), 4); + } + + return [ + 'hover' => $isDuplicate ? 'This query was called more than once.' : '', + 'class' => $isDuplicate ? 'duplicate' : '', + 'duration' => ((float) $query['query']->getDuration(5) * 1000) . ' ms', + 'sql' => $query['query']->debugToolbarDisplay(), + 'trace' => $query['trace'], + 'trace-file' => $firstNonSystemLine, + 'qid' => md5($query['query'] . Time::now()->format('0.u00 U')), + ]; + }, static::$queries); + + return $data; + } + + /** + * Gets the "badge" value for the button. + */ + public function getBadgeValue(): int + { + return count(static::$queries); + } + + /** + * Information to be displayed next to the title. + * + * @return string The number of queries (in parentheses) or an empty string. + */ + public function getTitleDetails(): string + { + $this->getConnections(); + + $queryCount = count(static::$queries); + $uniqueCount = count(array_filter(static::$queries, static fn ($query) => $query['duplicate'] === false)); + $connectionCount = count($this->connections); + + return sprintf( + '(%d total Quer%s, %d %s unique across %d Connection%s)', + $queryCount, + $queryCount > 1 ? 'ies' : 'y', + $uniqueCount, + $uniqueCount > 1 ? 'of them' : '', + $connectionCount, + $connectionCount > 1 ? 's' : '' + ); + } + + /** + * Does this collector have any data collected? + */ + public function isEmpty(): bool + { + return static::$queries === []; + } + + /** + * Display the icon. + * + * Icon from https://icons8.com - 1em package + */ + public function icon(): string + { + return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADMSURBVEhLY6A3YExLSwsA4nIycQDIDIhRWEBqamo/UNF/SjDQjF6ocZgAKPkRiFeEhoYyQ4WIBiA9QAuWAPEHqBAmgLqgHcolGQD1V4DMgHIxwbCxYD+QBqcKINseKo6eWrBioPrtQBq/BcgY5ht0cUIYbBg2AJKkRxCNWkDQgtFUNJwtABr+F6igE8olGQD114HMgHIxAVDyAhA/AlpSA8RYUwoeXAPVex5qHCbIyMgwBCkAuQJIY00huDBUz/mUlBQDqHGjgBjAwAAACexpph6oHSQAAAAASUVORK5CYII='; + } + + /** + * Gets the connections from the database config + */ + private function getConnections(): void + { + $this->connections = \Config\Database::getConnections(); + } +} diff --git a/system/Debug/Toolbar/Collectors/Events.php b/system/Debug/Toolbar/Collectors/Events.php new file mode 100644 index 0000000..173c71a --- /dev/null +++ b/system/Debug/Toolbar/Collectors/Events.php @@ -0,0 +1,125 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Debug\Toolbar\Collectors; + +/** + * Events collector + */ +class Events extends BaseCollector +{ + /** + * Whether this collector has data that can + * be displayed in the Timeline. + * + * @var bool + */ + protected $hasTimeline = true; + + /** + * Whether this collector needs to display + * content in a tab or not. + * + * @var bool + */ + protected $hasTabContent = true; + + /** + * Whether this collector has data that + * should be shown in the Vars tab. + * + * @var bool + */ + protected $hasVarData = false; + + /** + * The 'title' of this Collector. + * Used to name things in the toolbar HTML. + * + * @var string + */ + protected $title = 'Events'; + + /** + * Child classes should implement this to return the timeline data + * formatted for correct usage. + */ + protected function formatTimelineData(): array + { + $data = []; + + $rows = \CodeIgniter\Events\Events::getPerformanceLogs(); + + foreach ($rows as $info) { + $data[] = [ + 'name' => 'Event: ' . $info['event'], + 'component' => 'Events', + 'start' => $info['start'], + 'duration' => $info['end'] - $info['start'], + ]; + } + + return $data; + } + + /** + * Returns the data of this collector to be formatted in the toolbar + */ + public function display(): array + { + $data = [ + 'events' => [], + ]; + + foreach (\CodeIgniter\Events\Events::getPerformanceLogs() as $row) { + $key = $row['event']; + + if (! array_key_exists($key, $data['events'])) { + $data['events'][$key] = [ + 'event' => $key, + 'duration' => ($row['end'] - $row['start']) * 1000, + 'count' => 1, + ]; + + continue; + } + + $data['events'][$key]['duration'] += ($row['end'] - $row['start']) * 1000; + $data['events'][$key]['count']++; + } + + foreach ($data['events'] as &$row) { + $row['duration'] = number_format($row['duration'], 2); + } + + return $data; + } + + /** + * Gets the "badge" value for the button. + */ + public function getBadgeValue(): int + { + return count(\CodeIgniter\Events\Events::getPerformanceLogs()); + } + + /** + * Display the icon. + * + * Icon from https://icons8.com - 1em package + */ + public function icon(): string + { + return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEASURBVEhL7ZXNDcIwDIVTsRBH1uDQDdquUA6IM1xgCA6MwJUN2hk6AQzAz0vl0ETUxC5VT3zSU5w81/mRMGZysixbFEVR0jSKNt8geQU9aRpFmp/keX6AbjZ5oB74vsaN5lSzA4tLSjpBFxsjeSuRy4d2mDdQTWU7YLbXTNN05mKyovj5KL6B7q3hoy3KwdZxBlT+Ipz+jPHrBqOIynZgcZonoukb/0ckiTHqNvDXtXEAaygRbaB9FvUTjRUHsIYS0QaSp+Dw6wT4hiTmYHOcYZsdLQ2CbXa4ftuuYR4x9vYZgdb4vsFYUdmABMYeukK9/SUme3KMFQ77+Yfzh8eYF8+orDuDWU5LAAAAAElFTkSuQmCC'; + } +} diff --git a/system/Debug/Toolbar/Collectors/Files.php b/system/Debug/Toolbar/Collectors/Files.php new file mode 100644 index 0000000..a573388 --- /dev/null +++ b/system/Debug/Toolbar/Collectors/Files.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Debug\Toolbar\Collectors; + +/** + * Files collector + */ +class Files extends BaseCollector +{ + /** + * Whether this collector has data that can + * be displayed in the Timeline. + * + * @var bool + */ + protected $hasTimeline = false; + + /** + * Whether this collector needs to display + * content in a tab or not. + * + * @var bool + */ + protected $hasTabContent = true; + + /** + * The 'title' of this Collector. + * Used to name things in the toolbar HTML. + * + * @var string + */ + protected $title = 'Files'; + + /** + * Returns any information that should be shown next to the title. + */ + public function getTitleDetails(): string + { + return '( ' . count(get_included_files()) . ' )'; + } + + /** + * Returns the data of this collector to be formatted in the toolbar + */ + public function display(): array + { + $rawFiles = get_included_files(); + $coreFiles = []; + $userFiles = []; + + foreach ($rawFiles as $file) { + $path = clean_path($file); + + if (str_contains($path, 'SYSTEMPATH')) { + $coreFiles[] = [ + 'path' => $path, + 'name' => basename($file), + ]; + } else { + $userFiles[] = [ + 'path' => $path, + 'name' => basename($file), + ]; + } + } + + sort($userFiles); + sort($coreFiles); + + return [ + 'coreFiles' => $coreFiles, + 'userFiles' => $userFiles, + ]; + } + + /** + * Displays the number of included files as a badge in the tab button. + */ + public function getBadgeValue(): int + { + return count(get_included_files()); + } + + /** + * Display the icon. + * + * Icon from https://icons8.com - 1em package + */ + public function icon(): string + { + return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGBSURBVEhL7ZQ9S8NQGIVTBQUncfMfCO4uLgoKbuKQOWg+OkXERRE1IAXrIHbVDrqIDuLiJgj+gro7S3dnpfq88b1FMTE3VZx64HBzzvvZWxKnj15QCcPwCD5HUfSWR+JtzgmtsUcQBEva5IIm9SwSu+95CAWbUuy67qBa32ByZEDpIaZYZSZMjjQuPcQUq8yEyYEb8FSerYeQVGbAFzJkX1PyQWLhgCz0BxTCekC1Wp0hsa6yokzhed4oje6Iz6rlJEkyIKfUEFtITVtQdAibn5rMyaYsMS+a5wTv8qeXMhcU16QZbKgl3hbs+L4/pnpdc87MElZgq10p5DxGdq8I7xrvUWUKvG3NbSK7ubngYzdJwSsF7TiOh9VOgfcEz1UayNe3JUPM1RWC5GXYgTfc75B4NBmXJnAtTfpABX0iPvEd9ezALwkplCFXcr9styiNOKc1RRZpaPM9tcqBwlWzGY1qPL9wjqRBgF5BH6j8HWh2S7MHlX8PrmbK+k/8PzjOOzx1D3i1pKTTAAAAAElFTkSuQmCC'; + } +} diff --git a/system/Debug/Toolbar/Collectors/History.php b/system/Debug/Toolbar/Collectors/History.php new file mode 100644 index 0000000..4e5276b --- /dev/null +++ b/system/Debug/Toolbar/Collectors/History.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 CodeIgniter\Debug\Toolbar\Collectors; + +use DateTime; + +/** + * History collector + * + * @see \CodeIgniter\Debug\Toolbar\Collectors\HistoryTest + */ +class History extends BaseCollector +{ + /** + * Whether this collector has data that can + * be displayed in the Timeline. + * + * @var bool + */ + protected $hasTimeline = false; + + /** + * Whether this collector needs to display + * content in a tab or not. + * + * @var bool + */ + protected $hasTabContent = true; + + /** + * Whether this collector needs to display + * a label or not. + * + * @var bool + */ + protected $hasLabel = true; + + /** + * The 'title' of this Collector. + * Used to name things in the toolbar HTML. + * + * @var string + */ + protected $title = 'History'; + + /** + * @var array History files + */ + protected $files = []; + + /** + * Specify time limit & file count for debug history. + * + * @param string $current Current history time + * @param int $limit Max history files + * + * @return void + */ + public function setFiles(string $current, int $limit = 20) + { + $filenames = glob(WRITEPATH . 'debugbar/debugbar_*.json'); + + $files = []; + $counter = 0; + + foreach (array_reverse($filenames) as $filename) { + $counter++; + + // Oldest files will be deleted + if ($limit >= 0 && $counter > $limit) { + unlink($filename); + + continue; + } + + // Get the contents of this specific history request + $contents = file_get_contents($filename); + + $contents = @json_decode($contents); + if (json_last_error() === JSON_ERROR_NONE) { + preg_match('/debugbar_(.*)\.json$/s', $filename, $time); + $time = sprintf('%.6f', $time[1] ?? 0); + + // Debugbar files shown in History Collector + $files[] = [ + 'time' => $time, + 'datetime' => DateTime::createFromFormat('U.u', $time)->format('Y-m-d H:i:s.u'), + 'active' => $time === $current, + 'status' => $contents->vars->response->statusCode, + 'method' => $contents->method, + 'url' => $contents->url, + 'isAJAX' => $contents->isAJAX ? 'Yes' : 'No', + 'contentType' => $contents->vars->response->contentType, + ]; + } + } + + $this->files = $files; + } + + /** + * Returns the data of this collector to be formatted in the toolbar + */ + public function display(): array + { + return ['files' => $this->files]; + } + + /** + * Displays the number of included files as a badge in the tab button. + */ + public function getBadgeValue(): int + { + return count($this->files); + } + + /** + * Return true if there are no history files. + */ + public function isEmpty(): bool + { + return $this->files === []; + } + + /** + * Display the icon. + * + * Icon from https://icons8.com - 1em package + */ + public function icon(): string + { + return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAJySURBVEhL3ZU7aJNhGIVTpV6i4qCIgkIHxcXLErS4FBwUFNwiCKGhuTYJGaIgnRoo4qRu6iCiiIuIXXTTIkIpuqoFwaGgonUQlC5KafU5ycmNP0lTdPLA4fu+8573/a4/f6hXpFKpwUwmc9fDfweKbk+n07fgEv33TLSbtt/hvwNFT1PsG/zdTE0Gp+GFfD6/2fbVIxqNrqPIRbjg4t/hY8aztcngfDabHXbKyiiXy2vcrcPH8oDCry2FKDrA+Ar6L01E/ypyXzXaARjDGGcoeNxSDZXE0dHRA5VRE5LJ5CFy5jzJuOX2wHRHRnjbklZ6isQ3tIctBaAd4vlK3jLtkOVWqABBXd47jGHLmjTmSScttQV5J+SjfcUweFQEbsjAas5aqoCLXutJl7vtQsAzpRowYqkBinyCC8Vicb2lOih8zoldd0F8RD7qTFiqAnGrAy8stUAvi/hbqDM+YzkAFrLPdR5ZqoLXsd+Bh5YCIH7JniVdquUWxOPxDfboHhrI5XJ7HHhiqQXox+APe/Qk64+gGYVCYZs8cMpSFQj9JOoFzVqqo7k4HIvFYpscCoAjOmLffUsNUGRaQUwDlmofUa34ecsdgXdcXo4wbakBgiUFafXJV8A4DJ/2UrxUKm3E95H8RbjLcgOJRGILhnmCP+FBy5XvwN2uIPcy1AJvWgqC4xm2aU4Xb3lF4I+Tpyf8hRe5w3J7YLymSeA8Z3nSclv4WLRyFdfOjzrUFX0klJUEtZtntCNc+F69cz/FiDzEPtjzmcUMOr83kDQEX6pAJxJfpL3OX22n01YN7SZCoQnaSdoZ+Jz+PZihH3wt/xlCoT9M6nEtmRSPCQAAAABJRU5ErkJggg=='; + } +} diff --git a/system/Debug/Toolbar/Collectors/Logs.php b/system/Debug/Toolbar/Collectors/Logs.php new file mode 100644 index 0000000..2194526 --- /dev/null +++ b/system/Debug/Toolbar/Collectors/Logs.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Debug\Toolbar\Collectors; + +use Config\Services; + +/** + * Loags collector + */ +class Logs extends BaseCollector +{ + /** + * Whether this collector has data that can + * be displayed in the Timeline. + * + * @var bool + */ + protected $hasTimeline = false; + + /** + * Whether this collector needs to display + * content in a tab or not. + * + * @var bool + */ + protected $hasTabContent = true; + + /** + * The 'title' of this Collector. + * Used to name things in the toolbar HTML. + * + * @var string + */ + protected $title = 'Logs'; + + /** + * Our collected data. + * + * @var array + */ + protected $data; + + /** + * Returns the data of this collector to be formatted in the toolbar + */ + public function display(): array + { + return [ + 'logs' => $this->collectLogs(), + ]; + } + + /** + * Does this collector actually have any data to display? + */ + public function isEmpty(): bool + { + $this->collectLogs(); + + return empty($this->data); + } + + /** + * Display the icon. + * + * Icon from https://icons8.com - 1em package + */ + public function icon(): string + { + return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACYSURBVEhLYxgFJIHU1FSjtLS0i0D8AYj7gEKMEBkqAaAFF4D4ERCvAFrwH4gDoFIMKSkpFkB+OTEYqgUTACXfA/GqjIwMQyD9H2hRHlQKJFcBEiMGQ7VgAqCBvUgK32dmZspCpagGGNPT0/1BLqeF4bQHQJePpiIwhmrBBEADR1MRfgB0+WgqAmOoFkwANHA0FY0CUgEDAwCQ0PUpNB3kqwAAAABJRU5ErkJggg=='; + } + + /** + * Ensures the data has been collected. + * + * @return array + */ + protected function collectLogs() + { + if (! empty($this->data)) { + return $this->data; + } + + return $this->data = Services::logger(true)->logCache ?? []; + } +} diff --git a/system/Debug/Toolbar/Collectors/Routes.php b/system/Debug/Toolbar/Collectors/Routes.php new file mode 100644 index 0000000..b6862dc --- /dev/null +++ b/system/Debug/Toolbar/Collectors/Routes.php @@ -0,0 +1,170 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Debug\Toolbar\Collectors; + +use CodeIgniter\Router\DefinedRouteCollector; +use Config\Services; +use ReflectionException; +use ReflectionFunction; +use ReflectionMethod; + +/** + * Routes collector + */ +class Routes extends BaseCollector +{ + /** + * Whether this collector has data that can + * be displayed in the Timeline. + * + * @var bool + */ + protected $hasTimeline = false; + + /** + * Whether this collector needs to display + * content in a tab or not. + * + * @var bool + */ + protected $hasTabContent = true; + + /** + * The 'title' of this Collector. + * Used to name things in the toolbar HTML. + * + * @var string + */ + protected $title = 'Routes'; + + /** + * Returns the data of this collector to be formatted in the toolbar + * + * @return array{ + * matchedRoute: array + * }>, + * routes: list + * } + * + * @throws ReflectionException + */ + public function display(): array + { + $rawRoutes = Services::routes(true); + $router = Services::router(null, null, true); + + // Get our parameters + // Closure routes + if (is_callable($router->controllerName())) { + $method = new ReflectionFunction($router->controllerName()); + } else { + try { + $method = new ReflectionMethod($router->controllerName(), $router->methodName()); + } catch (ReflectionException) { + try { + // If we're here, the method doesn't exist + // and is likely calculated in _remap. + $method = new ReflectionMethod($router->controllerName(), '_remap'); + } catch (ReflectionException) { + // If we're here, page cache is returned. The router is not executed. + return [ + 'matchedRoute' => [], + 'routes' => [], + ]; + } + } + } + + $rawParams = $method->getParameters(); + + $params = []; + + foreach ($rawParams as $key => $param) { + $params[] = [ + 'name' => '$' . $param->getName() . ' = ', + 'value' => $router->params()[$key] ?? + ' | default: ' + . var_export( + $param->isDefaultValueAvailable() ? $param->getDefaultValue() : null, + true + ), + ]; + } + + $matchedRoute = [ + [ + 'directory' => $router->directory(), + 'controller' => $router->controllerName(), + 'method' => $router->methodName(), + 'paramCount' => count($router->params()), + 'truePCount' => count($params), + 'params' => $params, + ], + ]; + + // Defined Routes + $routes = []; + + $definedRouteCollector = new DefinedRouteCollector($rawRoutes); + + foreach ($definedRouteCollector->collect() as $route) { + // filter for strings, as callbacks aren't displayable + if ($route['handler'] !== '(Closure)') { + $routes[] = [ + 'method' => strtoupper($route['method']), + 'route' => $route['route'], + 'handler' => $route['handler'], + ]; + } + } + + return [ + 'matchedRoute' => $matchedRoute, + 'routes' => $routes, + ]; + } + + /** + * Returns a count of all the routes in the system. + */ + public function getBadgeValue(): int + { + $rawRoutes = Services::routes(true); + + return count($rawRoutes->getRoutes()); + } + + /** + * Display the icon. + * + * Icon from https://icons8.com - 1em package + */ + public function icon(): string + { + return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAFDSURBVEhL7ZRNSsNQFIUjVXSiOFEcuQIHDpzpxC0IGYeE/BEInbWlCHEDLsSiuANdhKDjgm6ggtSJ+l25ldrmmTwIgtgDh/t37r1J+16cX0dRFMtpmu5pWAkrvYjjOB7AETzStBFW+inxu3KUJMmhludQpoflS1zXban4LYqiO224h6VLTHr8Z+z8EpIHFF9gG78nDVmW7UgTHKjsCyY98QP+pcq+g8Ku2s8G8X3f3/I8b038WZTp+bO38zxfFd+I6YY6sNUvFlSDk9CRhiAI1jX1I9Cfw7GG1UB8LAuwbU0ZwQnbRDeEN5qqBxZMLtE1ti9LtbREnMIuOXnyIf5rGIb7Wq8HmlZgwYBH7ORTcKH5E4mpjeGt9fBZcHE2GCQ3Vt7oTNPNg+FXLHnSsHkw/FR+Gg2bB8Ptzrst/v6C/wrH+QB+duli6MYJdQAAAABJRU5ErkJggg=='; + } +} diff --git a/system/Debug/Toolbar/Collectors/Timers.php b/system/Debug/Toolbar/Collectors/Timers.php new file mode 100644 index 0000000..163c9f5 --- /dev/null +++ b/system/Debug/Toolbar/Collectors/Timers.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Debug\Toolbar\Collectors; + +use Config\Services; + +/** + * Timers collector + */ +class Timers extends BaseCollector +{ + /** + * Whether this collector has data that can + * be displayed in the Timeline. + * + * @var bool + */ + protected $hasTimeline = true; + + /** + * Whether this collector needs to display + * content in a tab or not. + * + * @var bool + */ + protected $hasTabContent = false; + + /** + * The 'title' of this Collector. + * Used to name things in the toolbar HTML. + * + * @var string + */ + protected $title = 'Timers'; + + /** + * Child classes should implement this to return the timeline data + * formatted for correct usage. + */ + protected function formatTimelineData(): array + { + $data = []; + + $benchmark = Services::timer(true); + $rows = $benchmark->getTimers(6); + + foreach ($rows as $name => $info) { + if ($name === 'total_execution') { + continue; + } + + $data[] = [ + 'name' => ucwords(str_replace('_', ' ', $name)), + 'component' => 'Timer', + 'start' => $info['start'], + 'duration' => $info['end'] - $info['start'], + ]; + } + + return $data; + } +} diff --git a/system/Debug/Toolbar/Collectors/Views.php b/system/Debug/Toolbar/Collectors/Views.php new file mode 100644 index 0000000..a63e172 --- /dev/null +++ b/system/Debug/Toolbar/Collectors/Views.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 CodeIgniter\Debug\Toolbar\Collectors; + +use CodeIgniter\View\RendererInterface; + +/** + * Views collector + */ +class Views extends BaseCollector +{ + /** + * Whether this collector has data that can + * be displayed in the Timeline. + * + * @var bool + */ + protected $hasTimeline = true; + + /** + * Whether this collector needs to display + * content in a tab or not. + * + * @var bool + */ + protected $hasTabContent = false; + + /** + * Whether this collector needs to display + * a label or not. + * + * @var bool + */ + protected $hasLabel = true; + + /** + * Whether this collector has data that + * should be shown in the Vars tab. + * + * @var bool + */ + protected $hasVarData = true; + + /** + * The 'title' of this Collector. + * Used to name things in the toolbar HTML. + * + * @var string + */ + protected $title = 'Views'; + + /** + * Instance of the shared Renderer service + * + * @var RendererInterface|null + */ + protected $viewer; + + /** + * Views counter + * + * @var array + */ + protected $views = []; + + private function initViewer(): void + { + $this->viewer ??= service('renderer'); + } + + /** + * Child classes should implement this to return the timeline data + * formatted for correct usage. + */ + protected function formatTimelineData(): array + { + $this->initViewer(); + + $data = []; + + $rows = $this->viewer->getPerformanceData(); + + foreach ($rows as $info) { + $data[] = [ + 'name' => 'View: ' . $info['view'], + 'component' => 'Views', + 'start' => $info['start'], + 'duration' => $info['end'] - $info['start'], + ]; + } + + return $data; + } + + /** + * Gets a collection of data that should be shown in the 'Vars' tab. + * The format is an array of sections, each with their own array + * of key/value pairs: + * + * $data = [ + * 'section 1' => [ + * 'foo' => 'bar, + * 'bar' => 'baz' + * ], + * 'section 2' => [ + * 'foo' => 'bar, + * 'bar' => 'baz' + * ], + * ]; + */ + public function getVarData(): array + { + $this->initViewer(); + + return [ + 'View Data' => $this->viewer->getData(), + ]; + } + + /** + * Returns a count of all views. + */ + public function getBadgeValue(): int + { + $this->initViewer(); + + return count($this->viewer->getPerformanceData()); + } + + /** + * Display the icon. + * + * Icon from https://icons8.com - 1em package + */ + public function icon(): string + { + return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADeSURBVEhL7ZSxDcIwEEWNYA0YgGmgyAaJLTcUaaBzQQEVjMEabBQxAdw53zTHiThEovGTfnE/9rsoRUxhKLOmaa6Uh7X2+UvguLCzVxN1XW9x4EYHzik033Hp3X0LO+DaQG8MDQcuq6qao4qkHuMgQggLvkPLjqh00ZgFDBacMJYFkuwFlH1mshdkZ5JPJERA9JpI6xNCBESvibQ+IURA9JpI6xNCBESvibQ+IURA9DTsuHTOrVFFxixgB/eUFlU8uKJ0eDBFOu/9EvoeKnlJS2/08Tc8NOwQ8sIfMeYFjqKDjdU2sp4AAAAASUVORK5CYII='; + } +} diff --git a/system/Debug/Toolbar/Views/_config.tpl b/system/Debug/Toolbar/Views/_config.tpl new file mode 100644 index 0000000..e3235ec --- /dev/null +++ b/system/Debug/Toolbar/Views/_config.tpl @@ -0,0 +1,48 @@ +

        + Read the CodeIgniter docs... +

        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        CodeIgniter Version:{ ciVersion }
        PHP Version:{ phpVersion }
        PHP SAPI:{ phpSAPI }
        Environment:{ environment }
        Base URL: + { if $baseURL == '' } +
        + The $baseURL should always be set manually to prevent possible URL personification from external parties. +
        + { else } + { baseURL } + { endif } +
        Timezone:{ timezone }
        Locale:{ locale }
        Content Security Policy Enabled:{ if $cspEnabled } Yes { else } No { endif }
        diff --git a/system/Debug/Toolbar/Views/_database.tpl b/system/Debug/Toolbar/Views/_database.tpl new file mode 100644 index 0000000..054dd36 --- /dev/null +++ b/system/Debug/Toolbar/Views/_database.tpl @@ -0,0 +1,26 @@ + + + + + + + + + {queries} + + + + + + + + + + {/queries} + +
        TimeQuery String
        {duration}{! sql !}{trace-file}
        + {trace} + {index}{file}
        + {function}

        + {/trace} +
        diff --git a/system/Debug/Toolbar/Views/_events.tpl b/system/Debug/Toolbar/Views/_events.tpl new file mode 100644 index 0000000..88d732f --- /dev/null +++ b/system/Debug/Toolbar/Views/_events.tpl @@ -0,0 +1,18 @@ + + + + + + + + + + {events} + + + + + + {/events} + +
        TimeEvent NameTimes Called
        { duration } ms{event}{count}
        diff --git a/system/Debug/Toolbar/Views/_files.tpl b/system/Debug/Toolbar/Views/_files.tpl new file mode 100644 index 0000000..9c992ab --- /dev/null +++ b/system/Debug/Toolbar/Views/_files.tpl @@ -0,0 +1,16 @@ + + + {userFiles} + + + + + {/userFiles} + {coreFiles} + + + + + {/coreFiles} + +
        {name}{path}
        {name}{path}
        diff --git a/system/Debug/Toolbar/Views/_history.tpl b/system/Debug/Toolbar/Views/_history.tpl new file mode 100644 index 0000000..7f22f56 --- /dev/null +++ b/system/Debug/Toolbar/Views/_history.tpl @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + {files} + + + + + + + + + + {/files} + +
        ActionDatetimeStatusMethodURLContent-TypeIs AJAX?
        + + {datetime}{status}{method}{url}{contentType}{isAJAX}
        diff --git a/system/Debug/Toolbar/Views/_logs.tpl b/system/Debug/Toolbar/Views/_logs.tpl new file mode 100644 index 0000000..7c80d84 --- /dev/null +++ b/system/Debug/Toolbar/Views/_logs.tpl @@ -0,0 +1,20 @@ +{ if $logs == [] } +

        Nothing was logged. If you were expecting logged items, ensure that LoggerConfig file has the correct threshold set.

        +{ else } + + + + + + + + + {logs} + + + + + {/logs} + +
        SeverityMessage
        {level}{msg}
        +{ endif } diff --git a/system/Debug/Toolbar/Views/_routes.tpl b/system/Debug/Toolbar/Views/_routes.tpl new file mode 100644 index 0000000..e277046 --- /dev/null +++ b/system/Debug/Toolbar/Views/_routes.tpl @@ -0,0 +1,52 @@ +

        Matched Route

        + + + + {matchedRoute} + + + + + + + + + + + + + + + + + {params} + + + + + {/params} + {/matchedRoute} + +
        Directory:{directory}
        Controller:{controller}
        Method:{method}
        Params:{paramCount} / {truePCount}
        {name}{value}
        + + +

        Defined Routes

        + + + + + + + + + + + {routes} + + + + + + {/routes} + +
        MethodRouteHandler
        {method}{route}{handler}
        diff --git a/system/Debug/Toolbar/Views/toolbar.css b/system/Debug/Toolbar/Views/toolbar.css new file mode 100644 index 0000000..b9f13c5 --- /dev/null +++ b/system/Debug/Toolbar/Views/toolbar.css @@ -0,0 +1,867 @@ +/** + * This file is part of the CodeIgniter 4 framework. + * + * (c) CodeIgniter Foundation + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +#debug-icon { + bottom: 0; + position: fixed; + right: 0; + z-index: 10000; + height: 36px; + width: 36px; + margin: 0; + padding: 0; + clear: both; + text-align: center; + cursor: pointer; +} +#debug-icon a svg { + margin: 8px; + max-width: 20px; + max-height: 20px; +} +#debug-icon.fixed-top { + bottom: auto; + top: 0; +} +#debug-icon .debug-bar-ndisplay { + display: none; +} + +.debug-bar-vars { + cursor: pointer; +} + +#debug-bar { + bottom: 0; + left: 0; + position: fixed; + right: 0; + z-index: 10000; + height: 36px; + line-height: 36px; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; + font-size: 16px; + font-weight: 400; +} +#debug-bar h1 { + display: flex; + font-weight: normal; + margin: 0 0 0 auto; + padding: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; +} +#debug-bar h1 svg { + width: 16px; + margin-right: 5px; +} +#debug-bar h2 { + font-weight: bold; + font-size: 16px; + margin: 0; + padding: 5px 0 10px 0; +} +#debug-bar h2 span { + font-size: 13px; +} +#debug-bar h3 { + font-size: 12px; + font-weight: 200; + margin: 0 0 0 10px; + padding: 0; + text-transform: uppercase; +} +#debug-bar p { + font-size: 12px; + margin: 0 0 0 15px; + padding: 0; +} +#debug-bar a { + text-decoration: none; +} +#debug-bar a:hover { + text-decoration: underline; +} +#debug-bar button { + border: 1px solid; + border-radius: 4px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + cursor: pointer; + line-height: 15px; +} +#debug-bar button:hover { + text-decoration: underline; +} +#debug-bar table { + border-collapse: collapse; + font-size: 14px; + line-height: normal; + margin: 5px 10px 15px 10px; + width: calc(100% - 10px); +} +#debug-bar table strong { + font-weight: 500; +} +#debug-bar table th { + display: table-cell; + font-weight: 600; + padding-bottom: 0.7em; + text-align: left; +} +#debug-bar table tr { + border: none; +} +#debug-bar table td { + border: none; + display: table-cell; + margin: 0; + text-align: left; +} +#debug-bar table td:first-child { + max-width: 20%; +} +#debug-bar table td:first-child.narrow { + width: 7em; +} +#debug-bar td[data-debugbar-route] form { + display: none; +} +#debug-bar td[data-debugbar-route]:hover form { + display: block; +} +#debug-bar td[data-debugbar-route]:hover > div { + display: none; +} +#debug-bar td[data-debugbar-route] input[type=text] { + padding: 2px; +} +#debug-bar .toolbar { + display: flex; + overflow: hidden; + overflow-y: auto; + padding: 0 12px 0 12px; + white-space: nowrap; + z-index: 10000; +} +#debug-bar .toolbar .rotate { + animation: toolbar-rotate 9s linear infinite; +} +@keyframes toolbar-rotate { + to { + transform: rotate(360deg); + } +} +#debug-bar.fixed-top { + bottom: auto; + top: 0; +} +#debug-bar.fixed-top .tab { + bottom: auto; + top: 36px; +} +#debug-bar #toolbar-position, +#debug-bar #toolbar-theme { + padding: 0 6px; + display: inline-flex; + vertical-align: top; + cursor: pointer; +} +#debug-bar #toolbar-position:hover, +#debug-bar #toolbar-theme:hover { + text-decoration: none; +} +#debug-bar #debug-bar-link { + display: flex; + padding: 6px; + cursor: pointer; +} +#debug-bar .ci-label { + display: inline-flex; + font-size: 14px; +} +#debug-bar .ci-label:hover { + cursor: pointer; +} +#debug-bar .ci-label a { + color: inherit; + display: flex; + letter-spacing: normal; + padding: 0 10px; + text-decoration: none; + align-items: center; +} +#debug-bar .ci-label img { + margin: 6px 3px 6px 0; + width: 16px !important; +} +#debug-bar .ci-label .badge { + border-radius: 12px; + -moz-border-radius: 12px; + -webkit-border-radius: 12px; + display: inline-block; + font-size: 75%; + font-weight: bold; + line-height: 12px; + margin-left: 5px; + padding: 2px 5px; + text-align: center; + vertical-align: baseline; + white-space: nowrap; +} +#debug-bar .tab { + height: fit-content; + text-align: left; + bottom: 35px; + display: none; + left: 0; + max-height: 62%; + overflow: hidden; + overflow-y: auto; + padding: 1em 2em; + position: fixed; + right: 0; + z-index: 9999; +} +#debug-bar .timeline { + position: static; + display: table; + margin-left: 0; + width: 100%; +} +#debug-bar .timeline th { + border-left: 1px solid; + font-size: 12px; + font-weight: 200; + padding: 5px 5px 10px 5px; + position: relative; + text-align: left; +} +#debug-bar .timeline th:first-child { + border-left: 0; +} +#debug-bar .timeline td { + border-left: 1px solid; + padding: 5px; + position: relative; +} +#debug-bar .timeline td:first-child { + border-left: 0; + max-width: none; +} +#debug-bar .timeline td.child-container { + padding: 0px; +} +#debug-bar .timeline td.child-container .timeline { + margin: 0px; +} +#debug-bar .timeline td.child-container .timeline td:first-child:not(.child-container) { + padding-left: calc(5px + 10px * var(--level)); +} +#debug-bar .timeline .timer { + border-radius: 4px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + display: inline-block; + padding: 5px; + position: absolute; + top: 30%; +} +#debug-bar .timeline .timeline-parent { + cursor: pointer; +} +#debug-bar .timeline .timeline-parent td:first-child nav { + background: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMCAxNTAiPjxwYXRoIGQ9Ik02IDdoMThsLTkgMTV6bTAgMzBoMThsLTkgMTV6bTAgNDVoMThsLTktMTV6bTAgMzBoMThsLTktMTV6bTAgMTJsMTggMThtLTE4IDBsMTgtMTgiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNNiAxMjZsMTggMThtLTE4IDBsMTgtMTgiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlPSIjNTU1Ii8+PC9zdmc+") no-repeat scroll 0 0/15px 75px transparent; + background-position: 0 25%; + display: inline-block; + height: 15px; + width: 15px; + margin-right: 3px; + vertical-align: middle; +} +#debug-bar .timeline .timeline-parent-open { + background-color: #DFDFDF; +} +#debug-bar .timeline .timeline-parent-open td:first-child nav { + background-position: 0 75%; +} +#debug-bar .timeline .child-row:hover { + background: transparent; +} +#debug-bar .route-params, +#debug-bar .route-params-item { + vertical-align: top; +} +#debug-bar .route-params td:first-child, +#debug-bar .route-params-item td:first-child { + font-style: italic; + padding-left: 1em; + text-align: right; +} +#debug-bar > .debug-bar-dblock { + display: block; +} + +.debug-view.show-view { + border: 1px solid; + margin: 4px; +} + +.debug-view-path { + font-family: monospace; + font-size: 12px; + letter-spacing: normal; + min-height: 16px; + padding: 2px; + text-align: left; +} + +.show-view .debug-view-path { + display: block !important; +} + +@media screen and (max-width: 1024px) { + #debug-bar .ci-label img { + margin: unset; + } + .hide-sm { + display: none !important; + } +} +#debug-icon { + background-color: #FFFFFF; + box-shadow: 0 0 4px #DFDFDF; + -moz-box-shadow: 0 0 4px #DFDFDF; + -webkit-box-shadow: 0 0 4px #DFDFDF; +} +#debug-icon a:active, +#debug-icon a:link, +#debug-icon a:visited { + color: #DD8615; +} + +#debug-bar { + background-color: #FFFFFF; + color: #434343; +} +#debug-bar h1, +#debug-bar h2, +#debug-bar h3, +#debug-bar p, +#debug-bar a, +#debug-bar button, +#debug-bar table, +#debug-bar thead, +#debug-bar tr, +#debug-bar td, +#debug-bar button, +#debug-bar .toolbar { + background-color: transparent; + color: #434343; +} +#debug-bar button { + background-color: #FFFFFF; +} +#debug-bar table strong { + color: #DD8615; +} +#debug-bar table tbody tr:hover { + background-color: #DFDFDF; +} +#debug-bar table tbody tr.current { + background-color: #FDC894; +} +#debug-bar table tbody tr.current:hover td { + background-color: #DD4814; + color: #FFFFFF; +} +#debug-bar .toolbar { + background-color: #FFFFFF; + box-shadow: 0 0 4px #DFDFDF; + -moz-box-shadow: 0 0 4px #DFDFDF; + -webkit-box-shadow: 0 0 4px #DFDFDF; +} +#debug-bar .toolbar img { + filter: brightness(0) invert(0.4); +} +#debug-bar.fixed-top .toolbar { + box-shadow: 0 0 4px #DFDFDF; + -moz-box-shadow: 0 0 4px #DFDFDF; + -webkit-box-shadow: 0 0 4px #DFDFDF; +} +#debug-bar.fixed-top .tab { + box-shadow: 0 1px 4px #DFDFDF; + -moz-box-shadow: 0 1px 4px #DFDFDF; + -webkit-box-shadow: 0 1px 4px #DFDFDF; +} +#debug-bar .muted { + color: #434343; +} +#debug-bar .muted td { + color: #DFDFDF; +} +#debug-bar .muted:hover td { + color: #434343; +} +#debug-bar #toolbar-position, +#debug-bar #toolbar-theme { + filter: brightness(0) invert(0.6); +} +#debug-bar .ci-label.active { + background-color: #DFDFDF; +} +#debug-bar .ci-label:hover { + background-color: #DFDFDF; +} +#debug-bar .ci-label .badge { + background-color: #DD4814; + color: #FFFFFF; +} +#debug-bar .tab { + background-color: #FFFFFF; + box-shadow: 0 -1px 4px #DFDFDF; + -moz-box-shadow: 0 -1px 4px #DFDFDF; + -webkit-box-shadow: 0 -1px 4px #DFDFDF; +} +#debug-bar .timeline th, +#debug-bar .timeline td { + border-color: #DFDFDF; +} +#debug-bar .timeline .timer { + background-color: #DD8615; +} + +.debug-view.show-view { + border-color: #DD8615; +} + +.debug-view-path { + background-color: #FDC894; + color: #434343; +} + +@media (prefers-color-scheme: dark) { + #debug-icon { + background-color: #252525; + box-shadow: 0 0 4px #DFDFDF; + -moz-box-shadow: 0 0 4px #DFDFDF; + -webkit-box-shadow: 0 0 4px #DFDFDF; + } + #debug-icon a:active, + #debug-icon a:link, + #debug-icon a:visited { + color: #DD8615; + } + #debug-bar { + background-color: #252525; + color: #DFDFDF; + } + #debug-bar h1, + #debug-bar h2, + #debug-bar h3, + #debug-bar p, + #debug-bar a, + #debug-bar button, + #debug-bar table, + #debug-bar thead, + #debug-bar tr, + #debug-bar td, + #debug-bar button, + #debug-bar .toolbar { + background-color: transparent; + color: #DFDFDF; + } + #debug-bar button { + background-color: #252525; + } + #debug-bar table strong { + color: #DD8615; + } + #debug-bar table tbody tr:hover { + background-color: #434343; + } + #debug-bar table tbody tr.current { + background-color: #FDC894; + } + #debug-bar table tbody tr.current td { + color: #252525; + } + #debug-bar table tbody tr.current:hover td { + background-color: #DD4814; + color: #FFFFFF; + } + #debug-bar .toolbar { + background-color: #434343; + box-shadow: 0 0 4px #434343; + -moz-box-shadow: 0 0 4px #434343; + -webkit-box-shadow: 0 0 4px #434343; + } + #debug-bar .toolbar img { + filter: brightness(0) invert(1); + } + #debug-bar.fixed-top .toolbar { + box-shadow: 0 0 4px #434343; + -moz-box-shadow: 0 0 4px #434343; + -webkit-box-shadow: 0 0 4px #434343; + } + #debug-bar.fixed-top .tab { + box-shadow: 0 1px 4px #434343; + -moz-box-shadow: 0 1px 4px #434343; + -webkit-box-shadow: 0 1px 4px #434343; + } + #debug-bar .muted { + color: #DFDFDF; + } + #debug-bar .muted td { + color: #434343; + } + #debug-bar .muted:hover td { + color: #DFDFDF; + } + #debug-bar #toolbar-position, + #debug-bar #toolbar-theme { + filter: brightness(0) invert(0.6); + } + #debug-bar .ci-label.active { + background-color: #252525; + } + #debug-bar .ci-label:hover { + background-color: #252525; + } + #debug-bar .ci-label .badge { + background-color: #DD4814; + color: #FFFFFF; + } + #debug-bar .tab { + background-color: #252525; + box-shadow: 0 -1px 4px #434343; + -moz-box-shadow: 0 -1px 4px #434343; + -webkit-box-shadow: 0 -1px 4px #434343; + } + #debug-bar .timeline th, + #debug-bar .timeline td { + border-color: #434343; + } + #debug-bar .timeline .timer { + background-color: #DD8615; + } + .debug-view.show-view { + border-color: #DD8615; + } + .debug-view-path { + background-color: #FDC894; + color: #434343; + } +} +#toolbarContainer.dark #debug-icon { + background-color: #252525; + box-shadow: 0 0 4px #DFDFDF; + -moz-box-shadow: 0 0 4px #DFDFDF; + -webkit-box-shadow: 0 0 4px #DFDFDF; +} +#toolbarContainer.dark #debug-icon a:active, +#toolbarContainer.dark #debug-icon a:link, +#toolbarContainer.dark #debug-icon a:visited { + color: #DD8615; +} +#toolbarContainer.dark #debug-bar { + background-color: #252525; + color: #DFDFDF; +} +#toolbarContainer.dark #debug-bar h1, +#toolbarContainer.dark #debug-bar h2, +#toolbarContainer.dark #debug-bar h3, +#toolbarContainer.dark #debug-bar p, +#toolbarContainer.dark #debug-bar a, +#toolbarContainer.dark #debug-bar button, +#toolbarContainer.dark #debug-bar table, +#toolbarContainer.dark #debug-bar thead, +#toolbarContainer.dark #debug-bar tr, +#toolbarContainer.dark #debug-bar td, +#toolbarContainer.dark #debug-bar button, +#toolbarContainer.dark #debug-bar .toolbar { + background-color: transparent; + color: #DFDFDF; +} +#toolbarContainer.dark #debug-bar button { + background-color: #252525; +} +#toolbarContainer.dark #debug-bar table strong { + color: #DD8615; +} +#toolbarContainer.dark #debug-bar table tbody tr:hover { + background-color: #434343; +} +#toolbarContainer.dark #debug-bar table tbody tr.current { + background-color: #FDC894; +} +#toolbarContainer.dark #debug-bar table tbody tr.current td { + color: #252525; +} +#toolbarContainer.dark #debug-bar table tbody tr.current:hover td { + background-color: #DD4814; + color: #FFFFFF; +} +#toolbarContainer.dark #debug-bar .toolbar { + background-color: #434343; + box-shadow: 0 0 4px #434343; + -moz-box-shadow: 0 0 4px #434343; + -webkit-box-shadow: 0 0 4px #434343; +} +#toolbarContainer.dark #debug-bar .toolbar img { + filter: brightness(0) invert(1); +} +#toolbarContainer.dark #debug-bar.fixed-top .toolbar { + box-shadow: 0 0 4px #434343; + -moz-box-shadow: 0 0 4px #434343; + -webkit-box-shadow: 0 0 4px #434343; +} +#toolbarContainer.dark #debug-bar.fixed-top .tab { + box-shadow: 0 1px 4px #434343; + -moz-box-shadow: 0 1px 4px #434343; + -webkit-box-shadow: 0 1px 4px #434343; +} +#toolbarContainer.dark #debug-bar .muted { + color: #DFDFDF; +} +#toolbarContainer.dark #debug-bar .muted td { + color: #434343; +} +#toolbarContainer.dark #debug-bar .muted:hover td { + color: #DFDFDF; +} +#toolbarContainer.dark #debug-bar #toolbar-position, +#toolbarContainer.dark #debug-bar #toolbar-theme { + filter: brightness(0) invert(0.6); +} +#toolbarContainer.dark #debug-bar .ci-label.active { + background-color: #252525; +} +#toolbarContainer.dark #debug-bar .ci-label:hover { + background-color: #252525; +} +#toolbarContainer.dark #debug-bar .ci-label .badge { + background-color: #DD4814; + color: #FFFFFF; +} +#toolbarContainer.dark #debug-bar .tab { + background-color: #252525; + box-shadow: 0 -1px 4px #434343; + -moz-box-shadow: 0 -1px 4px #434343; + -webkit-box-shadow: 0 -1px 4px #434343; +} +#toolbarContainer.dark #debug-bar .timeline th, +#toolbarContainer.dark #debug-bar .timeline td { + border-color: #434343; +} +#toolbarContainer.dark #debug-bar .timeline .timer { + background-color: #DD8615; +} +#toolbarContainer.dark .debug-view.show-view { + border-color: #DD8615; +} +#toolbarContainer.dark .debug-view-path { + background-color: #FDC894; + color: #434343; +} +#toolbarContainer.dark td[data-debugbar-route] input[type=text] { + background: #000; + color: #fff; +} + +#toolbarContainer.light #debug-icon { + background-color: #FFFFFF; + box-shadow: 0 0 4px #DFDFDF; + -moz-box-shadow: 0 0 4px #DFDFDF; + -webkit-box-shadow: 0 0 4px #DFDFDF; +} +#toolbarContainer.light #debug-icon a:active, +#toolbarContainer.light #debug-icon a:link, +#toolbarContainer.light #debug-icon a:visited { + color: #DD8615; +} +#toolbarContainer.light #debug-bar { + background-color: #FFFFFF; + color: #434343; +} +#toolbarContainer.light #debug-bar h1, +#toolbarContainer.light #debug-bar h2, +#toolbarContainer.light #debug-bar h3, +#toolbarContainer.light #debug-bar p, +#toolbarContainer.light #debug-bar a, +#toolbarContainer.light #debug-bar button, +#toolbarContainer.light #debug-bar table, +#toolbarContainer.light #debug-bar thead, +#toolbarContainer.light #debug-bar tr, +#toolbarContainer.light #debug-bar td, +#toolbarContainer.light #debug-bar button, +#toolbarContainer.light #debug-bar .toolbar { + background-color: transparent; + color: #434343; +} +#toolbarContainer.light #debug-bar button { + background-color: #FFFFFF; +} +#toolbarContainer.light #debug-bar table strong { + color: #DD8615; +} +#toolbarContainer.light #debug-bar table tbody tr:hover { + background-color: #DFDFDF; +} +#toolbarContainer.light #debug-bar table tbody tr.current { + background-color: #FDC894; +} +#toolbarContainer.light #debug-bar table tbody tr.current:hover td { + background-color: #DD4814; + color: #FFFFFF; +} +#toolbarContainer.light #debug-bar .toolbar { + background-color: #FFFFFF; + box-shadow: 0 0 4px #DFDFDF; + -moz-box-shadow: 0 0 4px #DFDFDF; + -webkit-box-shadow: 0 0 4px #DFDFDF; +} +#toolbarContainer.light #debug-bar .toolbar img { + filter: brightness(0) invert(0.4); +} +#toolbarContainer.light #debug-bar.fixed-top .toolbar { + box-shadow: 0 0 4px #DFDFDF; + -moz-box-shadow: 0 0 4px #DFDFDF; + -webkit-box-shadow: 0 0 4px #DFDFDF; +} +#toolbarContainer.light #debug-bar.fixed-top .tab { + box-shadow: 0 1px 4px #DFDFDF; + -moz-box-shadow: 0 1px 4px #DFDFDF; + -webkit-box-shadow: 0 1px 4px #DFDFDF; +} +#toolbarContainer.light #debug-bar .muted { + color: #434343; +} +#toolbarContainer.light #debug-bar .muted td { + color: #DFDFDF; +} +#toolbarContainer.light #debug-bar .muted:hover td { + color: #434343; +} +#toolbarContainer.light #debug-bar #toolbar-position, +#toolbarContainer.light #debug-bar #toolbar-theme { + filter: brightness(0) invert(0.6); +} +#toolbarContainer.light #debug-bar .ci-label.active { + background-color: #DFDFDF; +} +#toolbarContainer.light #debug-bar .ci-label:hover { + background-color: #DFDFDF; +} +#toolbarContainer.light #debug-bar .ci-label .badge { + background-color: #DD4814; + color: #FFFFFF; +} +#toolbarContainer.light #debug-bar .tab { + background-color: #FFFFFF; + box-shadow: 0 -1px 4px #DFDFDF; + -moz-box-shadow: 0 -1px 4px #DFDFDF; + -webkit-box-shadow: 0 -1px 4px #DFDFDF; +} +#toolbarContainer.light #debug-bar .timeline th, +#toolbarContainer.light #debug-bar .timeline td { + border-color: #DFDFDF; +} +#toolbarContainer.light #debug-bar .timeline .timer { + background-color: #DD8615; +} +#toolbarContainer.light .debug-view.show-view { + border-color: #DD8615; +} +#toolbarContainer.light .debug-view-path { + background-color: #FDC894; + color: #434343; +} + +.debug-bar-width30 { + width: 30%; +} + +.debug-bar-width10 { + width: 10%; +} + +.debug-bar-width70p { + width: 70px; +} + +.debug-bar-width190p { + width: 190px; +} + +.debug-bar-width20e { + width: 20em; +} + +.debug-bar-width6r { + width: 6rem; +} + +.debug-bar-ndisplay { + display: none; +} + +.debug-bar-alignRight { + text-align: right; +} + +.debug-bar-alignLeft { + text-align: left; +} + +.debug-bar-noverflow { + overflow: hidden; +} + +.debug-bar-dtableRow { + display: table-row; +} + +.debug-bar-dinlineBlock { + display: inline-block; +} + +.debug-bar-pointer { + cursor: pointer; +} + +.debug-bar-mleft4 { + margin-left: 4px; +} + +.debug-bar-level-0 { + --level: 0; +} + +.debug-bar-level-1 { + --level: 1; +} + +.debug-bar-level-2 { + --level: 2; +} + +.debug-bar-level-3 { + --level: 3; +} + +.debug-bar-level-4 { + --level: 4; +} + +.debug-bar-level-5 { + --level: 5; +} + +.debug-bar-level-6 { + --level: 6; +} diff --git a/system/Debug/Toolbar/Views/toolbar.js b/system/Debug/Toolbar/Views/toolbar.js new file mode 100644 index 0000000..54fe3d6 --- /dev/null +++ b/system/Debug/Toolbar/Views/toolbar.js @@ -0,0 +1,825 @@ +/* + * Functionality for the CodeIgniter Debug Toolbar. + */ + +var ciDebugBar = { + toolbarContainer: null, + toolbar: null, + icon: null, + + init: function () { + this.toolbarContainer = document.getElementById("toolbarContainer"); + this.toolbar = document.getElementById("debug-bar"); + this.icon = document.getElementById("debug-icon"); + + ciDebugBar.createListeners(); + ciDebugBar.setToolbarState(); + ciDebugBar.setToolbarPosition(); + ciDebugBar.setToolbarTheme(); + ciDebugBar.toggleViewsHints(); + ciDebugBar.routerLink(); + ciDebugBar.setHotReloadState(); + + document + .getElementById("debug-bar-link") + .addEventListener("click", ciDebugBar.toggleToolbar, true); + document + .getElementById("debug-icon-link") + .addEventListener("click", ciDebugBar.toggleToolbar, true); + + // Allows to highlight the row of the current history request + var btn = this.toolbar.querySelector( + 'button[data-time="' + localStorage.getItem("debugbar-time") + '"]' + ); + ciDebugBar.addClass(btn.parentNode.parentNode, "current"); + + historyLoad = this.toolbar.getElementsByClassName("ci-history-load"); + + for (var i = 0; i < historyLoad.length; i++) { + historyLoad[i].addEventListener( + "click", + function () { + loadDoc(this.getAttribute("data-time")); + }, + true + ); + } + + // Display the active Tab on page load + var tab = ciDebugBar.readCookie("debug-bar-tab"); + if (document.getElementById(tab)) { + var el = document.getElementById(tab); + ciDebugBar.switchClass(el, "debug-bar-ndisplay", "debug-bar-dblock"); + ciDebugBar.addClass(el, "active"); + tab = document.querySelector("[data-tab=" + tab + "]"); + if (tab) { + ciDebugBar.addClass(tab.parentNode, "active"); + } + } + }, + + createListeners: function () { + var buttons = [].slice.call( + this.toolbar.querySelectorAll(".ci-label a") + ); + + for (var i = 0; i < buttons.length; i++) { + buttons[i].addEventListener("click", ciDebugBar.showTab, true); + } + + // Hook up generic toggle via data attributes `data-toggle="foo"` + var links = this.toolbar.querySelectorAll("[data-toggle]"); + for (var i = 0; i < links.length; i++) { + let toggleData = links[i].getAttribute("data-toggle"); + if (toggleData === "datatable") { + + let datatable = links[i].getAttribute("data-table"); + links[i].addEventListener("click", function() { + ciDebugBar.toggleDataTable(datatable) + }, true); + + } else if (toggleData === "childrows") { + + let child = links[i].getAttribute("data-child"); + links[i].addEventListener("click", function() { + ciDebugBar.toggleChildRows(child) + }, true); + + } else { + links[i].addEventListener("click", ciDebugBar.toggleRows, true); + } + } + }, + + showTab: function () { + // Get the target tab, if any + var tab = document.getElementById(this.getAttribute("data-tab")); + + // If the label have not a tab stops here + if (! tab) { + return; + } + + // Remove debug-bar-tab cookie + ciDebugBar.createCookie("debug-bar-tab", "", -1); + + // Check our current state. + var state = tab.classList.contains("debug-bar-dblock"); + + // Hide all tabs + var tabs = document.querySelectorAll("#debug-bar .tab"); + + for (var i = 0; i < tabs.length; i++) { + ciDebugBar.switchClass(tabs[i], "debug-bar-dblock", "debug-bar-ndisplay"); + } + + // Mark all labels as inactive + var labels = document.querySelectorAll("#debug-bar .ci-label"); + + for (var i = 0; i < labels.length; i++) { + ciDebugBar.removeClass(labels[i], "active"); + } + + // Show/hide the selected tab + if (! state) { + ciDebugBar.switchClass(tab, "debug-bar-ndisplay", "debug-bar-dblock"); + ciDebugBar.addClass(this.parentNode, "active"); + // Create debug-bar-tab cookie to persistent state + ciDebugBar.createCookie( + "debug-bar-tab", + this.getAttribute("data-tab"), + 365 + ); + } + }, + + addClass: function (el, className) { + if (el.classList) { + el.classList.add(className); + } else { + el.className += " " + className; + } + }, + + removeClass: function (el, className) { + if (el.classList) { + el.classList.remove(className); + } else { + el.className = el.className.replace( + new RegExp( + "(^|\\b)" + className.split(" ").join("|") + "(\\b|$)", + "gi" + ), + " " + ); + } + }, + + switchClass : function(el, classFrom, classTo) { + ciDebugBar.removeClass(el, classFrom); + ciDebugBar.addClass(el, classTo); + }, + + /** + * Toggle display of another object based on + * the data-toggle value of this object + * + * @param event + */ + toggleRows: function (event) { + if (event.target) { + let row = event.target.closest("tr"); + let target = document.getElementById( + row.getAttribute("data-toggle") + ); + + if (target.classList.contains("debug-bar-ndisplay")) { + ciDebugBar.switchClass(target, "debug-bar-ndisplay", "debug-bar-dtableRow"); + } else { + ciDebugBar.switchClass(target, "debug-bar-dtableRow", "debug-bar-ndisplay"); + } + } + }, + + /** + * Toggle display of a data table + * + * @param obj + */ + toggleDataTable: function (obj) { + if (typeof obj == "string") { + obj = document.getElementById(obj + "_table"); + } + + if (obj) { + if (obj.classList.contains("debug-bar-ndisplay")) { + ciDebugBar.switchClass(obj, "debug-bar-ndisplay", "debug-bar-dblock"); + } else { + ciDebugBar.switchClass(obj, "debug-bar-dblock", "debug-bar-ndisplay"); + } + } + }, + + /** + * Toggle display of timeline child elements + * + * @param obj + */ + toggleChildRows: function (obj) { + if (typeof obj == "string") { + par = document.getElementById(obj + "_parent"); + obj = document.getElementById(obj + "_children"); + } + + if (par && obj) { + + if (obj.classList.contains("debug-bar-ndisplay")) { + ciDebugBar.removeClass(obj, "debug-bar-ndisplay"); + } else { + ciDebugBar.addClass(obj, "debug-bar-ndisplay"); + } + + par.classList.toggle("timeline-parent-open"); + } + }, + + //-------------------------------------------------------------------- + + /** + * Toggle tool bar from full to icon and icon to full + */ + toggleToolbar: function () { + var open = ! ciDebugBar.toolbar.classList.contains("debug-bar-ndisplay"); + + if (open) { + ciDebugBar.switchClass(ciDebugBar.icon, "debug-bar-ndisplay", "debug-bar-dinlineBlock"); + ciDebugBar.switchClass(ciDebugBar.toolbar, "debug-bar-dinlineBlock", "debug-bar-ndisplay"); + } else { + ciDebugBar.switchClass(ciDebugBar.icon, "debug-bar-dinlineBlock", "debug-bar-ndisplay"); + ciDebugBar.switchClass(ciDebugBar.toolbar, "debug-bar-ndisplay", "debug-bar-dinlineBlock"); + } + + // Remember it for other page loads on this site + ciDebugBar.createCookie("debug-bar-state", "", -1); + ciDebugBar.createCookie( + "debug-bar-state", + open == true ? "minimized" : "open", + 365 + ); + }, + + /** + * Sets the initial state of the toolbar (open or minimized) when + * the page is first loaded to allow it to remember the state between refreshes. + */ + setToolbarState: function () { + var open = ciDebugBar.readCookie("debug-bar-state"); + + if (open != "open") { + ciDebugBar.switchClass(ciDebugBar.icon, "debug-bar-ndisplay", "debug-bar-dinlineBlock"); + ciDebugBar.switchClass(ciDebugBar.toolbar, "debug-bar-dinlineBlock", "debug-bar-ndisplay"); + } else { + ciDebugBar.switchClass(ciDebugBar.icon, "debug-bar-dinlineBlock", "debug-bar-ndisplay"); + ciDebugBar.switchClass(ciDebugBar.toolbar, "debug-bar-ndisplay", "debug-bar-dinlineBlock"); + } + }, + + toggleViewsHints: function () { + // Avoid toggle hints on history requests that are not the initial + if ( + localStorage.getItem("debugbar-time") != + localStorage.getItem("debugbar-time-new") + ) { + var a = document.querySelector('a[data-tab="ci-views"]'); + a.href = "#"; + return; + } + + var nodeList = []; // [ Element, NewElement( 1 )/OldElement( 0 ) ] + var sortedComments = []; + var comments = []; + + var getComments = function () { + var nodes = []; + var result = []; + var xpathResults = document.evaluate( + "//comment()[starts-with(., ' DEBUG-VIEW')]", + document, + null, + XPathResult.ANY_TYPE, + null + ); + var nextNode = xpathResults.iterateNext(); + while (nextNode) { + nodes.push(nextNode); + nextNode = xpathResults.iterateNext(); + } + + // sort comment by opening and closing tags + for (var i = 0; i < nodes.length; ++i) { + // get file path + name to use as key + var path = nodes[i].nodeValue.substring( + 18, + nodes[i].nodeValue.length - 1 + ); + + if (nodes[i].nodeValue[12] === "S") { + // simple check for start comment + // create new entry + result[path] = [nodes[i], null]; + } else if (result[path]) { + // add to existing entry + result[path][1] = nodes[i]; + } + } + + return result; + }; + + // find node that has TargetNode as parentNode + var getParentNode = function (node, targetNode) { + if (node.parentNode === null) { + return null; + } + + if (node.parentNode !== targetNode) { + return getParentNode(node.parentNode, targetNode); + } + + return node; + }; + + // define invalid & outer ( also invalid ) elements + const INVALID_ELEMENTS = ["NOSCRIPT", "SCRIPT", "STYLE"]; + const OUTER_ELEMENTS = ["HTML", "BODY", "HEAD"]; + + var getValidElementInner = function (node, reverse) { + // handle invalid tags + if (OUTER_ELEMENTS.indexOf(node.nodeName) !== -1) { + for (var i = 0; i < document.body.children.length; ++i) { + var index = reverse + ? document.body.children.length - (i + 1) + : i; + var element = document.body.children[index]; + + // skip invalid tags + if (INVALID_ELEMENTS.indexOf(element.nodeName) !== -1) { + continue; + } + + return [element, reverse]; + } + + return null; + } + + // get to next valid element + while ( + node !== null && + INVALID_ELEMENTS.indexOf(node.nodeName) !== -1 + ) { + node = reverse + ? node.previousElementSibling + : node.nextElementSibling; + } + + // return non array if we couldnt find something + if (node === null) { + return null; + } + + return [node, reverse]; + }; + + // get next valid element ( to be safe to add divs ) + // @return [ element, skip element ] or null if we couldnt find a valid place + var getValidElement = function (nodeElement) { + if (nodeElement) { + if (nodeElement.nextElementSibling !== null) { + return ( + getValidElementInner( + nodeElement.nextElementSibling, + false + ) || + getValidElementInner( + nodeElement.previousElementSibling, + true + ) + ); + } + if (nodeElement.previousElementSibling !== null) { + return getValidElementInner( + nodeElement.previousElementSibling, + true + ); + } + } + + // something went wrong! -> element is not in DOM + return null; + }; + + function showHints() { + // Had AJAX? Reset view blocks + sortedComments = getComments(); + + for (var key in sortedComments) { + var startElement = getValidElement(sortedComments[key][0]); + var endElement = getValidElement(sortedComments[key][1]); + + // skip if we couldnt get a valid element + if (startElement === null || endElement === null) { + continue; + } + + // find element which has same parent as startelement + var jointParent = getParentNode( + endElement[0], + startElement[0].parentNode + ); + if (jointParent === null) { + // find element which has same parent as endelement + jointParent = getParentNode( + startElement[0], + endElement[0].parentNode + ); + if (jointParent === null) { + // both tries failed + continue; + } else { + startElement[0] = jointParent; + } + } else { + endElement[0] = jointParent; + } + + var debugDiv = document.createElement("div"); // holder + var debugPath = document.createElement("div"); // path + var childArray = startElement[0].parentNode.childNodes; // target child array + var parent = startElement[0].parentNode; + var start, end; + + // setup container + debugDiv.classList.add("debug-view"); + debugDiv.classList.add("show-view"); + debugPath.classList.add("debug-view-path"); + debugPath.innerText = key; + debugDiv.appendChild(debugPath); + + // calc distance between them + // start + for (var i = 0; i < childArray.length; ++i) { + // check for comment ( start & end ) -> if its before valid start element + if ( + childArray[i] === sortedComments[key][1] || + childArray[i] === sortedComments[key][0] || + childArray[i] === startElement[0] + ) { + start = i; + if (childArray[i] === sortedComments[key][0]) { + start++; // increase to skip the start comment + } + break; + } + } + // adjust if we want to skip the start element + if (startElement[1]) { + start++; + } + + // end + for (var i = start; i < childArray.length; ++i) { + if (childArray[i] === endElement[0]) { + end = i; + // dont break to check for end comment after end valid element + } else if (childArray[i] === sortedComments[key][1]) { + // if we found the end comment, we can break + end = i; + break; + } + } + + // move elements + var number = end - start; + if (endElement[1]) { + number++; + } + for (var i = 0; i < number; ++i) { + if (INVALID_ELEMENTS.indexOf(childArray[start]) !== -1) { + // skip invalid childs that can cause problems if moved + start++; + continue; + } + debugDiv.appendChild(childArray[start]); + } + + // add container to DOM + nodeList.push(parent.insertBefore(debugDiv, childArray[start])); + } + + ciDebugBar.createCookie("debug-view", "show", 365); + ciDebugBar.addClass(btn, "active"); + } + + function hideHints() { + for (var i = 0; i < nodeList.length; ++i) { + var index; + + // find index + for ( + var j = 0; + j < nodeList[i].parentNode.childNodes.length; + ++j + ) { + if (nodeList[i].parentNode.childNodes[j] === nodeList[i]) { + index = j; + break; + } + } + + // move child back + while (nodeList[i].childNodes.length !== 1) { + nodeList[i].parentNode.insertBefore( + nodeList[i].childNodes[1], + nodeList[i].parentNode.childNodes[index].nextSibling + ); + index++; + } + + nodeList[i].parentNode.removeChild(nodeList[i]); + } + nodeList.length = 0; + + ciDebugBar.createCookie("debug-view", "", -1); + ciDebugBar.removeClass(btn, "active"); + } + + var btn = document.querySelector("[data-tab=ci-views]"); + + // If the Views Collector is inactive stops here + if (! btn) { + return; + } + + btn.parentNode.onclick = function () { + if (ciDebugBar.readCookie("debug-view")) { + hideHints(); + } else { + showHints(); + } + }; + + // Determine Hints state on page load + if (ciDebugBar.readCookie("debug-view")) { + showHints(); + } + }, + + setToolbarPosition: function () { + var btnPosition = this.toolbar.querySelector("#toolbar-position"); + + if (ciDebugBar.readCookie("debug-bar-position") === "top") { + ciDebugBar.addClass(ciDebugBar.icon, "fixed-top"); + ciDebugBar.addClass(ciDebugBar.toolbar, "fixed-top"); + } + + btnPosition.addEventListener( + "click", + function () { + var position = ciDebugBar.readCookie("debug-bar-position"); + + ciDebugBar.createCookie("debug-bar-position", "", -1); + + if (! position || position === "bottom") { + ciDebugBar.createCookie("debug-bar-position", "top", 365); + ciDebugBar.addClass(ciDebugBar.icon, "fixed-top"); + ciDebugBar.addClass(ciDebugBar.toolbar, "fixed-top"); + } else { + ciDebugBar.createCookie( + "debug-bar-position", + "bottom", + 365 + ); + ciDebugBar.removeClass(ciDebugBar.icon, "fixed-top"); + ciDebugBar.removeClass(ciDebugBar.toolbar, "fixed-top"); + } + }, + true + ); + }, + + setToolbarTheme: function () { + var btnTheme = this.toolbar.querySelector("#toolbar-theme"); + var isDarkMode = window.matchMedia( + "(prefers-color-scheme: dark)" + ).matches; + var isLightMode = window.matchMedia( + "(prefers-color-scheme: light)" + ).matches; + + // If a cookie is set with a value, we force the color scheme + if (ciDebugBar.readCookie("debug-bar-theme") === "dark") { + ciDebugBar.removeClass(ciDebugBar.toolbarContainer, "light"); + ciDebugBar.addClass(ciDebugBar.toolbarContainer, "dark"); + } else if (ciDebugBar.readCookie("debug-bar-theme") === "light") { + ciDebugBar.removeClass(ciDebugBar.toolbarContainer, "dark"); + ciDebugBar.addClass(ciDebugBar.toolbarContainer, "light"); + } + + btnTheme.addEventListener( + "click", + function () { + var theme = ciDebugBar.readCookie("debug-bar-theme"); + + if ( + ! theme && + window.matchMedia("(prefers-color-scheme: dark)").matches + ) { + // If there is no cookie, and "prefers-color-scheme" is set to "dark" + // It means that the user wants to switch to light mode + ciDebugBar.createCookie("debug-bar-theme", "light", 365); + ciDebugBar.removeClass(ciDebugBar.toolbarContainer, "dark"); + ciDebugBar.addClass(ciDebugBar.toolbarContainer, "light"); + } else { + if (theme === "dark") { + ciDebugBar.createCookie( + "debug-bar-theme", + "light", + 365 + ); + ciDebugBar.removeClass( + ciDebugBar.toolbarContainer, + "dark" + ); + ciDebugBar.addClass( + ciDebugBar.toolbarContainer, + "light" + ); + } else { + // In any other cases: if there is no cookie, or the cookie is set to + // "light", or the "prefers-color-scheme" is "light"... + ciDebugBar.createCookie("debug-bar-theme", "dark", 365); + ciDebugBar.removeClass( + ciDebugBar.toolbarContainer, + "light" + ); + ciDebugBar.addClass( + ciDebugBar.toolbarContainer, + "dark" + ); + } + } + }, + true + ); + }, + + setHotReloadState: function () { + var btn = document.getElementById("debug-hot-reload").parentNode; + var btnImg = btn.getElementsByTagName("img")[0]; + var eventSource; + + // If the Hot Reload Collector is inactive stops here + if (! btn) { + return; + } + + btn.onclick = function () { + if (ciDebugBar.readCookie("debug-hot-reload")) { + ciDebugBar.createCookie("debug-hot-reload", "", -1); + ciDebugBar.removeClass(btn, "active"); + ciDebugBar.removeClass(btnImg, "rotate"); + + // Close the EventSource connection if it exists + if (typeof eventSource !== "undefined") { + eventSource.close(); + eventSource = void 0; // Undefine the variable + } + } else { + ciDebugBar.createCookie("debug-hot-reload", "show", 365); + ciDebugBar.addClass(btn, "active"); + ciDebugBar.addClass(btnImg, "rotate"); + + eventSource = ciDebugBar.hotReloadConnect(); + } + }; + + // Determine Hot Reload state on page load + if (ciDebugBar.readCookie("debug-hot-reload")) { + ciDebugBar.addClass(btn, "active"); + ciDebugBar.addClass(btnImg, "rotate"); + eventSource = ciDebugBar.hotReloadConnect(); + } + }, + + hotReloadConnect: function () { + const eventSource = new EventSource(ciSiteURL + "/__hot-reload"); + + eventSource.addEventListener("reload", function (e) { + console.log("reload", e); + window.location.reload(); + }); + + eventSource.onerror = (err) => { + console.error("EventSource failed:", err); + }; + + return eventSource; + }, + + /** + * Helper to create a cookie. + * + * @param name + * @param value + * @param days + */ + createCookie: function (name, value, days) { + if (days) { + var date = new Date(); + + date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000); + + var expires = "; expires=" + date.toGMTString(); + } else { + var expires = ""; + } + + document.cookie = + name + "=" + value + expires + "; path=/; samesite=Lax"; + }, + + readCookie: function (name) { + var nameEQ = name + "="; + var ca = document.cookie.split(";"); + + for (var i = 0; i < ca.length; i++) { + var c = ca[i]; + while (c.charAt(0) == " ") { + c = c.substring(1, c.length); + } + if (c.indexOf(nameEQ) == 0) { + return c.substring(nameEQ.length, c.length); + } + } + return null; + }, + + trimSlash: function (text) { + return text.replace(/^\/|\/$/g, ""); + }, + + routerLink: function () { + var row, _location; + var rowGet = this.toolbar.querySelectorAll( + 'td[data-debugbar-route="GET"]' + ); + var patt = /\((?:[^)(]+|\((?:[^)(]+|\([^)(]*\))*\))*\)/; + + for (var i = 0; i < rowGet.length; i++) { + row = rowGet[i]; + if (!/\/\(.+?\)/.test(rowGet[i].innerText)) { + ciDebugBar.addClass(row, "debug-bar-pointer"); + row.setAttribute( + "title", + location.origin + "/" + ciDebugBar.trimSlash(row.innerText) + ); + row.addEventListener("click", function (ev) { + _location = + location.origin + + "/" + + ciDebugBar.trimSlash(ev.target.innerText); + var redirectWindow = window.open(_location, "_blank"); + redirectWindow.location; + }); + } else { + row.innerHTML = + "
        " + + row.innerText + + "
        " + + '' + + row.innerText.replace( + patt, + '' + ) + + '' + + ""; + } + } + + rowGet = this.toolbar.querySelectorAll( + 'td[data-debugbar-route="GET"] form' + ); + for (var i = 0; i < rowGet.length; i++) { + row = rowGet[i]; + + row.addEventListener("submit", function (event) { + event.preventDefault(); + var inputArray = [], + t = 0; + var input = event.target.querySelectorAll("input[type=text]"); + var tpl = event.target.getAttribute("data-debugbar-route-tpl"); + + for (var n = 0; n < input.length; n++) { + if (input[n].value.length > 0) { + inputArray.push(input[n].value); + } + } + + if (inputArray.length > 0) { + _location = + location.origin + + "/" + + tpl.replace(/\?/g, function () { + return inputArray[t++]; + }); + + var redirectWindow = window.open(_location, "_blank"); + redirectWindow.location; + } + }); + } + }, +}; diff --git a/system/Debug/Toolbar/Views/toolbar.tpl.php b/system/Debug/Toolbar/Views/toolbar.tpl.php new file mode 100644 index 0000000..8179c1a --- /dev/null +++ b/system/Debug/Toolbar/Views/toolbar.tpl.php @@ -0,0 +1,277 @@ + + + + + +
        +
        + + 🔅 + + + + + + + + + ms   MB + + + + + + + + + + + + + + + + + + + + + + + Vars + + + +

        + + + + + + +

        + + + + + +
        + + +
        + + + + + + + + + + + + + renderTimeline($collectors, $startTime, $segmentCount, $segmentDuration, $styles) ?> + +
        NAMECOMPONENTDURATION ms
        +
        + + + + + +
        +

        + + setData($c['display'])->render("_{$c['titleSafe']}.tpl") ?> +
        + + + + + +
        + + + + $items) : ?> + + +

        +
        + + + + + + $value) : ?> + + + + + + +
        + + +

        No data to display.

        + + + + + + +

        Session User Data

        +
        + + + + + + $value) : ?> + + + + + + +
        + +

        No data to display.

        + + +

        Session doesn't seem to be active.

        + + +

        Request ( )

        + + + +

        $_GET

        +
        + + + + $value) : ?> + + + + + + +
        + + + + +

        $_POST

        +
        + + + + $value) : ?> + + + + + + +
        + + + + +

        Headers

        +
        + + + + $value) : ?> + + + + + + +
        + + + + +

        Cookies

        +
        + + + + $value) : ?> + + + + + + + + + +

        Response + ( ) +

        + + + +

        Headers

        +
        + + + + $value) : ?> + + + + + + +
        + +
        + + +
        +

        System Configuration

        + + setData($config)->render('_config.tpl') ?> +
        +
        + diff --git a/system/Debug/Toolbar/Views/toolbarloader.js b/system/Debug/Toolbar/Views/toolbarloader.js new file mode 100644 index 0000000..a8bacb4 --- /dev/null +++ b/system/Debug/Toolbar/Views/toolbarloader.js @@ -0,0 +1,90 @@ +document.addEventListener('DOMContentLoaded', loadDoc, false); + +function loadDoc(time) { + if (isNaN(time)) { + time = document.getElementById("debugbar_loader").getAttribute("data-time"); + localStorage.setItem('debugbar-time', time); + } + + localStorage.setItem('debugbar-time-new', time); + + let url = '{url}'; + let xhttp = new XMLHttpRequest(); + + xhttp.onreadystatechange = function() { + if (this.readyState === 4 && this.status === 200) { + let toolbar = document.getElementById("toolbarContainer"); + + if (! toolbar) { + toolbar = document.createElement('div'); + toolbar.setAttribute('id', 'toolbarContainer'); + document.body.appendChild(toolbar); + } + + let responseText = this.responseText; + let dynamicStyle = document.getElementById('debugbar_dynamic_style'); + let dynamicScript = document.getElementById('debugbar_dynamic_script'); + + // get the first style block, copy contents to dynamic_style, then remove here + let start = responseText.indexOf('>', responseText.indexOf('', start); + dynamicStyle.innerHTML = responseText.substr(start, end - start); + responseText = responseText.substr(end + 8); + + // get the first script after the first style, copy contents to dynamic_script, then remove here + start = responseText.indexOf('>', responseText.indexOf('', start); + dynamicScript.innerHTML = responseText.substr(start, end - start); + responseText = responseText.substr(end + 9); + + // check for last style block, append contents to dynamic_style, then remove here + start = responseText.indexOf('>', responseText.indexOf('', start); + dynamicStyle.innerHTML += responseText.substr(start, end - start); + responseText = responseText.substr(0, start - 8); + + toolbar.innerHTML = responseText; + + if (typeof ciDebugBar === 'object') { + ciDebugBar.init(); + } + } else if (this.readyState === 4 && this.status === 404) { + console.log('CodeIgniter DebugBar: File "WRITEPATH/debugbar/debugbar_' + time + '" not found.'); + } + }; + + xhttp.open("GET", url + "?debugbar_time=" + time, true); + xhttp.send(); +} + +window.oldXHR = window.ActiveXObject + ? new ActiveXObject('Microsoft.XMLHTTP') + : window.XMLHttpRequest; + +function newXHR() { + const realXHR = new window.oldXHR(); + + realXHR.addEventListener("readystatechange", function() { + // Only success responses and URLs that do not contains "debugbar_time" are tracked + if (realXHR.readyState === 4 && realXHR.status.toString()[0] === '2' && realXHR.responseURL.indexOf('debugbar_time') === -1) { + if (realXHR.getAllResponseHeaders().indexOf("Debugbar-Time") >= 0) { + let debugbarTime = realXHR.getResponseHeader('Debugbar-Time'); + + if (debugbarTime) { + let h2 = document.querySelector('#ci-history > h2'); + + if (h2) { + h2.innerHTML = 'History You have new debug data. '; + document.querySelector('a[data-tab="ci-history"] > span > .badge').className += ' active'; + document.getElementById('ci-history-update').addEventListener('click', function () { + loadDoc(debugbarTime); + }, false) + } + } + } + } + }, false); + return realXHR; +} + +window.XMLHttpRequest = newXHR; diff --git a/system/Email/Email.php b/system/Email/Email.php new file mode 100644 index 0000000..b82779e --- /dev/null +++ b/system/Email/Email.php @@ -0,0 +1,2274 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Email; + +use CodeIgniter\Events\Events; +use CodeIgniter\I18n\Time; +use Config\Mimes; +use ErrorException; + +/** + * CodeIgniter Email Class + * + * Permits email to be sent using Mail, Sendmail, or SMTP. + * + * @see \CodeIgniter\Email\EmailTest + */ +class Email +{ + /** + * Properties from the last successful send. + * + * @var array|null + */ + public $archive; + + /** + * Properties to be added to the next archive. + * + * @var array + */ + protected $tmpArchive = []; + + /** + * @var string + */ + public $fromEmail; + + /** + * @var string + */ + public $fromName; + + /** + * Used as the User-Agent and X-Mailer headers' value. + * + * @var string + */ + public $userAgent = 'CodeIgniter'; + + /** + * Path to the Sendmail binary. + * + * @var string + */ + public $mailPath = '/usr/sbin/sendmail'; + + /** + * Which method to use for sending e-mails. + * + * @var string 'mail', 'sendmail' or 'smtp' + */ + public $protocol = 'mail'; + + /** + * STMP Server Hostname + * + * @var string + */ + public $SMTPHost = ''; + + /** + * SMTP Username + * + * @var string + */ + public $SMTPUser = ''; + + /** + * SMTP Password + * + * @var string + */ + public $SMTPPass = ''; + + /** + * SMTP Server port + * + * @var int + */ + public $SMTPPort = 25; + + /** + * SMTP connection timeout in seconds + * + * @var int + */ + public $SMTPTimeout = 5; + + /** + * SMTP persistent connection + * + * @var bool + */ + public $SMTPKeepAlive = false; + + /** + * SMTP Encryption + * + * @var string '', 'tls' or 'ssl'. 'tls' will issue a STARTTLS command + * to the server. 'ssl' means implicit SSL. Connection on port + * 465 should set this to ''. + */ + public $SMTPCrypto = ''; + + /** + * Whether to apply word-wrapping to the message body. + * + * @var bool + */ + public $wordWrap = true; + + /** + * Number of characters to wrap at. + * + * @see Email::$wordWrap + * + * @var int + */ + public $wrapChars = 76; + + /** + * Message format. + * + * @var string 'text' or 'html' + */ + public $mailType = 'text'; + + /** + * Character set (default: utf-8) + * + * @var string + */ + public $charset = 'UTF-8'; + + /** + * Alternative message (for HTML messages only) + * + * @var string + */ + public $altMessage = ''; + + /** + * Whether to validate e-mail addresses. + * + * @var bool + */ + public $validate = true; + + /** + * X-Priority header value. + * + * @var int 1-5 + */ + public $priority = 3; + + /** + * Newline character sequence. + * Use "\r\n" to comply with RFC 822. + * + * @see http://www.ietf.org/rfc/rfc822.txt + * + * @var string "\r\n" or "\n" + */ + public $newline = "\r\n"; + + /** + * CRLF character sequence + * + * RFC 2045 specifies that for 'quoted-printable' encoding, + * "\r\n" must be used. However, it appears that some servers + * (even on the receiving end) don't handle it properly and + * switching to "\n", while improper, is the only solution + * that seems to work for all environments. + * + * @see http://www.ietf.org/rfc/rfc822.txt + * + * @var string + */ + public $CRLF = "\r\n"; + + /** + * Whether to use Delivery Status Notification. + * + * @var bool + */ + public $DSN = false; + + /** + * Whether to send multipart alternatives. + * Yahoo! doesn't seem to like these. + * + * @var bool + */ + public $sendMultipart = true; + + /** + * Whether to send messages to BCC recipients in batches. + * + * @var bool + */ + public $BCCBatchMode = false; + + /** + * BCC Batch max number size. + * + * @see Email::$BCCBatchMode + * + * @var int|string + */ + public $BCCBatchSize = 200; + + /** + * Subject header + * + * @var string + */ + protected $subject = ''; + + /** + * Message body + * + * @var string + */ + protected $body = ''; + + /** + * Final message body to be sent. + * + * @var string + */ + protected $finalBody = ''; + + /** + * Final headers to send + * + * @var string + */ + protected $headerStr = ''; + + /** + * SMTP Connection socket placeholder + * + * @var resource|null + */ + protected $SMTPConnect; + + /** + * Mail encoding + * + * @var string '8bit' or '7bit' + */ + protected $encoding = '8bit'; + + /** + * Whether to perform SMTP authentication + * + * @var bool + */ + protected $SMTPAuth = false; + + /** + * Whether to send a Reply-To header + * + * @var bool + */ + protected $replyToFlag = false; + + /** + * Debug messages + * + * @see Email::printDebugger() + * + * @var array + */ + protected $debugMessage = []; + + /** + * Raw debug messages + * + * @var list + */ + private array $debugMessageRaw = []; + + /** + * Recipients + * + * @var array|string + */ + protected $recipients = []; + + /** + * CC Recipients + * + * @var array + */ + protected $CCArray = []; + + /** + * BCC Recipients + * + * @var array + */ + protected $BCCArray = []; + + /** + * Message headers + * + * @var array + */ + protected $headers = []; + + /** + * Attachment data + * + * @var array + */ + protected $attachments = []; + + /** + * Valid $protocol values + * + * @see Email::$protocol + * + * @var array + */ + protected $protocols = [ + 'mail', + 'sendmail', + 'smtp', + ]; + + /** + * Character sets valid for 7-bit encoding, + * excluding language suffix. + * + * @var list + */ + protected $baseCharsets = [ + 'us-ascii', + 'iso-2022-', + ]; + + /** + * Bit depths + * + * Valid mail encodings + * + * @see Email::$encoding + * + * @var array + */ + protected $bitDepths = [ + '7bit', + '8bit', + ]; + + /** + * $priority translations + * + * Actual values to send with the X-Priority header + * + * @var array + */ + protected $priorities = [ + 1 => '1 (Highest)', + 2 => '2 (High)', + 3 => '3 (Normal)', + 4 => '4 (Low)', + 5 => '5 (Lowest)', + ]; + + /** + * mbstring.func_overload flag + * + * @var bool + */ + protected static $func_overload; + + /** + * @param array|\Config\Email|null $config + */ + public function __construct($config = null) + { + $this->initialize($config); + if (! isset(static::$func_overload)) { + static::$func_overload = (extension_loaded('mbstring') && ini_get('mbstring.func_overload')); + } + } + + /** + * Initialize preferences + * + * @param array|\Config\Email|null $config + * + * @return Email + */ + public function initialize($config) + { + $this->clear(); + + if ($config instanceof \Config\Email) { + $config = get_object_vars($config); + } + + foreach (array_keys(get_class_vars(static::class)) as $key) { + if (property_exists($this, $key) && isset($config[$key])) { + $method = 'set' . ucfirst($key); + + if (method_exists($this, $method)) { + $this->{$method}($config[$key]); + } else { + $this->{$key} = $config[$key]; + } + } + } + + $this->charset = strtoupper($this->charset); + $this->SMTPAuth = isset($this->SMTPUser[0], $this->SMTPPass[0]); + + return $this; + } + + /** + * @param bool $clearAttachments + * + * @return Email + */ + public function clear($clearAttachments = false) + { + $this->subject = ''; + $this->body = ''; + $this->finalBody = ''; + $this->headerStr = ''; + $this->replyToFlag = false; + $this->recipients = []; + $this->CCArray = []; + $this->BCCArray = []; + $this->headers = []; + $this->debugMessage = []; + $this->debugMessageRaw = []; + + $this->setHeader('Date', $this->setDate()); + + if ($clearAttachments !== false) { + $this->attachments = []; + } + + return $this; + } + + /** + * @param string $from + * @param string $name + * @param string|null $returnPath Return-Path + * + * @return Email + */ + public function setFrom($from, $name = '', $returnPath = null) + { + if (preg_match('/\<(.*)\>/', $from, $match)) { + $from = $match[1]; + } + + if ($this->validate) { + $this->validateEmail($this->stringToArray($from)); + + if ($returnPath) { + $this->validateEmail($this->stringToArray($returnPath)); + } + } + + $this->tmpArchive['fromEmail'] = $from; + $this->tmpArchive['fromName'] = $name; + + if ($name !== '') { + // only use Q encoding if there are characters that would require it + if (! preg_match('/[\200-\377]/', $name)) { + $name = '"' . addcslashes($name, "\0..\37\177'\"\\") . '"'; + } else { + $name = $this->prepQEncoding($name); + } + } + + $this->setHeader('From', $name . ' <' . $from . '>'); + if (! isset($returnPath)) { + $returnPath = $from; + } + $this->setHeader('Return-Path', '<' . $returnPath . '>'); + $this->tmpArchive['returnPath'] = $returnPath; + + return $this; + } + + /** + * @param string $replyto + * @param string $name + * + * @return Email + */ + public function setReplyTo($replyto, $name = '') + { + if (preg_match('/\<(.*)\>/', $replyto, $match)) { + $replyto = $match[1]; + } + + if ($this->validate) { + $this->validateEmail($this->stringToArray($replyto)); + } + + if ($name !== '') { + $this->tmpArchive['replyName'] = $name; + + // only use Q encoding if there are characters that would require it + if (! preg_match('/[\200-\377]/', $name)) { + $name = '"' . addcslashes($name, "\0..\37\177'\"\\") . '"'; + } else { + $name = $this->prepQEncoding($name); + } + } + + $this->setHeader('Reply-To', $name . ' <' . $replyto . '>'); + $this->replyToFlag = true; + $this->tmpArchive['replyTo'] = $replyto; + + return $this; + } + + /** + * @param array|string $to + * + * @return Email + */ + public function setTo($to) + { + $to = $this->stringToArray($to); + $to = $this->cleanEmail($to); + + if ($this->validate) { + $this->validateEmail($to); + } + + if ($this->getProtocol() !== 'mail') { + $this->setHeader('To', implode(', ', $to)); + } + + $this->recipients = $to; + + return $this; + } + + /** + * @param string $cc + * + * @return Email + */ + public function setCC($cc) + { + $cc = $this->cleanEmail($this->stringToArray($cc)); + + if ($this->validate) { + $this->validateEmail($cc); + } + + $this->setHeader('Cc', implode(', ', $cc)); + + if ($this->getProtocol() === 'smtp') { + $this->CCArray = $cc; + } + + $this->tmpArchive['CCArray'] = $cc; + + return $this; + } + + /** + * @param string $bcc + * @param string $limit + * + * @return Email + */ + public function setBCC($bcc, $limit = '') + { + if ($limit !== '' && is_numeric($limit)) { + $this->BCCBatchMode = true; + $this->BCCBatchSize = $limit; + } + + $bcc = $this->cleanEmail($this->stringToArray($bcc)); + + if ($this->validate) { + $this->validateEmail($bcc); + } + + if ($this->getProtocol() === 'smtp' || ($this->BCCBatchMode && count($bcc) > $this->BCCBatchSize)) { + $this->BCCArray = $bcc; + } else { + $this->setHeader('Bcc', implode(', ', $bcc)); + $this->tmpArchive['BCCArray'] = $bcc; + } + + return $this; + } + + /** + * @param string $subject + * + * @return Email + */ + public function setSubject($subject) + { + $this->tmpArchive['subject'] = $subject; + + $subject = $this->prepQEncoding($subject); + $this->setHeader('Subject', $subject); + + return $this; + } + + /** + * @param string $body + * + * @return Email + */ + public function setMessage($body) + { + $this->body = rtrim(str_replace("\r", '', $body)); + + return $this; + } + + /** + * @param string $file Can be local path, URL or buffered content + * @param string $disposition 'attachment' + * @param string|null $newname + * @param string $mime + * + * @return bool|Email + */ + public function attach($file, $disposition = '', $newname = null, $mime = '') + { + if ($mime === '') { + if (! str_contains($file, '://') && ! is_file($file)) { + $this->setErrorMessage(lang('Email.attachmentMissing', [$file])); + + return false; + } + + if (! $fp = @fopen($file, 'rb')) { + $this->setErrorMessage(lang('Email.attachmentUnreadable', [$file])); + + return false; + } + + $fileContent = stream_get_contents($fp); + + $mime = $this->mimeTypes(pathinfo($file, PATHINFO_EXTENSION)); + + fclose($fp); + } else { + $fileContent = &$file; // buffered file + } + + // declare names on their own, to make phpcbf happy + $namesAttached = [$file, $newname]; + + $this->attachments[] = [ + 'name' => $namesAttached, + 'disposition' => empty($disposition) ? 'attachment' : $disposition, + // Can also be 'inline' Not sure if it matters + 'type' => $mime, + 'content' => chunk_split(base64_encode($fileContent)), + 'multipart' => 'mixed', + ]; + + return $this; + } + + /** + * Set and return attachment Content-ID + * Useful for attached inline pictures + * + * @param string $filename + * + * @return bool|string + */ + public function setAttachmentCID($filename) + { + foreach ($this->attachments as $i => $attachment) { + // For file path. + if ($attachment['name'][0] === $filename) { + $this->attachments[$i]['multipart'] = 'related'; + + $this->attachments[$i]['cid'] = uniqid(basename($attachment['name'][0]) . '@', true); + + return $this->attachments[$i]['cid']; + } + + // For buffer string. + if ($attachment['name'][1] === $filename) { + $this->attachments[$i]['multipart'] = 'related'; + + $this->attachments[$i]['cid'] = uniqid(basename($attachment['name'][1]) . '@', true); + + return $this->attachments[$i]['cid']; + } + } + + return false; + } + + /** + * @param string $header + * @param string $value + * + * @return Email + */ + public function setHeader($header, $value) + { + $this->headers[$header] = str_replace(["\n", "\r"], '', $value); + + return $this; + } + + /** + * @param array|string $email + * + * @return array + */ + protected function stringToArray($email) + { + if (! is_array($email)) { + return (str_contains($email, ',')) ? preg_split('/[\s,]/', $email, -1, PREG_SPLIT_NO_EMPTY) : (array) trim($email); + } + + return $email; + } + + /** + * @param string $str + * + * @return Email + */ + public function setAltMessage($str) + { + $this->altMessage = (string) $str; + + return $this; + } + + /** + * @param string $type + * + * @return Email + */ + public function setMailType($type = 'text') + { + $this->mailType = ($type === 'html') ? 'html' : 'text'; + + return $this; + } + + /** + * @param bool $wordWrap + * + * @return Email + */ + public function setWordWrap($wordWrap = true) + { + $this->wordWrap = (bool) $wordWrap; + + return $this; + } + + /** + * @param string $protocol + * + * @return Email + */ + public function setProtocol($protocol = 'mail') + { + $this->protocol = in_array($protocol, $this->protocols, true) ? strtolower($protocol) : 'mail'; + + return $this; + } + + /** + * @param int $n + * + * @return Email + */ + public function setPriority($n = 3) + { + $this->priority = preg_match('/^[1-5]$/', (string) $n) ? (int) $n : 3; + + return $this; + } + + /** + * @param string $newline + * + * @return Email + */ + public function setNewline($newline = "\n") + { + $this->newline = in_array($newline, ["\n", "\r\n", "\r"], true) ? $newline : "\n"; + + return $this; + } + + /** + * @param string $CRLF + * + * @return Email + */ + public function setCRLF($CRLF = "\n") + { + $this->CRLF = ($CRLF !== "\n" && $CRLF !== "\r\n" && $CRLF !== "\r") ? "\n" : $CRLF; + + return $this; + } + + /** + * @return string + */ + protected function getMessageID() + { + $from = str_replace(['>', '<'], '', $this->headers['Return-Path']); + + return '<' . uniqid('', true) . strstr($from, '@') . '>'; + } + + /** + * @return string + */ + protected function getProtocol() + { + $this->protocol = strtolower($this->protocol); + + if (! in_array($this->protocol, $this->protocols, true)) { + $this->protocol = 'mail'; + } + + return $this->protocol; + } + + /** + * @return string + */ + protected function getEncoding() + { + if (! in_array($this->encoding, $this->bitDepths, true)) { + $this->encoding = '8bit'; + } + + foreach ($this->baseCharsets as $charset) { + if (str_starts_with($this->charset, $charset)) { + $this->encoding = '7bit'; + + break; + } + } + + return $this->encoding; + } + + /** + * @return string + */ + protected function getContentType() + { + if ($this->mailType === 'html') { + return empty($this->attachments) ? 'html' : 'html-attach'; + } + + if ($this->mailType === 'text' && ! empty($this->attachments)) { + return 'plain-attach'; + } + + return 'plain'; + } + + /** + * Set RFC 822 Date + * + * @return string + */ + protected function setDate() + { + $timezone = date('Z'); + $operator = ($timezone[0] === '-') ? '-' : '+'; + $timezone = abs((int) $timezone); + $timezone = floor($timezone / 3600) * 100 + ($timezone % 3600) / 60; + + return sprintf('%s %s%04d', date('D, j M Y H:i:s'), $operator, $timezone); + } + + /** + * @return string + */ + protected function getMimeMessage() + { + return 'This is a multi-part message in MIME format.' . $this->newline . 'Your email application may not support this format.'; + } + + /** + * @param array|string $email + * + * @return bool + */ + public function validateEmail($email) + { + if (! is_array($email)) { + $this->setErrorMessage(lang('Email.mustBeArray')); + + return false; + } + + foreach ($email as $val) { + if (! $this->isValidEmail($val)) { + $this->setErrorMessage(lang('Email.invalidAddress', [$val])); + + return false; + } + } + + return true; + } + + /** + * @param string $email + * + * @return bool + */ + public function isValidEmail($email) + { + if (function_exists('idn_to_ascii') && defined('INTL_IDNA_VARIANT_UTS46') && $atpos = strpos($email, '@')) { + $email = static::substr($email, 0, ++$atpos) + . idn_to_ascii(static::substr($email, $atpos), 0, INTL_IDNA_VARIANT_UTS46); + } + + return (bool) filter_var($email, FILTER_VALIDATE_EMAIL); + } + + /** + * @param array|string $email + * + * @return array|string + */ + public function cleanEmail($email) + { + if (! is_array($email)) { + return preg_match('/\<(.*)\>/', $email, $match) ? $match[1] : $email; + } + + $cleanEmail = []; + + foreach ($email as $addy) { + $cleanEmail[] = preg_match('/\<(.*)\>/', $addy, $match) ? $match[1] : $addy; + } + + return $cleanEmail; + } + + /** + * Build alternative plain text message + * + * Provides the raw message for use in plain-text headers of + * HTML-formatted emails. + * + * If the user hasn't specified his own alternative message + * it creates one by stripping the HTML + * + * @return string + */ + protected function getAltMessage() + { + if (! empty($this->altMessage)) { + return ($this->wordWrap) ? $this->wordWrap($this->altMessage, 76) : $this->altMessage; + } + + $body = preg_match('/\(.*)\<\/body\>/si', $this->body, $match) ? $match[1] : $this->body; + $body = str_replace("\t", '', preg_replace('# [Entity] --- (2) --> [Database] + * [App Code] <-- (4) --- [Entity] <-- (3) --- [Database] + */ +interface CastInterface +{ + /** + * Takes a raw value from Entity, returns its value for PHP. + * + * @param array|bool|float|int|object|string|null $value Data + * @param array $params Additional param + * + * @return array|bool|float|int|object|string|null + */ + public static function get($value, array $params = []); + + /** + * Takes a PHP value, returns its raw value for Entity. + * + * @param array|bool|float|int|object|string|null $value Data + * @param array $params Additional param + * + * @return array|bool|float|int|object|string|null + */ + public static function set($value, array $params = []); +} diff --git a/system/Entity/Cast/DatetimeCast.php b/system/Entity/Cast/DatetimeCast.php new file mode 100644 index 0000000..2d01ad7 --- /dev/null +++ b/system/Entity/Cast/DatetimeCast.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Entity\Cast; + +use CodeIgniter\I18n\Time; +use DateTime; +use Exception; + +/** + * Class DatetimeCast + */ +class DatetimeCast extends BaseCast +{ + /** + * {@inheritDoc} + * + * @return Time + * + * @throws Exception + */ + public static function get($value, array $params = []) + { + if ($value instanceof Time) { + return $value; + } + + if ($value instanceof DateTime) { + return Time::createFromInstance($value); + } + + if (is_numeric($value)) { + return Time::createFromTimestamp((int) $value); + } + + if (is_string($value)) { + return Time::parse($value); + } + + return $value; + } +} diff --git a/system/Entity/Cast/FloatCast.php b/system/Entity/Cast/FloatCast.php new file mode 100644 index 0000000..642e849 --- /dev/null +++ b/system/Entity/Cast/FloatCast.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 CodeIgniter\Entity\Cast; + +/** + * Class FloatCast + */ +class FloatCast extends BaseCast +{ + /** + * {@inheritDoc} + */ + public static function get($value, array $params = []): float + { + return (float) $value; + } +} diff --git a/system/Entity/Cast/IntBoolCast.php b/system/Entity/Cast/IntBoolCast.php new file mode 100644 index 0000000..fb1c470 --- /dev/null +++ b/system/Entity/Cast/IntBoolCast.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 CodeIgniter\Entity\Cast; + +/** + * Int Bool Cast + * + * DB column: int (0/1) <--> Class property: bool + */ +final class IntBoolCast extends BaseCast +{ + /** + * @param int $value + */ + public static function get($value, array $params = []): bool + { + return (bool) $value; + } + + /** + * @param bool|int|string $value + */ + public static function set($value, array $params = []): int + { + return (int) $value; + } +} diff --git a/system/Entity/Cast/IntegerCast.php b/system/Entity/Cast/IntegerCast.php new file mode 100644 index 0000000..c0ecec7 --- /dev/null +++ b/system/Entity/Cast/IntegerCast.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 CodeIgniter\Entity\Cast; + +/** + * Class IntegerCast + */ +class IntegerCast extends BaseCast +{ + /** + * {@inheritDoc} + */ + public static function get($value, array $params = []): int + { + return (int) $value; + } +} diff --git a/system/Entity/Cast/JsonCast.php b/system/Entity/Cast/JsonCast.php new file mode 100644 index 0000000..ef38926 --- /dev/null +++ b/system/Entity/Cast/JsonCast.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 CodeIgniter\Entity\Cast; + +use CodeIgniter\Entity\Exceptions\CastException; +use JsonException; +use stdClass; + +/** + * Class JsonCast + */ +class JsonCast extends BaseCast +{ + /** + * {@inheritDoc} + */ + public static function get($value, array $params = []) + { + $associative = in_array('array', $params, true); + + $tmp = $value !== null ? ($associative ? [] : new stdClass()) : null; + + if (function_exists('json_decode') + && ( + (is_string($value) + && strlen($value) > 1 + && in_array($value[0], ['[', '{', '"'], true)) + || is_numeric($value) + ) + ) { + try { + $tmp = json_decode($value, $associative, 512, JSON_THROW_ON_ERROR); + } catch (JsonException $e) { + throw CastException::forInvalidJsonFormat($e->getCode()); + } + } + + return $tmp; + } + + /** + * {@inheritDoc} + */ + public static function set($value, array $params = []): string + { + if (function_exists('json_encode')) { + try { + $value = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); + } catch (JsonException $e) { + throw CastException::forInvalidJsonFormat($e->getCode()); + } + } + + return $value; + } +} diff --git a/system/Entity/Cast/ObjectCast.php b/system/Entity/Cast/ObjectCast.php new file mode 100644 index 0000000..1bee213 --- /dev/null +++ b/system/Entity/Cast/ObjectCast.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 CodeIgniter\Entity\Cast; + +/** + * Class ObjectCast + */ +class ObjectCast extends BaseCast +{ + /** + * {@inheritDoc} + */ + public static function get($value, array $params = []): object + { + return (object) $value; + } +} diff --git a/system/Entity/Cast/StringCast.php b/system/Entity/Cast/StringCast.php new file mode 100644 index 0000000..e4ed04b --- /dev/null +++ b/system/Entity/Cast/StringCast.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 CodeIgniter\Entity\Cast; + +/** + * Class StringCast + */ +class StringCast extends BaseCast +{ + /** + * {@inheritDoc} + */ + public static function get($value, array $params = []): string + { + return (string) $value; + } +} diff --git a/system/Entity/Cast/TimestampCast.php b/system/Entity/Cast/TimestampCast.php new file mode 100644 index 0000000..a445bad --- /dev/null +++ b/system/Entity/Cast/TimestampCast.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Entity\Cast; + +use CodeIgniter\Entity\Exceptions\CastException; + +/** + * Class TimestampCast + */ +class TimestampCast extends BaseCast +{ + /** + * {@inheritDoc} + */ + public static function get($value, array $params = []) + { + $value = strtotime($value); + + if ($value === false) { + throw CastException::forInvalidTimestamp(); + } + + return $value; + } +} diff --git a/system/Entity/Cast/URICast.php b/system/Entity/Cast/URICast.php new file mode 100644 index 0000000..d0d510b --- /dev/null +++ b/system/Entity/Cast/URICast.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 CodeIgniter\Entity\Cast; + +use CodeIgniter\HTTP\URI; + +/** + * Class URICast + */ +class URICast extends BaseCast +{ + /** + * {@inheritDoc} + */ + public static function get($value, array $params = []): URI + { + return $value instanceof URI ? $value : new URI($value); + } +} diff --git a/system/Entity/Entity.php b/system/Entity/Entity.php new file mode 100644 index 0000000..5de8a42 --- /dev/null +++ b/system/Entity/Entity.php @@ -0,0 +1,572 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Entity; + +use CodeIgniter\DataCaster\DataCaster; +use CodeIgniter\Entity\Cast\ArrayCast; +use CodeIgniter\Entity\Cast\BooleanCast; +use CodeIgniter\Entity\Cast\CSVCast; +use CodeIgniter\Entity\Cast\DatetimeCast; +use CodeIgniter\Entity\Cast\FloatCast; +use CodeIgniter\Entity\Cast\IntBoolCast; +use CodeIgniter\Entity\Cast\IntegerCast; +use CodeIgniter\Entity\Cast\JsonCast; +use CodeIgniter\Entity\Cast\ObjectCast; +use CodeIgniter\Entity\Cast\StringCast; +use CodeIgniter\Entity\Cast\TimestampCast; +use CodeIgniter\Entity\Cast\URICast; +use CodeIgniter\Entity\Exceptions\CastException; +use CodeIgniter\I18n\Time; +use DateTime; +use Exception; +use JsonSerializable; +use ReturnTypeWillChange; + +/** + * Entity encapsulation, for use with CodeIgniter\Model + * + * @see \CodeIgniter\Entity\EntityTest + */ +class Entity implements JsonSerializable +{ + /** + * Maps names used in sets and gets against unique + * names within the class, allowing independence from + * database column names. + * + * Example: + * $datamap = [ + * 'class_property_name' => 'db_column_name' + * ]; + * + * @var array + */ + protected $datamap = []; + + /** + * The date fields. + * + * @var list + */ + protected $dates = [ + 'created_at', + 'updated_at', + 'deleted_at', + ]; + + /** + * Array of field names and the type of value to cast them as when + * they are accessed. + * + * @var array + */ + protected $casts = []; + + /** + * Custom convert handlers + * + * @var array + */ + protected $castHandlers = []; + + /** + * Default convert handlers + * + * @var array + */ + private array $defaultCastHandlers = [ + 'array' => ArrayCast::class, + 'bool' => BooleanCast::class, + 'boolean' => BooleanCast::class, + 'csv' => CSVCast::class, + 'datetime' => DatetimeCast::class, + 'double' => FloatCast::class, + 'float' => FloatCast::class, + 'int' => IntegerCast::class, + 'integer' => IntegerCast::class, + 'int-bool' => IntBoolCast::class, + 'json' => JsonCast::class, + 'object' => ObjectCast::class, + 'string' => StringCast::class, + 'timestamp' => TimestampCast::class, + 'uri' => URICast::class, + ]; + + /** + * Holds the current values of all class vars. + * + * @var array + */ + protected $attributes = []; + + /** + * Holds original copies of all class vars so we can determine + * what's actually been changed and not accidentally write + * nulls where we shouldn't. + * + * @var array + */ + protected $original = []; + + /** + * The data caster. + */ + protected DataCaster $dataCaster; + + /** + * Holds info whenever properties have to be casted + */ + private bool $_cast = true; + + /** + * Allows filling in Entity parameters during construction. + */ + public function __construct(?array $data = null) + { + $this->dataCaster = new DataCaster( + array_merge($this->defaultCastHandlers, $this->castHandlers), + null, + null, + false + ); + + $this->syncOriginal(); + + $this->fill($data); + } + + /** + * Takes an array of key/value pairs and sets them as class + * properties, using any `setCamelCasedProperty()` methods + * that may or may not exist. + * + * @param array $data + * + * @return $this + */ + public function fill(?array $data = null) + { + if (! is_array($data)) { + return $this; + } + + foreach ($data as $key => $value) { + $this->__set($key, $value); + } + + return $this; + } + + /** + * General method that will return all public and protected values + * of this entity as an array. All values are accessed through the + * __get() magic method so will have any casts, etc applied to them. + * + * @param bool $onlyChanged If true, only return values that have changed since object creation + * @param bool $cast If true, properties will be cast. + * @param bool $recursive If true, inner entities will be cast as array as well. + */ + public function toArray(bool $onlyChanged = false, bool $cast = true, bool $recursive = false): array + { + $this->_cast = $cast; + + $keys = array_filter(array_keys($this->attributes), static fn ($key) => ! str_starts_with($key, '_')); + + if (is_array($this->datamap)) { + $keys = array_unique( + [...array_diff($keys, $this->datamap), ...array_keys($this->datamap)] + ); + } + + $return = []; + + // Loop over the properties, to allow magic methods to do their thing. + foreach ($keys as $key) { + if ($onlyChanged && ! $this->hasChanged($key)) { + continue; + } + + $return[$key] = $this->__get($key); + + if ($recursive) { + if ($return[$key] instanceof self) { + $return[$key] = $return[$key]->toArray($onlyChanged, $cast, $recursive); + } elseif (is_callable([$return[$key], 'toArray'])) { + $return[$key] = $return[$key]->toArray(); + } + } + } + + $this->_cast = true; + + return $return; + } + + /** + * Returns the raw values of the current attributes. + * + * @param bool $onlyChanged If true, only return values that have changed since object creation + * @param bool $recursive If true, inner entities will be cast as array as well. + */ + public function toRawArray(bool $onlyChanged = false, bool $recursive = false): array + { + $return = []; + + if (! $onlyChanged) { + if ($recursive) { + return array_map(static function ($value) use ($onlyChanged, $recursive) { + if ($value instanceof self) { + $value = $value->toRawArray($onlyChanged, $recursive); + } elseif (is_callable([$value, 'toRawArray'])) { + $value = $value->toRawArray(); + } + + return $value; + }, $this->attributes); + } + + return $this->attributes; + } + + foreach ($this->attributes as $key => $value) { + if (! $this->hasChanged($key)) { + continue; + } + + if ($recursive) { + if ($value instanceof self) { + $value = $value->toRawArray($onlyChanged, $recursive); + } elseif (is_callable([$value, 'toRawArray'])) { + $value = $value->toRawArray(); + } + } + + $return[$key] = $value; + } + + return $return; + } + + /** + * Ensures our "original" values match the current values. + * + * @return $this + */ + public function syncOriginal() + { + $this->original = $this->attributes; + + return $this; + } + + /** + * Checks a property to see if it has changed since the entity + * was created. Or, without a parameter, checks if any + * properties have changed. + * + * @param string|null $key class property + */ + public function hasChanged(?string $key = null): bool + { + // If no parameter was given then check all attributes + if ($key === null) { + return $this->original !== $this->attributes; + } + + $dbColumn = $this->mapProperty($key); + + // Key doesn't exist in either + if (! array_key_exists($dbColumn, $this->original) && ! array_key_exists($dbColumn, $this->attributes)) { + return false; + } + + // It's a new element + if (! array_key_exists($dbColumn, $this->original) && array_key_exists($dbColumn, $this->attributes)) { + return true; + } + + return $this->original[$dbColumn] !== $this->attributes[$dbColumn]; + } + + /** + * Set raw data array without any mutations + * + * @return $this + */ + public function injectRawData(array $data) + { + $this->attributes = $data; + + $this->syncOriginal(); + + return $this; + } + + /** + * Set raw data array without any mutations + * + * @return $this + * + * @deprecated Use injectRawData() instead. + */ + public function setAttributes(array $data) + { + return $this->injectRawData($data); + } + + /** + * Checks the datamap to see if this property name is being mapped, + * and returns the db column name, if any, or the original property name. + * + * @return string db column name + */ + protected function mapProperty(string $key) + { + if ($this->datamap === []) { + return $key; + } + + if (! empty($this->datamap[$key])) { + return $this->datamap[$key]; + } + + return $key; + } + + /** + * Converts the given string|timestamp|DateTime|Time instance + * into the "CodeIgniter\I18n\Time" object. + * + * @param DateTime|float|int|string|Time $value + * + * @return Time + * + * @throws Exception + */ + protected function mutateDate($value) + { + return DatetimeCast::get($value); + } + + /** + * Provides the ability to cast an item as a specific data type. + * Add ? at the beginning of the type (i.e. ?string) to get `null` + * instead of casting $value when $value is null. + * + * @param bool|float|int|string|null $value Attribute value + * @param string $attribute Attribute name + * @param string $method Allowed to "get" and "set" + * + * @return array|bool|float|int|object|string|null + * + * @throws CastException + */ + protected function castAs($value, string $attribute, string $method = 'get') + { + return $this->dataCaster + // @TODO if $casts is readonly, we don't need the setTypes() method. + ->setTypes($this->casts) + ->castAs($value, $attribute, $method); + } + + /** + * Support for json_encode() + * + * @return array + */ + #[ReturnTypeWillChange] + public function jsonSerialize() + { + return $this->toArray(); + } + + /** + * Change the value of the private $_cast property + * + * @return bool|Entity + */ + public function cast(?bool $cast = null) + { + if ($cast === null) { + return $this->_cast; + } + + $this->_cast = $cast; + + return $this; + } + + /** + * Magic method to all protected/private class properties to be + * easily set, either through a direct access or a + * `setCamelCasedProperty()` method. + * + * Examples: + * $this->my_property = $p; + * $this->setMyProperty() = $p; + * + * @param array|bool|float|int|object|string|null $value + * + * @return void + * + * @throws Exception + */ + public function __set(string $key, $value = null) + { + $dbColumn = $this->mapProperty($key); + + // Check if the field should be mutated into a date + if (in_array($dbColumn, $this->dates, true)) { + $value = $this->mutateDate($value); + } + + $value = $this->castAs($value, $dbColumn, 'set'); + + // if a setter method exists for this key, use that method to + // insert this value. should be outside $isNullable check, + // so maybe wants to do sth with null value automatically + $method = 'set' . str_replace(' ', '', ucwords(str_replace(['-', '_'], ' ', $dbColumn))); + + // If a "`_set` + $key" method exists, it is a setter. + if (method_exists($this, '_' . $method)) { + $this->{'_' . $method}($value); + + return; + } + + // If a "`set` + $key" method exists, it is also a setter. + if (method_exists($this, $method) && $method !== 'setAttributes') { + $this->{$method}($value); + + return; + } + + // Otherwise, just the value. This allows for creation of new + // class properties that are undefined, though they cannot be + // saved. Useful for grabbing values through joins, assigning + // relationships, etc. + $this->attributes[$dbColumn] = $value; + } + + /** + * Magic method to allow retrieval of protected and private class properties + * either by their name, or through a `getCamelCasedProperty()` method. + * + * Examples: + * $p = $this->my_property + * $p = $this->getMyProperty() + * + * @return array|bool|float|int|object|string|null + * + * @throws Exception + * + * @params string $key class property + */ + public function __get(string $key) + { + $dbColumn = $this->mapProperty($key); + + $result = null; + + // Convert to CamelCase for the method + $method = 'get' . str_replace(' ', '', ucwords(str_replace(['-', '_'], ' ', $dbColumn))); + + // if a getter method exists for this key, + // use that method to insert this value. + if (method_exists($this, '_' . $method)) { + // If a "`_get` + $key" method exists, it is a getter. + $result = $this->{'_' . $method}(); + } elseif (method_exists($this, $method)) { + // If a "`get` + $key" method exists, it is also a getter. + $result = $this->{$method}(); + } + + // Otherwise return the protected property + // if it exists. + elseif (array_key_exists($dbColumn, $this->attributes)) { + $result = $this->attributes[$dbColumn]; + } + + // Do we need to mutate this into a date? + if (in_array($dbColumn, $this->dates, true)) { + $result = $this->mutateDate($result); + } + // Or cast it as something? + elseif ($this->_cast) { + $result = $this->castAs($result, $dbColumn); + } + + return $result; + } + + /** + * Returns true if a property exists names $key, or a getter method + * exists named like for __get(). + */ + public function __isset(string $key): bool + { + if ($this->isMappedDbColumn($key)) { + return false; + } + + $dbColumn = $this->mapProperty($key); + + $method = 'get' . str_replace(' ', '', ucwords(str_replace(['-', '_'], ' ', $dbColumn))); + + if (method_exists($this, $method)) { + return true; + } + + return isset($this->attributes[$dbColumn]); + } + + /** + * Unsets an attribute property. + */ + public function __unset(string $key): void + { + if ($this->isMappedDbColumn($key)) { + return; + } + + $dbColumn = $this->mapProperty($key); + + unset($this->attributes[$dbColumn]); + } + + /** + * Whether this key is mapped db column name? + */ + protected function isMappedDbColumn(string $key): bool + { + $dbColumn = $this->mapProperty($key); + + // The $key is a property name which has mapped db column name + if ($key !== $dbColumn) { + return false; + } + + return $this->hasMappedProperty($key); + } + + /** + * Whether this key has mapped property? + */ + protected function hasMappedProperty(string $key): bool + { + $property = array_search($key, $this->datamap, true); + + return $property !== false; + } +} diff --git a/system/Entity/Exceptions/CastException.php b/system/Entity/Exceptions/CastException.php new file mode 100644 index 0000000..90d3885 --- /dev/null +++ b/system/Entity/Exceptions/CastException.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 CodeIgniter\Entity\Exceptions; + +use CodeIgniter\Exceptions\FrameworkException; +use CodeIgniter\Exceptions\HasExitCodeInterface; + +/** + * CastException is thrown for invalid cast initialization and management. + */ +class CastException extends FrameworkException implements HasExitCodeInterface +{ + public function getExitCode(): int + { + return EXIT_CONFIG; + } + + /** + * Thrown when the cast class does not extends BaseCast. + * + * @return static + */ + public static function forInvalidInterface(string $class) + { + return new static(lang('Cast.baseCastMissing', [$class])); + } + + /** + * Thrown when the Json format is invalid. + * + * @return static + */ + public static function forInvalidJsonFormat(int $error) + { + return match ($error) { + JSON_ERROR_DEPTH => new static(lang('Cast.jsonErrorDepth')), + JSON_ERROR_STATE_MISMATCH => new static(lang('Cast.jsonErrorStateMismatch')), + JSON_ERROR_CTRL_CHAR => new static(lang('Cast.jsonErrorCtrlChar')), + JSON_ERROR_SYNTAX => new static(lang('Cast.jsonErrorSyntax')), + JSON_ERROR_UTF8 => new static(lang('Cast.jsonErrorUtf8')), + default => new static(lang('Cast.jsonErrorUnknown')), + }; + } + + /** + * Thrown when the cast method is not `get` or `set`. + * + * @return static + */ + public static function forInvalidMethod(string $method) + { + return new static(lang('Cast.invalidCastMethod', [$method])); + } + + /** + * Thrown when the casting timestamp is not correct timestamp. + * + * @return static + */ + public static function forInvalidTimestamp() + { + return new static(lang('Cast.invalidTimestamp')); + } +} diff --git a/system/Events/Events.php b/system/Events/Events.php new file mode 100644 index 0000000..a06bd79 --- /dev/null +++ b/system/Events/Events.php @@ -0,0 +1,285 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Events; + +use Config\Modules; + +/** + * Events + * + * @see \CodeIgniter\Events\EventsTest + */ +class Events +{ + public const PRIORITY_LOW = 200; + public const PRIORITY_NORMAL = 100; + public const PRIORITY_HIGH = 10; + + /** + * The list of listeners. + * + * @var array + */ + protected static $listeners = []; + + /** + * Flag to let us know if we've read from the Config file(s) + * and have all of the defined events. + * + * @var bool + */ + protected static $initialized = false; + + /** + * If true, events will not actually be fired. + * Useful during testing. + * + * @var bool + */ + protected static $simulate = false; + + /** + * Stores information about the events + * for display in the debug toolbar. + * + * @var list> + */ + protected static $performanceLog = []; + + /** + * A list of found files. + * + * @var list + */ + protected static $files = []; + + /** + * Ensures that we have a events file ready. + * + * @return void + */ + public static function initialize() + { + // Don't overwrite anything.... + if (static::$initialized) { + return; + } + + $config = config(Modules::class); + $events = APPPATH . 'Config' . DIRECTORY_SEPARATOR . 'Events.php'; + $files = []; + + if ($config->shouldDiscover('events')) { + $files = service('locator')->search('Config/Events.php'); + } + + $files = array_filter(array_map(static function (string $file) { + if (is_file($file)) { + return realpath($file) ?: $file; + } + + return false; // @codeCoverageIgnore + }, $files)); + + static::$files = array_unique(array_merge($files, [$events])); + + foreach (static::$files as $file) { + include $file; + } + + static::$initialized = true; + } + + /** + * Registers an action to happen on an event. The action can be any sort + * of callable: + * + * Events::on('create', 'myFunction'); // procedural function + * Events::on('create', ['myClass', 'myMethod']); // Class::method + * Events::on('create', [$myInstance, 'myMethod']); // Method on an existing instance + * Events::on('create', function() {}); // Closure + * + * @param string $eventName + * @param callable $callback + * @param int $priority + * + * @return void + */ + public static function on($eventName, $callback, $priority = self::PRIORITY_NORMAL) + { + if (! isset(static::$listeners[$eventName])) { + static::$listeners[$eventName] = [ + true, // If there's only 1 item, it's sorted. + [$priority], + [$callback], + ]; + } else { + static::$listeners[$eventName][0] = false; // Not sorted + static::$listeners[$eventName][1][] = $priority; + static::$listeners[$eventName][2][] = $callback; + } + } + + /** + * Runs through all subscribed methods running them one at a time, + * until either: + * a) All subscribers have finished or + * b) a method returns false, at which point execution of subscribers stops. + * + * @param string $eventName + * @param mixed $arguments + */ + public static function trigger($eventName, ...$arguments): bool + { + // Read in our Config/Events file so that we have them all! + if (! static::$initialized) { + static::initialize(); + } + + $listeners = static::listeners($eventName); + + foreach ($listeners as $listener) { + $start = microtime(true); + + $result = static::$simulate === false ? $listener(...$arguments) : true; + + if (CI_DEBUG) { + static::$performanceLog[] = [ + 'start' => $start, + 'end' => microtime(true), + 'event' => strtolower($eventName), + ]; + } + + if ($result === false) { + return false; + } + } + + return true; + } + + /** + * Returns an array of listeners for a single event. They are + * sorted by priority. + * + * @param string $eventName + */ + public static function listeners($eventName): array + { + if (! isset(static::$listeners[$eventName])) { + return []; + } + + // The list is not sorted + if (! static::$listeners[$eventName][0]) { + // Sort it! + array_multisort(static::$listeners[$eventName][1], SORT_NUMERIC, static::$listeners[$eventName][2]); + + // Mark it as sorted already! + static::$listeners[$eventName][0] = true; + } + + return static::$listeners[$eventName][2]; + } + + /** + * Removes a single listener from an event. + * + * If the listener couldn't be found, returns FALSE, else TRUE if + * it was removed. + * + * @param string $eventName + */ + public static function removeListener($eventName, callable $listener): bool + { + if (! isset(static::$listeners[$eventName])) { + return false; + } + + foreach (static::$listeners[$eventName][2] as $index => $check) { + if ($check === $listener) { + unset( + static::$listeners[$eventName][1][$index], + static::$listeners[$eventName][2][$index] + ); + + return true; + } + } + + return false; + } + + /** + * Removes all listeners. + * + * If the event_name is specified, only listeners for that event will be + * removed, otherwise all listeners for all events are removed. + * + * @param string|null $eventName + * + * @return void + */ + public static function removeAllListeners($eventName = null) + { + if ($eventName !== null) { + unset(static::$listeners[$eventName]); + } else { + static::$listeners = []; + } + } + + /** + * Sets the path to the file that routes are read from. + * + * @return void + */ + public static function setFiles(array $files) + { + static::$files = $files; + } + + /** + * Returns the files that were found/loaded during this request. + * + * @return list + */ + public static function getFiles() + { + return static::$files; + } + + /** + * Turns simulation on or off. When on, events will not be triggered, + * simply logged. Useful during testing when you don't actually want + * the tests to run. + * + * @return void + */ + public static function simulate(bool $choice = true) + { + static::$simulate = $choice; + } + + /** + * Getter for the performance log records. + * + * @return list> + */ + public static function getPerformanceLogs() + { + return static::$performanceLog; + } +} diff --git a/system/Exceptions/ConfigException.php b/system/Exceptions/ConfigException.php new file mode 100644 index 0000000..d8849b8 --- /dev/null +++ b/system/Exceptions/ConfigException.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Exceptions; + +/** + * Exception for automatic logging. + */ +class ConfigException extends CriticalError implements HasExitCodeInterface +{ + use DebugTraceableTrait; + + public function getExitCode(): int + { + return EXIT_CONFIG; + } + + /** + * @return static + */ + public static function forDisabledMigrations() + { + return new static(lang('Migrations.disabled')); + } +} diff --git a/system/Exceptions/CriticalError.php b/system/Exceptions/CriticalError.php new file mode 100644 index 0000000..756393d --- /dev/null +++ b/system/Exceptions/CriticalError.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Exceptions; + +use Error; + +/** + * Error: Critical conditions, like component unavailable, etc. + */ +class CriticalError extends Error +{ +} diff --git a/system/Exceptions/DebugTraceableTrait.php b/system/Exceptions/DebugTraceableTrait.php new file mode 100644 index 0000000..e8f204a --- /dev/null +++ b/system/Exceptions/DebugTraceableTrait.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Exceptions; + +use Throwable; + +/** + * This trait provides framework exceptions the ability to pinpoint + * accurately where the exception was raised rather than instantiated. + * + * This is used primarily for factory-instantiated exceptions. + */ +trait DebugTraceableTrait +{ + /** + * Tweaks the exception's constructor to assign the file/line to where + * it is actually raised rather than were it is instantiated. + */ + final public function __construct(string $message = '', int $code = 0, ?Throwable $previous = null) + { + parent::__construct($message, $code, $previous); + + $trace = $this->getTrace()[0]; + + if (isset($trace['class']) && $trace['class'] === static::class) { + [ + 'line' => $this->line, + 'file' => $this->file, + ] = $trace; + } + } +} diff --git a/system/Exceptions/DownloadException.php b/system/Exceptions/DownloadException.php new file mode 100644 index 0000000..df78127 --- /dev/null +++ b/system/Exceptions/DownloadException.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 CodeIgniter\Exceptions; + +use RuntimeException; + +/** + * Class DownloadException + */ +class DownloadException extends RuntimeException implements ExceptionInterface +{ + use DebugTraceableTrait; + + /** + * @return static + */ + public static function forCannotSetFilePath(string $path) + { + return new static(lang('HTTP.cannotSetFilepath', [$path])); + } + + /** + * @return static + */ + public static function forCannotSetBinary() + { + return new static(lang('HTTP.cannotSetBinary')); + } + + /** + * @return static + */ + public static function forNotFoundDownloadSource() + { + return new static(lang('HTTP.notFoundDownloadSource')); + } + + /** + * @return static + */ + public static function forCannotSetCache() + { + return new static(lang('HTTP.cannotSetCache')); + } + + /** + * @return static + */ + public static function forCannotSetStatusCode(int $code, string $reason) + { + return new static(lang('HTTP.cannotSetStatusCode', [$code, $reason])); + } +} diff --git a/system/Exceptions/ExceptionInterface.php b/system/Exceptions/ExceptionInterface.php new file mode 100644 index 0000000..27b9c2f --- /dev/null +++ b/system/Exceptions/ExceptionInterface.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 CodeIgniter\Exceptions; + +/** + * Provides a domain-level interface for broad capture + * of all framework-related exceptions. + * + * catch (\CodeIgniter\Exceptions\ExceptionInterface) { ... } + */ +interface ExceptionInterface +{ +} diff --git a/system/Exceptions/FrameworkException.php b/system/Exceptions/FrameworkException.php new file mode 100644 index 0000000..1fe47be --- /dev/null +++ b/system/Exceptions/FrameworkException.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 CodeIgniter\Exceptions; + +use RuntimeException; + +/** + * Class FrameworkException + * + * A collection of exceptions thrown by the framework + * that can only be determined at run time. + */ +class FrameworkException extends RuntimeException implements ExceptionInterface +{ + use DebugTraceableTrait; + + /** + * @return static + */ + public static function forEnabledZlibOutputCompression() + { + return new static(lang('Core.enabledZlibOutputCompression')); + } + + /** + * @return static + */ + public static function forInvalidFile(string $path) + { + return new static(lang('Core.invalidFile', [$path])); + } + + /** + * @return static + */ + public static function forInvalidDirectory(string $path) + { + return new static(lang('Core.invalidDirectory', [$path])); + } + + /** + * @return static + */ + public static function forCopyError(string $path) + { + return new static(lang('Core.copyError', [$path])); + } + + /** + * @return static + * + * @deprecated 4.5.0 No longer used. + */ + public static function forMissingExtension(string $extension) + { + if (str_contains($extension, 'intl')) { + // @codeCoverageIgnoreStart + $message = sprintf( + 'The framework needs the following extension(s) installed and loaded: %s.', + $extension + ); + // @codeCoverageIgnoreEnd + } else { + $message = lang('Core.missingExtension', [$extension]); + } + + return new static($message); + } + + /** + * @return static + */ + public static function forNoHandlers(string $class) + { + return new static(lang('Core.noHandlers', [$class])); + } + + /** + * @return static + */ + public static function forFabricatorCreateFailed(string $table, string $reason) + { + return new static(lang('Fabricator.createFailed', [$table, $reason])); + } +} diff --git a/system/Exceptions/HTTPExceptionInterface.php b/system/Exceptions/HTTPExceptionInterface.php new file mode 100644 index 0000000..1974d63 --- /dev/null +++ b/system/Exceptions/HTTPExceptionInterface.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 CodeIgniter\Exceptions; + +/** + * Interface for Exceptions that has exception code as HTTP status code. + */ +interface HTTPExceptionInterface +{ +} diff --git a/system/Exceptions/HasExitCodeInterface.php b/system/Exceptions/HasExitCodeInterface.php new file mode 100644 index 0000000..1557c82 --- /dev/null +++ b/system/Exceptions/HasExitCodeInterface.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Exceptions; + +/** + * Interface for Exceptions that has exception code as exit code. + */ +interface HasExitCodeInterface +{ + /** + * Returns exit status code. + */ + public function getExitCode(): int; +} diff --git a/system/Exceptions/ModelException.php b/system/Exceptions/ModelException.php new file mode 100644 index 0000000..a92e388 --- /dev/null +++ b/system/Exceptions/ModelException.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 CodeIgniter\Exceptions; + +/** + * Model Exceptions. + */ +class ModelException extends FrameworkException +{ + /** + * @return static + */ + public static function forNoPrimaryKey(string $modelName) + { + return new static(lang('Database.noPrimaryKey', [$modelName])); + } + + /** + * @return static + */ + public static function forNoDateFormat(string $modelName) + { + return new static(lang('Database.noDateFormat', [$modelName])); + } + + /** + * @return static + */ + public static function forMethodNotAvailable(string $modelName, string $methodName) + { + return new static(lang('Database.methodNotAvailable', [$modelName, $methodName])); + } +} diff --git a/system/Exceptions/PageNotFoundException.php b/system/Exceptions/PageNotFoundException.php new file mode 100644 index 0000000..b1af079 --- /dev/null +++ b/system/Exceptions/PageNotFoundException.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Exceptions; + +use Config\Services; +use OutOfBoundsException; + +class PageNotFoundException extends OutOfBoundsException implements ExceptionInterface, HTTPExceptionInterface +{ + use DebugTraceableTrait; + + /** + * HTTP status code + * + * @var int + */ + protected $code = 404; + + /** + * @return static + */ + public static function forPageNotFound(?string $message = null) + { + return new static($message ?? self::lang('HTTP.pageNotFound')); + } + + /** + * @return static + */ + public static function forEmptyController() + { + return new static(self::lang('HTTP.emptyController')); + } + + /** + * @return static + */ + public static function forControllerNotFound(string $controller, string $method) + { + return new static(self::lang('HTTP.controllerNotFound', [$controller, $method])); + } + + /** + * @return static + */ + public static function forMethodNotFound(string $method) + { + return new static(self::lang('HTTP.methodNotFound', [$method])); + } + + /** + * @return static + */ + public static function forLocaleNotSupported(string $locale) + { + return new static(self::lang('HTTP.localeNotSupported', [$locale])); + } + + /** + * Get translated system message + * + * Use a non-shared Language instance in the Services. + * If a shared instance is created, the Language will + * have the current locale, so even if users call + * `$this->request->setLocale()` in the controller afterwards, + * the Language locale will not be changed. + */ + private static function lang(string $line, array $args = []): string + { + $lang = Services::language(null, false); + + return $lang->getLine($line, $args); + } +} diff --git a/system/Exceptions/TestException.php b/system/Exceptions/TestException.php new file mode 100644 index 0000000..f533dc2 --- /dev/null +++ b/system/Exceptions/TestException.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 CodeIgniter\Exceptions; + +/** + * Exception for automatic logging. + */ +class TestException extends CriticalError +{ + use DebugTraceableTrait; + + /** + * @return static + */ + public static function forInvalidMockClass(string $name) + { + return new static(lang('Test.invalidMockClass', [$name])); + } +} diff --git a/system/Files/Exceptions/FileException.php b/system/Files/Exceptions/FileException.php new file mode 100644 index 0000000..5feb979 --- /dev/null +++ b/system/Files/Exceptions/FileException.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 CodeIgniter\Files\Exceptions; + +use CodeIgniter\Exceptions\DebugTraceableTrait; +use CodeIgniter\Exceptions\ExceptionInterface; +use RuntimeException; + +class FileException extends RuntimeException implements ExceptionInterface +{ + use DebugTraceableTrait; + + /** + * @return static + */ + public static function forUnableToMove(?string $from = null, ?string $to = null, ?string $error = null) + { + return new static(lang('Files.cannotMove', [$from, $to, $error])); + } + + /** + * Throws when an item is expected to be a directory but is not or is missing. + * + * @param string $caller The method causing the exception + * + * @return static + */ + public static function forExpectedDirectory(string $caller) + { + return new static(lang('Files.expectedDirectory', [$caller])); + } + + /** + * Throws when an item is expected to be a file but is not or is missing. + * + * @param string $caller The method causing the exception + * + * @return static + */ + public static function forExpectedFile(string $caller) + { + return new static(lang('Files.expectedFile', [$caller])); + } +} diff --git a/system/Files/Exceptions/FileNotFoundException.php b/system/Files/Exceptions/FileNotFoundException.php new file mode 100644 index 0000000..86b2262 --- /dev/null +++ b/system/Files/Exceptions/FileNotFoundException.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 CodeIgniter\Files\Exceptions; + +use CodeIgniter\Exceptions\DebugTraceableTrait; +use CodeIgniter\Exceptions\ExceptionInterface; +use RuntimeException; + +class FileNotFoundException extends RuntimeException implements ExceptionInterface +{ + use DebugTraceableTrait; + + /** + * @return static + */ + public static function forFileNotFound(string $path) + { + return new static(lang('Files.fileNotFound', [$path])); + } +} diff --git a/system/Files/File.php b/system/Files/File.php new file mode 100644 index 0000000..15b5326 --- /dev/null +++ b/system/Files/File.php @@ -0,0 +1,192 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Files; + +use CodeIgniter\Files\Exceptions\FileException; +use CodeIgniter\Files\Exceptions\FileNotFoundException; +use CodeIgniter\I18n\Time; +use Config\Mimes; +use ReturnTypeWillChange; +use SplFileInfo; + +/** + * Wrapper for PHP's built-in SplFileInfo, with goodies. + * + * @see \CodeIgniter\Files\FileTest + */ +class File extends SplFileInfo +{ + /** + * The files size in bytes + * + * @var int + */ + protected $size; + + /** + * @var string|null + */ + protected $originalMimeType; + + /** + * Run our SplFileInfo constructor with an optional verification + * that the path is really a file. + * + * @throws FileNotFoundException + */ + public function __construct(string $path, bool $checkFile = false) + { + if ($checkFile && ! is_file($path)) { + throw FileNotFoundException::forFileNotFound($path); + } + + parent::__construct($path); + } + + /** + * Retrieve the file size. + * + * Implementations SHOULD return the value stored in the "size" key of + * the file in the $_FILES array if available, as PHP calculates this based + * on the actual size transmitted. A RuntimeException will be thrown if the file + * does not exist or an error occurs. + * + * @return false|int The file size in bytes, or false on failure + */ + #[ReturnTypeWillChange] + public function getSize() + { + return $this->size ?? ($this->size = parent::getSize()); + } + + /** + * Retrieve the file size by unit. + * + * @return false|int|string + */ + public function getSizeByUnit(string $unit = 'b') + { + return match (strtolower($unit)) { + 'kb' => number_format($this->getSize() / 1024, 3), + 'mb' => number_format(($this->getSize() / 1024) / 1024, 3), + default => $this->getSize(), + }; + } + + /** + * Attempts to determine the file extension based on the trusted + * getType() method. If the mime type is unknown, will return null. + */ + public function guessExtension(): ?string + { + // naively get the path extension using pathinfo + $pathinfo = pathinfo($this->getRealPath() ?: $this->__toString()) + ['extension' => '']; + + $proposedExtension = $pathinfo['extension']; + + return Mimes::guessExtensionFromType($this->getMimeType(), $proposedExtension); + } + + /** + * Retrieve the media type of the file. SHOULD not use information from + * the $_FILES array, but should use other methods to more accurately + * determine the type of file, like finfo, or mime_content_type(). + * + * @return string The media type we determined it to be. + */ + public function getMimeType(): string + { + if (! function_exists('finfo_open')) { + return $this->originalMimeType ?? 'application/octet-stream'; // @codeCoverageIgnore + } + + $finfo = finfo_open(FILEINFO_MIME_TYPE); + $mimeType = finfo_file($finfo, $this->getRealPath() ?: $this->__toString()); + finfo_close($finfo); + + return $mimeType; + } + + /** + * Generates a random names based on a simple hash and the time, with + * the correct file extension attached. + */ + public function getRandomName(): string + { + $extension = $this->getExtension(); + $extension = empty($extension) ? '' : '.' . $extension; + + return Time::now()->getTimestamp() . '_' . bin2hex(random_bytes(10)) . $extension; + } + + /** + * Moves a file to a new location. + * + * @return File + */ + public function move(string $targetPath, ?string $name = null, bool $overwrite = false) + { + $targetPath = rtrim($targetPath, '/') . '/'; + $name ??= $this->getBasename(); + $destination = $overwrite ? $targetPath . $name : $this->getDestination($targetPath . $name); + + $oldName = $this->getRealPath() ?: $this->__toString(); + + if (! @rename($oldName, $destination)) { + $error = error_get_last(); + + throw FileException::forUnableToMove($this->getBasename(), $targetPath, strip_tags($error['message'])); + } + + @chmod($destination, 0777 & ~umask()); + + return new self($destination); + } + + /** + * Returns the destination path for the move operation where overwriting is not expected. + * + * First, it checks whether the delimiter is present in the filename, if it is, then it checks whether the + * last element is an integer as there may be cases that the delimiter may be present in the filename. + * For the all other cases, it appends an integer starting from zero before the file's extension. + */ + public function getDestination(string $destination, string $delimiter = '_', int $i = 0): string + { + if ($delimiter === '') { + $delimiter = '_'; + } + + while (is_file($destination)) { + $info = pathinfo($destination); + $extension = isset($info['extension']) ? '.' . $info['extension'] : ''; + + if (str_contains($info['filename'], $delimiter)) { + $parts = explode($delimiter, $info['filename']); + + if (is_numeric(end($parts))) { + $i = end($parts); + array_pop($parts); + $parts[] = ++$i; + $destination = $info['dirname'] . DIRECTORY_SEPARATOR . implode($delimiter, $parts) . $extension; + } else { + $destination = $info['dirname'] . DIRECTORY_SEPARATOR . $info['filename'] . $delimiter . ++$i . $extension; + } + } else { + $destination = $info['dirname'] . DIRECTORY_SEPARATOR . $info['filename'] . $delimiter . ++$i . $extension; + } + } + + return $destination; + } +} diff --git a/system/Files/FileCollection.php b/system/Files/FileCollection.php new file mode 100644 index 0000000..f131079 --- /dev/null +++ b/system/Files/FileCollection.php @@ -0,0 +1,370 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Files; + +use CodeIgniter\Files\Exceptions\FileException; +use CodeIgniter\Files\Exceptions\FileNotFoundException; +use Countable; +use Generator; +use InvalidArgumentException; +use IteratorAggregate; + +/** + * File Collection Class + * + * Representation for a group of files, with utilities for locating, + * filtering, and ordering them. + * + * @template-implements IteratorAggregate + * @see \CodeIgniter\Files\FileCollectionTest + */ +class FileCollection implements Countable, IteratorAggregate +{ + /** + * The current list of file paths. + * + * @var list + */ + protected $files = []; + + // -------------------------------------------------------------------- + // Support Methods + // -------------------------------------------------------------------- + + /** + * Resolves a full path and verifies it is an actual directory. + * + * @throws FileException + */ + final protected static function resolveDirectory(string $directory): string + { + if (! is_dir($directory = set_realpath($directory))) { + $caller = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]; + + throw FileException::forExpectedDirectory($caller['function']); + } + + return $directory; + } + + /** + * Resolves a full path and verifies it is an actual file. + * + * @throws FileException + */ + final protected static function resolveFile(string $file): string + { + if (! is_file($file = set_realpath($file))) { + $caller = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]; + + throw FileException::forExpectedFile($caller['function']); + } + + return $file; + } + + /** + * Removes files that are not part of the given directory (recursive). + * + * @param list $files + * + * @return list + */ + final protected static function filterFiles(array $files, string $directory): array + { + $directory = self::resolveDirectory($directory); + + return array_filter($files, static fn (string $value): bool => str_starts_with($value, $directory)); + } + + /** + * Returns any files whose `basename` matches the given pattern. + * + * @param list $files + * @param string $pattern Regex or pseudo-regex string + * + * @return list + */ + final protected static function matchFiles(array $files, string $pattern): array + { + // Convert pseudo-regex into their true form + if (@preg_match($pattern, '') === false) { + $pattern = str_replace( + ['#', '.', '*', '?'], + ['\#', '\.', '.*', '.'], + $pattern + ); + $pattern = "#\\A{$pattern}\\z#"; + } + + return array_filter($files, static fn ($value) => (bool) preg_match($pattern, basename($value))); + } + + // -------------------------------------------------------------------- + // Class Core + // -------------------------------------------------------------------- + + /** + * Loads the Filesystem helper and adds any initial files. + * + * @param list $files + */ + public function __construct(array $files = []) + { + helper(['filesystem']); + + $this->add($files)->define(); + } + + /** + * Applies any initial inputs after the constructor. + * This method is a stub to be implemented by child classes. + */ + protected function define(): void + { + } + + /** + * Optimizes and returns the current file list. + * + * @return list + */ + public function get(): array + { + $this->files = array_unique($this->files); + sort($this->files, SORT_STRING); + + return $this->files; + } + + /** + * Sets the file list directly, files are still subject to verification. + * This works as a "reset" method with []. + * + * @param list $files The new file list to use + * + * @return $this + */ + public function set(array $files) + { + $this->files = []; + + return $this->addFiles($files); + } + + /** + * Adds an array/single file or directory to the list. + * + * @param list|string $paths + * + * @return $this + */ + public function add($paths, bool $recursive = true) + { + $paths = (array) $paths; + + foreach ($paths as $path) { + if (! is_string($path)) { + throw new InvalidArgumentException('FileCollection paths must be strings.'); + } + + try { + // Test for a directory + self::resolveDirectory($path); + } catch (FileException) { + $this->addFile($path); + + continue; + } + + $this->addDirectory($path, $recursive); + } + + return $this; + } + + // -------------------------------------------------------------------- + // File Handling + // -------------------------------------------------------------------- + + /** + * Verifies and adds files to the list. + * + * @param list $files + * + * @return $this + */ + public function addFiles(array $files) + { + foreach ($files as $file) { + $this->addFile($file); + } + + return $this; + } + + /** + * Verifies and adds a single file to the file list. + * + * @return $this + */ + public function addFile(string $file) + { + $this->files[] = self::resolveFile($file); + + return $this; + } + + /** + * Removes files from the list. + * + * @param list $files + * + * @return $this + */ + public function removeFiles(array $files) + { + $this->files = array_diff($this->files, $files); + + return $this; + } + + /** + * Removes a single file from the list. + * + * @return $this + */ + public function removeFile(string $file) + { + return $this->removeFiles([$file]); + } + + // -------------------------------------------------------------------- + // Directory Handling + // -------------------------------------------------------------------- + + /** + * Verifies and adds files from each + * directory to the list. + * + * @param list $directories + * + * @return $this + */ + public function addDirectories(array $directories, bool $recursive = false) + { + foreach ($directories as $directory) { + $this->addDirectory($directory, $recursive); + } + + return $this; + } + + /** + * Verifies and adds all files from a directory. + * + * @return $this + */ + public function addDirectory(string $directory, bool $recursive = false) + { + $directory = self::resolveDirectory($directory); + + // Map the directory to depth 2 to so directories become arrays + foreach (directory_map($directory, 2, true) as $key => $path) { + if (is_string($path)) { + $this->addFile($directory . $path); + } elseif ($recursive && is_array($path)) { + $this->addDirectory($directory . $key, true); + } + } + + return $this; + } + + // -------------------------------------------------------------------- + // Filtering + // -------------------------------------------------------------------- + + /** + * Removes any files from the list that match the supplied pattern + * (within the optional scope). + * + * @param string $pattern Regex or pseudo-regex string + * @param string|null $scope The directory to limit the scope + * + * @return $this + */ + public function removePattern(string $pattern, ?string $scope = null) + { + if ($pattern === '') { + return $this; + } + + // Start with all files or those in scope + $files = $scope === null ? $this->files : self::filterFiles($this->files, $scope); + + // Remove any files that match the pattern + return $this->removeFiles(self::matchFiles($files, $pattern)); + } + + /** + * Keeps only the files from the list that match + * (within the optional scope). + * + * @param string $pattern Regex or pseudo-regex string + * @param string|null $scope A directory to limit the scope + * + * @return $this + */ + public function retainPattern(string $pattern, ?string $scope = null) + { + if ($pattern === '') { + return $this; + } + + // Start with all files or those in scope + $files = $scope === null ? $this->files : self::filterFiles($this->files, $scope); + + // Matches the pattern within the scoped files and remove their inverse. + return $this->removeFiles(array_diff($files, self::matchFiles($files, $pattern))); + } + + // -------------------------------------------------------------------- + // Interface Methods + // -------------------------------------------------------------------- + + /** + * Returns the current number of files in the collection. + * Fulfills Countable. + */ + public function count(): int + { + return count($this->files); + } + + /** + * Yields as an Iterator for the current files. + * Fulfills IteratorAggregate. + * + * @return Generator + * + * @throws FileNotFoundException + */ + public function getIterator(): Generator + { + foreach ($this->get() as $file) { + yield new File($file, true); + } + } +} diff --git a/system/Filters/CSRF.php b/system/Filters/CSRF.php new file mode 100644 index 0000000..90ccb9b --- /dev/null +++ b/system/Filters/CSRF.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 CodeIgniter\Filters; + +use CodeIgniter\HTTP\IncomingRequest; +use CodeIgniter\HTTP\RedirectResponse; +use CodeIgniter\HTTP\RequestInterface; +use CodeIgniter\HTTP\ResponseInterface; +use CodeIgniter\Security\Exceptions\SecurityException; + +/** + * CSRF filter. + * + * This filter is not intended to be used from the command line. + * + * @codeCoverageIgnore + * @see \CodeIgniter\Filters\CSRFTest + */ +class CSRF implements FilterInterface +{ + /** + * Do whatever processing this filter needs to do. + * By default it should not return anything during + * normal execution. However, when an abnormal state + * is found, it should return an instance of + * CodeIgniter\HTTP\Response. If it does, script + * execution will end and that Response will be + * sent back to the client, allowing for error pages, + * redirects, etc. + * + * @param list|null $arguments + * + * @return RedirectResponse|void + * + * @throws SecurityException + */ + public function before(RequestInterface $request, $arguments = null) + { + if (! $request instanceof IncomingRequest) { + return; + } + + $security = service('security'); + + try { + $security->verify($request); + } catch (SecurityException $e) { + if ($security->shouldRedirect() && ! $request->isAJAX()) { + return redirect()->back()->with('error', $e->getMessage()); + } + + throw $e; + } + } + + /** + * We don't have anything to do here. + * + * @param list|null $arguments + * + * @return void + */ + public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) + { + } +} diff --git a/system/Filters/Cors.php b/system/Filters/Cors.php new file mode 100644 index 0000000..93ca551 --- /dev/null +++ b/system/Filters/Cors.php @@ -0,0 +1,111 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Filters; + +use CodeIgniter\HTTP\Cors as CorsService; +use CodeIgniter\HTTP\IncomingRequest; +use CodeIgniter\HTTP\RequestInterface; +use CodeIgniter\HTTP\ResponseInterface; + +/** + * @see \CodeIgniter\Filters\CorsTest + */ +class Cors implements FilterInterface +{ + private ?CorsService $cors = null; + + /** + * @testTag $config is used for testing purposes only. + * + * @param array{ + * allowedOrigins?: list, + * allowedOriginsPatterns?: list, + * supportsCredentials?: bool, + * allowedHeaders?: list, + * exposedHeaders?: list, + * allowedMethods?: list, + * maxAge?: int, + * } $config + */ + public function __construct(array $config = []) + { + if ($config !== []) { + $this->cors = new CorsService($config); + } + } + + /** + * @param list|null $arguments + * + * @return ResponseInterface|string|void + */ + public function before(RequestInterface $request, $arguments = null) + { + if (! $request instanceof IncomingRequest) { + return; + } + + $this->createCorsService($arguments); + + if (! $this->cors->isPreflightRequest($request)) { + return; + } + + /** @var ResponseInterface $response */ + $response = service('response'); + + $response = $this->cors->handlePreflightRequest($request, $response); + + // Always adds `Vary: Access-Control-Request-Method` header for cacheability. + // If there is an intermediate cache server such as a CDN, if a plain + // OPTIONS request is sent, it may be cached. But valid preflight requests + // have this header, so it will be cached separately. + $response->appendHeader('Vary', 'Access-Control-Request-Method'); + + return $response; + } + + /** + * @param list|null $arguments + */ + private function createCorsService(?array $arguments): void + { + $this->cors ??= ($arguments === null) ? CorsService::factory() + : CorsService::factory($arguments[0]); + } + + /** + * @param list|null $arguments + * + * @return ResponseInterface|void + */ + public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) + { + if (! $request instanceof IncomingRequest) { + return; + } + + $this->createCorsService($arguments); + + // Always adds `Vary: Access-Control-Request-Method` header for cacheability. + // If there is an intermediate cache server such as a CDN, if a plain + // OPTIONS request is sent, it may be cached. But valid preflight requests + // have this header, so it will be cached separately. + if ($request->is('OPTIONS')) { + $response->appendHeader('Vary', 'Access-Control-Request-Method'); + } + + return $this->cors->addResponseHeaders($request, $response); + } +} diff --git a/system/Filters/DebugToolbar.php b/system/Filters/DebugToolbar.php new file mode 100644 index 0000000..9f864ee --- /dev/null +++ b/system/Filters/DebugToolbar.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 CodeIgniter\Filters; + +use CodeIgniter\HTTP\RequestInterface; +use CodeIgniter\HTTP\ResponseInterface; + +/** + * Debug toolbar filter + * + * @see \CodeIgniter\Filters\DebugToolbarTest + */ +class DebugToolbar implements FilterInterface +{ + /** + * We don't need to do anything here. + * + * @param list|null $arguments + */ + public function before(RequestInterface $request, $arguments = null) + { + } + + /** + * If the debug flag is set (CI_DEBUG) then collect performance + * and debug information and display it in a toolbar. + * + * @param list|null $arguments + */ + public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) + { + service('toolbar')->prepare($request, $response); + } +} diff --git a/system/Filters/Exceptions/FilterException.php b/system/Filters/Exceptions/FilterException.php new file mode 100644 index 0000000..1226ba3 --- /dev/null +++ b/system/Filters/Exceptions/FilterException.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 CodeIgniter\Filters\Exceptions; + +use CodeIgniter\Exceptions\ConfigException; +use CodeIgniter\Exceptions\ExceptionInterface; + +/** + * FilterException + */ +class FilterException extends ConfigException implements ExceptionInterface +{ + /** + * Thrown when the provided alias is not within + * the list of configured filter aliases. + * + * @return static + */ + public static function forNoAlias(string $alias) + { + return new static(lang('Filters.noFilter', [$alias])); + } + + /** + * Thrown when the filter class does not implement FilterInterface. + * + * @return static + */ + public static function forIncorrectInterface(string $class) + { + return new static(lang('Filters.incorrectInterface', [$class])); + } +} diff --git a/system/Filters/FilterInterface.php b/system/Filters/FilterInterface.php new file mode 100644 index 0000000..0353fb2 --- /dev/null +++ b/system/Filters/FilterInterface.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 CodeIgniter\Filters; + +use CodeIgniter\HTTP\RequestInterface; +use CodeIgniter\HTTP\ResponseInterface; + +/** + * Filter interface + */ +interface FilterInterface +{ + /** + * Do whatever processing this filter needs to do. + * By default it should not return anything during + * normal execution. However, when an abnormal state + * is found, it should return an instance of + * CodeIgniter\HTTP\Response. If it does, script + * execution will end and that Response will be + * sent back to the client, allowing for error pages, + * redirects, etc. + * + * @param list|null $arguments + * + * @return RequestInterface|ResponseInterface|string|void + */ + public function before(RequestInterface $request, $arguments = null); + + /** + * Allows After filters to inspect and modify the response + * object as needed. This method does not allow any way + * to stop execution of other after filters, short of + * throwing an Exception or Error. + * + * @param list|null $arguments + * + * @return ResponseInterface|void + */ + public function after(RequestInterface $request, ResponseInterface $response, $arguments = null); +} diff --git a/system/Filters/Filters.php b/system/Filters/Filters.php new file mode 100644 index 0000000..1255e0c --- /dev/null +++ b/system/Filters/Filters.php @@ -0,0 +1,869 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Filters; + +use CodeIgniter\Config\Filters as BaseFiltersConfig; +use CodeIgniter\Exceptions\ConfigException; +use CodeIgniter\Filters\Exceptions\FilterException; +use CodeIgniter\HTTP\RequestInterface; +use CodeIgniter\HTTP\ResponseInterface; +use Config\Feature; +use Config\Filters as FiltersConfig; +use Config\Modules; + +/** + * Filters + * + * @see \CodeIgniter\Filters\FiltersTest + */ +class Filters +{ + /** + * The original config file + * + * @var FiltersConfig + */ + protected $config; + + /** + * The active IncomingRequest or CLIRequest + * + * @var RequestInterface + */ + protected $request; + + /** + * The active Response instance + * + * @var ResponseInterface + */ + protected $response; + + /** + * Handle to the modules config. + * + * @var Modules + */ + protected $modules; + + /** + * Whether we've done initial processing + * on the filter lists. + * + * @var bool + */ + protected $initialized = false; + + /** + * The processed filters that will + * be used to check against. + * + * This does not include "Required Filters". + * + * @var array + */ + protected $filters = [ + 'before' => [], + 'after' => [], + ]; + + /** + * The collection of filters' class names that will + * be used to execute in each position. + * + * This does not include "Required Filters". + * + * @var array + */ + protected $filtersClass = [ + 'before' => [], + 'after' => [], + ]; + + /** + * Any arguments to be passed to filters. + * + * @var array|null> [name => params] + */ + protected $arguments = []; + + /** + * Any arguments to be passed to filtersClass. + * + * @var array|null> [classname => arguments] + */ + protected $argumentsClass = []; + + /** + * Constructor. + * + * @param FiltersConfig $config + */ + public function __construct($config, RequestInterface $request, ResponseInterface $response, ?Modules $modules = null) + { + $this->config = $config; + $this->request = &$request; + $this->setResponse($response); + + $this->modules = $modules ?? config(Modules::class); + + if ($this->modules->shouldDiscover('filters')) { + $this->discoverFilters(); + } + } + + /** + * If discoverFilters is enabled in Config then system will try to + * auto-discover custom filters files in Namespaces and allow access to + * the config object via the variable $filters as with the routes file + * + * Sample : + * $filters->aliases['custom-auth'] = \Acme\Blob\Filters\BlobAuth::class; + * + * @deprecated 4.4.2 Use Registrar instead. + */ + private function discoverFilters(): void + { + $locator = service('locator'); + + // for access by custom filters + $filters = $this->config; + + $files = $locator->search('Config/Filters.php'); + + foreach ($files as $file) { + // The $file may not be a class file. + $className = $locator->getClassname($file); + + // Don't include our main Filter config again... + if ($className === FiltersConfig::class || $className === BaseFiltersConfig::class) { + continue; + } + + include $file; + } + } + + /** + * Set the response explicitly. + * + * @return void + */ + public function setResponse(ResponseInterface $response) + { + $this->response = $response; + } + + /** + * Runs through all of the filters for the specified + * uri and position. + * + * @param string $uri URI path relative to baseURL + * @phpstan-param 'before'|'after' $position + * + * @return RequestInterface|ResponseInterface|string|null + * + * @throws FilterException + */ + public function run(string $uri, string $position = 'before') + { + $this->initialize(strtolower($uri)); + + if ($position === 'before') { + return $this->runBefore($this->filtersClass[$position]); + } + + // After + return $this->runAfter($this->filtersClass[$position]); + } + + /** + * @return RequestInterface|ResponseInterface|string + */ + private function runBefore(array $filterClasses) + { + foreach ($filterClasses as $className) { + $class = new $className(); + + if (! $class instanceof FilterInterface) { + throw FilterException::forIncorrectInterface($class::class); + } + + $result = $class->before( + $this->request, + $this->argumentsClass[$className] ?? null + ); + + if ($result instanceof RequestInterface) { + $this->request = $result; + + continue; + } + + // If the response object was sent back, + // then send it and quit. + if ($result instanceof ResponseInterface) { + // short circuit - bypass any other filters + return $result; + } + + // Ignore an empty result + if (empty($result)) { + continue; + } + + return $result; + } + + return $this->request; + } + + private function runAfter(array $filterClasses): ResponseInterface + { + foreach ($filterClasses as $className) { + $class = new $className(); + + if (! $class instanceof FilterInterface) { + throw FilterException::forIncorrectInterface($class::class); + } + + $result = $class->after( + $this->request, + $this->response, + $this->argumentsClass[$className] ?? null + ); + + if ($result instanceof ResponseInterface) { + $this->response = $result; + + continue; + } + } + + return $this->response; + } + + /** + * Runs "Required Filters" for the specified position. + * + * @return RequestInterface|ResponseInterface|string|null + * + * @phpstan-param 'before'|'after' $position + * + * @throws FilterException + * + * @internal + */ + public function runRequired(string $position = 'before') + { + [$filters, $aliases] = $this->getRequiredFilters($position); + + if ($filters === []) { + return $position === 'before' ? $this->request : $this->response; + } + + $filterClasses = []; + + foreach ($filters as $alias) { + if (is_array($aliases[$alias])) { + $filterClasses[$position] = array_merge($filterClasses[$position], $aliases[$alias]); + } else { + $filterClasses[$position][] = $aliases[$alias]; + } + } + + if ($position === 'before') { + return $this->runBefore($filterClasses[$position]); + } + + // After + return $this->runAfter($filterClasses[$position]); + } + + /** + * Returns "Required Filters" for the specified position. + * + * @phpstan-param 'before'|'after' $position + * + * @internal + */ + public function getRequiredFilters(string $position = 'before'): array + { + // For backward compatibility. For users who do not update Config\Filters. + if (! isset($this->config->required[$position])) { + $baseConfig = config(BaseFiltersConfig::class); // @phpstan-ignore-line + $filters = $baseConfig->required[$position]; + $aliases = $baseConfig->aliases; + } else { + $filters = $this->config->required[$position]; + $aliases = $this->config->aliases; + } + + if ($filters === []) { + return [[], $aliases]; + } + + if ($position === 'after') { + if (in_array('toolbar', $this->filters['after'], true)) { + // It was already run in globals filters. So remove it. + $filters = $this->setToolbarToLast($filters, true); + } else { + // Set the toolbar filter to the last position to be executed + $filters = $this->setToolbarToLast($filters); + } + } + + foreach ($filters as $alias) { + if (! array_key_exists($alias, $aliases)) { + throw FilterException::forNoAlias($alias); + } + } + + return [$filters, $aliases]; + } + + /** + * Set the toolbar filter to the last position to be executed. + * + * @param list $filters `after` filter array + * @param bool $remove if true, remove `toolbar` filter + */ + private function setToolbarToLast(array $filters, bool $remove = false): array + { + $afters = []; + $found = false; + + foreach ($filters as $alias) { + if ($alias === 'toolbar') { + $found = true; + + continue; + } + + $afters[] = $alias; + } + + if ($found && ! $remove) { + $afters[] = 'toolbar'; + } + + return $afters; + } + + /** + * Runs through our list of filters provided by the configuration + * object to get them ready for use, including getting uri masks + * to proper regex, removing those we can from the possibilities + * based on HTTP method, etc. + * + * The resulting $this->filters is an array of only filters + * that should be applied to this request. + * + * We go ahead and process the entire tree because we'll need to + * run through both a before and after and don't want to double + * process the rows. + * + * @param string|null $uri URI path relative to baseURL (all lowercase) + * + * @TODO We don't need to accept null as $uri. + * + * @return Filters + */ + public function initialize(?string $uri = null) + { + if ($this->initialized === true) { + return $this; + } + + // Decode URL-encoded string + $uri = urldecode($uri ?? ''); + + $oldFilterOrder = config(Feature::class)->oldFilterOrder ?? false; + if ($oldFilterOrder) { + $this->processGlobals($uri); + $this->processMethods(); + $this->processFilters($uri); + } else { + $this->processFilters($uri); + $this->processMethods(); + $this->processGlobals($uri); + } + + // Set the toolbar filter to the last position to be executed + $this->filters['after'] = $this->setToolbarToLast($this->filters['after']); + + $this->processAliasesToClass('before'); + $this->processAliasesToClass('after'); + + $this->initialized = true; + + return $this; + } + + /** + * Restores instance to its pre-initialized state. + * Most useful for testing so the service can be + * re-initialized to a different path. + */ + public function reset(): self + { + $this->initialized = false; + + $this->arguments = $this->argumentsClass = []; + + $this->filters = $this->filtersClass = [ + 'before' => [], + 'after' => [], + ]; + + return $this; + } + + /** + * Returns the processed filters array. + * This does not include "Required Filters". + */ + public function getFilters(): array + { + return $this->filters; + } + + /** + * Returns the filtersClass array. + * This does not include "Required Filters". + */ + public function getFiltersClass(): array + { + return $this->filtersClass; + } + + /** + * Adds a new alias to the config file. + * MUST be called prior to initialize(); + * Intended for use within routes files. + * + * @return $this + */ + public function addFilter(string $class, ?string $alias = null, string $when = 'before', string $section = 'globals') + { + $alias ??= md5($class); + + if (! isset($this->config->{$section})) { + $this->config->{$section} = []; + } + + if (! isset($this->config->{$section}[$when])) { + $this->config->{$section}[$when] = []; + } + + $this->config->aliases[$alias] = $class; + + $this->config->{$section}[$when][] = $alias; + + return $this; + } + + /** + * Ensures that a specific filter is on and enabled for the current request. + * + * Filters can have "arguments". This is done by placing a colon immediately + * after the filter name, followed by a comma-separated list of arguments that + * are passed to the filter when executed. + * + * @param string $name filter_name or filter_name:arguments like 'role:admin,manager' + */ + private function enableFilter(string $name, string $when = 'before'): void + { + // Get arguments and clean name + [$name, $arguments] = $this->getCleanName($name); + $this->arguments[$name] = ($arguments !== []) ? $arguments : null; + + if (class_exists($name)) { + $this->config->aliases[$name] = $name; + } elseif (! array_key_exists($name, $this->config->aliases)) { + throw FilterException::forNoAlias($name); + } + + $classNames = (array) $this->config->aliases[$name]; + + foreach ($classNames as $className) { + $this->argumentsClass[$className] = $this->arguments[$name] ?? null; + } + + if (! isset($this->filters[$when][$name])) { + $this->filters[$when][] = $name; + $this->filtersClass[$when] = array_merge($this->filtersClass[$when], $classNames); + } + } + + /** + * Get clean name and arguments + * + * @param string $name filter_name or filter_name:arguments like 'role:admin,manager' + * + * @return array{0: string, 1: list} [name, arguments] + */ + private function getCleanName(string $name): array + { + $arguments = []; + + if (str_contains($name, ':')) { + [$name, $arguments] = explode(':', $name); + + $arguments = explode(',', $arguments); + array_walk($arguments, static function (&$item): void { + $item = trim($item); + }); + } + + return [$name, $arguments]; + } + + /** + * Ensures that specific filters are on and enabled for the current request. + * + * Filters can have "arguments". This is done by placing a colon immediately + * after the filter name, followed by a comma-separated list of arguments that + * are passed to the filter when executed. + * + * @params array $names filter_name or filter_name:arguments like 'role:admin,manager' + * + * @return Filters + */ + public function enableFilters(array $names, string $when = 'before') + { + foreach ($names as $filter) { + $this->enableFilter($filter, $when); + } + + return $this; + } + + /** + * Returns the arguments for a specified key, or all. + * + * @return array|string + */ + public function getArguments(?string $key = null) + { + return $key === null ? $this->arguments : $this->arguments[$key]; + } + + // -------------------------------------------------------------------- + // Processors + // -------------------------------------------------------------------- + + /** + * Add any applicable (not excluded) global filter settings to the mix. + * + * @param string|null $uri URI path relative to baseURL (all lowercase) + * + * @return void + */ + protected function processGlobals(?string $uri = null) + { + if (! isset($this->config->globals) || ! is_array($this->config->globals)) { + return; + } + + $uri = strtolower(trim($uri ?? '', '/ ')); + + // Add any global filters, unless they are excluded for this URI + $sets = ['before', 'after']; + + $filters = []; + + foreach ($sets as $set) { + if (isset($this->config->globals[$set])) { + // look at each alias in the group + foreach ($this->config->globals[$set] as $alias => $rules) { + $keep = true; + if (is_array($rules)) { + // see if it should be excluded + if (isset($rules['except'])) { + // grab the exclusion rules + $check = $rules['except']; + if ($this->checkExcept($uri, $check)) { + $keep = false; + } + } + } else { + $alias = $rules; // simple name of filter to apply + } + + if ($keep) { + $filters[$set][] = $alias; + } + } + } + } + + if (isset($filters['before'])) { + $oldFilterOrder = config(Feature::class)->oldFilterOrder ?? false; + if ($oldFilterOrder) { + $this->filters['before'] = array_merge($this->filters['before'], $filters['before']); + } else { + $this->filters['before'] = array_merge($filters['before'], $this->filters['before']); + } + } + + if (isset($filters['after'])) { + $this->filters['after'] = array_merge($this->filters['after'], $filters['after']); + } + } + + /** + * Add any method-specific filters to the mix. + * + * @return void + */ + protected function processMethods() + { + if (! isset($this->config->methods) || ! is_array($this->config->methods)) { + return; + } + + $method = $this->request->getMethod(); + + $found = false; + + if (array_key_exists($method, $this->config->methods)) { + $found = true; + } + // Checks lowercase HTTP method for backward compatibility. + // @deprecated 4.5.0 + // @TODO remove this in the future. + elseif (array_key_exists(strtolower($method), $this->config->methods)) { + @trigger_error( + 'Setting lowercase HTTP method key "' . strtolower($method) . '" is deprecated.' + . ' Use uppercase HTTP method like "' . strtoupper($method) . '".', + E_USER_DEPRECATED + ); + + $found = true; + $method = strtolower($method); + } + + if ($found) { + $oldFilterOrder = config(Feature::class)->oldFilterOrder ?? false; + if ($oldFilterOrder) { + $this->filters['before'] = array_merge($this->filters['before'], $this->config->methods[$method]); + } else { + $this->filters['before'] = array_merge($this->config->methods[$method], $this->filters['before']); + } + } + } + + /** + * Add any applicable configured filters to the mix. + * + * @param string|null $uri URI path relative to baseURL (all lowercase) + * + * @return void + */ + protected function processFilters(?string $uri = null) + { + if (! isset($this->config->filters) || ! $this->config->filters) { + return; + } + + $uri = strtolower(trim($uri, '/ ')); + + // Add any filters that apply to this URI + $filters = []; + + foreach ($this->config->filters as $alias => $settings) { + // Look for inclusion rules + if (isset($settings['before'])) { + $path = $settings['before']; + + if ($this->pathApplies($uri, $path)) { + // Get arguments and clean name + [$name, $arguments] = $this->getCleanName($alias); + + $filters['before'][] = $name; + + $this->registerArguments($name, $arguments); + } + } + + if (isset($settings['after'])) { + $path = $settings['after']; + + if ($this->pathApplies($uri, $path)) { + // Get arguments and clean name + [$name, $arguments] = $this->getCleanName($alias); + + $filters['after'][] = $name; + + // The arguments may have already been registered in the before filter. + // So disable check. + $this->registerArguments($name, $arguments, false); + } + } + } + + $oldFilterOrder = config(Feature::class)->oldFilterOrder ?? false; + + if (isset($filters['before'])) { + if ($oldFilterOrder) { + $this->filters['before'] = array_merge($this->filters['before'], $filters['before']); + } else { + $this->filters['before'] = array_merge($filters['before'], $this->filters['before']); + } + } + + if (isset($filters['after'])) { + if (! $oldFilterOrder) { + $filters['after'] = array_reverse($filters['after']); + } + + $this->filters['after'] = array_merge($this->filters['after'], $filters['after']); + } + } + + /** + * @param string $name filter alias + * @param array $arguments filter arguments + * @param bool $check if true, check if already defined + */ + private function registerArguments(string $name, array $arguments, bool $check = true): void + { + if ($arguments !== []) { + if ($check && array_key_exists($name, $this->arguments)) { + throw new ConfigException( + '"' . $name . '" already has arguments: ' + . (($this->arguments[$name] === null) ? 'null' : implode(',', $this->arguments[$name])) + ); + } + + $this->arguments[$name] = $arguments; + } + + $classNames = (array) $this->config->aliases[$name]; + + foreach ($classNames as $className) { + $this->argumentsClass[$className] = $this->arguments[$name] ?? null; + } + } + + /** + * Maps filter aliases to the equivalent filter classes + * + * @return void + * + * @throws FilterException + */ + protected function processAliasesToClass(string $position) + { + $filterClasses = []; + + foreach ($this->filters[$position] as $alias => $rules) { + if (is_numeric($alias) && is_string($rules)) { + $alias = $rules; + } + + if (! array_key_exists($alias, $this->config->aliases)) { + throw FilterException::forNoAlias($alias); + } + + if (is_array($this->config->aliases[$alias])) { + $filterClasses = [...$filterClasses, ...$this->config->aliases[$alias]]; + } else { + $filterClasses[] = $this->config->aliases[$alias]; + } + } + + // when using enableFilter() we already write the class name in $filterClasses as well as the + // alias in $filters. This leads to duplicates when using route filters. + if ($position === 'before') { + $this->filtersClass[$position] = array_merge($filterClasses, $this->filtersClass[$position]); + } else { + $this->filtersClass[$position] = array_merge($this->filtersClass[$position], $filterClasses); + } + + // Since some filters like rate limiters rely on being executed once a request we filter em here. + $this->filtersClass[$position] = array_values(array_unique($this->filtersClass[$position])); + } + + /** + * Check paths for match for URI + * + * @param string $uri URI to test against + * @param array|string $paths The path patterns to test + * + * @return bool True if any of the paths apply to the URI + */ + private function pathApplies(string $uri, $paths) + { + // empty path matches all + if ($paths === '' || $paths === []) { + return true; + } + + // make sure the paths are iterable + if (is_string($paths)) { + $paths = [$paths]; + } + + return $this->checkPseudoRegex($uri, $paths); + } + + /** + * Check except paths + * + * @param string $uri URI path relative to baseURL (all lowercase) + * @param array|string $paths The except path patterns + * + * @return bool True if the URI matches except paths. + */ + private function checkExcept(string $uri, $paths): bool + { + // empty array does not match anything + if ($paths === []) { + return false; + } + + // make sure the paths are iterable + if (is_string($paths)) { + $paths = [$paths]; + } + + return $this->checkPseudoRegex($uri, $paths); + } + + /** + * Check the URI path as pseudo-regex + * + * @param string $uri URI path relative to baseURL (all lowercase, URL-decoded) + * @param array $paths The except path patterns + */ + private function checkPseudoRegex(string $uri, array $paths): bool + { + // treat each path as pseudo-regex + foreach ($paths as $path) { + // need to escape path separators + $path = str_replace('/', '\/', trim($path, '/ ')); + // need to make pseudo wildcard real + $path = strtolower(str_replace('*', '.*', $path)); + + // Does this rule apply here? + if (preg_match('#\A' . $path . '\z#u', $uri, $match) === 1) { + return true; + } + } + + return false; + } +} diff --git a/system/Filters/ForceHTTPS.php b/system/Filters/ForceHTTPS.php new file mode 100644 index 0000000..f415bd0 --- /dev/null +++ b/system/Filters/ForceHTTPS.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 CodeIgniter\Filters; + +use CodeIgniter\HTTP\Exceptions\RedirectException; +use CodeIgniter\HTTP\RequestInterface; +use CodeIgniter\HTTP\ResponseInterface; +use Config\App; + +/** + * Force HTTPS filter + */ +class ForceHTTPS implements FilterInterface +{ + /** + * Force Secure Site Access? If the config value 'forceGlobalSecureRequests' + * is true, will enforce that all requests to this site are made through + * HTTPS. Will redirect the user to the current page with HTTPS, as well + * as set the HTTP Strict Transport Security (HSTS) header for those browsers + * that support it. + * + * @param array|null $arguments + * + * @return ResponseInterface|void + */ + public function before(RequestInterface $request, $arguments = null) + { + $config = config(App::class); + + if ($config->forceGlobalSecureRequests !== true) { + return; + } + + $response = service('response'); + + try { + force_https(YEAR, $request, $response); + } catch (RedirectException $e) { + return $e->getResponse(); + } + } + + /** + * We don't have anything to do here. + * + * @param array|null $arguments + * + * @return void + */ + public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) + { + } +} diff --git a/system/Filters/Honeypot.php b/system/Filters/Honeypot.php new file mode 100644 index 0000000..c2fb98c --- /dev/null +++ b/system/Filters/Honeypot.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 CodeIgniter\Filters; + +use CodeIgniter\Honeypot\Exceptions\HoneypotException; +use CodeIgniter\HTTP\IncomingRequest; +use CodeIgniter\HTTP\RequestInterface; +use CodeIgniter\HTTP\ResponseInterface; +use Config\Services; + +/** + * Honeypot filter + * + * @see \CodeIgniter\Filters\HoneypotTest + */ +class Honeypot implements FilterInterface +{ + /** + * Checks if Honeypot field is empty, if not then the + * requester is a bot + * + * @param list|null $arguments + * + * @throws HoneypotException + */ + public function before(RequestInterface $request, $arguments = null) + { + if (! $request instanceof IncomingRequest) { + return; + } + + if (Services::honeypot()->hasContent($request)) { + throw HoneypotException::isBot(); + } + } + + /** + * Attach a honeypot to the current response. + * + * @param list|null $arguments + */ + public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) + { + service('honeypot')->attachHoneypot($response); + } +} diff --git a/system/Filters/InvalidChars.php b/system/Filters/InvalidChars.php new file mode 100644 index 0000000..542b12d --- /dev/null +++ b/system/Filters/InvalidChars.php @@ -0,0 +1,128 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Filters; + +use CodeIgniter\HTTP\IncomingRequest; +use CodeIgniter\HTTP\RequestInterface; +use CodeIgniter\HTTP\ResponseInterface; +use CodeIgniter\Security\Exceptions\SecurityException; + +/** + * InvalidChars filter. + * + * Check if user input data ($_GET, $_POST, $_COOKIE, php://input) do not contain + * invalid characters: + * - invalid UTF-8 characters + * - control characters except line break and tab code + * + * @see \CodeIgniter\Filters\InvalidCharsTest + */ +class InvalidChars implements FilterInterface +{ + /** + * Data source + * + * @var string + */ + protected $source; + + /** + * Regular expressions for valid control codes + * + * @var string + */ + protected $controlCodeRegex = '/\A[\r\n\t[:^cntrl:]]*\z/u'; + + /** + * Check invalid characters. + * + * @param list|null $arguments + * + * @return void + */ + public function before(RequestInterface $request, $arguments = null) + { + if (! $request instanceof IncomingRequest) { + return; + } + + $data = [ + 'get' => $request->getGet(), + 'post' => $request->getPost(), + 'cookie' => $request->getCookie(), + 'rawInput' => $request->getRawInput(), + ]; + + foreach ($data as $source => $values) { + $this->source = $source; + $this->checkEncoding($values); + $this->checkControl($values); + } + } + + /** + * We don't have anything to do here. + * + * @param list|null $arguments + * + * @return void + */ + public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) + { + } + + /** + * Check the character encoding is valid UTF-8. + * + * @param array|string $value + * + * @return array|string + */ + protected function checkEncoding($value) + { + if (is_array($value)) { + array_map($this->checkEncoding(...), $value); + + return $value; + } + + if (mb_check_encoding($value, 'UTF-8')) { + return $value; + } + + throw SecurityException::forInvalidUTF8Chars($this->source, $value); + } + + /** + * Check for the presence of control characters except line breaks and tabs. + * + * @param array|string $value + * + * @return array|string + */ + protected function checkControl($value) + { + if (is_array($value)) { + array_map($this->checkControl(...), $value); + + return $value; + } + + if (preg_match($this->controlCodeRegex, $value) === 1) { + return $value; + } + + throw SecurityException::forInvalidControlChars($this->source, $value); + } +} diff --git a/system/Filters/PageCache.php b/system/Filters/PageCache.php new file mode 100644 index 0000000..a3d3af8 --- /dev/null +++ b/system/Filters/PageCache.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 CodeIgniter\Filters; + +use CodeIgniter\Cache\ResponseCache; +use CodeIgniter\HTTP\CLIRequest; +use CodeIgniter\HTTP\DownloadResponse; +use CodeIgniter\HTTP\IncomingRequest; +use CodeIgniter\HTTP\RedirectResponse; +use CodeIgniter\HTTP\RequestInterface; +use CodeIgniter\HTTP\ResponseInterface; + +/** + * Page Cache filter + */ +class PageCache implements FilterInterface +{ + private readonly ResponseCache $pageCache; + + public function __construct() + { + $this->pageCache = service('responsecache'); + } + + /** + * Checks page cache and return if found. + * + * @param array|null $arguments + * + * @return ResponseInterface|void + */ + public function before(RequestInterface $request, $arguments = null) + { + assert($request instanceof CLIRequest || $request instanceof IncomingRequest); + + $response = service('response'); + + $cachedResponse = $this->pageCache->get($request, $response); + + if ($cachedResponse instanceof ResponseInterface) { + return $cachedResponse; + } + } + + /** + * Cache the page. + * + * @param array|null $arguments + * + * @return void + */ + public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) + { + assert($request instanceof CLIRequest || $request instanceof IncomingRequest); + + if ( + ! $response instanceof DownloadResponse + && ! $response instanceof RedirectResponse + ) { + // Cache it without the performance metrics replaced + // so that we can have live speed updates along the way. + // Must be run after filters to preserve the Response headers. + $this->pageCache->make($request, $response); + } + } +} diff --git a/system/Filters/PerformanceMetrics.php b/system/Filters/PerformanceMetrics.php new file mode 100644 index 0000000..f2371c7 --- /dev/null +++ b/system/Filters/PerformanceMetrics.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 CodeIgniter\Filters; + +use CodeIgniter\HTTP\RequestInterface; +use CodeIgniter\HTTP\ResponseInterface; + +/** + * Performance Metrics filter + */ +class PerformanceMetrics implements FilterInterface +{ + /** + * We don't need to do anything here. + * + * @param array|null $arguments + */ + public function before(RequestInterface $request, $arguments = null) + { + } + + /** + * Replaces the performance metrics. + * + * @param array|null $arguments + * + * @return void + */ + public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) + { + $body = $response->getBody(); + + if ($body !== null) { + $benchmark = service('timer'); + + $output = str_replace( + [ + '{elapsed_time}', + '{memory_usage}', + ], + [ + (string) $benchmark->getElapsedTime('total_execution'), + number_format(memory_get_peak_usage() / 1024 / 1024, 3), + ], + $body + ); + + $response->setBody($output); + } + } +} diff --git a/system/Filters/SecureHeaders.php b/system/Filters/SecureHeaders.php new file mode 100644 index 0000000..b8bd6e0 --- /dev/null +++ b/system/Filters/SecureHeaders.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 CodeIgniter\Filters; + +use CodeIgniter\HTTP\RequestInterface; +use CodeIgniter\HTTP\ResponseInterface; + +/** + * Add Common Security Headers + * + * @see \CodeIgniter\Filters\SecureHeadersTest + */ +class SecureHeaders implements FilterInterface +{ + /** + * @var array + */ + protected $headers = [ + // https://owasp.org/www-project-secure-headers/#x-frame-options + 'X-Frame-Options' => 'SAMEORIGIN', + + // https://owasp.org/www-project-secure-headers/#x-content-type-options + 'X-Content-Type-Options' => 'nosniff', + + // https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/compatibility/jj542450(v=vs.85)#the-noopen-directive + 'X-Download-Options' => 'noopen', + + // https://owasp.org/www-project-secure-headers/#x-permitted-cross-domain-policies + 'X-Permitted-Cross-Domain-Policies' => 'none', + + // https://owasp.org/www-project-secure-headers/#referrer-policy + 'Referrer-Policy' => 'same-origin', + + // https://owasp.org/www-project-secure-headers/#x-xss-protection + // If you do not need to support legacy browsers, it is recommended that you use + // Content-Security-Policy without allowing unsafe-inline scripts instead. + // 'X-XSS-Protection' => '1; mode=block', + ]; + + /** + * We don't have anything to do here. + * + * @param list|null $arguments + * + * @return void + */ + public function before(RequestInterface $request, $arguments = null) + { + } + + /** + * Add security headers. + * + * @param list|null $arguments + * + * @return void + */ + public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) + { + foreach ($this->headers as $header => $value) { + $response->setHeader($header, $value); + } + } +} diff --git a/system/Format/Exceptions/FormatException.php b/system/Format/Exceptions/FormatException.php new file mode 100644 index 0000000..5a8c2d2 --- /dev/null +++ b/system/Format/Exceptions/FormatException.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Format\Exceptions; + +use CodeIgniter\Exceptions\DebugTraceableTrait; +use CodeIgniter\Exceptions\ExceptionInterface; +use RuntimeException; + +/** + * FormatException + */ +class FormatException extends RuntimeException implements ExceptionInterface +{ + use DebugTraceableTrait; + + /** + * Thrown when the instantiated class does not exist. + * + * @return static + */ + public static function forInvalidFormatter(string $class) + { + return new static(lang('Format.invalidFormatter', [$class])); + } + + /** + * Thrown in JSONFormatter when the json_encode produces + * an error code other than JSON_ERROR_NONE and JSON_ERROR_RECURSION. + * + * @param string $error The error message + * + * @return static + */ + public static function forInvalidJSON(?string $error = null) + { + return new static(lang('Format.invalidJSON', [$error])); + } + + /** + * Thrown when the supplied MIME type has no + * defined Formatter class. + * + * @return static + */ + public static function forInvalidMime(string $mime) + { + return new static(lang('Format.invalidMime', [$mime])); + } + + /** + * Thrown on XMLFormatter when the `simplexml` extension + * is not installed. + * + * @return static + * + * @codeCoverageIgnore + */ + public static function forMissingExtension() + { + return new static(lang('Format.missingExtension')); + } +} diff --git a/system/Format/Format.php b/system/Format/Format.php new file mode 100644 index 0000000..4a9cb60 --- /dev/null +++ b/system/Format/Format.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Format; + +use CodeIgniter\Format\Exceptions\FormatException; +use Config\Format as FormatConfig; + +/** + * The Format class is a convenient place to create Formatters. + * + * @see \CodeIgniter\Format\FormatTest + */ +class Format +{ + /** + * Configuration instance + * + * @var FormatConfig + */ + protected $config; + + /** + * Constructor. + */ + public function __construct(FormatConfig $config) + { + $this->config = $config; + } + + /** + * Returns the current configuration instance. + * + * @return FormatConfig + */ + public function getConfig() + { + return $this->config; + } + + /** + * A Factory method to return the appropriate formatter for the given mime type. + * + * @throws FormatException + */ + public function getFormatter(string $mime): FormatterInterface + { + if (! array_key_exists($mime, $this->config->formatters)) { + throw FormatException::forInvalidMime($mime); + } + + $className = $this->config->formatters[$mime]; + + if (! class_exists($className)) { + throw FormatException::forInvalidFormatter($className); + } + + $class = new $className(); + + if (! $class instanceof FormatterInterface) { + throw FormatException::forInvalidFormatter($className); + } + + return $class; + } +} diff --git a/system/Format/FormatterInterface.php b/system/Format/FormatterInterface.php new file mode 100644 index 0000000..0f00556 --- /dev/null +++ b/system/Format/FormatterInterface.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 CodeIgniter\Format; + +/** + * Formatter interface + */ +interface FormatterInterface +{ + /** + * Takes the given data and formats it. + * + * @param array|object|string $data + * + * @return false|string + */ + public function format($data); +} diff --git a/system/Format/JSONFormatter.php b/system/Format/JSONFormatter.php new file mode 100644 index 0000000..7d2ad52 --- /dev/null +++ b/system/Format/JSONFormatter.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 CodeIgniter\Format; + +use CodeIgniter\Format\Exceptions\FormatException; +use Config\Format; + +/** + * JSON data formatter + * + * @see \CodeIgniter\Format\JSONFormatterTest + */ +class JSONFormatter implements FormatterInterface +{ + /** + * Takes the given data and formats it. + * + * @param array|bool|float|int|object|string|null $data + * + * @return false|string (JSON string | false) + */ + public function format($data) + { + $config = new Format(); + + $options = $config->formatterOptions['application/json'] ?? JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES; + $options |= JSON_PARTIAL_OUTPUT_ON_ERROR; + + $options = ENVIRONMENT === 'production' ? $options : $options | JSON_PRETTY_PRINT; + + $result = json_encode($data, $options, 512); + + if (! in_array(json_last_error(), [JSON_ERROR_NONE, JSON_ERROR_RECURSION], true)) { + throw FormatException::forInvalidJSON(json_last_error_msg()); + } + + return $result; + } +} diff --git a/system/Format/XMLFormatter.php b/system/Format/XMLFormatter.php new file mode 100644 index 0000000..c85eae5 --- /dev/null +++ b/system/Format/XMLFormatter.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Format; + +use CodeIgniter\Format\Exceptions\FormatException; +use Config\Format; +use SimpleXMLElement; + +/** + * XML data formatter + * + * @see \CodeIgniter\Format\XMLFormatterTest + */ +class XMLFormatter implements FormatterInterface +{ + /** + * Takes the given data and formats it. + * + * @param array|bool|float|int|object|string|null $data + * + * @return false|string (XML string | false) + */ + public function format($data) + { + $config = new Format(); + + // SimpleXML is installed but default + // but best to check, and then provide a fallback. + if (! extension_loaded('simplexml')) { + throw FormatException::forMissingExtension(); // @codeCoverageIgnore + } + + $options = $config->formatterOptions['application/xml'] ?? 0; + $output = new SimpleXMLElement('', $options); + + $this->arrayToXML((array) $data, $output); + + return $output->asXML(); + } + + /** + * A recursive method to convert an array into a valid XML string. + * + * Written by CodexWorld. Received permission by email on Nov 24, 2016 to use this code. + * + * @see http://www.codexworld.com/convert-array-to-xml-in-php/ + * + * @param SimpleXMLElement $output + * + * @return void + */ + protected function arrayToXML(array $data, &$output) + { + foreach ($data as $key => $value) { + $key = $this->normalizeXMLTag($key); + + if (is_array($value)) { + $subnode = $output->addChild("{$key}"); + $this->arrayToXML($value, $subnode); + } else { + $output->addChild("{$key}", htmlspecialchars("{$value}")); + } + } + } + + /** + * Normalizes tags into the allowed by W3C. + * Regex adopted from this StackOverflow answer. + * + * @param int|string $key + * + * @return string + * + * @see https://stackoverflow.com/questions/60001029/invalid-characters-in-xml-tag-name + */ + protected function normalizeXMLTag($key) + { + $startChar = 'A-Z_a-z' . + '\\x{C0}-\\x{D6}\\x{D8}-\\x{F6}\\x{F8}-\\x{2FF}\\x{370}-\\x{37D}' . + '\\x{37F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{2070}-\\x{218F}' . + '\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}' . + '\\x{FDF0}-\\x{FFFD}\\x{10000}-\\x{EFFFF}'; + $validName = $startChar . '\\.\\d\\x{B7}\\x{300}-\\x{36F}\\x{203F}-\\x{2040}'; + + $key = (string) $key; + + $key = trim($key); + $key = preg_replace("/[^{$validName}-]+/u", '', $key); + $key = preg_replace("/^[^{$startChar}]+/u", 'item$0', $key); + + return preg_replace('/^(xml).*/iu', 'item$0', $key); // XML is a reserved starting word + } +} diff --git a/system/HTTP/CLIRequest.php b/system/HTTP/CLIRequest.php new file mode 100644 index 0000000..0b2c573 --- /dev/null +++ b/system/HTTP/CLIRequest.php @@ -0,0 +1,327 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\HTTP; + +use Config\App; +use Locale; +use RuntimeException; + +/** + * Represents a request from the command-line. Provides additional + * tools to interact with that request since CLI requests are not + * static like HTTP requests might be. + * + * Portions of this code were initially from the FuelPHP Framework, + * version 1.7.x, and used here under the MIT license they were + * originally made available under. + * + * http://fuelphp.com + * + * @see \CodeIgniter\HTTP\CLIRequestTest + */ +class CLIRequest extends Request +{ + /** + * Stores the segments of our cli "URI" command. + * + * @var array + */ + protected $segments = []; + + /** + * Command line options and their values. + * + * @var array + */ + protected $options = []; + + /** + * Command line arguments (segments and options). + * + * @var array + */ + protected $args = []; + + /** + * Set the expected HTTP verb + * + * @var string + */ + protected $method = 'CLI'; + + /** + * Constructor + */ + public function __construct(App $config) + { + if (! is_cli()) { + throw new RuntimeException(static::class . ' needs to run from the command line.'); // @codeCoverageIgnore + } + + parent::__construct($config); + + // Don't terminate the script when the cli's tty goes away + ignore_user_abort(true); + + $this->parseCommand(); + + // Set SiteURI for this request + $this->uri = new SiteURI($config, $this->getPath()); + } + + /** + * Returns the "path" of the request script so that it can be used + * in routing to the appropriate controller/method. + * + * The path is determined by treating the command line arguments + * as if it were a URL - up until we hit our first option. + * + * Example: + * php index.php users 21 profile -foo bar + * + * // Routes to /users/21/profile (index is removed for routing sake) + * // with the option foo = bar. + */ + public function getPath(): string + { + $path = implode('/', $this->segments); + + return ($path === '') ? '' : $path; + } + + /** + * Returns an associative array of all CLI options found, with + * their values. + */ + public function getOptions(): array + { + return $this->options; + } + + /** + * Returns an array of all CLI arguments (segments and options). + */ + public function getArgs(): array + { + return $this->args; + } + + /** + * Returns the path segments. + */ + public function getSegments(): array + { + return $this->segments; + } + + /** + * Returns the value for a single CLI option that was passed in. + * + * @return string|null + */ + public function getOption(string $key) + { + return $this->options[$key] ?? null; + } + + /** + * Returns the options as a string, suitable for passing along on + * the CLI to other commands. + * + * Example: + * $options = [ + * 'foo' => 'bar', + * 'baz' => 'queue some stuff' + * ]; + * + * getOptionString() = '-foo bar -baz "queue some stuff"' + */ + public function getOptionString(bool $useLongOpts = false): string + { + if ($this->options === []) { + return ''; + } + + $out = ''; + + foreach ($this->options as $name => $value) { + if ($useLongOpts && mb_strlen($name) > 1) { + $out .= "--{$name} "; + } else { + $out .= "-{$name} "; + } + + if ($value === null) { + continue; + } + + if (mb_strpos($value, ' ') !== false) { + $out .= '"' . $value . '" '; + } else { + $out .= "{$value} "; + } + } + + return trim($out); + } + + /** + * Parses the command line it was called from and collects all options + * and valid segments. + * + * NOTE: I tried to use getopt but had it fail occasionally to find + * any options, where argv has always had our back. + * + * @return void + */ + protected function parseCommand() + { + $args = $this->getServer('argv'); + array_shift($args); // Scrap index.php + + $optionValue = false; + + foreach ($args as $i => $arg) { + if (mb_strpos($arg, '-') !== 0) { + if ($optionValue) { + $optionValue = false; + } else { + $this->segments[] = $arg; + $this->args[] = $arg; + } + + continue; + } + + $arg = ltrim($arg, '-'); + $value = null; + + if (isset($args[$i + 1]) && mb_strpos($args[$i + 1], '-') !== 0) { + $value = $args[$i + 1]; + $optionValue = true; + } + + $this->options[$arg] = $value; + $this->args[$arg] = $value; + } + } + + /** + * Determines if this request was made from the command line (CLI). + */ + public function isCLI(): bool + { + return true; + } + + /** + * Fetch an item from GET data. + * + * @param array|string|null $index Index for item to fetch from $_GET. + * @param int|null $filter A filter name to apply. + * @param array|int|null $flags + * + * @return array|null + */ + public function getGet($index = null, $filter = null, $flags = null) + { + return $this->returnNullOrEmptyArray($index); + } + + /** + * Fetch an item from POST. + * + * @param array|string|null $index Index for item to fetch from $_POST. + * @param int|null $filter A filter name to apply + * @param array|int|null $flags + * + * @return array|null + */ + public function getPost($index = null, $filter = null, $flags = null) + { + return $this->returnNullOrEmptyArray($index); + } + + /** + * Fetch an item from POST data with fallback to GET. + * + * @param array|string|null $index Index for item to fetch from $_POST or $_GET + * @param int|null $filter A filter name to apply + * @param array|int|null $flags + * + * @return array|null + */ + public function getPostGet($index = null, $filter = null, $flags = null) + { + return $this->returnNullOrEmptyArray($index); + } + + /** + * Fetch an item from GET data with fallback to POST. + * + * @param array|string|null $index Index for item to be fetched from $_GET or $_POST + * @param int|null $filter A filter name to apply + * @param array|int|null $flags + * + * @return array|null + */ + public function getGetPost($index = null, $filter = null, $flags = null) + { + return $this->returnNullOrEmptyArray($index); + } + + /** + * This is a place holder for calls from cookie_helper get_cookie(). + * + * @param array|string|null $index Index for item to be fetched from $_COOKIE + * @param int|null $filter A filter name to be applied + * @param mixed $flags + * + * @return array|null + */ + public function getCookie($index = null, $filter = null, $flags = null) + { + return $this->returnNullOrEmptyArray($index); + } + + /** + * @param array|string|null $index + * + * @return array|null + */ + private function returnNullOrEmptyArray($index) + { + return ($index === null || is_array($index)) ? [] : null; + } + + /** + * Gets the current locale, with a fallback to the default + * locale if none is set. + */ + public function getLocale(): string + { + return Locale::getDefault(); + } + + /** + * Checks this request type. + * + * @param string $type HTTP verb or 'json' or 'ajax' + * @phpstan-param string|'get'|'post'|'put'|'delete'|'head'|'patch'|'options'|'json'|'ajax' $type + */ + public function is(string $type): bool + { + return false; + } +} diff --git a/system/HTTP/CURLRequest.php b/system/HTTP/CURLRequest.php new file mode 100644 index 0000000..9da231c --- /dev/null +++ b/system/HTTP/CURLRequest.php @@ -0,0 +1,701 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\HTTP; + +use CodeIgniter\HTTP\Exceptions\HTTPException; +use Config\App; +use Config\CURLRequest as ConfigCURLRequest; +use InvalidArgumentException; + +/** + * A lightweight HTTP client for sending synchronous HTTP requests via cURL. + * + * @see \CodeIgniter\HTTP\CURLRequestTest + */ +class CURLRequest extends OutgoingRequest +{ + /** + * The response object associated with this request + * + * @var ResponseInterface|null + */ + protected $response; + + /** + * The original response object associated with this request + * + * @var ResponseInterface|null + */ + protected $responseOrig; + + /** + * The URI associated with this request + * + * @var URI + */ + protected $baseURI; + + /** + * The setting values + * + * @var array + */ + protected $config; + + /** + * The default setting values + * + * @var array + */ + protected $defaultConfig = [ + 'timeout' => 0.0, + 'connect_timeout' => 150, + 'debug' => false, + 'verify' => true, + ]; + + /** + * Default values for when 'allow_redirects' + * option is true. + * + * @var array + */ + protected $redirectDefaults = [ + 'max' => 5, + 'strict' => true, + 'protocols' => [ + 'http', + 'https', + ], + ]; + + /** + * The number of milliseconds to delay before + * sending the request. + * + * @var float + */ + protected $delay = 0.0; + + /** + * The default options from the constructor. Applied to all requests. + */ + private readonly array $defaultOptions; + + /** + * Whether share options between requests or not. + * + * If true, all the options won't be reset between requests. + * It may cause an error request with unnecessary headers. + */ + private readonly bool $shareOptions; + + /** + * Takes an array of options to set the following possible class properties: + * + * - baseURI + * - timeout + * - any other request options to use as defaults. + * + * @param array $options + */ + public function __construct(App $config, URI $uri, ?ResponseInterface $response = null, array $options = []) + { + if (! function_exists('curl_version')) { + throw HTTPException::forMissingCurl(); // @codeCoverageIgnore + } + + parent::__construct(Method::GET, $uri); + + $this->responseOrig = $response ?? new Response($config); + // Remove the default Content-Type header. + $this->responseOrig->removeHeader('Content-Type'); + + $this->baseURI = $uri->useRawQueryString(); + $this->defaultOptions = $options; + + /** @var ConfigCURLRequest|null $configCURLRequest */ + $configCURLRequest = config(ConfigCURLRequest::class); + $this->shareOptions = $configCURLRequest->shareOptions ?? true; + + $this->config = $this->defaultConfig; + $this->parseOptions($options); + } + + /** + * Sends an HTTP request to the specified $url. If this is a relative + * URL, it will be merged with $this->baseURI to form a complete URL. + * + * @param string $method HTTP method + */ + public function request($method, string $url, array $options = []): ResponseInterface + { + $this->response = clone $this->responseOrig; + + $this->parseOptions($options); + + $url = $this->prepareURL($url); + + $method = esc(strip_tags($method)); + + $this->send($method, $url); + + if ($this->shareOptions === false) { + $this->resetOptions(); + } + + return $this->response; + } + + /** + * Reset all options to default. + * + * @return void + */ + protected function resetOptions() + { + // Reset headers + $this->headers = []; + $this->headerMap = []; + + // Reset body + $this->body = null; + + // Reset configs + $this->config = $this->defaultConfig; + + // Set the default options for next request + $this->parseOptions($this->defaultOptions); + } + + /** + * Convenience method for sending a GET request. + */ + public function get(string $url, array $options = []): ResponseInterface + { + return $this->request(Method::GET, $url, $options); + } + + /** + * Convenience method for sending a DELETE request. + */ + public function delete(string $url, array $options = []): ResponseInterface + { + return $this->request('DELETE', $url, $options); + } + + /** + * Convenience method for sending a HEAD request. + */ + public function head(string $url, array $options = []): ResponseInterface + { + return $this->request('HEAD', $url, $options); + } + + /** + * Convenience method for sending an OPTIONS request. + */ + public function options(string $url, array $options = []): ResponseInterface + { + return $this->request('OPTIONS', $url, $options); + } + + /** + * Convenience method for sending a PATCH request. + */ + public function patch(string $url, array $options = []): ResponseInterface + { + return $this->request('PATCH', $url, $options); + } + + /** + * Convenience method for sending a POST request. + */ + public function post(string $url, array $options = []): ResponseInterface + { + return $this->request(Method::POST, $url, $options); + } + + /** + * Convenience method for sending a PUT request. + */ + public function put(string $url, array $options = []): ResponseInterface + { + return $this->request(Method::PUT, $url, $options); + } + + /** + * Set the HTTP Authentication. + * + * @param string $type basic or digest + * + * @return $this + */ + public function setAuth(string $username, string $password, string $type = 'basic') + { + $this->config['auth'] = [ + $username, + $password, + $type, + ]; + + return $this; + } + + /** + * Set form data to be sent. + * + * @param bool $multipart Set TRUE if you are sending CURLFiles + * + * @return $this + */ + public function setForm(array $params, bool $multipart = false) + { + if ($multipart) { + $this->config['multipart'] = $params; + } else { + $this->config['form_params'] = $params; + } + + return $this; + } + + /** + * Set JSON data to be sent. + * + * @param array|bool|float|int|object|string|null $data + * + * @return $this + */ + public function setJSON($data) + { + $this->config['json'] = $data; + + return $this; + } + + /** + * Sets the correct settings based on the options array + * passed in. + * + * @return void + */ + protected function parseOptions(array $options) + { + if (array_key_exists('baseURI', $options)) { + $this->baseURI = $this->baseURI->setURI($options['baseURI']); + unset($options['baseURI']); + } + + if (array_key_exists('headers', $options) && is_array($options['headers'])) { + foreach ($options['headers'] as $name => $value) { + $this->setHeader($name, $value); + } + + unset($options['headers']); + } + + if (array_key_exists('delay', $options)) { + // Convert from the milliseconds passed in + // to the seconds that sleep requires. + $this->delay = (float) $options['delay'] / 1000; + unset($options['delay']); + } + + if (array_key_exists('body', $options)) { + $this->setBody($options['body']); + unset($options['body']); + } + + foreach ($options as $key => $value) { + $this->config[$key] = $value; + } + } + + /** + * If the $url is a relative URL, will attempt to create + * a full URL by prepending $this->baseURI to it. + */ + protected function prepareURL(string $url): string + { + // If it's a full URI, then we have nothing to do here... + if (str_contains($url, '://')) { + return $url; + } + + $uri = $this->baseURI->resolveRelativeURI($url); + + // Create the string instead of casting to prevent baseURL muddling + return URI::createURIString( + $uri->getScheme(), + $uri->getAuthority(), + $uri->getPath(), + $uri->getQuery(), + $uri->getFragment() + ); + } + + /** + * Fires the actual cURL request. + * + * @return ResponseInterface + */ + public function send(string $method, string $url) + { + // Reset our curl options so we're on a fresh slate. + $curlOptions = []; + + if (! empty($this->config['query']) && is_array($this->config['query'])) { + // This is likely too naive a solution. + // Should look into handling when $url already + // has query vars on it. + $url .= '?' . http_build_query($this->config['query']); + unset($this->config['query']); + } + + $curlOptions[CURLOPT_URL] = $url; + $curlOptions[CURLOPT_RETURNTRANSFER] = true; + $curlOptions[CURLOPT_HEADER] = true; + $curlOptions[CURLOPT_FRESH_CONNECT] = true; + // Disable @file uploads in post data. + $curlOptions[CURLOPT_SAFE_UPLOAD] = true; + + $curlOptions = $this->setCURLOptions($curlOptions, $this->config); + $curlOptions = $this->applyMethod($method, $curlOptions); + $curlOptions = $this->applyRequestHeaders($curlOptions); + + // Do we need to delay this request? + if ($this->delay > 0) { + usleep((int) $this->delay * 1_000_000); + } + + $output = $this->sendRequest($curlOptions); + + // Set the string we want to break our response from + $breakString = "\r\n\r\n"; + + while (str_starts_with($output, 'HTTP/1.1 100 Continue')) { + $output = substr($output, strpos($output, $breakString) + 4); + } + + if (str_starts_with($output, 'HTTP/1.1 200 Connection established')) { + $output = substr($output, strpos($output, $breakString) + 4); + } + + // If request and response have Digest + if (isset($this->config['auth'][2]) && $this->config['auth'][2] === 'digest' && str_contains($output, 'WWW-Authenticate: Digest')) { + $output = substr($output, strpos($output, $breakString) + 4); + } + + // Split out our headers and body + $break = strpos($output, $breakString); + + if ($break !== false) { + // Our headers + $headers = explode("\n", substr($output, 0, $break)); + + $this->setResponseHeaders($headers); + + // Our body + $body = substr($output, $break + 4); + $this->response->setBody($body); + } else { + $this->response->setBody($output); + } + + return $this->response; + } + + /** + * Adds $this->headers to the cURL request. + */ + protected function applyRequestHeaders(array $curlOptions = []): array + { + if (empty($this->headers)) { + return $curlOptions; + } + + $set = []; + + foreach (array_keys($this->headers) as $name) { + $set[] = $name . ': ' . $this->getHeaderLine($name); + } + + $curlOptions[CURLOPT_HTTPHEADER] = $set; + + return $curlOptions; + } + + /** + * Apply method + */ + protected function applyMethod(string $method, array $curlOptions): array + { + $this->method = $method; + $curlOptions[CURLOPT_CUSTOMREQUEST] = $method; + + $size = strlen($this->body ?? ''); + + // Have content? + if ($size > 0) { + return $this->applyBody($curlOptions); + } + + if ($method === Method::PUT || $method === Method::POST) { + // See http://tools.ietf.org/html/rfc7230#section-3.3.2 + if ($this->header('content-length') === null && ! isset($this->config['multipart'])) { + $this->setHeader('Content-Length', '0'); + } + } elseif ($method === 'HEAD') { + $curlOptions[CURLOPT_NOBODY] = 1; + } + + return $curlOptions; + } + + /** + * Apply body + */ + protected function applyBody(array $curlOptions = []): array + { + if (! empty($this->body)) { + $curlOptions[CURLOPT_POSTFIELDS] = (string) $this->getBody(); + } + + return $curlOptions; + } + + /** + * Parses the header retrieved from the cURL response into + * our Response object. + * + * @return void + */ + protected function setResponseHeaders(array $headers = []) + { + foreach ($headers as $header) { + if (($pos = strpos($header, ':')) !== false) { + $title = trim(substr($header, 0, $pos)); + $value = trim(substr($header, $pos + 1)); + + if ($this->response instanceof Response) { + $this->response->addHeader($title, $value); + } else { + $this->response->setHeader($title, $value); + } + } elseif (str_starts_with($header, 'HTTP')) { + preg_match('#^HTTP\/([12](?:\.[01])?) (\d+) (.+)#', $header, $matches); + + if (isset($matches[1])) { + $this->response->setProtocolVersion($matches[1]); + } + + if (isset($matches[2])) { + $this->response->setStatusCode((int) $matches[2], $matches[3] ?? null); + } + } + } + } + + /** + * Set CURL options + * + * @return array + * + * @throws InvalidArgumentException + */ + protected function setCURLOptions(array $curlOptions = [], array $config = []) + { + // Auth Headers + if (! empty($config['auth'])) { + $curlOptions[CURLOPT_USERPWD] = $config['auth'][0] . ':' . $config['auth'][1]; + + if (! empty($config['auth'][2]) && strtolower($config['auth'][2]) === 'digest') { + $curlOptions[CURLOPT_HTTPAUTH] = CURLAUTH_DIGEST; + } else { + $curlOptions[CURLOPT_HTTPAUTH] = CURLAUTH_BASIC; + } + } + + // Certificate + if (! empty($config['cert'])) { + $cert = $config['cert']; + + if (is_array($cert)) { + $curlOptions[CURLOPT_SSLCERTPASSWD] = $cert[1]; + $cert = $cert[0]; + } + + if (! is_file($cert)) { + throw HTTPException::forSSLCertNotFound($cert); + } + + $curlOptions[CURLOPT_SSLCERT] = $cert; + } + + // SSL Verification + if (isset($config['verify'])) { + if (is_string($config['verify'])) { + $file = realpath($config['verify']) ?: $config['verify']; + + if (! is_file($file)) { + throw HTTPException::forInvalidSSLKey($config['verify']); + } + + $curlOptions[CURLOPT_CAINFO] = $file; + $curlOptions[CURLOPT_SSL_VERIFYPEER] = true; + $curlOptions[CURLOPT_SSL_VERIFYHOST] = 2; + } elseif (is_bool($config['verify'])) { + $curlOptions[CURLOPT_SSL_VERIFYPEER] = $config['verify']; + $curlOptions[CURLOPT_SSL_VERIFYHOST] = $config['verify'] ? 2 : 0; + } + } + + // Proxy + if (isset($config['proxy'])) { + $curlOptions[CURLOPT_HTTPPROXYTUNNEL] = true; + $curlOptions[CURLOPT_PROXY] = $config['proxy']; + } + + // Debug + if ($config['debug']) { + $curlOptions[CURLOPT_VERBOSE] = 1; + $curlOptions[CURLOPT_STDERR] = is_string($config['debug']) ? fopen($config['debug'], 'a+b') : fopen('php://stderr', 'wb'); + } + + // Decode Content + if (! empty($config['decode_content'])) { + $accept = $this->getHeaderLine('Accept-Encoding'); + + if ($accept !== '') { + $curlOptions[CURLOPT_ENCODING] = $accept; + } else { + $curlOptions[CURLOPT_ENCODING] = ''; + $curlOptions[CURLOPT_HTTPHEADER] = 'Accept-Encoding'; + } + } + + // Allow Redirects + if (array_key_exists('allow_redirects', $config)) { + $settings = $this->redirectDefaults; + + if (is_array($config['allow_redirects'])) { + $settings = array_merge($settings, $config['allow_redirects']); + } + + if ($config['allow_redirects'] === false) { + $curlOptions[CURLOPT_FOLLOWLOCATION] = 0; + } else { + $curlOptions[CURLOPT_FOLLOWLOCATION] = 1; + $curlOptions[CURLOPT_MAXREDIRS] = $settings['max']; + + if ($settings['strict'] === true) { + $curlOptions[CURLOPT_POSTREDIR] = 1 | 2 | 4; + } + + $protocols = 0; + + foreach ($settings['protocols'] as $proto) { + $protocols += constant('CURLPROTO_' . strtoupper($proto)); + } + + $curlOptions[CURLOPT_REDIR_PROTOCOLS] = $protocols; + } + } + + // Timeout + $curlOptions[CURLOPT_TIMEOUT_MS] = (float) $config['timeout'] * 1000; + + // Connection Timeout + $curlOptions[CURLOPT_CONNECTTIMEOUT_MS] = (float) $config['connect_timeout'] * 1000; + + // Post Data - application/x-www-form-urlencoded + if (! empty($config['form_params']) && is_array($config['form_params'])) { + $postFields = http_build_query($config['form_params']); + $curlOptions[CURLOPT_POSTFIELDS] = $postFields; + + // Ensure content-length is set, since CURL doesn't seem to + // calculate it when HTTPHEADER is set. + $this->setHeader('Content-Length', (string) strlen($postFields)); + $this->setHeader('Content-Type', 'application/x-www-form-urlencoded'); + } + + // Post Data - multipart/form-data + if (! empty($config['multipart']) && is_array($config['multipart'])) { + // setting the POSTFIELDS option automatically sets multipart + $curlOptions[CURLOPT_POSTFIELDS] = $config['multipart']; + } + + // HTTP Errors + $curlOptions[CURLOPT_FAILONERROR] = array_key_exists('http_errors', $config) ? (bool) $config['http_errors'] : true; + + // JSON + if (isset($config['json'])) { + // Will be set as the body in `applyBody()` + $json = json_encode($config['json']); + $this->setBody($json); + $this->setHeader('Content-Type', 'application/json'); + $this->setHeader('Content-Length', (string) strlen($json)); + } + + // version + if (! empty($config['version'])) { + $version = sprintf('%.1F', $config['version']); + if ($version === '1.0') { + $curlOptions[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0; + } elseif ($version === '1.1') { + $curlOptions[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_1; + } elseif ($version === '2.0') { + $curlOptions[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2_0; + } + } + + // Cookie + if (isset($config['cookie'])) { + $curlOptions[CURLOPT_COOKIEJAR] = $config['cookie']; + $curlOptions[CURLOPT_COOKIEFILE] = $config['cookie']; + } + + // User Agent + if (isset($config['user_agent'])) { + $curlOptions[CURLOPT_USERAGENT] = $config['user_agent']; + } + + return $curlOptions; + } + + /** + * Does the actual work of initializing cURL, setting the options, + * and grabbing the output. + * + * @codeCoverageIgnore + */ + protected function sendRequest(array $curlOptions = []): string + { + $ch = curl_init(); + + curl_setopt_array($ch, $curlOptions); + + // Send the request and wait for a response. + $output = curl_exec($ch); + + if ($output === false) { + throw HTTPException::forCurlError((string) curl_errno($ch), curl_error($ch)); + } + + curl_close($ch); + + return $output; + } +} diff --git a/system/HTTP/ContentSecurityPolicy.php b/system/HTTP/ContentSecurityPolicy.php new file mode 100644 index 0000000..945c3e0 --- /dev/null +++ b/system/HTTP/ContentSecurityPolicy.php @@ -0,0 +1,840 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\HTTP; + +use Config\App; +use Config\ContentSecurityPolicy as ContentSecurityPolicyConfig; + +/** + * Provides tools for working with the Content-Security-Policy header + * to help defeat XSS attacks. + * + * @see http://www.w3.org/TR/CSP/ + * @see http://www.html5rocks.com/en/tutorials/security/content-security-policy/ + * @see http://content-security-policy.com/ + * @see https://www.owasp.org/index.php/Content_Security_Policy + * @see \CodeIgniter\HTTP\ContentSecurityPolicyTest + */ +class ContentSecurityPolicy +{ + /** + * CSP directives + * + * @var array + */ + protected array $directives = [ + 'base-uri' => 'baseURI', + 'child-src' => 'childSrc', + 'connect-src' => 'connectSrc', + 'default-src' => 'defaultSrc', + 'font-src' => 'fontSrc', + 'form-action' => 'formAction', + 'frame-ancestors' => 'frameAncestors', + 'frame-src' => 'frameSrc', + 'img-src' => 'imageSrc', + 'media-src' => 'mediaSrc', + 'object-src' => 'objectSrc', + 'plugin-types' => 'pluginTypes', + 'script-src' => 'scriptSrc', + 'style-src' => 'styleSrc', + 'manifest-src' => 'manifestSrc', + 'sandbox' => 'sandbox', + 'report-uri' => 'reportURI', + ]; + + /** + * Used for security enforcement + * + * @var array|string + */ + protected $baseURI = []; + + /** + * Used for security enforcement + * + * @var array|string + */ + protected $childSrc = []; + + /** + * Used for security enforcement + * + * @var array + */ + protected $connectSrc = []; + + /** + * Used for security enforcement + * + * @var array|string + */ + protected $defaultSrc = []; + + /** + * Used for security enforcement + * + * @var array|string + */ + protected $fontSrc = []; + + /** + * Used for security enforcement + * + * @var array|string + */ + protected $formAction = []; + + /** + * Used for security enforcement + * + * @var array|string + */ + protected $frameAncestors = []; + + /** + * Used for security enforcement + * + * @var array|string + */ + protected $frameSrc = []; + + /** + * Used for security enforcement + * + * @var array|string + */ + protected $imageSrc = []; + + /** + * Used for security enforcement + * + * @var array|string + */ + protected $mediaSrc = []; + + /** + * Used for security enforcement + * + * @var array|string + */ + protected $objectSrc = []; + + /** + * Used for security enforcement + * + * @var array|string + */ + protected $pluginTypes = []; + + /** + * Used for security enforcement + * + * @var array|string + */ + protected $scriptSrc = []; + + /** + * Used for security enforcement + * + * @var array|string + */ + protected $styleSrc = []; + + /** + * Used for security enforcement + * + * @var array|string + */ + protected $manifestSrc = []; + + /** + * Used for security enforcement + * + * @var array|string + */ + protected $sandbox = []; + + /** + * Used for security enforcement + * + * @var string|null + */ + protected $reportURI; + + /** + * Used for security enforcement + * + * @var bool + */ + protected $upgradeInsecureRequests = false; + + /** + * Used for security enforcement + * + * @var bool + */ + protected $reportOnly = false; + + /** + * Used for security enforcement + * + * @var array + */ + protected $validSources = [ + 'self', + 'none', + 'unsafe-inline', + 'unsafe-eval', + ]; + + /** + * Used for security enforcement + * + * @var array + */ + protected $nonces = []; + + /** + * Nonce for style + * + * @var string + */ + protected $styleNonce; + + /** + * Nonce for script + * + * @var string + */ + protected $scriptNonce; + + /** + * Nonce tag for style + * + * @var string + */ + protected $styleNonceTag = '{csp-style-nonce}'; + + /** + * Nonce tag for script + * + * @var string + */ + protected $scriptNonceTag = '{csp-script-nonce}'; + + /** + * Replace nonce tag automatically + * + * @var bool + */ + protected $autoNonce = true; + + /** + * An array of header info since we have + * to build ourself before passing to Response. + * + * @var array + */ + protected $tempHeaders = []; + + /** + * An array of header info to build + * that should only be reported. + * + * @var array + */ + protected $reportOnlyHeaders = []; + + /** + * Whether Content Security Policy is being enforced. + * + * @var bool + */ + protected $CSPEnabled = false; + + /** + * Constructor. + * + * Stores our default values from the Config file. + */ + public function __construct(ContentSecurityPolicyConfig $config) + { + $appConfig = config(App::class); + $this->CSPEnabled = $appConfig->CSPEnabled; + + foreach (get_object_vars($config) as $setting => $value) { + if (property_exists($this, $setting)) { + $this->{$setting} = $value; + } + } + + if (! is_array($this->styleSrc)) { + $this->styleSrc = [$this->styleSrc]; + } + + if (! is_array($this->scriptSrc)) { + $this->scriptSrc = [$this->scriptSrc]; + } + } + + /** + * Whether Content Security Policy is being enforced. + */ + public function enabled(): bool + { + return $this->CSPEnabled; + } + + /** + * Get the nonce for the style tag. + */ + public function getStyleNonce(): string + { + if ($this->styleNonce === null) { + $this->styleNonce = bin2hex(random_bytes(12)); + $this->styleSrc[] = 'nonce-' . $this->styleNonce; + } + + return $this->styleNonce; + } + + /** + * Get the nonce for the script tag. + */ + public function getScriptNonce(): string + { + if ($this->scriptNonce === null) { + $this->scriptNonce = bin2hex(random_bytes(12)); + $this->scriptSrc[] = 'nonce-' . $this->scriptNonce; + } + + return $this->scriptNonce; + } + + /** + * Compiles and sets the appropriate headers in the request. + * + * Should be called just prior to sending the response to the user agent. + * + * @return void + */ + public function finalize(ResponseInterface $response) + { + if ($this->autoNonce) { + $this->generateNonces($response); + } + + $this->buildHeaders($response); + } + + /** + * If TRUE, nothing will be restricted. Instead all violations will + * be reported to the reportURI for monitoring. This is useful when + * you are just starting to implement the policy, and will help + * determine what errors need to be addressed before you turn on + * all filtering. + * + * @return $this + */ + public function reportOnly(bool $value = true) + { + $this->reportOnly = $value; + + return $this; + } + + /** + * Adds a new base_uri value. Can be either a URI class or a simple string. + * + * base_uri restricts the URLs that can appear in a page's element. + * + * @see http://www.w3.org/TR/CSP/#directive-base-uri + * + * @param array|string $uri + * + * @return $this + */ + public function addBaseURI($uri, ?bool $explicitReporting = null) + { + $this->addOption($uri, 'baseURI', $explicitReporting ?? $this->reportOnly); + + return $this; + } + + /** + * Adds a new valid endpoint for a form's action. Can be either + * a URI class or a simple string. + * + * child-src lists the URLs for workers and embedded frame contents. + * For example: child-src https://youtube.com would enable embedding + * videos from YouTube but not from other origins. + * + * @see http://www.w3.org/TR/CSP/#directive-child-src + * + * @param array|string $uri + * + * @return $this + */ + public function addChildSrc($uri, ?bool $explicitReporting = null) + { + $this->addOption($uri, 'childSrc', $explicitReporting ?? $this->reportOnly); + + return $this; + } + + /** + * Adds a new valid endpoint for a form's action. Can be either + * a URI class or a simple string. + * + * connect-src limits the origins to which you can connect + * (via XHR, WebSockets, and EventSource). + * + * @see http://www.w3.org/TR/CSP/#directive-connect-src + * + * @param array|string $uri + * + * @return $this + */ + public function addConnectSrc($uri, ?bool $explicitReporting = null) + { + $this->addOption($uri, 'connectSrc', $explicitReporting ?? $this->reportOnly); + + return $this; + } + + /** + * Adds a new valid endpoint for a form's action. Can be either + * a URI class or a simple string. + * + * default_src is the URI that is used for many of the settings when + * no other source has been set. + * + * @see http://www.w3.org/TR/CSP/#directive-default-src + * + * @param array|string $uri + * + * @return $this + */ + public function setDefaultSrc($uri, ?bool $explicitReporting = null) + { + $this->defaultSrc = [(string) $uri => $explicitReporting ?? $this->reportOnly]; + + return $this; + } + + /** + * Adds a new valid endpoint for a form's action. Can be either + * a URI class or a simple string. + * + * font-src specifies the origins that can serve web fonts. + * + * @see http://www.w3.org/TR/CSP/#directive-font-src + * + * @param array|string $uri + * + * @return $this + */ + public function addFontSrc($uri, ?bool $explicitReporting = null) + { + $this->addOption($uri, 'fontSrc', $explicitReporting ?? $this->reportOnly); + + return $this; + } + + /** + * Adds a new valid endpoint for a form's action. Can be either + * a URI class or a simple string. + * + * @see http://www.w3.org/TR/CSP/#directive-form-action + * + * @param array|string $uri + * + * @return $this + */ + public function addFormAction($uri, ?bool $explicitReporting = null) + { + $this->addOption($uri, 'formAction', $explicitReporting ?? $this->reportOnly); + + return $this; + } + + /** + * Adds a new resource that should allow embedding the resource using + * ,