From 428140b9313b372c648b7eed0f88c71fbf97a0d9 Mon Sep 17 00:00:00 2001 From: Saeed Vaziry <61919774+saeedvaziry@users.noreply.github.com> Date: Thu, 14 Mar 2024 20:03:43 +0100 Subject: [PATCH] refactoring (#116) - refactoring architecture - fix incomplete ssh logs - code editor for scripts in the app - remove Jobs and SSHCommands --- .gitignore | 2 + .prettierrc | 3 +- app/Actions/CronJob/CreateCronJob.php | 10 +- app/Actions/CronJob/DeleteCronJob.php | 10 +- app/Actions/Database/CreateBackup.php | 2 +- app/Actions/Database/CreateDatabase.php | 2 - app/Actions/Database/CreateDatabaseUser.php | 6 +- app/Actions/Database/RestoreBackup.php | 39 + app/Actions/Database/RunBackup.php | 30 + app/Actions/FirewallRule/CreateRule.php | 4 +- app/Actions/FirewallRule/DeleteRule.php | 2 +- app/Actions/PHP/GetPHPIni.php | 5 +- app/Actions/PHP/InstallNewPHP.php | 9 +- app/Actions/PHP/InstallPHPExtension.php | 14 +- app/Actions/PHP/UninstallPHP.php | 11 +- app/Actions/Queue/CreateQueue.php | 18 +- app/Actions/Queue/DeleteQueue.php | 14 + app/Actions/Queue/ManageQueue.php | 52 + app/Actions/SSL/CreateSSL.php | 25 +- app/Actions/SSL/DeleteSSL.php | 14 + app/Actions/Server/CreateServer.php | 56 +- app/Actions/Server/RebootServer.php | 3 +- .../ServerProvider/CreateServerProvider.php | 2 +- app/Actions/Service/InstallPHPMyAdmin.php | 60 - app/Actions/Service/Manage.php | 63 + app/Actions/Site/CreateRedirect.php | 51 - app/Actions/Site/CreateSite.php | 24 +- app/Actions/Site/DeleteSite.php | 14 + app/Actions/Site/Deploy.php | 51 + app/Actions/Site/EditSite.php | 41 - app/Actions/Site/GetSites.php | 14 - app/Actions/Site/UpdateBranch.php | 8 +- app/Actions/Site/UpdateDeploymentScript.php | 2 +- app/Actions/Site/UpdateEnv.php | 14 +- app/Actions/SshKey/DeleteKeyFromServer.php | 19 + app/Actions/SshKey/DeployKeyToServer.php | 38 + app/Console/Commands/RunBackup.php | 2 +- app/Contracts/SSHCommand.php | 10 - .../DeploymentScriptIsEmptyException.php | 9 + app/Exceptions/SSHCommandError.php | 4 +- app/Exceptions/SSHConnectionError.php | 4 +- .../{InstallationFailed.php => SSHError.php} | 2 +- app/Exceptions/ServiceInstallationFailed.php | 10 + app/Facades/Notifier.php | 4 +- app/Facades/SSH.php | 10 +- app/Helpers/Notifier.php | 4 +- app/Helpers/SSH.php | 39 +- .../Controllers/ApplicationController.php | 20 +- app/Http/Controllers/DaemonController.php | 15 - .../Controllers/DatabaseBackupController.php | 14 +- app/Http/Controllers/GitHookController.php | 10 +- app/Http/Controllers/QueueController.php | 6 +- app/Http/Controllers/SSHKeyController.php | 27 +- app/Http/Controllers/SSLController.php | 5 +- app/Http/Controllers/ServerLogController.php | 2 +- .../Controllers/ServerSettingController.php | 5 +- app/Http/Controllers/SiteController.php | 3 +- .../Middleware/ServerIsReadyMiddleware.php | 6 +- app/Jobs/Backup/RestoreDatabase.php | 36 - app/Jobs/Backup/RunBackup.php | 33 - .../Installation/ContinueInstallation.php | 37 - app/Jobs/Installation/Initialize.php | 72 - app/Jobs/Installation/InstallCertbot.php | 25 - app/Jobs/Installation/InstallComposer.php | 25 - app/Jobs/Installation/InstallMariadb.php | 35 - app/Jobs/Installation/InstallMysql.php | 35 - app/Jobs/Installation/InstallNginx.php | 35 - app/Jobs/Installation/InstallNodejs.php | 25 - app/Jobs/Installation/InstallPHP.php | 35 - app/Jobs/Installation/InstallPHPMyAdmin.php | 114 - app/Jobs/Installation/InstallRedis.php | 35 - app/Jobs/Installation/InstallRequirements.php | 28 - app/Jobs/Installation/InstallSupervisor.php | 35 - app/Jobs/Installation/InstallUfw.php | 35 - app/Jobs/Installation/InstallationJob.php | 16 - app/Jobs/Installation/UninstallPHP.php | 28 - app/Jobs/Installation/UninstallPHPMyAdmin.php | 66 - app/Jobs/Installation/Upgrade.php | 25 - app/Jobs/Job.php | 14 - app/Jobs/LongJob.php | 8 - app/Jobs/PHP/InstallPHPExtension.php | 47 - app/Jobs/Queue/Deploy.php | 38 - app/Jobs/Queue/GetLogs.php | 26 - app/Jobs/Queue/Manage.php | 56 - app/Jobs/Queue/Remove.php | 29 - app/Jobs/Redirect/AddToServer.php | 31 - app/Jobs/Redirect/DeleteFromServer.php | 33 - app/Jobs/Script/ExecuteOn.php | 50 - app/Jobs/Service/Install.php | 24 - app/Jobs/Service/Manage.php | 63 - app/Jobs/Site/CloneRepository.php | 35 - app/Jobs/Site/ComposerInstall.php | 34 - app/Jobs/Site/CreateVHost.php | 21 - app/Jobs/Site/DeleteSite.php | 29 - app/Jobs/Site/Deploy.php | 53 - app/Jobs/Site/DeployEnv.php | 35 - app/Jobs/Site/DeployKey.php | 42 - app/Jobs/Site/InstallWordpress.php | 109 - app/Jobs/Site/UpdateBranch.php | 42 - app/Jobs/Site/UpdateVHost.php | 21 - app/Jobs/SshKey/DeleteSshKeyFromServer.php | 39 - app/Jobs/SshKey/DeploySshKeyToServer.php | 42 - app/Jobs/Ssl/Deploy.php | 37 - app/Jobs/Ssl/Remove.php | 28 - app/Jobs/StorageProvider/DeleteFile.php | 24 - app/Models/Backup.php | 15 - app/Models/BackupFile.php | 30 +- app/Models/CronJob.php | 7 +- app/Models/DatabaseUser.php | 12 - app/Models/Deployment.php | 13 - app/Models/FirewallRule.php | 7 +- app/Models/GitHook.php | 23 +- app/Models/NotificationChannel.php | 6 +- app/Models/Queue.php | 71 +- app/Models/Redirect.php | 14 - app/Models/Script.php | 6 - app/Models/Server.php | 115 +- app/Models/ServerLog.php | 16 +- app/Models/Service.php | 134 +- app/Models/Site.php | 208 +- app/Models/SourceControl.php | 2 +- app/Models/SshKey.php | 19 - app/Models/Ssl.php | 36 +- app/Models/StorageProvider.php | 2 +- .../AbstractNotificationChannel.php | 2 +- app/NotificationChannels/Discord.php | 4 +- app/NotificationChannels/Email.php | 6 +- .../Email}/NotificationMail.php | 2 +- .../NotificationChannel.php | 6 +- app/NotificationChannels/Slack.php | 4 +- app/NotificationChannels/Telegram.php | 4 +- app/Notifications/AbstractNotification.php | 1 - .../NotificationInterface.php} | 4 +- app/Providers/AppServiceProvider.php | 4 - app/SSH/Composer/Composer.php | 22 + .../SSH/Composer/scripts}/composer-install.sh | 0 app/SSH/Cron/Cron.php | 27 + app/SSH/Git/Git.php | 38 + .../SSH/Git/scripts/checkout.sh | 0 .../SSH/Git/scripts/clone.sh | 0 app/SSH/HasScripts.php | 20 + app/SSH/OS/OS.php | 138 + .../SSH/OS/scripts}/create-user.sh | 0 app/SSH/OS/scripts/delete-ssh-key.sh | 1 + .../SSH/OS/scripts}/deploy-ssh-key.sh | 0 .../SSH/OS/scripts}/edit-file.sh | 0 .../SSH/OS/scripts}/generate-ssh-key.sh | 0 .../SSH/OS/scripts}/get-public-key.sh | 0 app/SSH/OS/scripts/install-dependencies.sh | 3 + app/SSH/OS/scripts/read-file.sh | 1 + .../SSH/OS/scripts}/read-ssh-key.sh | 0 .../system => app/SSH/OS/scripts}/reboot.sh | 0 .../SSH/OS/scripts}/run-script.sh | 0 app/SSH/OS/scripts/upgrade.sh | 9 + .../Services/Database/AbstractDatabase.php | 32 + .../Services/Database}/Database.php | 2 +- app/SSH/Services/Database/Mariadb.php | 8 + app/SSH/Services/Database/Mysql.php | 122 + .../Database/scripts/mariadb/install-10.sh | 4 +- .../Database/scripts}/mysql/backup.sh | 0 .../Database/scripts}/mysql/create-user.sh | 0 .../Database/scripts}/mysql/create.sh | 0 .../Database/scripts}/mysql/delete-user.sh | 0 .../Database/scripts}/mysql/delete.sh | 0 .../Database/scripts/mysql/install-5.7.sh | 2 +- .../Database/scripts/mysql/install-8.0.sh | 4 +- .../Services/Database/scripts}/mysql/link.sh | 0 .../Database/scripts}/mysql/restore.sh | 0 .../Database/scripts}/mysql/unlink.sh | 0 .../Services}/Firewall/AbstractFirewall.php | 6 +- .../Services/Firewall}/Firewall.php | 2 +- app/SSH/Services/Firewall/Ufw.php | 46 + .../Firewall/scripts}/ufw/add-rule.sh | 0 .../Firewall/scripts}/ufw/install-ufw.sh | 0 .../Firewall/scripts}/ufw/remove-rule.sh | 0 app/SSH/Services/PHP/PHP.php | 88 + .../PHP/scripts}/change-default-php.sh | 0 .../SSH/Services/PHP/scripts}/get-php-ini.sh | 0 .../Services/PHP/scripts}/install-composer.sh | 0 .../PHP/scripts}/install-php-extension.sh | 2 +- app/SSH/Services/PHP/scripts/install-php.sh | 13 + app/SSH/Services/PHP/scripts/uninstall-php.sh | 5 + .../PHP/scripts}/update-php-settings.sh | 0 .../ProcessManager/AbstractProcessManager.php | 6 +- .../ProcessManager}/ProcessManager.php | 2 +- .../Services}/ProcessManager/Supervisor.php | 64 +- .../scripts}/supervisor/create-worker.sh | 0 .../scripts}/supervisor/delete-worker.sh | 0 .../scripts}/supervisor/install-supervisor.sh | 0 .../scripts}/supervisor/restart-worker.sh | 0 .../scripts}/supervisor/start-worker.sh | 0 .../scripts}/supervisor/stop-worker.sh | 0 .../scripts}/supervisor/worker.conf | 0 app/SSH/Services/Redis/Redis.php | 24 + .../SSH/Services/Redis/scripts/install.sh | 2 +- app/SSH/Services/ServiceInterface.php | 8 + .../Services/Webserver/AbstractWebserver.php | 13 + app/SSH/Services/Webserver/Nginx.php | 176 + .../Services/Webserver}/Webserver.php | 4 +- .../scripts}/nginx/change-php-version.sh | 0 .../scripts/nginx}/create-custom-ssl.sh | 0 .../scripts/nginx}/create-letsencrypt-ssl.sh | 0 .../Webserver/scripts}/nginx/create-vhost.sh | 0 .../Webserver/scripts}/nginx/delete-site.sh | 0 .../Webserver/scripts}/nginx/get-vhost.sh | 0 .../Webserver/scripts/nginx/install-nginx.sh | 8 + .../Webserver/scripts}/nginx/nginx.conf | 0 .../scripts}/nginx/php-vhost-ssl.conf | 0 .../Webserver/scripts}/nginx/php-vhost.conf | 0 .../scripts}/nginx/phpmyadmin-vhost.conf | 0 .../Webserver/scripts}/nginx/redirect.conf | 0 .../scripts}/nginx/reverse-vhost-ssl.conf | 0 .../scripts}/nginx/reverse-vhost.conf | 0 .../scripts}/nginx/update-redirects.sh | 0 .../Webserver/scripts}/nginx/update-vhost.sh | 0 .../Webserver/scripts}/nginx/vhost-ssl.conf | 0 .../Webserver/scripts}/nginx/vhost.conf | 0 app/SSH/Storage/AbstractStorage.php | 13 + app/SSH/Storage/Dropbox.php | 45 + app/SSH/Storage/FTP.php | 48 + app/SSH/Storage/Storage.php | 10 + .../SSH/Storage/scripts/dropbox/download.sh | 0 .../SSH/Storage/scripts/dropbox/upload.sh | 0 .../SSH/Storage/scripts/ftp/download.sh | 0 .../SSH/Storage/scripts/ftp/upload.sh | 0 app/SSH/Systemd/Systemd.php | 51 + app/SSH/Wordpress/Wordpress.php | 31 + app/SSH/Wordpress/scripts/install.sh | 27 + app/SSHCommands/Command.php | 10 - .../CronJob/UpdateCronJobsCommand.php | 26 - .../Database/BackupDatabaseCommand.php | 26 - app/SSHCommands/Database/CreateCommand.php | 25 - .../Database/CreateUserCommand.php | 31 - app/SSHCommands/Database/DeleteCommand.php | 25 - .../Database/DeleteUserCommand.php | 26 - .../Database/InstallMariadbCommand.php | 19 - .../Database/InstallMysqlCommand.php | 27 - app/SSHCommands/Database/LinkCommand.php | 31 - .../Database/RestoreDatabaseCommand.php | 26 - app/SSHCommands/Database/UnlinkCommand.php | 29 - app/SSHCommands/Firewall/AddRuleCommand.php | 26 - app/SSHCommands/Firewall/CommandContent.php | 17 - .../Firewall/InstallUfwCommand.php | 19 - .../Firewall/RemoveRuleCommand.php | 26 - .../Installation/InstallNodejsCommand.php | 19 - .../Installation/InstallRedisCommand.php | 19 - .../InstallRequirementsCommand.php | 26 - .../Nginx/ChangeNginxPHPVersionCommand.php | 27 - .../Nginx/CreateNginxVHostCommand.php | 30 - .../Nginx/DeleteNginxSiteCommand.php | 26 - .../Nginx/GetNginxVHostCommand.php | 26 - app/SSHCommands/Nginx/InstallNginxCommand.php | 31 - .../Nginx/UpdateNginxRedirectsCommand.php | 28 - .../Nginx/UpdateNginxVHostCommand.php | 30 - .../PHP/ChangeDefaultPHPCommand.php | 25 - app/SSHCommands/PHP/GetPHPIniCommand.php | 25 - .../PHP/InstallComposerCommand.php | 19 - app/SSHCommands/PHP/InstallPHPCommand.php | 27 - .../PHP/InstallPHPExtensionCommand.php | 26 - app/SSHCommands/PHP/UninstallPHPCommand.php | 27 - .../CreateNginxPHPMyAdminVHostCommand.php | 25 - .../DeleteNginxPHPMyAdminVHostCommand.php | 25 - .../PHPMyAdmin/DownloadPHPMyAdminCommand.php | 19 - .../SSL/CreateCustomSSLCommand.php | 34 - .../SSL/CreateLetsencryptSSLCommand.php | 27 - app/SSHCommands/SSL/InstallCertbotCommand.php | 19 - app/SSHCommands/SSL/RemoveSSLCommand.php | 22 - .../Service/RestartServiceCommand.php | 25 - .../Service/ServiceStatusCommand.php | 25 - .../Service/StartServiceCommand.php | 25 - .../Service/StopServiceCommand.php | 25 - .../Storage/DownloadFromDropboxCommand.php | 27 - .../Storage/DownloadFromFTPCommand.php | 40 - .../Storage/UploadToDropboxCommand.php | 27 - .../Storage/UploadToFTPCommand.php | 40 - .../Supervisor/CreateWorkerCommand.php | 26 - .../Supervisor/DeleteWorkerCommand.php | 25 - .../Supervisor/InstallSupervisorCommand.php | 19 - .../Supervisor/RestartWorkerCommand.php | 25 - .../Supervisor/StartWorkerCommand.php | 25 - .../Supervisor/StopWorkerCommand.php | 25 - app/SSHCommands/System/CreateUserCommand.php | 27 - .../System/DeleteSshKeyCommand.php | 25 - .../System/DeploySshKeyCommand.php | 25 - app/SSHCommands/System/EditFileCommand.php | 26 - .../System/GenerateSshKeyCommand.php | 25 - .../System/GetPublicKeyCommand.php | 19 - app/SSHCommands/System/ReadFileCommand.php | 25 - app/SSHCommands/System/ReadSshKeyCommand.php | 25 - app/SSHCommands/System/RebootCommand.php | 19 - app/SSHCommands/System/RunScriptCommand.php | 26 - app/SSHCommands/System/UpgradeCommand.php | 19 - .../Website/CloneRepositoryCommand.php | 33 - .../Website/ComposerInstallCommand.php | 25 - app/SSHCommands/Website/GetEnvCommand.php | 26 - .../Website/UpdateBranchCommand.php | 26 - .../Wordpress/InstallWordpressCommand.php | 46 - .../Wordpress/UpdateWordpressCommand.php | 37 - app/ServerProviders/AWS.php | 2 +- app/ServerProviders/AbstractProvider.php | 6 +- app/ServerProviders/Custom.php | 2 +- app/ServerProviders/DigitalOcean.php | 2 +- app/ServerProviders/Hetzner.php | 2 +- app/ServerProviders/Linode.php | 2 +- .../ServerProvider.php | 6 +- app/ServerProviders/Vultr.php | 2 +- app/ServerTypes/AbstractType.php | 113 +- app/ServerTypes/Database.php | 102 +- app/ServerTypes/Regular.php | 139 +- app/{Contracts => ServerTypes}/ServerType.php | 4 +- .../Database/AbstractDatabase.php | 16 - app/ServiceHandlers/Database/Mysql.php | 138 - app/ServiceHandlers/Firewall/Ufw.php | 32 - app/ServiceHandlers/PHP.php | 30 - .../Webserver/AbstractWebserver.php | 16 - app/ServiceHandlers/Webserver/Nginx.php | 206 - app/SiteTypes/AbstractSiteType.php | 27 +- app/SiteTypes/PHPBlank.php | 26 +- app/SiteTypes/PHPSite.php | 48 +- app/{Contracts => SiteTypes}/SiteType.php | 8 +- app/SiteTypes/Wordpress.php | 93 +- .../AbstractSourceControlProvider.php | 1 - .../SourceControlProvider.php | 2 +- .../AbstractStorageProvider.php | 2 +- app/StorageProviders/Dropbox.php | 44 +- app/StorageProviders/FTP.php | 47 +- .../StorageProvider.php | 7 +- app/Support/Testing/SSHFake.php | 89 +- composer.lock | 62 +- config/core.php | 43 +- config/filesystems.php | 4 +- config/log-viewer.php | 2 +- config/queue.php | 12 +- database/factories/ServerLogFactory.php | 2 +- database/factories/StorageProviderFactory.php | 4 +- docker-compose.yml | 55 +- docker/8.1/Dockerfile | 61 - docker/8.1/php.ini | 4 - docker/8.1/start-container | 17 - docker/8.1/supervisord.conf | 14 - phpunit.xml | 2 +- public/build/assets/app-5db42bc3.css | 1 - public/build/assets/app-e2e337dc.css | 1 + public/build/manifest.json | 2 +- public/static/libs/ace/ace.js | 21370 +++++++++++++++- public/static/libs/ace/mode-sh.js | 1 - public/static/libs/ace/theme-github.js | 5 - public/static/libs/ace/theme-one-dark.js | 5 - .../installation/install-requirements.sh | 3 - resources/commands/php/install-php.sh | 13 - resources/commands/php/uninstall-php.sh | 5 - resources/commands/system/read-file.sh | 1 - resources/commands/system/upgrade.sh | 7 - .../commands/webserver/nginx/install-nginx.sh | 8 - resources/commands/website/get-env.sh | 1 - .../application/auto-deployment.blade.php | 7 +- resources/views/application/deploy.blade.php | 2 +- .../application/deployment-script.blade.php | 9 +- .../application/deployments-list.blade.php | 8 +- resources/views/application/env.blade.php | 15 +- .../views/components/alert-danger.blade.php | 5 + .../views/components/alert-success.blade.php | 5 + .../views/components/alert-warning.blade.php | 5 + resources/views/components/alert.blade.php | 5 + .../views/components/code-editor.blade.php | 45 + .../views/components/console-view.blade.php | 2 +- resources/views/components/live.blade.php | 8 +- resources/views/cronjobs/index.blade.php | 2 +- .../create-cronjob.blade.php | 4 +- .../cronjobs-list.blade.php | 6 +- .../{partrials => partials}/status.blade.php | 0 resources/views/daemons/index.blade.php | 3 - resources/views/layouts/app.blade.php | 278 +- .../layouts/partials/color-scheme.blade.php | 5 + resources/views/layouts/site.blade.php | 73 +- resources/views/php/index.blade.php | 6 + .../php/partials/install-extension.blade.php | 10 +- .../php/partials/installed-versions.blade.php | 4 +- .../queues/partials/create-queue.blade.php | 7 +- .../views/queues/partials/status.blade.php | 4 - .../partials/add-existing-key.blade.php | 2 +- .../servers/partials/create-server.blade.php | 11 +- .../services/partials/services-list.blade.php | 2 +- .../partials/change-php-version.blade.php | 4 +- .../partials/update-v-host.blade.php | 42 +- .../sites/partials/create-site.blade.php | 4 +- tests/Feature/ApplicationTest.php | 99 +- .../Feature/Auth/PasswordConfirmationTest.php | 6 +- tests/Feature/Auth/PasswordResetTest.php | 2 +- tests/Feature/CronjobTest.php | 38 +- tests/Feature/DatabaseBackupTest.php | 23 +- tests/Feature/DatabaseTest.php | 8 +- tests/Feature/DatabaseUserTest.php | 4 +- tests/Feature/FirewallTest.php | 4 +- tests/Feature/NotificationChannelsTest.php | 27 +- tests/Feature/PHPTest.php | 40 +- tests/Feature/ProjectsTest.php | 16 +- tests/Feature/QueuesTest.php | 10 +- tests/Feature/ServerKeysTest.php | 23 +- tests/Feature/ServerProvidersTest.php | 11 +- tests/Feature/ServerTest.php | 59 +- tests/Feature/ServicesTest.php | 109 +- tests/Feature/SitesTest.php | 27 +- tests/Feature/SourceControlsTest.php | 4 +- tests/Feature/SshKeysTest.php | 11 +- tests/Feature/SslTest.php | 17 +- tests/Feature/StorageProvidersTest.php | 11 +- tests/TestCase.php | 26 +- tests/Unit/NotificationChannels/EmailTest.php | 2 +- .../CronJob/UpdateCronJobsCommandTest.php | 18 - .../Database/BackupDatabaseCommandTest.php | 20 - .../Database/CreateCommandTest.php | 18 - .../Database/CreateUserCommandTest.php | 18 - .../Database/DeleteCommandTest.php | 16 - .../Database/DeleteUserCommandTest.php | 18 - .../Database/InstallMariadbCommandTest.php | 31 - .../Database/InstallMysqlCommandTest.php | 61 - .../SSHCommands/Database/LinkCommandTest.php | 28 - .../Database/UnlinkCommandTest.php | 24 - .../Firewall/AddRuleCommandTest.php | 30 - .../Firewall/InstallUfwCommandTest.php | 46 - .../Firewall/RemoveRuleCommandTest.php | 31 - .../Installation/InstallNodejsCommandTest.php | 24 - .../Installation/InstallRedisCommandTest.php | 26 - .../InstallRequirementsCommandTest.php | 22 - .../ChangeNginxPHPVersionCommandTest.php | 28 - .../Nginx/CreateNginxVHostCommandTest.php | 46 - .../Nginx/DeleteNginxSiteCommandTest.php | 26 - .../Nginx/InstallNginxCommandTest.php | 115 - .../Nginx/UpdateNginxRedirectsCommandTest.php | 26 - .../Nginx/UpdateNginxVHostCommandTest.php | 26 - .../PHP/ChangeDefaultPHPCommandTest.php | 30 - .../SSHCommands/PHP/GetPHPIniCommandTest.php | 20 - .../PHP/InstallComposerCommandTest.php | 26 - .../SSHCommands/PHP/InstallPHPCommandTest.php | 32 - .../PHP/InstallPHPExtensionCommandTest.php | 24 - .../PHP/UninstallPHPCommandTest.php | 24 - .../CreateNginxPHPMyAdminVHostCommandTest.php | 36 - .../DeleteNginxPHPMyAdminVHostCommandTest.php | 30 - .../DownloadPHPMyAdminCommandTest.php | 40 - .../SSL/CreateCustomSSLCommandTest.php | 32 - .../SSL/CreateLetsencryptSSLCommandTest.php | 22 - .../SSL/InstallCertbotCommandTest.php | 20 - .../SSHCommands/SSL/RemoveSSLCommandTest.php | 20 - .../Service/RestartServiceCommandTest.php | 22 - .../Service/ServiceStatusCommandTest.php | 20 - .../Service/StartServiceCommandTest.php | 22 - .../Service/StopServiceCommandTest.php | 22 - .../DownloadFromDropboxCommandTest.php | 23 - .../Storage/DownloadFromFTPCommandTest.php | 29 - .../Storage/UploadToDropboxCommandTest.php | 25 - .../Storage/UploadToFTPCommandTest.php | 29 - .../Supervisor/CreateWorkerCommandTest.php | 40 - .../Supervisor/DeleteWorkerCommandTest.php | 38 - .../InstallSupervisorCommandTest.php | 24 - .../Supervisor/RestartWorkerCommandTest.php | 22 - .../Supervisor/StartWorkerCommandTest.php | 22 - .../Supervisor/StopWorkerCommandTest.php | 22 - .../System/CreateUserCommandTest.php | 30 - .../System/DeleteSshKeyCommandTest.php | 20 - .../System/DeploySshKeyCommandTest.php | 22 - .../System/EditFileCommandTest.php | 22 - .../System/GenerateSshKeyCommandTest.php | 20 - .../System/GetPublicKeyCommandTest.php | 20 - .../System/ReadFileCommandTest.php | 20 - .../System/ReadSshKeyCommandTest.php | 20 - .../SSHCommands/System/RebootCommandTest.php | 22 - .../System/RunScriptCommandTest.php | 24 - .../SSHCommands/System/UpgradeCommandTest.php | 26 - .../Website/CloneRepositoryCommandTest.php | 46 - .../Website/ComposerInstallCommandTest.php | 26 - .../Website/UpdateBranchCommandTest.php | 26 - 472 files changed, 24110 insertions(+), 8159 deletions(-) create mode 100644 app/Actions/Database/RestoreBackup.php create mode 100644 app/Actions/Database/RunBackup.php create mode 100644 app/Actions/Queue/DeleteQueue.php create mode 100644 app/Actions/Queue/ManageQueue.php create mode 100644 app/Actions/SSL/DeleteSSL.php delete mode 100644 app/Actions/Service/InstallPHPMyAdmin.php create mode 100644 app/Actions/Service/Manage.php delete mode 100644 app/Actions/Site/CreateRedirect.php create mode 100644 app/Actions/Site/DeleteSite.php create mode 100644 app/Actions/Site/Deploy.php delete mode 100755 app/Actions/Site/EditSite.php delete mode 100755 app/Actions/Site/GetSites.php create mode 100644 app/Actions/SshKey/DeleteKeyFromServer.php create mode 100644 app/Actions/SshKey/DeployKeyToServer.php delete mode 100755 app/Contracts/SSHCommand.php create mode 100644 app/Exceptions/DeploymentScriptIsEmptyException.php rename app/Exceptions/{InstallationFailed.php => SSHError.php} (58%) mode change 100644 => 100755 create mode 100644 app/Exceptions/ServiceInstallationFailed.php delete mode 100644 app/Http/Controllers/DaemonController.php delete mode 100644 app/Jobs/Backup/RestoreDatabase.php delete mode 100644 app/Jobs/Backup/RunBackup.php delete mode 100644 app/Jobs/Installation/ContinueInstallation.php delete mode 100755 app/Jobs/Installation/Initialize.php delete mode 100755 app/Jobs/Installation/InstallCertbot.php delete mode 100755 app/Jobs/Installation/InstallComposer.php delete mode 100755 app/Jobs/Installation/InstallMariadb.php delete mode 100755 app/Jobs/Installation/InstallMysql.php delete mode 100755 app/Jobs/Installation/InstallNginx.php delete mode 100755 app/Jobs/Installation/InstallNodejs.php delete mode 100755 app/Jobs/Installation/InstallPHP.php delete mode 100644 app/Jobs/Installation/InstallPHPMyAdmin.php delete mode 100755 app/Jobs/Installation/InstallRedis.php delete mode 100755 app/Jobs/Installation/InstallRequirements.php delete mode 100755 app/Jobs/Installation/InstallSupervisor.php delete mode 100755 app/Jobs/Installation/InstallUfw.php delete mode 100755 app/Jobs/Installation/InstallationJob.php delete mode 100755 app/Jobs/Installation/UninstallPHP.php delete mode 100644 app/Jobs/Installation/UninstallPHPMyAdmin.php delete mode 100755 app/Jobs/Installation/Upgrade.php delete mode 100755 app/Jobs/Job.php delete mode 100755 app/Jobs/LongJob.php delete mode 100755 app/Jobs/PHP/InstallPHPExtension.php delete mode 100644 app/Jobs/Queue/Deploy.php delete mode 100644 app/Jobs/Queue/GetLogs.php delete mode 100644 app/Jobs/Queue/Manage.php delete mode 100644 app/Jobs/Queue/Remove.php delete mode 100644 app/Jobs/Redirect/AddToServer.php delete mode 100644 app/Jobs/Redirect/DeleteFromServer.php delete mode 100644 app/Jobs/Script/ExecuteOn.php delete mode 100644 app/Jobs/Service/Install.php delete mode 100644 app/Jobs/Service/Manage.php delete mode 100755 app/Jobs/Site/CloneRepository.php delete mode 100755 app/Jobs/Site/ComposerInstall.php delete mode 100755 app/Jobs/Site/CreateVHost.php delete mode 100755 app/Jobs/Site/DeleteSite.php delete mode 100644 app/Jobs/Site/Deploy.php delete mode 100644 app/Jobs/Site/DeployEnv.php delete mode 100755 app/Jobs/Site/DeployKey.php delete mode 100755 app/Jobs/Site/InstallWordpress.php delete mode 100644 app/Jobs/Site/UpdateBranch.php delete mode 100755 app/Jobs/Site/UpdateVHost.php delete mode 100644 app/Jobs/SshKey/DeleteSshKeyFromServer.php delete mode 100644 app/Jobs/SshKey/DeploySshKeyToServer.php delete mode 100644 app/Jobs/Ssl/Deploy.php delete mode 100644 app/Jobs/Ssl/Remove.php delete mode 100644 app/Jobs/StorageProvider/DeleteFile.php rename app/{Mail => NotificationChannels/Email}/NotificationMail.php (91%) rename app/{Contracts => NotificationChannels}/NotificationChannel.php (56%) rename app/{Contracts/Notification.php => Notifications/NotificationInterface.php} (84%) create mode 100644 app/SSH/Composer/Composer.php rename {resources/commands/website => app/SSH/Composer/scripts}/composer-install.sh (100%) create mode 100644 app/SSH/Cron/Cron.php create mode 100644 app/SSH/Git/Git.php rename resources/commands/website/update-branch.sh => app/SSH/Git/scripts/checkout.sh (100%) rename resources/commands/website/clone-repository.sh => app/SSH/Git/scripts/clone.sh (100%) create mode 100644 app/SSH/HasScripts.php create mode 100644 app/SSH/OS/OS.php rename {resources/commands/system => app/SSH/OS/scripts}/create-user.sh (100%) create mode 100644 app/SSH/OS/scripts/delete-ssh-key.sh rename {resources/commands/system => app/SSH/OS/scripts}/deploy-ssh-key.sh (100%) rename {resources/commands/system => app/SSH/OS/scripts}/edit-file.sh (100%) rename {resources/commands/system => app/SSH/OS/scripts}/generate-ssh-key.sh (100%) rename {resources/commands/system => app/SSH/OS/scripts}/get-public-key.sh (100%) create mode 100755 app/SSH/OS/scripts/install-dependencies.sh create mode 100644 app/SSH/OS/scripts/read-file.sh rename {resources/commands/system => app/SSH/OS/scripts}/read-ssh-key.sh (100%) rename {resources/commands/system => app/SSH/OS/scripts}/reboot.sh (100%) rename {resources/commands/system => app/SSH/OS/scripts}/run-script.sh (100%) create mode 100755 app/SSH/OS/scripts/upgrade.sh create mode 100755 app/SSH/Services/Database/AbstractDatabase.php rename app/{Contracts => SSH/Services/Database}/Database.php (94%) create mode 100644 app/SSH/Services/Database/Mariadb.php create mode 100755 app/SSH/Services/Database/Mysql.php rename resources/commands/database/install-mariadb.sh => app/SSH/Services/Database/scripts/mariadb/install-10.sh (62%) rename {resources/commands/database => app/SSH/Services/Database/scripts}/mysql/backup.sh (100%) rename {resources/commands/database => app/SSH/Services/Database/scripts}/mysql/create-user.sh (100%) rename {resources/commands/database => app/SSH/Services/Database/scripts}/mysql/create.sh (100%) rename {resources/commands/database => app/SSH/Services/Database/scripts}/mysql/delete-user.sh (100%) rename {resources/commands/database => app/SSH/Services/Database/scripts}/mysql/delete.sh (100%) rename resources/commands/database/install-mysql.sh => app/SSH/Services/Database/scripts/mysql/install-5.7.sh (79%) rename resources/commands/database/install-mysql-8.sh => app/SSH/Services/Database/scripts/mysql/install-8.0.sh (77%) rename {resources/commands/database => app/SSH/Services/Database/scripts}/mysql/link.sh (100%) rename {resources/commands/database => app/SSH/Services/Database/scripts}/mysql/restore.sh (100%) rename {resources/commands/database => app/SSH/Services/Database/scripts}/mysql/unlink.sh (100%) rename app/{ServiceHandlers => SSH/Services}/Firewall/AbstractFirewall.php (53%) rename app/{Contracts => SSH/Services/Firewall}/Firewall.php (87%) create mode 100755 app/SSH/Services/Firewall/Ufw.php rename {resources/commands/firewall => app/SSH/Services/Firewall/scripts}/ufw/add-rule.sh (100%) rename {resources/commands/firewall => app/SSH/Services/Firewall/scripts}/ufw/install-ufw.sh (100%) rename {resources/commands/firewall => app/SSH/Services/Firewall/scripts}/ufw/remove-rule.sh (100%) create mode 100644 app/SSH/Services/PHP/PHP.php rename {resources/commands/php => app/SSH/Services/PHP/scripts}/change-default-php.sh (100%) rename {resources/commands/php => app/SSH/Services/PHP/scripts}/get-php-ini.sh (100%) rename {resources/commands/php => app/SSH/Services/PHP/scripts}/install-composer.sh (100%) rename {resources/commands/php => app/SSH/Services/PHP/scripts}/install-php-extension.sh (55%) create mode 100755 app/SSH/Services/PHP/scripts/install-php.sh create mode 100755 app/SSH/Services/PHP/scripts/uninstall-php.sh rename {resources/commands/php => app/SSH/Services/PHP/scripts}/update-php-settings.sh (100%) rename app/{ServiceHandlers => SSH/Services}/ProcessManager/AbstractProcessManager.php (50%) rename app/{Contracts => SSH/Services/ProcessManager}/ProcessManager.php (93%) rename app/{ServiceHandlers => SSH/Services}/ProcessManager/Supervisor.php (61%) rename {resources/commands => app/SSH/Services/ProcessManager/scripts}/supervisor/create-worker.sh (100%) rename {resources/commands => app/SSH/Services/ProcessManager/scripts}/supervisor/delete-worker.sh (100%) rename {resources/commands => app/SSH/Services/ProcessManager/scripts}/supervisor/install-supervisor.sh (100%) rename {resources/commands => app/SSH/Services/ProcessManager/scripts}/supervisor/restart-worker.sh (100%) rename {resources/commands => app/SSH/Services/ProcessManager/scripts}/supervisor/start-worker.sh (100%) rename {resources/commands => app/SSH/Services/ProcessManager/scripts}/supervisor/stop-worker.sh (100%) rename {resources/commands => app/SSH/Services/ProcessManager/scripts}/supervisor/worker.conf (100%) create mode 100644 app/SSH/Services/Redis/Redis.php rename resources/commands/installation/install-redis.sh => app/SSH/Services/Redis/scripts/install.sh (64%) create mode 100644 app/SSH/Services/ServiceInterface.php create mode 100755 app/SSH/Services/Webserver/AbstractWebserver.php create mode 100755 app/SSH/Services/Webserver/Nginx.php rename app/{Contracts => SSH/Services/Webserver}/Webserver.php (83%) rename {resources/commands/webserver => app/SSH/Services/Webserver/scripts}/nginx/change-php-version.sh (100%) rename {resources/commands/ssl => app/SSH/Services/Webserver/scripts/nginx}/create-custom-ssl.sh (100%) rename {resources/commands/ssl => app/SSH/Services/Webserver/scripts/nginx}/create-letsencrypt-ssl.sh (100%) rename {resources/commands/webserver => app/SSH/Services/Webserver/scripts}/nginx/create-vhost.sh (100%) rename {resources/commands/webserver => app/SSH/Services/Webserver/scripts}/nginx/delete-site.sh (100%) rename {resources/commands/webserver => app/SSH/Services/Webserver/scripts}/nginx/get-vhost.sh (100%) create mode 100755 app/SSH/Services/Webserver/scripts/nginx/install-nginx.sh rename {resources/commands/webserver => app/SSH/Services/Webserver/scripts}/nginx/nginx.conf (100%) rename {resources/commands/webserver => app/SSH/Services/Webserver/scripts}/nginx/php-vhost-ssl.conf (100%) rename {resources/commands/webserver => app/SSH/Services/Webserver/scripts}/nginx/php-vhost.conf (100%) rename {resources/commands/webserver => app/SSH/Services/Webserver/scripts}/nginx/phpmyadmin-vhost.conf (100%) rename {resources/commands/webserver => app/SSH/Services/Webserver/scripts}/nginx/redirect.conf (100%) rename {resources/commands/webserver => app/SSH/Services/Webserver/scripts}/nginx/reverse-vhost-ssl.conf (100%) rename {resources/commands/webserver => app/SSH/Services/Webserver/scripts}/nginx/reverse-vhost.conf (100%) rename {resources/commands/webserver => app/SSH/Services/Webserver/scripts}/nginx/update-redirects.sh (100%) rename {resources/commands/webserver => app/SSH/Services/Webserver/scripts}/nginx/update-vhost.sh (100%) rename {resources/commands/webserver => app/SSH/Services/Webserver/scripts}/nginx/vhost-ssl.conf (100%) rename {resources/commands/webserver => app/SSH/Services/Webserver/scripts}/nginx/vhost.conf (100%) create mode 100644 app/SSH/Storage/AbstractStorage.php create mode 100644 app/SSH/Storage/Dropbox.php create mode 100644 app/SSH/Storage/FTP.php create mode 100644 app/SSH/Storage/Storage.php rename resources/commands/storage/download-from-dropbox.sh => app/SSH/Storage/scripts/dropbox/download.sh (100%) rename resources/commands/storage/upload-to-dropbox.sh => app/SSH/Storage/scripts/dropbox/upload.sh (100%) rename resources/commands/storage/download-from-ftp.sh => app/SSH/Storage/scripts/ftp/download.sh (100%) rename resources/commands/storage/upload-to-ftp.sh => app/SSH/Storage/scripts/ftp/upload.sh (100%) create mode 100644 app/SSH/Systemd/Systemd.php create mode 100644 app/SSH/Wordpress/Wordpress.php create mode 100644 app/SSH/Wordpress/scripts/install.sh delete mode 100755 app/SSHCommands/Command.php delete mode 100755 app/SSHCommands/CronJob/UpdateCronJobsCommand.php delete mode 100644 app/SSHCommands/Database/BackupDatabaseCommand.php delete mode 100755 app/SSHCommands/Database/CreateCommand.php delete mode 100755 app/SSHCommands/Database/CreateUserCommand.php delete mode 100755 app/SSHCommands/Database/DeleteCommand.php delete mode 100755 app/SSHCommands/Database/DeleteUserCommand.php delete mode 100755 app/SSHCommands/Database/InstallMariadbCommand.php delete mode 100755 app/SSHCommands/Database/InstallMysqlCommand.php delete mode 100755 app/SSHCommands/Database/LinkCommand.php delete mode 100644 app/SSHCommands/Database/RestoreDatabaseCommand.php delete mode 100755 app/SSHCommands/Database/UnlinkCommand.php delete mode 100755 app/SSHCommands/Firewall/AddRuleCommand.php delete mode 100755 app/SSHCommands/Firewall/CommandContent.php delete mode 100755 app/SSHCommands/Firewall/InstallUfwCommand.php delete mode 100755 app/SSHCommands/Firewall/RemoveRuleCommand.php delete mode 100755 app/SSHCommands/Installation/InstallNodejsCommand.php delete mode 100755 app/SSHCommands/Installation/InstallRedisCommand.php delete mode 100755 app/SSHCommands/Installation/InstallRequirementsCommand.php delete mode 100755 app/SSHCommands/Nginx/ChangeNginxPHPVersionCommand.php delete mode 100755 app/SSHCommands/Nginx/CreateNginxVHostCommand.php delete mode 100755 app/SSHCommands/Nginx/DeleteNginxSiteCommand.php delete mode 100755 app/SSHCommands/Nginx/GetNginxVHostCommand.php delete mode 100755 app/SSHCommands/Nginx/InstallNginxCommand.php delete mode 100755 app/SSHCommands/Nginx/UpdateNginxRedirectsCommand.php delete mode 100755 app/SSHCommands/Nginx/UpdateNginxVHostCommand.php delete mode 100755 app/SSHCommands/PHP/ChangeDefaultPHPCommand.php delete mode 100755 app/SSHCommands/PHP/GetPHPIniCommand.php delete mode 100755 app/SSHCommands/PHP/InstallComposerCommand.php delete mode 100755 app/SSHCommands/PHP/InstallPHPCommand.php delete mode 100755 app/SSHCommands/PHP/InstallPHPExtensionCommand.php delete mode 100755 app/SSHCommands/PHP/UninstallPHPCommand.php delete mode 100755 app/SSHCommands/PHPMyAdmin/CreateNginxPHPMyAdminVHostCommand.php delete mode 100755 app/SSHCommands/PHPMyAdmin/DeleteNginxPHPMyAdminVHostCommand.php delete mode 100644 app/SSHCommands/PHPMyAdmin/DownloadPHPMyAdminCommand.php delete mode 100755 app/SSHCommands/SSL/CreateCustomSSLCommand.php delete mode 100755 app/SSHCommands/SSL/CreateLetsencryptSSLCommand.php delete mode 100755 app/SSHCommands/SSL/InstallCertbotCommand.php delete mode 100755 app/SSHCommands/SSL/RemoveSSLCommand.php delete mode 100644 app/SSHCommands/Service/RestartServiceCommand.php delete mode 100755 app/SSHCommands/Service/ServiceStatusCommand.php delete mode 100644 app/SSHCommands/Service/StartServiceCommand.php delete mode 100644 app/SSHCommands/Service/StopServiceCommand.php delete mode 100644 app/SSHCommands/Storage/DownloadFromDropboxCommand.php delete mode 100644 app/SSHCommands/Storage/DownloadFromFTPCommand.php delete mode 100644 app/SSHCommands/Storage/UploadToDropboxCommand.php delete mode 100644 app/SSHCommands/Storage/UploadToFTPCommand.php delete mode 100644 app/SSHCommands/Supervisor/CreateWorkerCommand.php delete mode 100644 app/SSHCommands/Supervisor/DeleteWorkerCommand.php delete mode 100755 app/SSHCommands/Supervisor/InstallSupervisorCommand.php delete mode 100644 app/SSHCommands/Supervisor/RestartWorkerCommand.php delete mode 100644 app/SSHCommands/Supervisor/StartWorkerCommand.php delete mode 100644 app/SSHCommands/Supervisor/StopWorkerCommand.php delete mode 100755 app/SSHCommands/System/CreateUserCommand.php delete mode 100755 app/SSHCommands/System/DeleteSshKeyCommand.php delete mode 100755 app/SSHCommands/System/DeploySshKeyCommand.php delete mode 100644 app/SSHCommands/System/EditFileCommand.php delete mode 100755 app/SSHCommands/System/GenerateSshKeyCommand.php delete mode 100755 app/SSHCommands/System/GetPublicKeyCommand.php delete mode 100644 app/SSHCommands/System/ReadFileCommand.php delete mode 100755 app/SSHCommands/System/ReadSshKeyCommand.php delete mode 100644 app/SSHCommands/System/RebootCommand.php delete mode 100644 app/SSHCommands/System/RunScriptCommand.php delete mode 100755 app/SSHCommands/System/UpgradeCommand.php delete mode 100755 app/SSHCommands/Website/CloneRepositoryCommand.php delete mode 100755 app/SSHCommands/Website/ComposerInstallCommand.php delete mode 100755 app/SSHCommands/Website/GetEnvCommand.php delete mode 100644 app/SSHCommands/Website/UpdateBranchCommand.php delete mode 100755 app/SSHCommands/Wordpress/InstallWordpressCommand.php delete mode 100755 app/SSHCommands/Wordpress/UpdateWordpressCommand.php rename app/{Contracts => ServerProviders}/ServerProvider.php (78%) rename app/{Contracts => ServerTypes}/ServerType.php (66%) delete mode 100755 app/ServiceHandlers/Database/AbstractDatabase.php delete mode 100755 app/ServiceHandlers/Database/Mysql.php delete mode 100755 app/ServiceHandlers/Firewall/Ufw.php delete mode 100644 app/ServiceHandlers/PHP.php delete mode 100755 app/ServiceHandlers/Webserver/AbstractWebserver.php delete mode 100755 app/ServiceHandlers/Webserver/Nginx.php rename app/{Contracts => SiteTypes}/SiteType.php (61%) rename app/{Contracts => SourceControlProviders}/SourceControlProvider.php (93%) rename app/{Contracts => StorageProviders}/StorageProvider.php (58%) delete mode 100644 docker/8.1/Dockerfile delete mode 100644 docker/8.1/php.ini delete mode 100644 docker/8.1/start-container delete mode 100644 docker/8.1/supervisord.conf delete mode 100644 public/build/assets/app-5db42bc3.css create mode 100644 public/build/assets/app-e2e337dc.css mode change 100755 => 100644 public/static/libs/ace/ace.js delete mode 100755 public/static/libs/ace/mode-sh.js delete mode 100755 public/static/libs/ace/theme-github.js delete mode 100644 public/static/libs/ace/theme-one-dark.js delete mode 100755 resources/commands/installation/install-requirements.sh delete mode 100755 resources/commands/php/install-php.sh delete mode 100755 resources/commands/php/uninstall-php.sh delete mode 100644 resources/commands/system/read-file.sh delete mode 100755 resources/commands/system/upgrade.sh delete mode 100755 resources/commands/webserver/nginx/install-nginx.sh delete mode 100644 resources/commands/website/get-env.sh create mode 100644 resources/views/components/alert-danger.blade.php create mode 100644 resources/views/components/alert-success.blade.php create mode 100644 resources/views/components/alert-warning.blade.php create mode 100644 resources/views/components/alert.blade.php create mode 100644 resources/views/components/code-editor.blade.php rename resources/views/cronjobs/{partrials => partials}/create-cronjob.blade.php (96%) rename resources/views/cronjobs/{partrials => partials}/cronjobs-list.blade.php (90%) rename resources/views/cronjobs/{partrials => partials}/status.blade.php (100%) delete mode 100644 resources/views/daemons/index.blade.php delete mode 100644 tests/Unit/SSHCommands/CronJob/UpdateCronJobsCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Database/BackupDatabaseCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Database/CreateCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Database/CreateUserCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Database/DeleteCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Database/DeleteUserCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Database/InstallMariadbCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Database/InstallMysqlCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Database/LinkCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Database/UnlinkCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Firewall/AddRuleCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Firewall/InstallUfwCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Firewall/RemoveRuleCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Installation/InstallNodejsCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Installation/InstallRedisCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Installation/InstallRequirementsCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Nginx/ChangeNginxPHPVersionCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Nginx/CreateNginxVHostCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Nginx/DeleteNginxSiteCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Nginx/InstallNginxCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Nginx/UpdateNginxRedirectsCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Nginx/UpdateNginxVHostCommandTest.php delete mode 100644 tests/Unit/SSHCommands/PHP/ChangeDefaultPHPCommandTest.php delete mode 100644 tests/Unit/SSHCommands/PHP/GetPHPIniCommandTest.php delete mode 100644 tests/Unit/SSHCommands/PHP/InstallComposerCommandTest.php delete mode 100644 tests/Unit/SSHCommands/PHP/InstallPHPCommandTest.php delete mode 100644 tests/Unit/SSHCommands/PHP/InstallPHPExtensionCommandTest.php delete mode 100644 tests/Unit/SSHCommands/PHP/UninstallPHPCommandTest.php delete mode 100644 tests/Unit/SSHCommands/PHPMyAdmin/CreateNginxPHPMyAdminVHostCommandTest.php delete mode 100644 tests/Unit/SSHCommands/PHPMyAdmin/DeleteNginxPHPMyAdminVHostCommandTest.php delete mode 100644 tests/Unit/SSHCommands/PHPMyAdmin/DownloadPHPMyAdminCommandTest.php delete mode 100644 tests/Unit/SSHCommands/SSL/CreateCustomSSLCommandTest.php delete mode 100644 tests/Unit/SSHCommands/SSL/CreateLetsencryptSSLCommandTest.php delete mode 100644 tests/Unit/SSHCommands/SSL/InstallCertbotCommandTest.php delete mode 100644 tests/Unit/SSHCommands/SSL/RemoveSSLCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Service/RestartServiceCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Service/ServiceStatusCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Service/StartServiceCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Service/StopServiceCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Storage/DownloadFromDropboxCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Storage/DownloadFromFTPCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Storage/UploadToDropboxCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Storage/UploadToFTPCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Supervisor/CreateWorkerCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Supervisor/DeleteWorkerCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Supervisor/InstallSupervisorCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Supervisor/RestartWorkerCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Supervisor/StartWorkerCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Supervisor/StopWorkerCommandTest.php delete mode 100644 tests/Unit/SSHCommands/System/CreateUserCommandTest.php delete mode 100644 tests/Unit/SSHCommands/System/DeleteSshKeyCommandTest.php delete mode 100644 tests/Unit/SSHCommands/System/DeploySshKeyCommandTest.php delete mode 100644 tests/Unit/SSHCommands/System/EditFileCommandTest.php delete mode 100644 tests/Unit/SSHCommands/System/GenerateSshKeyCommandTest.php delete mode 100644 tests/Unit/SSHCommands/System/GetPublicKeyCommandTest.php delete mode 100644 tests/Unit/SSHCommands/System/ReadFileCommandTest.php delete mode 100644 tests/Unit/SSHCommands/System/ReadSshKeyCommandTest.php delete mode 100644 tests/Unit/SSHCommands/System/RebootCommandTest.php delete mode 100644 tests/Unit/SSHCommands/System/RunScriptCommandTest.php delete mode 100644 tests/Unit/SSHCommands/System/UpgradeCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Website/CloneRepositoryCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Website/ComposerInstallCommandTest.php delete mode 100644 tests/Unit/SSHCommands/Website/UpdateBranchCommandTest.php diff --git a/.gitignore b/.gitignore index 8fade24..a935506 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ /public/storage /storage/*.key /storage/*.pem +/storage/test-key +/storage/test-key.pub /vendor .env .env.backup diff --git a/.prettierrc b/.prettierrc index a2ddf73..3ff0972 100644 --- a/.prettierrc +++ b/.prettierrc @@ -8,7 +8,8 @@ "printWidth": 120, "htmlWhitespaceSensitivity": "ignore", "tabWidth": 4, - "quoteProps": "consistent" + "quoteProps": "consistent", + "trailingComma": "none" } } ] diff --git a/app/Actions/CronJob/CreateCronJob.php b/app/Actions/CronJob/CreateCronJob.php index 05fc131..972f6b9 100755 --- a/app/Actions/CronJob/CreateCronJob.php +++ b/app/Actions/CronJob/CreateCronJob.php @@ -5,17 +5,12 @@ use App\Enums\CronjobStatus; use App\Models\CronJob; use App\Models\Server; -use App\SSHCommands\CronJob\UpdateCronJobsCommand; use App\ValidationRules\CronRule; use Illuminate\Support\Facades\Validator; use Illuminate\Validation\ValidationException; -use Throwable; class CreateCronJob { - /** - * @throws Throwable - */ public function create(Server $server, array $input): void { $this->validate($input); @@ -29,10 +24,7 @@ public function create(Server $server, array $input): void ]); $cronJob->save(); - $server->ssh()->exec( - new UpdateCronJobsCommand($cronJob->user, CronJob::crontab($server, $cronJob->user)), - 'update-crontab' - ); + $server->cron()->update($cronJob->user, CronJob::crontab($server, $cronJob->user)); $cronJob->status = CronjobStatus::READY; $cronJob->save(); } diff --git a/app/Actions/CronJob/DeleteCronJob.php b/app/Actions/CronJob/DeleteCronJob.php index b579f85..9eeacf3 100755 --- a/app/Actions/CronJob/DeleteCronJob.php +++ b/app/Actions/CronJob/DeleteCronJob.php @@ -4,21 +4,13 @@ use App\Models\CronJob; use App\Models\Server; -use App\SSHCommands\CronJob\UpdateCronJobsCommand; -use Throwable; class DeleteCronJob { - /** - * @throws Throwable - */ public function delete(Server $server, CronJob $cronJob): void { $user = $cronJob->user; $cronJob->delete(); - $server->ssh()->exec( - new UpdateCronJobsCommand($user, CronJob::crontab($server, $user)), - 'update-crontab' - ); + $server->cron()->update($cronJob->user, CronJob::crontab($server, $user)); } } diff --git a/app/Actions/Database/CreateBackup.php b/app/Actions/Database/CreateBackup.php index c31038f..f3057fc 100644 --- a/app/Actions/Database/CreateBackup.php +++ b/app/Actions/Database/CreateBackup.php @@ -32,7 +32,7 @@ public function create($type, Server $server, array $input): Backup ]); $backup->save(); - $backup->run(); + app(RunBackup::class)->run($backup); return $backup; } diff --git a/app/Actions/Database/CreateDatabase.php b/app/Actions/Database/CreateDatabase.php index 58e3fd9..f825d06 100755 --- a/app/Actions/Database/CreateDatabase.php +++ b/app/Actions/Database/CreateDatabase.php @@ -22,8 +22,6 @@ public function create(Server $server, array $input): Database 'server_id' => $server->id, 'name' => $input['name'], ]); - $database->save(); - $server->database()->handler()->create($database->name); $database->status = DatabaseStatus::READY; $database->save(); diff --git a/app/Actions/Database/CreateDatabaseUser.php b/app/Actions/Database/CreateDatabaseUser.php index d22345e..2c39587 100755 --- a/app/Actions/Database/CreateDatabaseUser.php +++ b/app/Actions/Database/CreateDatabaseUser.php @@ -25,8 +25,6 @@ public function create(Server $server, array $input, array $links = []): Databas 'host' => isset($input['remote']) && $input['remote'] ? $input['host'] : 'localhost', 'databases' => $links, ]); - $databaseUser->save(); - $server->database()->handler()->createUser( $databaseUser->username, $databaseUser->password, @@ -35,8 +33,8 @@ public function create(Server $server, array $input, array $links = []): Databas $databaseUser->status = DatabaseUserStatus::READY; $databaseUser->save(); - if (count($databaseUser->databases) > 0) { - app(LinkUser::class)->link($databaseUser, $databaseUser->databases); + if (count($links) > 0) { + app(LinkUser::class)->link($databaseUser, ['databases' => $links]); } return $databaseUser; diff --git a/app/Actions/Database/RestoreBackup.php b/app/Actions/Database/RestoreBackup.php new file mode 100644 index 0000000..2c9bea6 --- /dev/null +++ b/app/Actions/Database/RestoreBackup.php @@ -0,0 +1,39 @@ +validate($input); + + /** @var Database $database */ + $database = Database::query()->findOrFail($input['database']); + $backupFile->status = BackupFileStatus::RESTORING; + $backupFile->restored_to = $database->name; + $backupFile->save(); + + dispatch(function () use ($backupFile, $database) { + $database->server->database()->handler()->restoreBackup($backupFile, $database->name); + $backupFile->status = BackupFileStatus::RESTORED; + $backupFile->restored_at = now(); + $backupFile->save(); + })->catch(function () use ($backupFile) { + $backupFile->status = BackupFileStatus::RESTORE_FAILED; + $backupFile->save(); + })->onConnection('ssh'); + } + + private function validate(array $input): void + { + Validator::make($input, [ + 'database' => 'required|exists:databases,id', + ])->validate(); + } +} diff --git a/app/Actions/Database/RunBackup.php b/app/Actions/Database/RunBackup.php new file mode 100644 index 0000000..8f37bf2 --- /dev/null +++ b/app/Actions/Database/RunBackup.php @@ -0,0 +1,30 @@ + $backup->id, + 'name' => Str::of($backup->database->name)->slug().'-'.now()->format('YmdHis'), + 'status' => BackupFileStatus::CREATING, + ]); + $file->save(); + + dispatch(function () use ($file) { + $file->backup->server->database()->handler()->runBackup($file); + $file->status = BackupFileStatus::CREATED; + $file->save(); + })->catch(function () use ($file) { + $file->status = BackupFileStatus::FAILED; + $file->save(); + })->onConnection('ssh'); + } +} diff --git a/app/Actions/FirewallRule/CreateRule.php b/app/Actions/FirewallRule/CreateRule.php index 01670e9..b5a4bab 100755 --- a/app/Actions/FirewallRule/CreateRule.php +++ b/app/Actions/FirewallRule/CreateRule.php @@ -21,15 +21,13 @@ public function create(Server $server, array $input): FirewallRule 'port' => $input['port'], 'source' => $input['source'], 'mask' => $input['mask'] ?? null, - 'status' => FirewallRuleStatus::CREATING, ]); - $rule->save(); $server->firewall() ->handler() ->addRule( $rule->type, - $rule->real_protocol, + $rule->getRealProtocol(), $rule->port, $rule->source, $rule->mask diff --git a/app/Actions/FirewallRule/DeleteRule.php b/app/Actions/FirewallRule/DeleteRule.php index d9addf8..e4ce83e 100755 --- a/app/Actions/FirewallRule/DeleteRule.php +++ b/app/Actions/FirewallRule/DeleteRule.php @@ -17,7 +17,7 @@ public function delete(Server $server, FirewallRule $rule): void ->handler() ->removeRule( $rule->type, - $rule->real_protocol, + $rule->getRealProtocol(), $rule->port, $rule->source, $rule->mask diff --git a/app/Actions/PHP/GetPHPIni.php b/app/Actions/PHP/GetPHPIni.php index 1500ae4..ef717d6 100644 --- a/app/Actions/PHP/GetPHPIni.php +++ b/app/Actions/PHP/GetPHPIni.php @@ -3,7 +3,6 @@ namespace App\Actions\PHP; use App\Models\Server; -use App\SSHCommands\PHP\GetPHPIniCommand; use Illuminate\Validation\ValidationException; class GetPHPIni @@ -12,8 +11,10 @@ public function getIni(Server $server, array $input): string { $this->validate($server, $input); + $php = $server->php($input['version']); + try { - return $server->ssh()->exec(new GetPHPIniCommand($input['version'])); + return $php->handler()->getPHPIni(); } catch (\Throwable $e) { throw ValidationException::withMessages( ['ini' => $e->getMessage()] diff --git a/app/Actions/PHP/InstallNewPHP.php b/app/Actions/PHP/InstallNewPHP.php index 0609f94..b104975 100755 --- a/app/Actions/PHP/InstallNewPHP.php +++ b/app/Actions/PHP/InstallNewPHP.php @@ -28,7 +28,14 @@ public function install(Server $server, array $input): void 'is_default' => false, ]); $php->save(); - $php->install(); + + dispatch(function () use ($php) { + $php->handler()->install(); + $php->status = ServiceStatus::READY; + $php->save(); + })->catch(function () use ($php) { + $php->delete(); + })->onConnection('ssh'); } /** diff --git a/app/Actions/PHP/InstallPHPExtension.php b/app/Actions/PHP/InstallPHPExtension.php index 52eb189..eba6a41 100755 --- a/app/Actions/PHP/InstallPHPExtension.php +++ b/app/Actions/PHP/InstallPHPExtension.php @@ -10,9 +10,6 @@ class InstallPHPExtension { - /** - * @throws ValidationException - */ public function install(Server $server, array $input): Service { $this->validate($server, $input); @@ -21,10 +18,19 @@ public function install(Server $server, array $input): Service $service = $server->php($input['version']); $typeData = $service->type_data; $typeData['extensions'] = $typeData['extensions'] ?? []; + $typeData['extensions'][] = $input['extension']; $service->type_data = $typeData; $service->save(); - $service->handler()->installExtension($input['extension']); + dispatch(function () use ($service, $input) { + $service->handler()->installExtension($input['extension']); + })->catch(function () use ($service, $input) { + $service->refresh(); + $typeData = $service->type_data; + $typeData['extensions'] = array_values(array_diff($typeData['extensions'], [$input['extension']])); + $service->type_data = $typeData; + $service->save(); + })->onConnection('ssh'); return $service; } diff --git a/app/Actions/PHP/UninstallPHP.php b/app/Actions/PHP/UninstallPHP.php index 55b77df..9c7e8d5 100755 --- a/app/Actions/PHP/UninstallPHP.php +++ b/app/Actions/PHP/UninstallPHP.php @@ -2,6 +2,7 @@ namespace App\Actions\PHP; +use App\Enums\ServiceStatus; use App\Models\Server; use App\Models\Service; use Illuminate\Support\Facades\Validator; @@ -15,8 +16,16 @@ public function uninstall(Server $server, array $input): void /** @var Service $php */ $php = $server->php($input['version']); + $php->status = ServiceStatus::UNINSTALLING; + $php->save(); - $php->uninstall(); + dispatch(function () use ($php) { + $php->handler()->uninstall(); + $php->delete(); + })->catch(function () use ($php) { + $php->status = ServiceStatus::FAILED; + $php->save(); + })->onConnection('ssh'); } /** diff --git a/app/Actions/Queue/CreateQueue.php b/app/Actions/Queue/CreateQueue.php index 32182b3..29809ba 100644 --- a/app/Actions/Queue/CreateQueue.php +++ b/app/Actions/Queue/CreateQueue.php @@ -29,7 +29,23 @@ public function create(mixed $queueable, array $input): void 'status' => QueueStatus::CREATING, ]); $queue->save(); - $queue->deploy(); + + dispatch(function () use ($queue) { + $queue->server->processManager()->handler()->create( + $queue->id, + $queue->command, + $queue->user, + $queue->auto_start, + $queue->auto_restart, + $queue->numprocs, + $queue->getLogFile(), + $queue->site_id + ); + $queue->status = QueueStatus::RUNNING; + $queue->save(); + })->catch(function () use ($queue) { + $queue->delete(); + })->onConnection('ssh'); } /** diff --git a/app/Actions/Queue/DeleteQueue.php b/app/Actions/Queue/DeleteQueue.php new file mode 100644 index 0000000..03af4af --- /dev/null +++ b/app/Actions/Queue/DeleteQueue.php @@ -0,0 +1,14 @@ +server->processManager()->handler()->delete($queue->id, $queue->site_id); + $queue->delete(); + } +} diff --git a/app/Actions/Queue/ManageQueue.php b/app/Actions/Queue/ManageQueue.php new file mode 100644 index 0000000..b73bf5f --- /dev/null +++ b/app/Actions/Queue/ManageQueue.php @@ -0,0 +1,52 @@ +status = QueueStatus::STARTING; + $queue->save(); + dispatch(function () use ($queue) { + $queue->server->processManager()->handler()->start($queue->id, $queue->site_id); + $queue->status = ServiceStatus::READY; + $queue->save(); + })->catch(function () use ($queue) { + $queue->status = ServiceStatus::FAILED; + $queue->save(); + })->onConnection('ssh'); + } + + public function stop(Queue $queue): void + { + $queue->status = QueueStatus::STOPPING; + $queue->save(); + dispatch(function () use ($queue) { + $queue->server->processManager()->handler()->stop($queue->id, $queue->site_id); + $queue->status = ServiceStatus::STOPPED; + $queue->save(); + })->catch(function () use ($queue) { + $queue->status = ServiceStatus::FAILED; + $queue->save(); + })->onConnection('ssh'); + } + + public function restart(Queue $queue): void + { + $queue->status = QueueStatus::RESTARTING; + $queue->save(); + dispatch(function () use ($queue) { + $queue->server->processManager()->handler()->restart($queue->id, $queue->site_id); + $queue->status = ServiceStatus::READY; + $queue->save(); + })->catch(function () use ($queue) { + $queue->status = ServiceStatus::FAILED; + $queue->save(); + })->onConnection('ssh'); + } +} diff --git a/app/Actions/SSL/CreateSSL.php b/app/Actions/SSL/CreateSSL.php index 5197f0e..a39acce 100644 --- a/app/Actions/SSL/CreateSSL.php +++ b/app/Actions/SSL/CreateSSL.php @@ -2,8 +2,10 @@ namespace App\Actions\SSL; +use App\Enums\SslStatus; use App\Enums\SslType; use App\Models\Site; +use App\Models\Ssl; use Illuminate\Support\Facades\Validator; use Illuminate\Validation\Rule; use Illuminate\Validation\ValidationException; @@ -17,13 +19,24 @@ public function create(Site $site, array $input): void { $this->validate($input); - if ($input['type'] == SslType::LETSENCRYPT) { - $site->createFreeSsl(); - } + $ssl = new Ssl([ + 'site_id' => $site->id, + 'type' => $input['type'], + 'certificate' => $input['certificate'] ?? null, + 'pk' => $input['private'] ?? null, + 'expires_at' => $input['type'] === SslType::LETSENCRYPT ? now()->addMonths(3) : null, + 'status' => SslStatus::CREATING, + ]); + $ssl->save(); - if ($input['type'] == SslType::CUSTOM) { - $site->createCustomSsl($input['certificate'], $input['private']); - } + dispatch(function () use ($site, $ssl) { + $site->server->webserver()->handler()->setupSSL($ssl); + $ssl->status = SslStatus::CREATED; + $ssl->save(); + $site->type()->edit(); + })->catch(function () use ($ssl) { + $ssl->delete(); + }); } /** diff --git a/app/Actions/SSL/DeleteSSL.php b/app/Actions/SSL/DeleteSSL.php new file mode 100644 index 0000000..82e06e8 --- /dev/null +++ b/app/Actions/SSL/DeleteSSL.php @@ -0,0 +1,14 @@ +site->server->webserver()->handler()->removeSSL($ssl); + $ssl->delete(); + } +} diff --git a/app/Actions/Server/CreateServer.php b/app/Actions/Server/CreateServer.php index c64d8c5..1dbf6c8 100755 --- a/app/Actions/Server/CreateServer.php +++ b/app/Actions/Server/CreateServer.php @@ -3,13 +3,19 @@ namespace App\Actions\Server; use App\Enums\FirewallRuleStatus; +use App\Enums\ServerProvider; +use App\Enums\ServerStatus; use App\Exceptions\ServerProviderError; -use App\Jobs\Installation\ContinueInstallation; +use App\Facades\Notifier; use App\Models\Server; use App\Models\User; +use App\Notifications\ServerInstallationFailed; +use App\Notifications\ServerInstallationSucceed; use App\ValidationRules\RestrictedIPAddressesRule; use Exception; +use Illuminate\Support\Facades\Bus; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Validator; use Illuminate\Support\Str; use Illuminate\Validation\Rule; @@ -71,14 +77,8 @@ public function create(User $creator, array $input): Server $server->type()->createServices($input); // install server - if ($server->provider == 'custom') { - $server->install(); - } else { - $server->progress_step = __('Installation will begin in 3 minutes!'); - $server->save(); - dispatch(new ContinueInstallation($server)) - ->delay(now()->addMinutes(2)); - } + $this->install($server); + DB::commit(); return $server; @@ -88,12 +88,44 @@ public function create(User $creator, array $input): Server if ($e instanceof ServerProviderError) { throw ValidationException::withMessages([ 'provider' => __('Provider Error: ').$e->getMessage(), - ])->errorBag('createServer'); + ]); } throw $e; } } + private function install(Server $server): void + { + $bus = Bus::chain([ + function () use ($server) { + if (! $server->provider()->isRunning()) { + sleep(2); + } + $server->type()->install(); + $server->update([ + 'status' => ServerStatus::READY, + ]); + Notifier::send($server, new ServerInstallationSucceed($server)); + }, + ])->catch(function (Throwable $e) use ($server) { + $server->update([ + 'status' => ServerStatus::INSTALLATION_FAILED, + ]); + Notifier::send($server, new ServerInstallationFailed($server)); + Log::error('server-installation-error', [ + 'error' => (string) $e, + ]); + }); + + if ($server->provider != ServerProvider::CUSTOM) { + $server->progress_step = 'waiting-for-provider'; + $server->save(); + $bus->delay(now()->addMinutes(3)); + } + + $bus->onConnection('ssh')->dispatch(); + } + /** * @throws ValidationException */ @@ -136,7 +168,7 @@ private function validateInputs(array $input): void */ private function validateType(Server $server, array $input): void { - Validator::make($input, $server->type()->createValidationRules($input)) + Validator::make($input, $server->type()->createRules($input)) ->validate(); } @@ -145,7 +177,7 @@ private function validateType(Server $server, array $input): void */ private function validateProvider(Server $server, array $input): void { - Validator::make($input, $server->provider()->createValidationRules($input)) + Validator::make($input, $server->provider()->createRules($input)) ->validate(); } diff --git a/app/Actions/Server/RebootServer.php b/app/Actions/Server/RebootServer.php index 20ba2cc..c026485 100644 --- a/app/Actions/Server/RebootServer.php +++ b/app/Actions/Server/RebootServer.php @@ -4,7 +4,6 @@ use App\Enums\ServerStatus; use App\Models\Server; -use App\SSHCommands\System\RebootCommand; use Throwable; class RebootServer @@ -12,7 +11,7 @@ class RebootServer public function reboot(Server $server): Server { try { - $server->ssh()->exec(new RebootCommand(), 'reboot'); + $server->os()->reboot(); $server->status = ServerStatus::DISCONNECTED; $server->save(); } catch (Throwable) { diff --git a/app/Actions/ServerProvider/CreateServerProvider.php b/app/Actions/ServerProvider/CreateServerProvider.php index 750bed7..06bb7f3 100644 --- a/app/Actions/ServerProvider/CreateServerProvider.php +++ b/app/Actions/ServerProvider/CreateServerProvider.php @@ -2,9 +2,9 @@ namespace App\Actions\ServerProvider; -use App\Contracts\ServerProvider as ServerProviderContract; use App\Models\ServerProvider; use App\Models\User; +use App\ServerProviders\ServerProvider as ServerProviderContract; use Exception; use Illuminate\Support\Facades\Validator; use Illuminate\Validation\Rule; diff --git a/app/Actions/Service/InstallPHPMyAdmin.php b/app/Actions/Service/InstallPHPMyAdmin.php deleted file mode 100644 index c71aa28..0000000 --- a/app/Actions/Service/InstallPHPMyAdmin.php +++ /dev/null @@ -1,60 +0,0 @@ -validate($input); - - $phpMyAdmin = $server->defaultService('phpmyadmin'); - if ($phpMyAdmin) { - throw ValidationException::withMessages([ - 'allowed_ip' => __('Already installed'), - ]); - } - $phpMyAdmin = new Service([ - 'server_id' => $server->id, - 'type' => 'phpmyadmin', - 'type_data' => [ - 'allowed_ip' => $input['allowed_ip'], - 'port' => $input['port'], - 'php' => $server->defaultService('php')->version, - ], - 'name' => 'phpmyadmin', - 'version' => '5.1.2', - 'status' => ServiceStatus::INSTALLING, - 'is_default' => 1, - ]); - $phpMyAdmin->save(); - $phpMyAdmin->install(); - - return $phpMyAdmin; - } - - /** - * @throws ValidationException - */ - private function validate(array $input): void - { - Validator::make($input, [ - 'allowed_ip' => 'required', - 'port' => [ - 'required', - 'numeric', - 'min:1', - 'max:65535', - ], - ])->validate(); - } -} diff --git a/app/Actions/Service/Manage.php b/app/Actions/Service/Manage.php new file mode 100644 index 0000000..761c536 --- /dev/null +++ b/app/Actions/Service/Manage.php @@ -0,0 +1,63 @@ +status = ServiceStatus::STARTING; + $service->save(); + dispatch(function () use ($service) { + $status = $service->server->systemd()->start($service->unit); + if (str($status)->contains('Active: active')) { + $service->status = ServiceStatus::READY; + } else { + $service->status = ServiceStatus::FAILED; + } + $service->save(); + })->catch(function () use ($service) { + $service->status = ServiceStatus::FAILED; + $service->save(); + })->onConnection('ssh'); + } + + public function stop(Service $service): void + { + $service->status = ServiceStatus::STOPPING; + $service->save(); + dispatch(function () use ($service) { + $status = $service->server->systemd()->stop($service->unit); + if (str($status)->contains('Active: inactive')) { + $service->status = ServiceStatus::STOPPED; + } else { + $service->status = ServiceStatus::FAILED; + } + $service->save(); + })->catch(function () use ($service) { + $service->status = ServiceStatus::FAILED; + $service->save(); + })->onConnection('ssh'); + } + + public function restart(Service $service): void + { + $service->status = ServiceStatus::RESTARTING; + $service->save(); + dispatch(function () use ($service) { + $status = $service->server->systemd()->restart($service->unit); + if (str($status)->contains('Active: active')) { + $service->status = ServiceStatus::READY; + } else { + $service->status = ServiceStatus::FAILED; + } + $service->save(); + })->catch(function () use ($service) { + $service->status = ServiceStatus::FAILED; + $service->save(); + })->onConnection('ssh'); + } +} diff --git a/app/Actions/Site/CreateRedirect.php b/app/Actions/Site/CreateRedirect.php deleted file mode 100644 index b685275..0000000 --- a/app/Actions/Site/CreateRedirect.php +++ /dev/null @@ -1,51 +0,0 @@ -validate($input); - - $redirect = new Redirect([ - 'site_id' => $site->id, - 'mode' => $input['mode'], - 'from' => $input['from'], - 'to' => $input['to'], - ]); - $redirect->save(); - $redirect->addToServer(); - } - - /** - * @throws ValidationException - */ - private function validate(array $input): void - { - $rules = [ - 'mode' => [ - 'required', - 'in:301,302', - ], - 'from' => [ - 'required', - ], - 'to' => [ - 'required', - 'url', - ], - ]; - - Validator::make($input, $rules)->validateWithBag('createRedirect'); - } -} diff --git a/app/Actions/Site/CreateSite.php b/app/Actions/Site/CreateSite.php index 21bfbd8..d1aca35 100755 --- a/app/Actions/Site/CreateSite.php +++ b/app/Actions/Site/CreateSite.php @@ -4,8 +4,11 @@ use App\Enums\SiteStatus; use App\Exceptions\SourceControlIsNotConnected; +use App\Facades\Notifier; use App\Models\Server; use App\Models\Site; +use App\Notifications\SiteInstallationFailed; +use App\Notifications\SiteInstallationSucceed; use App\ValidationRules\DomainRule; use Exception; use Illuminate\Support\Facades\DB; @@ -30,11 +33,11 @@ public function create(Server $server, array $input): Site 'type' => $input['type'], 'domain' => $input['domain'], 'aliases' => isset($input['alias']) ? [$input['alias']] : [], - 'path' => '/home/'.$server->ssh_user.'/'.$input['domain'], + 'path' => '/home/'.$server->getSshUser().'/'.$input['domain'], 'status' => SiteStatus::INSTALLING, ]); - // fields based on type + // fields based on the type $site->fill($site->type()->createFields($input)); // check has access to repository @@ -63,8 +66,19 @@ public function create(Server $server, array $input): Site 'content' => '', ]); - // install server - $site->install(); + // install site + dispatch(function () use ($site) { + $site->type()->install(); + $site->update([ + 'status' => SiteStatus::READY, + 'progress' => 100, + ]); + Notifier::send($site, new SiteInstallationSucceed($site)); + })->catch(function () use ($site) { + $site->status = SiteStatus::INSTALLATION_FAILED; + $site->save(); + Notifier::send($site, new SiteInstallationFailed($site)); + })->onConnection('ssh'); DB::commit(); @@ -105,7 +119,7 @@ private function validateInputs(Server $server, array $input): void */ private function validateType(Site $site, array $input): void { - $rules = $site->type()->createValidationRules($input); + $rules = $site->type()->createRules($input); Validator::make($input, $rules)->validate(); } diff --git a/app/Actions/Site/DeleteSite.php b/app/Actions/Site/DeleteSite.php new file mode 100644 index 0000000..fa23947 --- /dev/null +++ b/app/Actions/Site/DeleteSite.php @@ -0,0 +1,14 @@ +server->webserver()->handler()->deleteSite($site); + $site->delete(); + } +} diff --git a/app/Actions/Site/Deploy.php b/app/Actions/Site/Deploy.php new file mode 100644 index 0000000..fccf265 --- /dev/null +++ b/app/Actions/Site/Deploy.php @@ -0,0 +1,51 @@ +sourceControl()) { + $site->sourceControl()->getRepo($site->repository); + } + + if (! $site->deploymentScript?->content) { + throw new DeploymentScriptIsEmptyException(); + } + + $deployment = new Deployment([ + 'site_id' => $site->id, + 'deployment_script_id' => $site->deploymentScript->id, + 'status' => DeploymentStatus::DEPLOYING, + ]); + $lastCommit = $site->sourceControl()->provider()->getLastCommit($site->repository, $site->branch); + if ($lastCommit) { + $deployment->commit_id = $lastCommit['commit_id']; + $deployment->commit_data = $lastCommit['commit_data']; + } + $deployment->save(); + + dispatch(function () use ($site, $deployment) { + $log = $site->server->os()->runScript($site->path, $site->deploymentScript->content, $site->id); + $deployment->status = DeploymentStatus::FINISHED; + $deployment->log_id = $log->id; + $deployment->save(); + })->catch(function () use ($deployment) { + $deployment->status = DeploymentStatus::FAILED; + $deployment->save(); + })->onConnection('ssh'); + + return $deployment; + } +} diff --git a/app/Actions/Site/EditSite.php b/app/Actions/Site/EditSite.php deleted file mode 100755 index 02d678d..0000000 --- a/app/Actions/Site/EditSite.php +++ /dev/null @@ -1,41 +0,0 @@ -validateType($site, $input); - - // set type data - $site->type_data = $site->type()->data($input); - - // save - $site->port = $input['port'] ?? null; - $site->save(); - - // edit - $site->type()->edit(); - - return $site; - } - - /** - * @throws ValidationException - */ - private function validateType(Site $site, array $input): void - { - $rules = $site->type()->editValidationRules($input); - - Validator::make($input, $rules)->validateWithBag('editSite'); - } -} diff --git a/app/Actions/Site/GetSites.php b/app/Actions/Site/GetSites.php deleted file mode 100755 index 6a69214..0000000 --- a/app/Actions/Site/GetSites.php +++ /dev/null @@ -1,14 +0,0 @@ -sites()->orderBy('id', 'desc')->get(); - } -} diff --git a/app/Actions/Site/UpdateBranch.php b/app/Actions/Site/UpdateBranch.php index 7cc5b0d..9996cd3 100755 --- a/app/Actions/Site/UpdateBranch.php +++ b/app/Actions/Site/UpdateBranch.php @@ -3,6 +3,7 @@ namespace App\Actions\Site; use App\Models\Site; +use App\SSH\Git\Git; use Illuminate\Support\Facades\Validator; use Illuminate\Validation\ValidationException; @@ -14,8 +15,9 @@ class UpdateBranch public function update(Site $site, array $input): void { $this->validate($input); - - $site->updateBranch($input['branch']); + $site->branch = $input['branch']; + app(Git::class)->checkout($site); + $site->save(); } /** @@ -25,6 +27,6 @@ protected function validate(array $input): void { Validator::make($input, [ 'branch' => 'required', - ])->validateWithBag('updateBranch'); + ]); } } diff --git a/app/Actions/Site/UpdateDeploymentScript.php b/app/Actions/Site/UpdateDeploymentScript.php index eaf207b..4410353 100755 --- a/app/Actions/Site/UpdateDeploymentScript.php +++ b/app/Actions/Site/UpdateDeploymentScript.php @@ -27,6 +27,6 @@ protected function validate(array $input): void { Validator::make($input, [ 'script' => 'required', - ])->validateWithBag('updateDeploymentScript'); + ]); } } diff --git a/app/Actions/Site/UpdateEnv.php b/app/Actions/Site/UpdateEnv.php index 1538fb5..ed8c078 100644 --- a/app/Actions/Site/UpdateEnv.php +++ b/app/Actions/Site/UpdateEnv.php @@ -3,22 +3,14 @@ namespace App\Actions\Site; use App\Models\Site; -use App\SSHCommands\System\EditFileCommand; class UpdateEnv { public function update(Site $site, array $input): void { - $typeData = $site->type_data; - $typeData['env'] = $input['env']; - $site->type_data = $typeData; - $site->save(); - - $site->server->ssh()->exec( - new EditFileCommand( - $site->path.'/.env', - $site->env - ) + $site->server->os()->editFile( + $site->path.'/.env', + $input['env'] ); } } diff --git a/app/Actions/SshKey/DeleteKeyFromServer.php b/app/Actions/SshKey/DeleteKeyFromServer.php new file mode 100644 index 0000000..2d28173 --- /dev/null +++ b/app/Actions/SshKey/DeleteKeyFromServer.php @@ -0,0 +1,19 @@ +servers()->updateExistingPivot($server->id, [ + 'status' => SshKeyStatus::DELETING, + ]); + $server->os()->deleteSSHKey($sshKey->public_key); + $server->sshKeys()->detach($sshKey); + } +} diff --git a/app/Actions/SshKey/DeployKeyToServer.php b/app/Actions/SshKey/DeployKeyToServer.php new file mode 100644 index 0000000..4327e4c --- /dev/null +++ b/app/Actions/SshKey/DeployKeyToServer.php @@ -0,0 +1,38 @@ +validate($user, $input); + + /** @var SshKey $sshKey */ + $sshKey = SshKey::findOrFail($input['key_id']); + $server->sshKeys()->attach($sshKey, [ + 'status' => SshKeyStatus::ADDING, + ]); + $server->os()->deploySSHKey($sshKey->public_key); + $sshKey->servers()->updateExistingPivot($server->id, [ + 'status' => SshKeyStatus::ADDED, + ]); + } + + private function validate(User $user, array $input): void + { + Validator::make($input, [ + 'key_id' => [ + 'required', + Rule::exists('ssh_keys', 'id')->where('user_id', $user->id), + ], + ])->validate(); + } +} diff --git a/app/Console/Commands/RunBackup.php b/app/Console/Commands/RunBackup.php index 5d8df57..b7e61f9 100644 --- a/app/Console/Commands/RunBackup.php +++ b/app/Console/Commands/RunBackup.php @@ -42,7 +42,7 @@ public function handle(): void ->chunk(100, function ($backups) { /** @var Backup $backup */ foreach ($backups as $backup) { - $backup->run(); + app(\App\Actions\Database\RunBackup::class)->run($backup); } }); } diff --git a/app/Contracts/SSHCommand.php b/app/Contracts/SSHCommand.php deleted file mode 100755 index f6c36a6..0000000 --- a/app/Contracts/SSHCommand.php +++ /dev/null @@ -1,10 +0,0 @@ -log = null; $this->asUser = null; $this->server = $server->refresh(); - $this->user = $server->ssh_user; - if ($asUser && $asUser != $server->ssh_user) { + $this->user = $server->getSshUser(); + if ($asUser && $asUser != $server->getSshUser()) { $this->user = $asUser; $this->asUser = $asUser; } @@ -51,7 +50,7 @@ public function init(Server $server, ?string $asUser = null): self return $this; } - public function setLog(string $logType, $siteId = null): void + public function setLog(string $logType, $siteId = null): self { $this->log = $this->server->logs()->create([ 'site_id' => $siteId, @@ -59,6 +58,8 @@ public function setLog(string $logType, $siteId = null): void 'type' => $logType, 'disk' => config('core.logs_disk'), ]); + + return $this; } /** @@ -66,6 +67,7 @@ public function setLog(string $logType, $siteId = null): void */ public function connect(bool $sftp = false): void { + // If the IP is an IPv6 address, we need to wrap it in square brackets $ip = $this->server->ip; if (str($ip)->contains(':')) { $ip = '['.$ip.']'; @@ -91,9 +93,10 @@ public function connect(bool $sftp = false): void } /** - * @throws Throwable + * @throws SSHCommandError + * @throws SSHConnectionError */ - public function exec(string|array|SSHCommand $commands, string $log = '', ?int $siteId = null): string + public function exec(string|array $commands, string $log = '', ?int $siteId = null): string { if ($log) { $this->setLog($log, $siteId); @@ -141,19 +144,15 @@ public function upload(string $local, string $remote): void /** * @throws Exception */ - protected function executeCommand(string|SSHCommand $command): string + protected function executeCommand(string $command): string { - if ($command instanceof SSHCommand) { - $commandContent = $command->content(); - } else { - $commandContent = $command; - } - if ($this->asUser) { - $commandContent = 'sudo su - '.$this->asUser.' -c '.'"'.addslashes($commandContent).'"'; + $command = 'sudo su - '.$this->asUser.' -c '.'"'.addslashes($command).'"'; } - $output = $this->connection->exec($commandContent); + $this->connection->setTimeout(0); + + $output = $this->connection->exec($command); $this->log?->write($output); @@ -174,4 +173,12 @@ public function disconnect(): void $this->connection = null; } } + + /** + * @throws Exception + */ + public function __destruct() + { + $this->disconnect(); + } } diff --git a/app/Http/Controllers/ApplicationController.php b/app/Http/Controllers/ApplicationController.php index 222570a..bb6ac01 100644 --- a/app/Http/Controllers/ApplicationController.php +++ b/app/Http/Controllers/ApplicationController.php @@ -2,9 +2,11 @@ namespace App\Http\Controllers; +use App\Actions\Site\Deploy; use App\Actions\Site\UpdateBranch; use App\Actions\Site\UpdateDeploymentScript; use App\Actions\Site\UpdateEnv; +use App\Exceptions\DeploymentScriptIsEmptyException; use App\Exceptions\SourceControlIsNotConnected; use App\Facades\Toast; use App\Helpers\HtmxResponse; @@ -19,13 +21,15 @@ class ApplicationController extends Controller public function deploy(Server $server, Site $site): HtmxResponse { try { - $site->deploy(); + app(Deploy::class)->run($site); Toast::success('Deployment started!'); } catch (SourceControlIsNotConnected $e) { Toast::error($e->getMessage()); return htmx()->redirect(route('source-controls')); + } catch (DeploymentScriptIsEmptyException) { + Toast::error('Deployment script is empty!'); } return htmx()->back(); @@ -33,7 +37,7 @@ public function deploy(Server $server, Site $site): HtmxResponse public function showDeploymentLog(Server $server, Site $site, Deployment $deployment): RedirectResponse { - return back()->with('content', $deployment->log->content); + return back()->with('content', $deployment->log?->getContent()); } public function updateDeploymentScript(Server $server, Site $site, Request $request): RedirectResponse @@ -68,9 +72,9 @@ public function updateEnv(Server $server, Site $site, Request $request): Redirec return back(); } - public function enableAutoDeployment(Server $server, Site $site): RedirectResponse + public function enableAutoDeployment(Server $server, Site $site): HtmxResponse { - if (! $site->auto_deployment) { + if (! $site->isAutoDeployment()) { try { $site->enableAutoDeployment(); @@ -82,12 +86,12 @@ public function enableAutoDeployment(Server $server, Site $site): RedirectRespon } } - return back(); + return htmx()->back(); } - public function disableAutoDeployment(Server $server, Site $site): RedirectResponse + public function disableAutoDeployment(Server $server, Site $site): HtmxResponse { - if ($site->auto_deployment) { + if ($site->isAutoDeployment()) { try { $site->disableAutoDeployment(); @@ -99,6 +103,6 @@ public function disableAutoDeployment(Server $server, Site $site): RedirectRespo } } - return back(); + return htmx()->back(); } } diff --git a/app/Http/Controllers/DaemonController.php b/app/Http/Controllers/DaemonController.php deleted file mode 100644 index 933d439..0000000 --- a/app/Http/Controllers/DaemonController.php +++ /dev/null @@ -1,15 +0,0 @@ - $server, - ]); - } -} diff --git a/app/Http/Controllers/DatabaseBackupController.php b/app/Http/Controllers/DatabaseBackupController.php index b18f944..7ab6367 100644 --- a/app/Http/Controllers/DatabaseBackupController.php +++ b/app/Http/Controllers/DatabaseBackupController.php @@ -3,11 +3,12 @@ namespace App\Http\Controllers; use App\Actions\Database\CreateBackup; +use App\Actions\Database\RestoreBackup; +use App\Actions\Database\RunBackup; use App\Facades\Toast; use App\Helpers\HtmxResponse; use App\Models\Backup; use App\Models\BackupFile; -use App\Models\Database; use App\Models\Server; use Illuminate\Contracts\View\View; use Illuminate\Http\RedirectResponse; @@ -27,7 +28,7 @@ public function show(Server $server, Backup $backup): View public function run(Server $server, Backup $backup): RedirectResponse { - $backup->run(); + app(RunBackup::class)->run($backup); Toast::success('Backup is running.'); @@ -54,14 +55,7 @@ public function destroy(Server $server, Backup $backup): RedirectResponse public function restore(Server $server, Backup $backup, BackupFile $backupFile, Request $request): HtmxResponse { - $this->validate($request, [ - 'database' => 'required|exists:databases,id', - ]); - - /** @var Database $database */ - $database = Database::query()->findOrFail($request->input('database')); - - $backupFile->restore($database); + app(RestoreBackup::class)->restore($backupFile, $request->input()); Toast::success('Backup restored successfully.'); diff --git a/app/Http/Controllers/GitHookController.php b/app/Http/Controllers/GitHookController.php index 493da27..816a636 100644 --- a/app/Http/Controllers/GitHookController.php +++ b/app/Http/Controllers/GitHookController.php @@ -2,9 +2,11 @@ namespace App\Http\Controllers; +use App\Actions\Site\Deploy; use App\Exceptions\SourceControlIsNotConnected; use App\Facades\Notifier; use App\Models\GitHook; +use App\Models\ServerLog; use App\Notifications\SourceControlDisconnected; use Illuminate\Http\Request; use Illuminate\Support\Facades\Log; @@ -26,10 +28,16 @@ public function __invoke(Request $request) foreach ($gitHook->actions as $action) { if ($action == 'deploy') { try { - $gitHook->site->deploy(); + app(Deploy::class)->run($gitHook->site); } catch (SourceControlIsNotConnected) { Notifier::send($gitHook->sourceControl, new SourceControlDisconnected($gitHook->sourceControl)); } catch (Throwable $e) { + ServerLog::log( + $gitHook->site->server, + 'deploy-failed', + $e->getMessage(), + $gitHook->site + ); Log::error('git-hook-exception', (array) $e); } } diff --git a/app/Http/Controllers/QueueController.php b/app/Http/Controllers/QueueController.php index e21e4c6..2b798eb 100644 --- a/app/Http/Controllers/QueueController.php +++ b/app/Http/Controllers/QueueController.php @@ -3,6 +3,8 @@ namespace App\Http\Controllers; use App\Actions\Queue\CreateQueue; +use App\Actions\Queue\DeleteQueue; +use App\Actions\Queue\ManageQueue; use App\Facades\Toast; use App\Helpers\HtmxResponse; use App\Models\Queue; @@ -34,7 +36,7 @@ public function store(Server $server, Site $site, Request $request): HtmxRespons public function action(Server $server, Site $site, Queue $queue, string $action): HtmxResponse { - $queue->{$action}(); + app(ManageQueue::class)->{$action}($queue); Toast::success('Queue is about to '.$action); @@ -43,7 +45,7 @@ public function action(Server $server, Site $site, Queue $queue, string $action) public function destroy(Server $server, Site $site, Queue $queue): RedirectResponse { - $queue->remove(); + app(DeleteQueue::class)->delete($queue); Toast::success('Queue is being deleted.'); diff --git a/app/Http/Controllers/SSHKeyController.php b/app/Http/Controllers/SSHKeyController.php index c06a4b6..4d7f0f5 100644 --- a/app/Http/Controllers/SSHKeyController.php +++ b/app/Http/Controllers/SSHKeyController.php @@ -3,6 +3,8 @@ namespace App\Http\Controllers; use App\Actions\SshKey\CreateSshKey; +use App\Actions\SshKey\DeleteKeyFromServer; +use App\Actions\SshKey\DeployKeyToServer; use App\Facades\Toast; use App\Helpers\HtmxResponse; use App\Models\Server; @@ -29,34 +31,29 @@ public function store(Server $server, Request $request): HtmxResponse $request->input() ); - $key->deployTo($server); + $request->merge(['key_id' => $key->id]); - Toast::success('SSH Key added and being deployed to the server.'); - - return htmx()->back(); + return $this->deploy($server, $request); } public function destroy(Server $server, SshKey $sshKey): RedirectResponse { - $sshKey->deleteFrom($server); + app(DeleteKeyFromServer::class)->delete($server, $sshKey); - Toast::success('SSH Key is being deleted.'); + Toast::success('SSH Key has been deleted.'); return back(); } public function deploy(Server $server, Request $request): HtmxResponse { - $this->validate($request, [ - 'key_id' => 'required|exists:ssh_keys,id', - ]); + app(DeployKeyToServer::class)->deploy( + $request->user(), + $server, + $request->input() + ); - /** @var SshKey $sshKey */ - $sshKey = SshKey::query()->findOrFail($request->input('key_id')); - - $sshKey->deployTo($server); - - Toast::success('SSH Key is being deployed to the server.'); + Toast::success('SSH Key has been deployed to the server.'); return htmx()->back(); } diff --git a/app/Http/Controllers/SSLController.php b/app/Http/Controllers/SSLController.php index aa37783..18d7d52 100644 --- a/app/Http/Controllers/SSLController.php +++ b/app/Http/Controllers/SSLController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers; use App\Actions\SSL\CreateSSL; +use App\Actions\SSL\DeleteSSL; use App\Facades\Toast; use App\Helpers\HtmxResponse; use App\Models\Server; @@ -34,9 +35,9 @@ public function store(Server $server, Site $site, Request $request): HtmxRespons public function destroy(Server $server, Site $site, Ssl $ssl): RedirectResponse { - $ssl->remove(); + app(DeleteSSL::class)->delete($ssl); - Toast::success('SSL certificate is being deleted.'); + Toast::success('SSL certificate has been deleted.'); return back(); } diff --git a/app/Http/Controllers/ServerLogController.php b/app/Http/Controllers/ServerLogController.php index f571c00..7639c80 100644 --- a/app/Http/Controllers/ServerLogController.php +++ b/app/Http/Controllers/ServerLogController.php @@ -23,7 +23,7 @@ public function show(Server $server, ServerLog $serverLog): RedirectResponse } return back()->with([ - 'content' => $serverLog->content, + 'content' => $serverLog->getContent(), ]); } } diff --git a/app/Http/Controllers/ServerSettingController.php b/app/Http/Controllers/ServerSettingController.php index 9063427..15f689e 100644 --- a/app/Http/Controllers/ServerSettingController.php +++ b/app/Http/Controllers/ServerSettingController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers; use App\Actions\Server\EditServer; +use App\Actions\Server\RebootServer; use App\Facades\Toast; use App\Helpers\HtmxResponse; use App\Models\Server; @@ -40,7 +41,9 @@ public function checkConnection(Server $server): RedirectResponse|HtmxResponse public function reboot(Server $server): HtmxResponse { - $server->reboot(); + app(RebootServer::class)->reboot($server); + + Toast::info('Server is rebooting.'); return htmx()->redirect(back()->getTargetUrl()); } diff --git a/app/Http/Controllers/SiteController.php b/app/Http/Controllers/SiteController.php index 8edf315..4b57cb7 100644 --- a/app/Http/Controllers/SiteController.php +++ b/app/Http/Controllers/SiteController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers; use App\Actions\Site\CreateSite; +use App\Actions\Site\DeleteSite; use App\Enums\SiteType; use App\Facades\Toast; use App\Helpers\HtmxResponse; @@ -51,7 +52,7 @@ public function show(Server $server, Site $site): View public function destroy(Server $server, Site $site): RedirectResponse { - $site->remove(); + app(DeleteSite::class)->delete($site); Toast::success('Site is being deleted'); diff --git a/app/Http/Middleware/ServerIsReadyMiddleware.php b/app/Http/Middleware/ServerIsReadyMiddleware.php index c9e6220..8b27926 100644 --- a/app/Http/Middleware/ServerIsReadyMiddleware.php +++ b/app/Http/Middleware/ServerIsReadyMiddleware.php @@ -2,6 +2,8 @@ namespace App\Http\Middleware; +use App\Enums\ServerStatus; +use App\Facades\Toast; use App\Models\Server; use Closure; use Illuminate\Http\Request; @@ -13,7 +15,9 @@ public function handle(Request $request, Closure $next) /** @var Server $server */ $server = $request->route('server'); - if (! $server->isReady()) { + if ($server->status !== ServerStatus::READY) { + Toast::error('Server is not ready yet'); + return redirect()->route('servers.show', ['server' => $server]); } diff --git a/app/Jobs/Backup/RestoreDatabase.php b/app/Jobs/Backup/RestoreDatabase.php deleted file mode 100644 index aaa5773..0000000 --- a/app/Jobs/Backup/RestoreDatabase.php +++ /dev/null @@ -1,36 +0,0 @@ -backupFile = $backupFile; - $this->database = $database; - } - - public function handle(): void - { - $this->database->server->database()->handler()->restoreBackup($this->backupFile, $this->database->name); - - $this->backupFile->status = BackupFileStatus::RESTORED; - $this->backupFile->restored_at = now(); - $this->backupFile->save(); - } - - public function failed(): void - { - $this->backupFile->status = BackupFileStatus::RESTORE_FAILED; - $this->backupFile->save(); - } -} diff --git a/app/Jobs/Backup/RunBackup.php b/app/Jobs/Backup/RunBackup.php deleted file mode 100644 index d4d9445..0000000 --- a/app/Jobs/Backup/RunBackup.php +++ /dev/null @@ -1,33 +0,0 @@ -backupFile = $backupFile; - } - - public function handle(): void - { - if ($this->backupFile->backup->type === 'database') { - $this->backupFile->backup->server->database()->handler()->runBackup($this->backupFile); - } - - $this->backupFile->status = BackupFileStatus::CREATED; - $this->backupFile->save(); - } - - public function failed(): void - { - $this->backupFile->status = BackupFileStatus::FAILED; - $this->backupFile->save(); - } -} diff --git a/app/Jobs/Installation/ContinueInstallation.php b/app/Jobs/Installation/ContinueInstallation.php deleted file mode 100644 index 8a5b7c1..0000000 --- a/app/Jobs/Installation/ContinueInstallation.php +++ /dev/null @@ -1,37 +0,0 @@ -server = $server; - $this->attempts = $attempts; - } - - public function handle(): void - { - if ($this->server->provider()->isRunning()) { - $this->server->install(); - - return; - } - - if ($this->attempts >= 2) { - $this->server->update([ - 'status' => 'installation_failed', - ]); - - return; - } - - dispatch(new self($this->server, $this->attempts++))->delay(now()->addMinute()); - } -} diff --git a/app/Jobs/Installation/Initialize.php b/app/Jobs/Installation/Initialize.php deleted file mode 100755 index 0d3f7e6..0000000 --- a/app/Jobs/Installation/Initialize.php +++ /dev/null @@ -1,72 +0,0 @@ -server = $server->refresh(); - $this->asUser = $asUser; - } - - /** - * @throws Throwable - */ - public function handle(): void - { - $this->authentication(); - $this->publicKey(); - // $this->setHostname(); - } - - /** - * @throws Throwable - */ - protected function authentication(): void - { - $this->server - ->ssh($this->asUser ?? $this->server->ssh_user) - ->exec( - new CreateUserCommand( - $this->server->authentication['user'], - $this->server->authentication['pass'], - $this->server->sshKey()['public_key'] - ), - 'create-user' - ); - - $this->server->ssh_user = config('core.ssh_user'); - $this->server->save(); - } - - /** - * @throws Throwable - */ - protected function publicKey(): void - { - $publicKey = $this->server->ssh()->exec(new GetPublicKeyCommand()); - $this->server->update([ - 'public_key' => $publicKey, - ]); - } - - /** - * @throws Throwable - */ - protected function setHostname(): void - { - $this->server - ->ssh() - ->exec('sudo hostnamectl set-hostname '.$this->server->hostname); - } -} diff --git a/app/Jobs/Installation/InstallCertbot.php b/app/Jobs/Installation/InstallCertbot.php deleted file mode 100755 index c9dc3ea..0000000 --- a/app/Jobs/Installation/InstallCertbot.php +++ /dev/null @@ -1,25 +0,0 @@ -server = $server; - } - - /** - * @throws Throwable - */ - public function handle(): void - { - $this->server->ssh()->exec(new InstallCertbotCommand(), 'install-certbot'); - } -} diff --git a/app/Jobs/Installation/InstallComposer.php b/app/Jobs/Installation/InstallComposer.php deleted file mode 100755 index f864559..0000000 --- a/app/Jobs/Installation/InstallComposer.php +++ /dev/null @@ -1,25 +0,0 @@ -server = $server; - } - - /** - * @throws Throwable - */ - public function handle(): void - { - $this->server->ssh()->exec(new InstallComposerCommand(), 'install-composer'); - } -} diff --git a/app/Jobs/Installation/InstallMariadb.php b/app/Jobs/Installation/InstallMariadb.php deleted file mode 100755 index 1ad4955..0000000 --- a/app/Jobs/Installation/InstallMariadb.php +++ /dev/null @@ -1,35 +0,0 @@ -service = $service; - } - - /** - * @throws InstallationFailed - * @throws Throwable - */ - public function handle(): void - { - $ssh = $this->service->server->ssh(); - $ssh->exec(new InstallMariadbCommand(), 'install-mariadb'); - $status = $ssh->exec(new ServiceStatusCommand($this->service->unit), 'mariadb-status'); - $this->service->validateInstall($status); - $this->service->update([ - 'status' => ServiceStatus::READY, - ]); - } -} diff --git a/app/Jobs/Installation/InstallMysql.php b/app/Jobs/Installation/InstallMysql.php deleted file mode 100755 index 4627de0..0000000 --- a/app/Jobs/Installation/InstallMysql.php +++ /dev/null @@ -1,35 +0,0 @@ -service = $service; - } - - /** - * @throws InstallationFailed - * @throws Throwable - */ - public function handle(): void - { - $ssh = $this->service->server->ssh(); - $ssh->exec(new InstallMysqlCommand($this->service->version), 'install-mysql'); - $status = $ssh->exec(new ServiceStatusCommand($this->service->unit), 'mysql-status'); - $this->service->validateInstall($status); - $this->service->update([ - 'status' => ServiceStatus::READY, - ]); - } -} diff --git a/app/Jobs/Installation/InstallNginx.php b/app/Jobs/Installation/InstallNginx.php deleted file mode 100755 index 142feef..0000000 --- a/app/Jobs/Installation/InstallNginx.php +++ /dev/null @@ -1,35 +0,0 @@ -service = $service; - } - - /** - * @throws InstallationFailed - * @throws Throwable - */ - public function handle(): void - { - $ssh = $this->service->server->ssh(); - $ssh->exec(new InstallNginxCommand(), 'install-nginx'); - $status = $ssh->exec(new ServiceStatusCommand($this->service->unit), 'nginx-status'); - $this->service->validateInstall($status); - $this->service->update([ - 'status' => ServiceStatus::READY, - ]); - } -} diff --git a/app/Jobs/Installation/InstallNodejs.php b/app/Jobs/Installation/InstallNodejs.php deleted file mode 100755 index c30edde..0000000 --- a/app/Jobs/Installation/InstallNodejs.php +++ /dev/null @@ -1,25 +0,0 @@ -server = $server; - } - - /** - * @throws Throwable - */ - public function handle(): void - { - $this->server->ssh()->exec(new InstallNodejsCommand(), 'install-nodejs'); - } -} diff --git a/app/Jobs/Installation/InstallPHP.php b/app/Jobs/Installation/InstallPHP.php deleted file mode 100755 index ed9dcff..0000000 --- a/app/Jobs/Installation/InstallPHP.php +++ /dev/null @@ -1,35 +0,0 @@ -service = $service; - } - - /** - * @throws InstallationFailed - * @throws Throwable - */ - public function handle(): void - { - $ssh = $this->service->server->ssh(); - $ssh->exec(new InstallPHPCommand($this->service->version), 'install-php'); - $status = $ssh->exec(new ServiceStatusCommand($this->service->unit), 'php-status'); - $this->service->validateInstall($status); - $this->service->update([ - 'status' => ServiceStatus::READY, - ]); - } -} diff --git a/app/Jobs/Installation/InstallPHPMyAdmin.php b/app/Jobs/Installation/InstallPHPMyAdmin.php deleted file mode 100644 index cd83cbb..0000000 --- a/app/Jobs/Installation/InstallPHPMyAdmin.php +++ /dev/null @@ -1,114 +0,0 @@ -service = $service; - } - - /** - * @throws Throwable - */ - public function handle(): void - { - $this->setUpFirewall(); - $this->downloadSource(); - $this->setUpVHost(); - $this->restartPHP(); - $this->service->update([ - 'status' => ServiceStatus::READY, - ]); - } - - /** - * @throws Throwable - */ - private function setUpFirewall(): void - { - $this->firewallRule = FirewallRule::query() - ->where('server_id', $this->service->server_id) - ->where('port', $this->service->type_data['port']) - ->first(); - if ($this->firewallRule) { - $this->firewallRule->source = $this->service->type_data['allowed_ip']; - $this->firewallRule->save(); - } else { - $this->firewallRule = app(CreateRule::class)->create( - $this->service->server, - [ - 'type' => 'allow', - 'protocol' => 'tcp', - 'port' => $this->service->type_data['port'], - 'source' => $this->service->type_data['allowed_ip'], - 'mask' => '0', - ] - ); - } - } - - /** - * @throws Throwable - */ - private function downloadSource(): void - { - $this->service->server->ssh()->exec( - new DownloadPHPMyAdminCommand(), - 'download-phpmyadmin' - ); - } - - /** - * @throws Throwable - */ - private function setUpVHost(): void - { - $vhost = File::get(resource_path('commands/webserver/nginx/phpmyadmin-vhost.conf')); - $vhost = Str::replace('__php_version__', $this->service->server->defaultService('php')->version, $vhost); - $vhost = Str::replace('__port__', $this->service->type_data['port'], $vhost); - $this->service->server->ssh()->exec( - new CreateNginxPHPMyAdminVHostCommand($vhost), - 'create-phpmyadmin-vhost' - ); - } - - private function restartPHP(): void - { - $this->service->server->service( - 'php', - $this->service->type_data['php'] - )?->restart(); - } - - /** - * @throws Throwable - */ - public function failed(Throwable $throwable): Throwable - { - $this->firewallRule?->removeFromServer(); - $this->service->update([ - 'status' => ServiceStatus::INSTALLATION_FAILED, - ]); - throw $throwable; - } -} diff --git a/app/Jobs/Installation/InstallRedis.php b/app/Jobs/Installation/InstallRedis.php deleted file mode 100755 index e70333d..0000000 --- a/app/Jobs/Installation/InstallRedis.php +++ /dev/null @@ -1,35 +0,0 @@ -service = $service; - } - - /** - * @throws InstallationFailed - * @throws Throwable - */ - public function handle(): void - { - $ssh = $this->service->server->ssh(); - $ssh->exec(new InstallRedisCommand(), 'install-redis'); - $status = $ssh->exec(new ServiceStatusCommand($this->service->unit), 'redis-status'); - $this->service->validateInstall($status); - $this->service->update([ - 'status' => ServiceStatus::READY, - ]); - } -} diff --git a/app/Jobs/Installation/InstallRequirements.php b/app/Jobs/Installation/InstallRequirements.php deleted file mode 100755 index 7e0a88f..0000000 --- a/app/Jobs/Installation/InstallRequirements.php +++ /dev/null @@ -1,28 +0,0 @@ -server = $server; - } - - /** - * @throws Throwable - */ - public function handle(): void - { - $this->server->ssh()->exec(new InstallRequirementsCommand( - $this->server->creator->email, - $this->server->creator->name, - ), 'install-requirements'); - } -} diff --git a/app/Jobs/Installation/InstallSupervisor.php b/app/Jobs/Installation/InstallSupervisor.php deleted file mode 100755 index 28df41c..0000000 --- a/app/Jobs/Installation/InstallSupervisor.php +++ /dev/null @@ -1,35 +0,0 @@ -service = $service; - } - - /** - * @throws InstallationFailed - * @throws Throwable - */ - public function handle(): void - { - $ssh = $this->service->server->ssh(); - $ssh->exec(new InstallSupervisorCommand(), 'install-supervisor'); - $status = $ssh->exec(new ServiceStatusCommand($this->service->unit), 'supervisor-status'); - $this->service->validateInstall($status); - $this->service->update([ - 'status' => ServiceStatus::READY, - ]); - } -} diff --git a/app/Jobs/Installation/InstallUfw.php b/app/Jobs/Installation/InstallUfw.php deleted file mode 100755 index 67e5dbd..0000000 --- a/app/Jobs/Installation/InstallUfw.php +++ /dev/null @@ -1,35 +0,0 @@ -service = $service; - } - - /** - * @throws InstallationFailed - * @throws Throwable - */ - public function handle(): void - { - $ssh = $this->service->server->ssh(); - $ssh->exec(new InstallUfwCommand(), 'install-ufw'); - $status = $ssh->exec(new ServiceStatusCommand($this->service->unit), 'ufw-status'); - $this->service->validateInstall($status); - $this->service->update([ - 'status' => ServiceStatus::READY, - ]); - } -} diff --git a/app/Jobs/Installation/InstallationJob.php b/app/Jobs/Installation/InstallationJob.php deleted file mode 100755 index f9d0f07..0000000 --- a/app/Jobs/Installation/InstallationJob.php +++ /dev/null @@ -1,16 +0,0 @@ -service = $service; - } - - /** - * @throws InstallationFailed - * @throws Throwable - */ - public function handle(): void - { - $ssh = $this->service->server->ssh(); - $ssh->exec(new UninstallPHPCommand($this->service->version), 'uninstall-php'); - } -} diff --git a/app/Jobs/Installation/UninstallPHPMyAdmin.php b/app/Jobs/Installation/UninstallPHPMyAdmin.php deleted file mode 100644 index 9a80ec6..0000000 --- a/app/Jobs/Installation/UninstallPHPMyAdmin.php +++ /dev/null @@ -1,66 +0,0 @@ -service = $service; - } - - /** - * @throws Exception - * @throws Throwable - */ - public function handle(): void - { - $this->removeFirewallRule(); - $this->deleteVHost(); - $this->restartPHP(); - } - - /** - * @throws Exception - */ - private function removeFirewallRule(): void - { - /** @var ?FirewallRule $rule */ - $rule = FirewallRule::query() - ->where('server_id', $this->service->server_id) - ->where('port', $this->service->type_data['port']) - ->first(); - $rule?->removeFromServer(); - } - - /** - * @throws Throwable - */ - private function deleteVHost(): void - { - $this->service->server->ssh()->exec( - new DeleteNginxPHPMyAdminVHostCommand('/home/vito/phpmyadmin'), - 'delete-phpmyadmin-vhost' - ); - } - - private function restartPHP(): void - { - $this->service->server->service( - 'php', - $this->service->type_data['php'] - )?->restart(); - } -} diff --git a/app/Jobs/Installation/Upgrade.php b/app/Jobs/Installation/Upgrade.php deleted file mode 100755 index 0c977aa..0000000 --- a/app/Jobs/Installation/Upgrade.php +++ /dev/null @@ -1,25 +0,0 @@ -server = $server; - } - - /** - * @throws Throwable - */ - public function handle(): void - { - $this->server->ssh()->exec(new UpgradeCommand(), 'upgrade'); - } -} diff --git a/app/Jobs/Job.php b/app/Jobs/Job.php deleted file mode 100755 index be45a6a..0000000 --- a/app/Jobs/Job.php +++ /dev/null @@ -1,14 +0,0 @@ -service = $service; - $this->name = $name; - } - - /** - * @throws ProcessFailed - * @throws Throwable - */ - public function handle(): void - { - $result = $this->service->server->ssh()->exec( - new InstallPHPExtensionCommand($this->service->version, $this->name), - 'install-php-extension' - ); - $result = Str::substr($result, strpos($result, '[PHP Modules]')); - if (! Str::contains($result, $this->name)) { - throw new ProcessFailed('Extension failed'); - } - $typeData = $this->service->type_data; - $typeData['extensions'][] = $this->name; - $this->service->type_data = $typeData; - $this->service->save(); - } - - public function failed(): void - { - } -} diff --git a/app/Jobs/Queue/Deploy.php b/app/Jobs/Queue/Deploy.php deleted file mode 100644 index 2e74b2a..0000000 --- a/app/Jobs/Queue/Deploy.php +++ /dev/null @@ -1,38 +0,0 @@ -worker = $worker; - } - - public function handle(): void - { - $this->worker->server->processManager()->handler()->create( - $this->worker->id, - $this->worker->command, - $this->worker->user, - $this->worker->auto_start, - $this->worker->auto_restart, - $this->worker->numprocs, - $this->worker->log_file, - $this->worker->site_id - ); - $this->worker->status = QueueStatus::RUNNING; - $this->worker->save(); - } - - public function failed(): void - { - $this->worker->delete(); - } -} diff --git a/app/Jobs/Queue/GetLogs.php b/app/Jobs/Queue/GetLogs.php deleted file mode 100644 index 2f8f917..0000000 --- a/app/Jobs/Queue/GetLogs.php +++ /dev/null @@ -1,26 +0,0 @@ -worker = $worker; - } - - public function handle(): void - { - $logs = $this->worker->server->processManager()->handler()->getLogs($this->worker->log_file); - } - - public function failed(): void - { - - } -} diff --git a/app/Jobs/Queue/Manage.php b/app/Jobs/Queue/Manage.php deleted file mode 100644 index 83c137c..0000000 --- a/app/Jobs/Queue/Manage.php +++ /dev/null @@ -1,56 +0,0 @@ -worker = $worker; - $this->action = $action; - $this->successStatus = $successStatus; - $this->failStatus = $failStatus; - $this->failMessage = $failMessage; - } - - public function handle(): void - { - switch ($this->action) { - case 'start': - $this->worker->server->processManager()->handler()->start($this->worker->id, $this->worker->site_id); - break; - case 'stop': - $this->worker->server->processManager()->handler()->stop($this->worker->id, $this->worker->site_id); - break; - case 'restart': - $this->worker->server->processManager()->handler()->restart($this->worker->id, $this->worker->site_id); - break; - } - $this->worker->status = $this->successStatus; - $this->worker->save(); - } - - public function failed(): void - { - $this->worker->status = $this->failStatus; - $this->worker->save(); - } -} diff --git a/app/Jobs/Queue/Remove.php b/app/Jobs/Queue/Remove.php deleted file mode 100644 index 81b8903..0000000 --- a/app/Jobs/Queue/Remove.php +++ /dev/null @@ -1,29 +0,0 @@ -worker = $worker; - } - - public function handle(): void - { - $this->worker->server->processManager()->handler()->delete($this->worker->id, $this->worker->site_id); - $this->worker->delete(); - } - - public function failed(): void - { - $this->worker->status = QueueStatus::FAILED; - $this->worker->save(); - } -} diff --git a/app/Jobs/Redirect/AddToServer.php b/app/Jobs/Redirect/AddToServer.php deleted file mode 100644 index 73a5fbf..0000000 --- a/app/Jobs/Redirect/AddToServer.php +++ /dev/null @@ -1,31 +0,0 @@ -redirect = $redirect; - } - - public function handle(): void - { - /** @var array $redirects */ - $redirects = Redirect::query()->where('site_id', $this->redirect->site_id)->get(); - $this->redirect->site->server->webserver()->handler()->updateRedirects($this->redirect->site, $redirects); - $this->redirect->status = 'ready'; - $this->redirect->save(); - } - - public function failed(): void - { - $this->redirect->status = 'failed'; - $this->redirect->delete(); - } -} diff --git a/app/Jobs/Redirect/DeleteFromServer.php b/app/Jobs/Redirect/DeleteFromServer.php deleted file mode 100644 index e393f50..0000000 --- a/app/Jobs/Redirect/DeleteFromServer.php +++ /dev/null @@ -1,33 +0,0 @@ -redirect = $redirect; - } - - public function handle(): void - { - /** @var array $redirects */ - $redirects = Redirect::query() - ->where('site_id', $this->redirect->site_id) - ->where('id', '!=', $this->redirect->id) - ->get(); - $this->redirect->site->server->webserver()->handler()->updateRedirects($this->redirect->site, $redirects); - $this->redirect->delete(); - } - - public function failed(): void - { - $this->redirect->status = 'failed'; - $this->redirect->save(); - } -} diff --git a/app/Jobs/Script/ExecuteOn.php b/app/Jobs/Script/ExecuteOn.php deleted file mode 100644 index 23c0c8a..0000000 --- a/app/Jobs/Script/ExecuteOn.php +++ /dev/null @@ -1,50 +0,0 @@ -script = $script; - $this->server = $server; - $this->user = $user; - } - - /** - * @throws Throwable - */ - public function handle(): void - { - $this->scriptExecution = $this->script->executions()->create([ - 'server_id' => $this->server->id, - 'user' => $this->user, - ]); - $this->server->ssh($this->scriptExecution->user)->exec( - $this->script->content, - 'execute-script' - ); - $this->scriptExecution->finished_at = now(); - $this->scriptExecution->save(); - } - - public function failed(): void - { - $this->scriptExecution->finished_at = now(); - $this->scriptExecution->save(); - } -} diff --git a/app/Jobs/Service/Install.php b/app/Jobs/Service/Install.php deleted file mode 100644 index 78896a9..0000000 --- a/app/Jobs/Service/Install.php +++ /dev/null @@ -1,24 +0,0 @@ -service = $service; - } - - public function handle() - { - } - - public function failed(\Throwable $throwable) - { - } -} diff --git a/app/Jobs/Service/Manage.php b/app/Jobs/Service/Manage.php deleted file mode 100644 index 8843e84..0000000 --- a/app/Jobs/Service/Manage.php +++ /dev/null @@ -1,63 +0,0 @@ -service = $service; - $this->action = $action; - $this->successStatus = $successStatus; - $this->failStatus = $failStatus; - $this->failMessage = $failMessage; - } - - /** - * @throws Throwable - */ - public function handle(): void - { - $command = match ($this->action) { - 'start' => new StartServiceCommand($this->service->unit), - 'stop' => new StopServiceCommand($this->service->unit), - 'restart' => new RestartServiceCommand($this->service->unit), - default => throw new Exception('Invalid action'), - }; - $this->service->server->ssh()->exec( - $command, - $this->action.'-'.$this->service->name - ); - $this->service->status = $this->successStatus; - $this->service->save(); - } - - public function failed(): void - { - $this->service->status = $this->failStatus; - $this->service->save(); - } -} diff --git a/app/Jobs/Site/CloneRepository.php b/app/Jobs/Site/CloneRepository.php deleted file mode 100755 index 4898ddf..0000000 --- a/app/Jobs/Site/CloneRepository.php +++ /dev/null @@ -1,35 +0,0 @@ -site = $site; - } - - /** - * @throws Throwable - */ - public function handle(): void - { - $this->site->server->ssh()->exec( - new CloneRepositoryCommand( - $this->site->full_repository_url, - $this->site->path, - $this->site->branch, - $this->site->ssh_key_name - ), - 'clone-repository', - $this->site->id - ); - } -} diff --git a/app/Jobs/Site/ComposerInstall.php b/app/Jobs/Site/ComposerInstall.php deleted file mode 100755 index e71deef..0000000 --- a/app/Jobs/Site/ComposerInstall.php +++ /dev/null @@ -1,34 +0,0 @@ -site = $site; - } - - /** - * @throws ComposerInstallFailed - * @throws Throwable - */ - public function handle(): void - { - $this->site->server->ssh()->exec( - new ComposerInstallCommand( - $this->site->path - ), - 'composer-install', - $this->site->id - ); - } -} diff --git a/app/Jobs/Site/CreateVHost.php b/app/Jobs/Site/CreateVHost.php deleted file mode 100755 index 95a93e2..0000000 --- a/app/Jobs/Site/CreateVHost.php +++ /dev/null @@ -1,21 +0,0 @@ -site = $site; - } - - public function handle(): void - { - $this->site->server->webserver()->handler()->createVHost($this->site); - } -} diff --git a/app/Jobs/Site/DeleteSite.php b/app/Jobs/Site/DeleteSite.php deleted file mode 100755 index ab9b200..0000000 --- a/app/Jobs/Site/DeleteSite.php +++ /dev/null @@ -1,29 +0,0 @@ -site = $site; - } - - public function handle(): void - { - $this->site->server->webserver()->handler()->deleteSite($this->site); - $this->site->delete(); - } - - public function failed(): void - { - $this->site->status = SiteStatus::READY; - $this->site->save(); - } -} diff --git a/app/Jobs/Site/Deploy.php b/app/Jobs/Site/Deploy.php deleted file mode 100644 index 973aa13..0000000 --- a/app/Jobs/Site/Deploy.php +++ /dev/null @@ -1,53 +0,0 @@ -script = $deployment->deploymentScript->content; - $this->deployment = $deployment; - $this->path = $path; - } - - /** - * @throws Throwable - */ - public function handle(): void - { - $ssh = $this->deployment->site->server->ssh(); - try { - $ssh->exec( - new RunScriptCommand($this->path, $this->script), - 'deploy', - $this->deployment->site_id - ); - $this->deployment->status = DeploymentStatus::FINISHED; - $this->deployment->log_id = $ssh->log->id; - $this->deployment->save(); - } catch (Throwable) { - $this->deployment->log_id = $ssh->log->id; - $this->deployment->save(); - $this->failed(); - } - } - - public function failed(): void - { - $this->deployment->status = DeploymentStatus::FAILED; - $this->deployment->save(); - } -} diff --git a/app/Jobs/Site/DeployEnv.php b/app/Jobs/Site/DeployEnv.php deleted file mode 100644 index 964452f..0000000 --- a/app/Jobs/Site/DeployEnv.php +++ /dev/null @@ -1,35 +0,0 @@ -site = $site; - } - - /** - * @throws Throwable - */ - public function handle(): void - { - $this->site->server->ssh()->exec( - new EditFileCommand( - $this->site->path.'/.env', - $this->site->env - ) - ); - } - - public function failed(): void - { - } -} diff --git a/app/Jobs/Site/DeployKey.php b/app/Jobs/Site/DeployKey.php deleted file mode 100755 index d99ac7a..0000000 --- a/app/Jobs/Site/DeployKey.php +++ /dev/null @@ -1,42 +0,0 @@ -site = $site; - } - - /** - * @throws Throwable - */ - public function handle(): void - { - $this->site->server->ssh()->exec( - new GenerateSshKeyCommand($this->site->ssh_key_name), - 'generate-ssh-key', - $this->site->id - ); - $this->site->ssh_key = $this->site->server->ssh()->exec( - new ReadSshKeyCommand($this->site->ssh_key_name), - 'read-public-key', - $this->site->id - ); - $this->site->save(); - $this->site->sourceControl()->provider()->deployKey( - $this->site->domain.'-key-'.$this->site->id, - $this->site->repository, - $this->site->ssh_key - ); - } -} diff --git a/app/Jobs/Site/InstallWordpress.php b/app/Jobs/Site/InstallWordpress.php deleted file mode 100755 index 760d5c1..0000000 --- a/app/Jobs/Site/InstallWordpress.php +++ /dev/null @@ -1,109 +0,0 @@ -site = $site; - } - - /** - * @throws ValidationException - * @throws FailedToInstallWordpress - * @throws Throwable - */ - public function handle(): void - { - $this->setupDatabase(); - - $result = $this->site->server->ssh()->exec( - new InstallWordpressCommand( - $this->site->path, - $this->site->domain, - $this->database->name, - $this->databaseUser->username, - $this->databaseUser->password, - 'localhost', - 'wp_', - $this->site->type_data['username'], - $this->site->type_data['password'], - $this->site->type_data['email'], - $this->site->type_data['title'], - ), - 'install-wordpress', - $this->site->id - ); - - if (! Str::contains($result, 'Success')) { - throw new FailedToInstallWordpress($result); - } - } - - /** - * @throws ValidationException - */ - private function setupDatabase() - { - // create database - $this->database = $this->site->server->databases()->where('name', $this->site->type_data['database'])->first(); - if (! $this->database) { - $this->database = new Database([ - 'server_id' => $this->site->server_id, - 'name' => $this->site->type_data['database'], - ]); - $this->database->server->database()->handler()->create($this->database->name); - $this->database->is_created = true; - $this->database->save(); - } - - // create database user - $this->databaseUser = $this->site->server->databaseUsers()->where('username', $this->site->type_data['database_user'])->first(); - if (! $this->databaseUser) { - $this->databaseUser = new DatabaseUser([ - 'server_id' => $this->site->server_id, - 'username' => $this->site->type_data['database_user'], - 'password' => Str::random(10), - 'host' => 'localhost', - ]); - $this->databaseUser->save(); - $this->databaseUser->server->database()->handler()->createUser($this->databaseUser->username, $this->databaseUser->password, $this->databaseUser->host); - $this->databaseUser->is_created = true; - $this->databaseUser->save(); - } - - // link database user - $linkedDatabases = $this->databaseUser->databases ?? []; - if (! in_array($this->database->name, $linkedDatabases)) { - $linkedDatabases[] = $this->database->name; - $this->databaseUser->databases = $linkedDatabases; - $this->databaseUser->server->database()->handler()->unlink( - $this->databaseUser->username, - $this->databaseUser->host, - ); - $this->databaseUser->server->database()->handler()->link( - $this->databaseUser->username, - $this->databaseUser->host, - $this->databaseUser->databases - ); - $this->databaseUser->save(); - } - } -} diff --git a/app/Jobs/Site/UpdateBranch.php b/app/Jobs/Site/UpdateBranch.php deleted file mode 100644 index afc5687..0000000 --- a/app/Jobs/Site/UpdateBranch.php +++ /dev/null @@ -1,42 +0,0 @@ -site = $site; - $this->branch = $branch; - } - - /** - * @throws Throwable - */ - public function handle(): void - { - $this->site->server->ssh()->exec( - new UpdateBranchCommand( - $this->site->path, - $this->branch - ), - 'update-branch', - $this->site->id - ); - $this->site->branch = $this->branch; - $this->site->save(); - } - - public function failed(): void - { - } -} diff --git a/app/Jobs/Site/UpdateVHost.php b/app/Jobs/Site/UpdateVHost.php deleted file mode 100755 index 6dd616e..0000000 --- a/app/Jobs/Site/UpdateVHost.php +++ /dev/null @@ -1,21 +0,0 @@ -site = $site; - } - - public function handle(): void - { - $this->site->server->webserver()->handler()->updateVHost($this->site); - } -} diff --git a/app/Jobs/SshKey/DeleteSshKeyFromServer.php b/app/Jobs/SshKey/DeleteSshKeyFromServer.php deleted file mode 100644 index acc23ba..0000000 --- a/app/Jobs/SshKey/DeleteSshKeyFromServer.php +++ /dev/null @@ -1,39 +0,0 @@ -server = $server; - $this->sshKey = $sshKey; - } - - /** - * @throws Throwable - */ - public function handle() - { - $this->server->ssh()->exec( - new DeleteSshKeyCommand($this->sshKey->public_key), - 'delete-ssh-key' - ); - $this->server->sshKeys()->detach($this->sshKey); - } - - public function failed(): void - { - $this->server->sshKeys()->attach($this->sshKey); - } -} diff --git a/app/Jobs/SshKey/DeploySshKeyToServer.php b/app/Jobs/SshKey/DeploySshKeyToServer.php deleted file mode 100644 index 5d663b7..0000000 --- a/app/Jobs/SshKey/DeploySshKeyToServer.php +++ /dev/null @@ -1,42 +0,0 @@ -server = $server; - $this->sshKey = $sshKey; - } - - /** - * @throws Throwable - */ - public function handle(): void - { - $this->server->ssh()->exec( - new DeploySshKeyCommand($this->sshKey->public_key), - 'deploy-ssh-key' - ); - $this->sshKey->servers()->updateExistingPivot($this->server->id, [ - 'status' => SshKeyStatus::ADDED, - ]); - } - - public function failed(): void - { - $this->server->sshKeys()->detach($this->sshKey); - } -} diff --git a/app/Jobs/Ssl/Deploy.php b/app/Jobs/Ssl/Deploy.php deleted file mode 100644 index 2ebb73c..0000000 --- a/app/Jobs/Ssl/Deploy.php +++ /dev/null @@ -1,37 +0,0 @@ -ssl = $ssl; - } - - public function handle(): void - { - $this->ssl->site->server->webserver()->handler()->setupSSL($this->ssl); - $this->ssl->status = SslStatus::CREATED; - $this->ssl->save(); - if ($this->ssl->site->type == SiteType::WORDPRESS) { - $typeData = $this->ssl->site->type_data; - $typeData['url'] = $this->ssl->site->url; - $this->ssl->site->type_data = $typeData; - $this->ssl->site->save(); - $this->ssl->site->type()->edit(); - } - } - - public function failed(): void - { - $this->ssl->delete(); - } -} diff --git a/app/Jobs/Ssl/Remove.php b/app/Jobs/Ssl/Remove.php deleted file mode 100644 index d277855..0000000 --- a/app/Jobs/Ssl/Remove.php +++ /dev/null @@ -1,28 +0,0 @@ -ssl = $ssl; - } - - public function handle(): void - { - $this->ssl->site->server->webserver()->handler()->removeSSL($this->ssl); - $this->ssl->delete(); - } - - public function failed(): void - { - $this->ssl->status = 'failed'; - $this->ssl->save(); - } -} diff --git a/app/Jobs/StorageProvider/DeleteFile.php b/app/Jobs/StorageProvider/DeleteFile.php deleted file mode 100644 index ae715c8..0000000 --- a/app/Jobs/StorageProvider/DeleteFile.php +++ /dev/null @@ -1,24 +0,0 @@ -storageProvider = $storageProvider; - $this->paths = $paths; - } - - public function handle(): void - { - $this->storageProvider->provider()->delete($this->paths); - } -} diff --git a/app/Models/Backup.php b/app/Models/Backup.php index 2a8c382..21a2c81 100644 --- a/app/Models/Backup.php +++ b/app/Models/Backup.php @@ -2,12 +2,9 @@ namespace App\Models; -use App\Enums\BackupFileStatus; -use App\Jobs\Backup\RunBackup; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; -use Illuminate\Support\Str; /** * @property string $type @@ -71,16 +68,4 @@ public function files(): HasMany { return $this->hasMany(BackupFile::class, 'backup_id'); } - - public function run(): void - { - $file = new BackupFile([ - 'backup_id' => $this->id, - 'name' => Str::of($this->database->name)->slug().'-'.now()->format('YmdHis'), - 'status' => BackupFileStatus::CREATING, - ]); - $file->save(); - - dispatch(new RunBackup($file))->onConnection('ssh'); - } } diff --git a/app/Models/BackupFile.php b/app/Models/BackupFile.php index f3f8bfe..926a99f 100644 --- a/app/Models/BackupFile.php +++ b/app/Models/BackupFile.php @@ -2,9 +2,6 @@ namespace App\Models; -use App\Enums\BackupFileStatus; -use App\Jobs\Backup\RestoreDatabase; -use App\Jobs\StorageProvider\DeleteFile; use Carbon\Carbon; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -17,8 +14,6 @@ * @property string $restored_to * @property Carbon $restored_at * @property Backup $backup - * @property string $path - * @property string $storage_path */ class BackupFile extends AbstractModel { @@ -56,11 +51,12 @@ protected static function booted(): void } }); - static::deleted(function (BackupFile $backupFile) { - dispatch(new DeleteFile( - $backupFile->backup->storage, - [$backupFile->storage_path] - )); + static::deleting(function (BackupFile $backupFile) { + $provider = $backupFile->backup->storage->provider(); + $path = $backupFile->storagePath(); + dispatch(function () use ($provider, $path) { + $provider->delete([$path]); + }); }); } @@ -69,21 +65,13 @@ public function backup(): BelongsTo return $this->belongsTo(Backup::class); } - public function getPathAttribute(): string + public function path(): string { - return '/home/'.$this->backup->server->ssh_user.'/'.$this->name.'.zip'; + return '/home/'.$this->backup->server->getSshUser().'/'.$this->name.'.zip'; } - public function getStoragePathAttribute(): string + public function storagePath(): string { return '/'.$this->name.'.zip'; } - - public function restore(Database $database): void - { - $this->status = BackupFileStatus::RESTORING; - $this->restored_to = $database->name; - $this->save(); - dispatch(new RestoreDatabase($this, $database))->onConnection('ssh'); - } } diff --git a/app/Models/CronJob.php b/app/Models/CronJob.php index 4a2cbed..2618a87 100755 --- a/app/Models/CronJob.php +++ b/app/Models/CronJob.php @@ -10,7 +10,6 @@ * @property string $command * @property string $user * @property string $frequency - * @property string $frequency_label * @property bool $hidden * @property string $status * @property string $crontab @@ -34,10 +33,6 @@ class CronJob extends AbstractModel 'hidden' => 'boolean', ]; - protected $appends = [ - 'frequency_label', - ]; - public function server(): BelongsTo { return $this->belongsTo(Server::class); @@ -57,7 +52,7 @@ public static function crontab(Server $server, string $user): string return $data; } - public function getFrequencyLabelAttribute(): string + public function frequencyLabel(): string { $labels = [ '* * * * *' => 'Every minute', diff --git a/app/Models/DatabaseUser.php b/app/Models/DatabaseUser.php index 072eb21..9b75b98 100755 --- a/app/Models/DatabaseUser.php +++ b/app/Models/DatabaseUser.php @@ -2,7 +2,6 @@ namespace App\Models; -use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -14,7 +13,6 @@ * @property string $host * @property string $status * @property Server $server - * @property string $full_user */ class DatabaseUser extends AbstractModel { @@ -43,14 +41,4 @@ public function server(): BelongsTo { return $this->belongsTo(Server::class); } - - public function scopeHasDatabase(Builder $query, string $databaseName): Builder - { - return $query->where('databases', 'like', "%\"$databaseName\"%"); - } - - public function getFullUserAttribute(): string - { - return $this->username.'@'.$this->host; - } } diff --git a/app/Models/Deployment.php b/app/Models/Deployment.php index 4626af7..4ded05f 100755 --- a/app/Models/Deployment.php +++ b/app/Models/Deployment.php @@ -37,10 +37,6 @@ class Deployment extends AbstractModel 'commit_data' => 'json', ]; - protected $appends = [ - 'commit_id_short', - ]; - public function site(): BelongsTo { return $this->belongsTo(Site::class); @@ -55,13 +51,4 @@ public function log(): BelongsTo { return $this->belongsTo(ServerLog::class, 'log_id'); } - - public function getCommitIdShortAttribute(): string - { - if ($this->commit_id) { - return substr($this->commit_id, 0, 7); - } - - return ''; - } } diff --git a/app/Models/FirewallRule.php b/app/Models/FirewallRule.php index e78d355..aec1fe8 100755 --- a/app/Models/FirewallRule.php +++ b/app/Models/FirewallRule.php @@ -9,7 +9,6 @@ * @property int $server_id * @property string $type * @property string $protocol - * @property string $real_protocol * @property int $port * @property string $source * @property ?string $mask @@ -37,16 +36,12 @@ class FirewallRule extends AbstractModel 'port' => 'integer', ]; - protected $appends = [ - 'real_protocol', - ]; - public function server(): BelongsTo { return $this->belongsTo(Server::class); } - public function getRealProtocolAttribute(): string + public function getRealProtocol(): string { return $this->protocol === 'udp' ? 'udp' : 'tcp'; } diff --git a/app/Models/GitHook.php b/app/Models/GitHook.php index 34b3294..9c1cd58 100755 --- a/app/Models/GitHook.php +++ b/app/Models/GitHook.php @@ -2,12 +2,8 @@ namespace App\Models; -use Exception; -use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Support\Facades\DB; -use Throwable; /** * @property int $site_id @@ -52,11 +48,6 @@ public function sourceControl(): BelongsTo return $this->belongsTo(SourceControl::class); } - public function scopeHasEvent(Builder $query, string $event): Builder - { - return $query->where('events', 'like', "%\"{$event}\"%"); - } - public function deployHook(): void { $this->update( @@ -64,19 +55,9 @@ public function deployHook(): void ); } - /** - * @throws Throwable - */ public function destroyHook(): void { - DB::beginTransaction(); - try { - $this->sourceControl->provider()->destroyHook($this->site->repository, $this->hook_id); - $this->delete(); - DB::commit(); - } catch (Exception $e) { - DB::rollBack(); - throw $e; - } + $this->sourceControl->provider()->destroyHook($this->site->repository, $this->hook_id); + $this->delete(); } } diff --git a/app/Models/NotificationChannel.php b/app/Models/NotificationChannel.php index cd8522c..803cc07 100644 --- a/app/Models/NotificationChannel.php +++ b/app/Models/NotificationChannel.php @@ -2,7 +2,7 @@ namespace App\Models; -use App\Contracts\Notification; +use App\Notifications\NotificationInterface; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Notifications\Notifiable; @@ -33,14 +33,14 @@ class NotificationChannel extends AbstractModel 'is_default' => 'boolean', ]; - public function provider(): \App\Contracts\NotificationChannel + public function provider(): \App\NotificationChannels\NotificationChannel { $class = config('core.notification_channels_providers_class')[$this->provider]; return new $class($this); } - public static function notifyAll(Notification $notification): void + public static function notifyAll(NotificationInterface $notification): void { $channels = self::all(); foreach ($channels as $channel) { diff --git a/app/Models/Queue.php b/app/Models/Queue.php index ccecb4d..56aeb56 100644 --- a/app/Models/Queue.php +++ b/app/Models/Queue.php @@ -2,11 +2,6 @@ namespace App\Models; -use App\Enums\QueueStatus; -use App\Jobs\Queue\Deploy; -use App\Jobs\Queue\GetLogs; -use App\Jobs\Queue\Manage; -use App\Jobs\Queue\Remove; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -21,8 +16,6 @@ * @property int $redirect_stderr * @property string $stdout_logfile * @property string $status - * @property string $log_directory - * @property string $log_file * @property Server $server * @property Site $site */ @@ -73,71 +66,13 @@ public function site(): BelongsTo return $this->belongsTo(Site::class); } - public function getLogDirectoryAttribute(): string + public function getLogDirectory(): string { return '/home/'.$this->user.'/.logs/workers'; } - public function getLogFileAttribute(): string + public function getLogFile(): string { - return $this->log_directory.'/'.$this->id.'.log'; - } - - public function deploy(): void - { - dispatch(new Deploy($this))->onConnection('ssh'); - } - - private function action(string $type, string $status, string $successStatus, string $failStatus, string $failMessage): void - { - $this->status = $status; - $this->save(); - dispatch(new Manage($this, $type, $successStatus, $failStatus, $failMessage)) - ->onConnection('ssh'); - } - - public function start(): void - { - $this->action( - 'start', - QueueStatus::STARTING, - QueueStatus::RUNNING, - QueueStatus::FAILED, - __('Failed to start') - ); - } - - public function stop(): void - { - $this->action( - 'stop', - QueueStatus::STOPPING, - QueueStatus::STOPPED, - QueueStatus::FAILED, - __('Failed to stop') - ); - } - - public function restart(): void - { - $this->action( - 'restart', - QueueStatus::RESTARTING, - QueueStatus::RUNNING, - QueueStatus::FAILED, - __('Failed to restart') - ); - } - - public function remove(): void - { - $this->status = QueueStatus::DELETING; - $this->save(); - dispatch(new Remove($this))->onConnection('ssh'); - } - - public function getLogs(): void - { - dispatch(new GetLogs($this))->onConnection('ssh'); + return $this->getLogDirectory().'/'.$this->id.'.log'; } } diff --git a/app/Models/Redirect.php b/app/Models/Redirect.php index 214fc16..6221029 100644 --- a/app/Models/Redirect.php +++ b/app/Models/Redirect.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Jobs\Redirect\AddToServer; -use App\Jobs\Redirect\DeleteFromServer; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -35,16 +33,4 @@ public function site(): BelongsTo { return $this->belongsTo(Site::class); } - - public function addToServer(): void - { - dispatch(new AddToServer($this))->onConnection('ssh'); - } - - public function deleteFromServer(): void - { - $this->status = 'deleting'; - $this->save(); - dispatch(new DeleteFromServer($this))->onConnection('ssh'); - } } diff --git a/app/Models/Script.php b/app/Models/Script.php index 2abed49..8b085ae 100644 --- a/app/Models/Script.php +++ b/app/Models/Script.php @@ -2,7 +2,6 @@ namespace App\Models; -use App\Jobs\Script\ExecuteOn; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; @@ -36,9 +35,4 @@ public function executions(): HasMany { return $this->hasMany(ScriptExecution::class, 'script_id'); } - - public function executeOn(Server $server, string $user): void - { - dispatch(new ExecuteOn($this, $server, $user))->onConnection('ssh'); - } } diff --git a/app/Models/Server.php b/app/Models/Server.php index a91cd7f..e6dfea4 100755 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -3,15 +3,12 @@ namespace App\Models; use App\Actions\Server\CheckConnection; -use App\Actions\Server\RebootServer; -use App\Contracts\ServerType; -use App\Enums\ServerStatus; use App\Enums\ServiceStatus; -use App\Facades\Notifier; use App\Facades\SSH; -use App\Jobs\Installation\Upgrade; -use App\Notifications\ServerInstallationStarted; -use App\Support\Testing\SSHFake; +use App\ServerTypes\ServerType; +use App\SSH\Cron\Cron; +use App\SSH\OS\OS; +use App\SSH\Systemd\Systemd; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsToMany; @@ -205,6 +202,22 @@ public function daemons(): HasMany return $this->queues()->whereNull('site_id'); } + public function sshKeys(): BelongsToMany + { + return $this->belongsToMany(SshKey::class, 'server_ssh_keys') + ->withPivot('status') + ->withTimestamps(); + } + + public function getSshUser(): string + { + if ($this->ssh_user) { + return $this->ssh_user; + } + + return config('core.ssh_user'); + } + public function service($type, $version = null): ?Service { /* @var Service $service */ @@ -230,6 +243,7 @@ public function defaultService($type): ?Service // If no default service found, get the first service with status ready or stopped if (! $service) { + /** @var Service $service */ $service = $this->services() ->where('type', $type) ->whereIn('status', [ServiceStatus::READY, ServiceStatus::STOPPED]) @@ -243,24 +257,7 @@ public function defaultService($type): ?Service return $service; } - public function getServiceByUnit($unit): ?Service - { - /* @var Service $service */ - $service = $this->services() - ->where('unit', $unit) - ->where('is_default', 1) - ->first(); - - return $service; - } - - public function install(): void - { - $this->type()->install(); - Notifier::send($this, new ServerInstallationStarted($this)); - } - - public function ssh(?string $user = null): \App\Helpers\SSH|SSHFake + public function ssh(?string $user = null): mixed { return SSH::init($this, $user); } @@ -283,7 +280,7 @@ public function type(): ServerType return new $typeClass($this); } - public function provider(): \App\Contracts\ServerProvider + public function provider(): \App\ServerProviders\ServerProvider { $providerClass = config('core.server_providers_class')[$this->provider]; @@ -335,32 +332,8 @@ public function php(?string $version = null): ?Service return $this->service('php', $version); } - public function sshKeys(): BelongsToMany - { - return $this->belongsToMany(SshKey::class, 'server_ssh_keys') - ->withPivot('status') - ->withTimestamps(); - } - - public function getSshUserAttribute(string $value): string - { - if ($value) { - return $value; - } - - return config('core.ssh_user'); - } - public function sshKey(): array { - if (app()->environment() == 'testing') { - return [ - 'public_key' => 'public', - 'public_key_path' => '/path', - 'private_key_path' => '/path', - ]; - } - /** @var FilesystemAdapter $storageDisk */ $storageDisk = Storage::disk(config('core.key_pairs_disk')); @@ -371,44 +344,28 @@ public function sshKey(): array ]; } - public function getServiceUnits(): array - { - $units = []; - $services = $this->services; - foreach ($services as $service) { - if ($service->unit) { - $units[] = $service->unit; - } - } - - return $units; - } - public function checkConnection(): self { return app(CheckConnection::class)->check($this); } - public function installUpdates(): void - { - $this->available_updates = 0; - $this->security_updates = 0; - $this->save(); - dispatch(new Upgrade($this))->onConnection('ssh'); - } - - public function reboot(): self - { - return app(RebootServer::class)->reboot($this); - } - - public function getHostnameAttribute(): string + public function hostname(): string { return Str::of($this->name)->slug(); } - public function isReady(): bool + public function os(): OS { - return $this->status == ServerStatus::READY; + return new OS($this); + } + + public function systemd(): Systemd + { + return new Systemd($this); + } + + public function cron(): Cron + { + return new Cron($this); } } diff --git a/app/Models/ServerLog.php b/app/Models/ServerLog.php index d52c39a..79c8895 100755 --- a/app/Models/ServerLog.php +++ b/app/Models/ServerLog.php @@ -15,7 +15,6 @@ * @property string $disk * @property Server $server * @property ?Site $site - * @property string $content */ class ServerLog extends AbstractModel { @@ -72,7 +71,7 @@ public function write($buf): void } } - public function getContentAttribute(): ?string + public function getContent(): ?string { if (Storage::disk($this->disk)->exists($this->name)) { return Storage::disk($this->disk)->get($this->name); @@ -80,4 +79,17 @@ public function getContentAttribute(): ?string return ''; } + + public static function log(Server $server, string $type, string $content, ?Site $site = null): void + { + $log = new static([ + 'server_id' => $server->id, + 'site_id' => $site?->id, + 'name' => $server->id.'-'.strtotime('now').'-'.$type.'.log', + 'type' => $type, + 'disk' => config('core.logs_disk'), + ]); + $log->save(); + $log->write($content); + } } diff --git a/app/Models/Service.php b/app/Models/Service.php index 338b84b..2ceeecd 100755 --- a/app/Models/Service.php +++ b/app/Models/Service.php @@ -2,17 +2,16 @@ namespace App\Models; -use App\Contracts\Database; -use App\Contracts\Firewall; -use App\Contracts\ProcessManager; -use App\Contracts\Webserver; -use App\Enums\ServiceStatus; -use App\Exceptions\InstallationFailed; -use App\Jobs\Service\Manage; -use App\ServiceHandlers\PHP; +use App\Actions\Service\Manage; +use App\Exceptions\ServiceInstallationFailed; +use App\SSH\Services\Database\Database as DatabaseHandler; +use App\SSH\Services\Firewall\Firewall as FirewallHandler; +use App\SSH\Services\PHP\PHP as PHPHandler; +use App\SSH\Services\ProcessManager\ProcessManager as ProcessManagerHandler; +use App\SSH\Services\Redis\Redis as RedisHandler; +use App\SSH\Services\Webserver\Webserver as WebserverHandler; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Support\Facades\Bus; use Illuminate\Support\Str; /** @@ -49,134 +48,49 @@ class Service extends AbstractModel 'is_default' => 'boolean', ]; + public static function boot(): void + { + parent::boot(); + + static::creating(function (Service $service) { + $service->unit = config('core.service_units')[$service->name][$service->server->os][$service->version]; + }); + } + public function server(): BelongsTo { return $this->belongsTo(Server::class); } - public function handler(): Database|Firewall|Webserver|PHP|ProcessManager - { + public function handler( + ): PHPHandler|WebserverHandler|DatabaseHandler|FirewallHandler|ProcessManagerHandler|RedisHandler { $handler = config('core.service_handlers')[$this->name]; return new $handler($this); } - public function installer(): mixed - { - $installer = config('core.service_installers')[$this->name]; - - return new $installer($this); - } - - public function uninstaller(): mixed - { - $uninstaller = config('core.service_uninstallers')[$this->name]; - - return new $uninstaller($this); - } - - public function getUnitAttribute($value): ?string - { - if ($value) { - return $value; - } - if (isset(config('core.service_units')[$this->name])) { - $value = config('core.service_units')[$this->name][$this->server->os][$this->version]; - if ($value) { - $this->fill(['unit' => $value]); - $this->save(); - } - } - - return $value; - } - - public function install(): void - { - Bus::chain([ - $this->installer(), - ])->onConnection('ssh-long')->dispatch(); - } - /** - * @throws InstallationFailed + * @throws ServiceInstallationFailed */ public function validateInstall($result): void { if (! Str::contains($result, 'Active: active')) { - throw new InstallationFailed(); + throw new ServiceInstallationFailed(); } } - public function uninstall(): void - { - $this->status = ServiceStatus::UNINSTALLING; - $this->save(); - Bus::chain([ - $this->uninstaller(), - function () { - $this->delete(); - }, - ])->catch(function () { - $this->status = ServiceStatus::FAILED; - $this->save(); - })->onConnection('ssh')->dispatch(); - } - public function start(): void { - $this->action( - 'start', - ServiceStatus::STARTING, - ServiceStatus::READY, - ServiceStatus::STOPPED, - __('Failed to start') - ); + app(Manage::class)->start($this); } public function stop(): void { - $this->action( - 'stop', - ServiceStatus::STOPPING, - ServiceStatus::STOPPED, - ServiceStatus::FAILED, - __('Failed to stop') - ); + app(Manage::class)->stop($this); } public function restart(): void { - $this->action( - 'restart', - ServiceStatus::RESTARTING, - ServiceStatus::READY, - ServiceStatus::FAILED, - __('Failed to restart') - ); - } - - public function action( - string $type, - string $status, - string $successStatus, - string $failStatus, - string $failMessage - ): void { - $this->status = $status; - $this->save(); - dispatch(new Manage($this, $type, $successStatus, $failStatus, $failMessage)) - ->onConnection('ssh'); - } - - public function installedVersions(): array - { - $versions = []; - $services = $this->server->services()->where('type', $this->type)->get(['version']); - foreach ($services as $service) { - $versions[] = $service->version; - } - - return $versions; + app(Manage::class)->restart($this); } } diff --git a/app/Models/Site.php b/app/Models/Site.php index dc640fd..b1355de 100755 --- a/app/Models/Site.php +++ b/app/Models/Site.php @@ -2,27 +2,14 @@ namespace App\Models; -use App\Contracts\SiteType; -use App\Enums\DeploymentStatus; -use App\Enums\SiteStatus; -use App\Enums\SslStatus; use App\Exceptions\SourceControlIsNotConnected; -use App\Facades\Notifier; -use App\Jobs\Site\Deploy; -use App\Jobs\Site\UpdateBranch; -use App\Notifications\SiteInstallationFailed; -use App\Notifications\SiteInstallationSucceed; -use App\SSHCommands\Website\GetEnvCommand; -use Exception; +use App\SiteTypes\SiteType; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasOne; -use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Log; use Illuminate\Support\Str; -use Throwable; /** * @property int $server_id @@ -31,7 +18,6 @@ * @property string $domain * @property array $aliases * @property string $web_directory - * @property string $web_directory_path * @property string $path * @property string $php_version * @property string $source_control @@ -42,8 +28,6 @@ * @property string $status * @property int $port * @property int $progress - * @property bool $auto_deployment - * @property string $url * @property Server $server * @property ServerLog[] $logs * @property Deployment[] $deployments @@ -53,10 +37,6 @@ * @property Queue[] $queues * @property Ssl[] $ssls * @property ?Ssl $activeSsl - * @property string $full_repository_url - * @property string $aliases_string - * @property string $deployment_script_text - * @property string $env * @property string $ssh_key_name */ class Site extends AbstractModel @@ -87,16 +67,10 @@ class Site extends AbstractModel 'type_data' => 'json', 'port' => 'integer', 'progress' => 'integer', - 'auto_deployment' => 'boolean', 'aliases' => 'array', 'source_control_id' => 'integer', ]; - protected $appends = [ - 'url', - 'auto_deployment', - ]; - public static function boot(): void { parent::boot(); @@ -186,12 +160,12 @@ public function sourceControl(): SourceControl|HasOne|null|Model /** * @throws SourceControlIsNotConnected */ - public function getFullRepositoryUrlAttribute() + public function getFullRepositoryUrl() { - return $this->sourceControl()->provider()->fullRepoUrl($this->repository, $this->ssh_key_name); + return $this->sourceControl()->provider()->fullRepoUrl($this->repository, $this->getSshKeyName()); } - public function getAliasesStringAttribute(): string + public function getAliasesString(): string { if (count($this->aliases) > 0) { return implode(' ', $this->aliases); @@ -207,19 +181,6 @@ public function type(): SiteType return new $typeClass($this); } - public function install(): void - { - $this->type()->install(); - } - - public function remove(): void - { - $this->update([ - 'status' => SiteStatus::DELETING, - ]); - $this->type()->delete(); - } - public function php(): ?Service { if ($this->php_version) { @@ -233,57 +194,7 @@ public function changePHPVersion($version): void { $this->php_version = $version; $this->server->webserver()->handler()->changePHPVersion($this, $version); - } - - public function getDeploymentScriptTextAttribute(): string - { - /* @var DeploymentScript $script */ - $script = $this->deploymentScript()->firstOrCreate([ - 'site_id' => $this->id, - ], [ - 'site_id' => $this->id, - 'name' => 'default', - ]); - - return $script->content; - } - - /** - * @throws SourceControlIsNotConnected - */ - public function deploy(): Deployment - { - if ($this->sourceControl()) { - $this->sourceControl()->getRepo($this->repository); - } - - $deployment = new Deployment([ - 'site_id' => $this->id, - 'deployment_script_id' => $this->deploymentScript->id, - 'status' => DeploymentStatus::DEPLOYING, - ]); - $lastCommit = $this->sourceControl()->provider()->getLastCommit($this->repository, $this->branch); - if ($lastCommit) { - $deployment->commit_id = $lastCommit['commit_id']; - $deployment->commit_data = $lastCommit['commit_data']; - } - $deployment->save(); - - dispatch(new Deploy($deployment, $this->path))->onConnection('ssh'); - - return $deployment; - } - - public function getEnvAttribute(): string - { - $typeData = $this->type_data; - if (! isset($typeData['env'])) { - $typeData['env'] = ''; - $this->type_data = $typeData; - $this->save(); - } - - return $typeData['env']; + $this->save(); } public function activeSsl(): HasOne @@ -293,33 +204,7 @@ public function activeSsl(): HasOne ->orderByDesc('id'); } - public function createFreeSsl(): void - { - $ssl = new Ssl([ - 'site_id' => $this->id, - 'type' => 'letsencrypt', - 'expires_at' => now()->addMonths(3), - 'status' => SslStatus::CREATING, - ]); - $ssl->save(); - $ssl->deploy(); - } - - public function createCustomSsl(string $certificate, string $pk): void - { - $ssl = new Ssl([ - 'site_id' => $this->id, - 'type' => 'custom', - 'certificate' => $certificate, - 'pk' => $pk, - 'expires_at' => '', - 'status' => SslStatus::CREATING, - ]); - $ssl->save(); - $ssl->deploy(); - } - - public function getUrlAttribute(): string + public function getUrl(): string { if ($this->activeSsl) { return 'https://'.$this->domain; @@ -328,7 +213,7 @@ public function getUrlAttribute(): string return 'http://'.$this->domain; } - public function getWebDirectoryPathAttribute(): string + public function getWebDirectoryPath(): string { if ($this->web_directory) { return $this->path.'/'.$this->web_directory; @@ -339,7 +224,6 @@ public function getWebDirectoryPathAttribute(): string /** * @throws SourceControlIsNotConnected - * @throws Throwable */ public function enableAutoDeployment(): void { @@ -347,92 +231,50 @@ public function enableAutoDeployment(): void return; } - if (! $this->sourceControl()) { + if (! $this->sourceControl()?->getRepo($this->repository)) { throw new SourceControlIsNotConnected($this->source_control); } - DB::beginTransaction(); - try { - $gitHook = new GitHook([ - 'site_id' => $this->id, - 'source_control_id' => $this->sourceControl()->id, - 'secret' => Str::uuid()->toString(), - 'actions' => ['deploy'], - 'events' => ['push'], - ]); - $gitHook->save(); - $gitHook->deployHook(); - DB::commit(); - } catch (Exception $e) { - DB::rollBack(); - throw $e; - } + $gitHook = new GitHook([ + 'site_id' => $this->id, + 'source_control_id' => $this->sourceControl()->id, + 'secret' => Str::uuid()->toString(), + 'actions' => ['deploy'], + 'events' => ['push'], + ]); + $gitHook->save(); + $gitHook->deployHook(); } /** - * @throws Throwable + * @throws SourceControlIsNotConnected */ public function disableAutoDeployment(): void { + if (! $this->sourceControl()?->getRepo($this->repository)) { + throw new SourceControlIsNotConnected($this->source_control); + } + $this->gitHook?->destroyHook(); } - public function getAutoDeploymentAttribute(): bool + public function isAutoDeployment(): bool { return (bool) $this->gitHook; } - public function updateBranch(string $branch): void - { - dispatch(new UpdateBranch($this, $branch))->onConnection('ssh'); - } - - public function getSshKeyNameAttribute(): string + public function getSshKeyName(): string { return str('site_'.$this->id)->toString(); } - public function installationFinished(): void - { - $this->update([ - 'status' => SiteStatus::READY, - 'progress' => 100, - ]); - Notifier::send($this, new SiteInstallationSucceed($this)); - } - - /** - * @throws Throwable - */ - public function installationFailed(Throwable $e): void - { - $this->update([ - 'status' => SiteStatus::INSTALLATION_FAILED, - ]); - Notifier::send($this, new SiteInstallationFailed($this)); - Log::error('install-site-error', [ - 'error' => (string) $e, - ]); - - throw $e; - } - public function hasFeature(string $feature): bool { return in_array($feature, $this->type()->supportedFeatures()); } - public function isReady(): bool - { - return $this->status === SiteStatus::READY; - } - public function getEnv(): string { - return $this->server->ssh()->exec( - new GetEnvCommand( - $this->domain - ) - ); + return $this->server->os()->readFile($this->path.'/.env'); } } diff --git a/app/Models/SourceControl.php b/app/Models/SourceControl.php index e3f10c4..00f16d2 100755 --- a/app/Models/SourceControl.php +++ b/app/Models/SourceControl.php @@ -2,7 +2,7 @@ namespace App\Models; -use App\Contracts\SourceControlProvider; +use App\SourceControlProviders\SourceControlProvider; use Illuminate\Database\Eloquent\Factories\HasFactory; /** diff --git a/app/Models/SshKey.php b/app/Models/SshKey.php index 4f80343..dcb27e5 100644 --- a/app/Models/SshKey.php +++ b/app/Models/SshKey.php @@ -2,9 +2,6 @@ namespace App\Models; -use App\Enums\SshKeyStatus; -use App\Jobs\SshKey\DeleteSshKeyFromServer; -use App\Jobs\SshKey\DeploySshKeyToServer; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsToMany; @@ -47,20 +44,4 @@ public function existsOnServer(Server $server): bool { return (bool) $this->servers()->where('id', $server->id)->first(); } - - public function deployTo(Server $server): void - { - $server->sshKeys()->attach($this, [ - 'status' => SshKeyStatus::ADDING, - ]); - dispatch(new DeploySshKeyToServer($server, $this))->onConnection('ssh'); - } - - public function deleteFrom(Server $server): void - { - $this->servers()->updateExistingPivot($server->id, [ - 'status' => SshKeyStatus::DELETING, - ]); - dispatch(new DeleteSshKeyFromServer($server, $this))->onConnection('ssh'); - } } diff --git a/app/Models/Ssl.php b/app/Models/Ssl.php index b16baf2..29bd96d 100644 --- a/app/Models/Ssl.php +++ b/app/Models/Ssl.php @@ -2,9 +2,6 @@ namespace App\Models; -use App\Enums\SslStatus; -use App\Jobs\Ssl\Deploy; -use App\Jobs\Ssl\Remove; use Carbon\Carbon; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -19,9 +16,6 @@ * @property Carbon $expires_at * @property string $status * @property Site $site - * @property string $certs_directory_path - * @property string $certificate_path - * @property string $pk_path * @property string $ca_path */ class Ssl extends AbstractModel @@ -51,7 +45,7 @@ public function site(): BelongsTo return $this->belongsTo(Site::class); } - public function getCertsDirectoryPathAttribute(): ?string + public function getCertsDirectoryPath(): ?string { if ($this->type == 'letsencrypt') { return '/etc/letsencrypt/live/'.$this->site->domain; @@ -64,57 +58,45 @@ public function getCertsDirectoryPathAttribute(): ?string return ''; } - public function getCertificatePathAttribute(): ?string + public function getCertificatePath(): ?string { if ($this->type == 'letsencrypt') { return $this->certificate; } if ($this->type == 'custom') { - return $this->certs_directory_path.'/cert.pem'; + return $this->getCertsDirectoryPath().'/cert.pem'; } return ''; } - public function getPkPathAttribute(): ?string + public function getPkPath(): ?string { if ($this->type == 'letsencrypt') { return $this->pk; } if ($this->type == 'custom') { - return $this->certs_directory_path.'/privkey.pem'; + return $this->getCertsDirectoryPath().'/privkey.pem'; } return ''; } - public function getCaPathAttribute(): ?string + public function getCaPath(): ?string { if ($this->type == 'letsencrypt') { return $this->ca; } if ($this->type == 'custom') { - return $this->certs_directory_path.'/fullchain.pem'; + return $this->getCertsDirectoryPath().'/fullchain.pem'; } return ''; } - public function deploy(): void - { - dispatch(new Deploy($this))->onConnection('ssh'); - } - - public function remove(): void - { - $this->status = SslStatus::DELETING; - $this->save(); - dispatch(new Remove($this))->onConnection('ssh'); - } - public function validateSetup(string $result): bool { if (! Str::contains($result, 'Successfully received certificate')) { @@ -122,8 +104,8 @@ public function validateSetup(string $result): bool } if ($this->type == 'letsencrypt') { - $this->certificate = $this->certs_directory_path.'/fullchain.pem'; - $this->pk = $this->certs_directory_path.'/privkey.pem'; + $this->certificate = $this->getCertsDirectoryPath().'/fullchain.pem'; + $this->pk = $this->getCertsDirectoryPath().'/privkey.pem'; $this->save(); } diff --git a/app/Models/StorageProvider.php b/app/Models/StorageProvider.php index 6098f5b..bd69d9e 100644 --- a/app/Models/StorageProvider.php +++ b/app/Models/StorageProvider.php @@ -33,7 +33,7 @@ public function user(): BelongsTo return $this->belongsTo(User::class); } - public function provider(): \App\Contracts\StorageProvider + public function provider(): \App\StorageProviders\StorageProvider { $providerClass = config('core.storage_providers_class')[$this->provider]; diff --git a/app/NotificationChannels/AbstractNotificationChannel.php b/app/NotificationChannels/AbstractNotificationChannel.php index 07d4468..d894580 100644 --- a/app/NotificationChannels/AbstractNotificationChannel.php +++ b/app/NotificationChannels/AbstractNotificationChannel.php @@ -2,8 +2,8 @@ namespace App\NotificationChannels; -use App\Contracts\NotificationChannel as NotificationChannelInterface; use App\Models\NotificationChannel; +use App\NotificationChannels\NotificationChannel as NotificationChannelInterface; abstract class AbstractNotificationChannel implements NotificationChannelInterface { diff --git a/app/NotificationChannels/Discord.php b/app/NotificationChannels/Discord.php index 069886a..3655adf 100644 --- a/app/NotificationChannels/Discord.php +++ b/app/NotificationChannels/Discord.php @@ -2,8 +2,8 @@ namespace App\NotificationChannels; -use App\Contracts\Notification; use App\Models\NotificationChannel; +use App\Notifications\NotificationInterface; use Illuminate\Support\Facades\Http; class Discord extends AbstractNotificationChannel @@ -59,7 +59,7 @@ private function checkConnection(string $subject, string $text): bool return $connect->ok(); } - public function send(object $notifiable, Notification $notification): void + public function send(object $notifiable, NotificationInterface $notification): void { /** @var NotificationChannel $notifiable */ $this->notificationChannel = $notifiable; diff --git a/app/NotificationChannels/Email.php b/app/NotificationChannels/Email.php index 440296b..6ff6006 100644 --- a/app/NotificationChannels/Email.php +++ b/app/NotificationChannels/Email.php @@ -2,9 +2,9 @@ namespace App\NotificationChannels; -use App\Contracts\Notification; -use App\Mail\NotificationMail; use App\Models\NotificationChannel; +use App\NotificationChannels\Email\NotificationMail; +use App\Notifications\NotificationInterface; use Illuminate\Support\Facades\Mail; use Throwable; @@ -47,7 +47,7 @@ public function connect(): bool return true; } - public function send(object $notifiable, Notification $notification): void + public function send(object $notifiable, NotificationInterface $notification): void { /** @var NotificationChannel $notifiable */ $this->notificationChannel = $notifiable; diff --git a/app/Mail/NotificationMail.php b/app/NotificationChannels/Email/NotificationMail.php similarity index 91% rename from app/Mail/NotificationMail.php rename to app/NotificationChannels/Email/NotificationMail.php index d0ecfe8..50a9cfe 100644 --- a/app/Mail/NotificationMail.php +++ b/app/NotificationChannels/Email/NotificationMail.php @@ -1,6 +1,6 @@ ok(); } - public function send(object $notifiable, Notification $notification): void + public function send(object $notifiable, NotificationInterface $notification): void { /** @var NotificationChannel $notifiable */ $this->notificationChannel = $notifiable; diff --git a/app/NotificationChannels/Telegram.php b/app/NotificationChannels/Telegram.php index d57e627..b692323 100644 --- a/app/NotificationChannels/Telegram.php +++ b/app/NotificationChannels/Telegram.php @@ -2,8 +2,8 @@ namespace App\NotificationChannels; -use App\Contracts\Notification; use App\Models\NotificationChannel; +use App\Notifications\NotificationInterface; use Illuminate\Support\Facades\Http; use Throwable; @@ -46,7 +46,7 @@ public function connect(): bool return true; } - public function send(object $notifiable, Notification $notification): void + public function send(object $notifiable, NotificationInterface $notification): void { /** @var NotificationChannel $notifiable */ $this->notificationChannel = $notifiable; diff --git a/app/Notifications/AbstractNotification.php b/app/Notifications/AbstractNotification.php index 9afd974..78aa2f2 100644 --- a/app/Notifications/AbstractNotification.php +++ b/app/Notifications/AbstractNotification.php @@ -2,7 +2,6 @@ namespace App\Notifications; -use App\Contracts\Notification as NotificationInterface; use App\Models\NotificationChannel; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; diff --git a/app/Contracts/Notification.php b/app/Notifications/NotificationInterface.php similarity index 84% rename from app/Contracts/Notification.php rename to app/Notifications/NotificationInterface.php index 62bae46..5c59cc9 100644 --- a/app/Contracts/Notification.php +++ b/app/Notifications/NotificationInterface.php @@ -1,10 +1,10 @@ startsWith('https://')) { URL::forceScheme('https'); } - - if ($this->app->environment('local')) { - \App\Facades\SSH::fake(); - } } /** diff --git a/app/SSH/Composer/Composer.php b/app/SSH/Composer/Composer.php new file mode 100644 index 0000000..654600d --- /dev/null +++ b/app/SSH/Composer/Composer.php @@ -0,0 +1,22 @@ +server->ssh()->exec( + $this->getScript('composer-install.sh', [ + 'path' => $site->path, + ]), + 'composer-install', + $site->id + ); + } +} diff --git a/resources/commands/website/composer-install.sh b/app/SSH/Composer/scripts/composer-install.sh similarity index 100% rename from resources/commands/website/composer-install.sh rename to app/SSH/Composer/scripts/composer-install.sh diff --git a/app/SSH/Cron/Cron.php b/app/SSH/Cron/Cron.php new file mode 100644 index 0000000..5bf69bf --- /dev/null +++ b/app/SSH/Cron/Cron.php @@ -0,0 +1,27 @@ +server->ssh()->exec($command); + } +} diff --git a/app/SSH/Git/Git.php b/app/SSH/Git/Git.php new file mode 100644 index 0000000..22c1f0d --- /dev/null +++ b/app/SSH/Git/Git.php @@ -0,0 +1,38 @@ +server->ssh()->exec( + $this->getScript('clone.sh', [ + 'host' => str($site->getFullRepositoryUrl())->after('@')->before('-'), + 'repo' => $site->getFullRepositoryUrl(), + 'path' => $site->path, + 'branch' => $site->branch, + 'key' => $site->getSshKeyName(), + ]), + 'clone-repository', + $site->id + ); + } + + public function checkout(Site $site): void + { + $site->server->ssh()->exec( + $this->getScript('checkout.sh', [ + 'path' => $site->path, + 'branch' => $site->branch, + ]), + 'checkout-branch', + $site->id + ); + } +} diff --git a/resources/commands/website/update-branch.sh b/app/SSH/Git/scripts/checkout.sh similarity index 100% rename from resources/commands/website/update-branch.sh rename to app/SSH/Git/scripts/checkout.sh diff --git a/resources/commands/website/clone-repository.sh b/app/SSH/Git/scripts/clone.sh similarity index 100% rename from resources/commands/website/clone-repository.sh rename to app/SSH/Git/scripts/clone.sh diff --git a/app/SSH/HasScripts.php b/app/SSH/HasScripts.php new file mode 100644 index 0000000..7a5ddf3 --- /dev/null +++ b/app/SSH/HasScripts.php @@ -0,0 +1,20 @@ +getFileName()).'/scripts'; + $script = file_get_contents($scriptsDir.'/'.$name); + foreach ($vars as $key => $value) { + $script = str_replace('__'.$key.'__', $value, $script); + } + + return $script; + } +} diff --git a/app/SSH/OS/OS.php b/app/SSH/OS/OS.php new file mode 100644 index 0000000..228fd6c --- /dev/null +++ b/app/SSH/OS/OS.php @@ -0,0 +1,138 @@ +server->ssh()->exec( + $this->getScript('install-dependencies.sh'), + 'install-dependencies' + ); + } + + public function upgrade(): void + { + $this->server->ssh()->exec( + $this->getScript('upgrade.sh'), + 'upgrade' + ); + } + + public function createUser(string $user, string $password, string $key): void + { + $this->server->ssh()->exec( + $this->getScript('create-user.sh', [ + 'user' => $user, + 'password' => $password, + 'key' => $key, + ]), + 'create-user' + ); + } + + public function getPublicKey(string $user): string + { + return $this->server->ssh()->exec( + $this->getScript('read-file.sh', [ + 'path' => '/home/'.$user.'/.ssh/id_rsa.pub', + ]) + ); + } + + public function deploySSHKey(string $key): void + { + $this->server->ssh()->exec( + $this->getScript('deploy-ssh-key.sh', [ + 'key' => $key, + ]), + 'deploy-ssh-key' + ); + } + + public function deleteSSHKey(string $key): void + { + info($this->getScript('delete-ssh-key.sh', [ + 'key' => $key, + 'user' => $this->server->getSshUser(), + ])); + $this->server->ssh()->exec( + $this->getScript('delete-ssh-key.sh', [ + 'key' => $key, + 'user' => $this->server->getSshUser(), + ]), + 'delete-ssh-key' + ); + } + + public function generateSSHKey(string $name): void + { + $this->server->ssh()->exec( + $this->getScript('generate-ssh-key.sh', [ + 'name' => $name, + ]), + 'generate-ssh-key' + ); + } + + public function readSSHKey(string $name): string + { + return $this->server->ssh()->exec( + $this->getScript('read-ssh-key.sh', [ + 'name' => $name, + ]), + ); + } + + public function reboot(): void + { + $this->server->ssh()->exec( + $this->getScript('reboot.sh'), + ); + } + + public function editFile(string $path, string $content): void + { + $this->server->ssh()->exec( + $this->getScript('edit-file.sh', [ + 'path' => $path, + 'content' => $content, + ]), + ); + } + + public function readFile(string $path): string + { + return $this->server->ssh()->exec( + $this->getScript('read-file.sh', [ + 'path' => $path, + ]) + ); + } + + public function runScript(string $path, string $script, ?int $siteId = null): ServerLog + { + $ssh = $this->server->ssh(); + $ssh->exec( + $this->getScript('run-script.sh', [ + 'path' => $path, + 'script' => $script, + ]), + 'run-script', + $siteId + ); + + return $ssh->log; + } +} diff --git a/resources/commands/system/create-user.sh b/app/SSH/OS/scripts/create-user.sh similarity index 100% rename from resources/commands/system/create-user.sh rename to app/SSH/OS/scripts/create-user.sh diff --git a/app/SSH/OS/scripts/delete-ssh-key.sh b/app/SSH/OS/scripts/delete-ssh-key.sh new file mode 100644 index 0000000..5a86e04 --- /dev/null +++ b/app/SSH/OS/scripts/delete-ssh-key.sh @@ -0,0 +1 @@ +bash -c 'ssh_key_to_delete="$1"; sed -i "\#${ssh_key_to_delete//\//\\/}#d" /home/vito/.ssh/authorized_keys' bash '__key__' diff --git a/resources/commands/system/deploy-ssh-key.sh b/app/SSH/OS/scripts/deploy-ssh-key.sh similarity index 100% rename from resources/commands/system/deploy-ssh-key.sh rename to app/SSH/OS/scripts/deploy-ssh-key.sh diff --git a/resources/commands/system/edit-file.sh b/app/SSH/OS/scripts/edit-file.sh similarity index 100% rename from resources/commands/system/edit-file.sh rename to app/SSH/OS/scripts/edit-file.sh diff --git a/resources/commands/system/generate-ssh-key.sh b/app/SSH/OS/scripts/generate-ssh-key.sh similarity index 100% rename from resources/commands/system/generate-ssh-key.sh rename to app/SSH/OS/scripts/generate-ssh-key.sh diff --git a/resources/commands/system/get-public-key.sh b/app/SSH/OS/scripts/get-public-key.sh similarity index 100% rename from resources/commands/system/get-public-key.sh rename to app/SSH/OS/scripts/get-public-key.sh diff --git a/app/SSH/OS/scripts/install-dependencies.sh b/app/SSH/OS/scripts/install-dependencies.sh new file mode 100755 index 0000000..f21b06f --- /dev/null +++ b/app/SSH/OS/scripts/install-dependencies.sh @@ -0,0 +1,3 @@ +sudo DEBIAN_FRONTEND=noninteractive apt-get install -y software-properties-common curl zip unzip git gcc +git config --global user.email "__email__" +git config --global user.name "__name__" diff --git a/app/SSH/OS/scripts/read-file.sh b/app/SSH/OS/scripts/read-file.sh new file mode 100644 index 0000000..becea3d --- /dev/null +++ b/app/SSH/OS/scripts/read-file.sh @@ -0,0 +1 @@ +[ -f __path__ ] && cat __path__ diff --git a/resources/commands/system/read-ssh-key.sh b/app/SSH/OS/scripts/read-ssh-key.sh similarity index 100% rename from resources/commands/system/read-ssh-key.sh rename to app/SSH/OS/scripts/read-ssh-key.sh diff --git a/resources/commands/system/reboot.sh b/app/SSH/OS/scripts/reboot.sh similarity index 100% rename from resources/commands/system/reboot.sh rename to app/SSH/OS/scripts/reboot.sh diff --git a/resources/commands/system/run-script.sh b/app/SSH/OS/scripts/run-script.sh similarity index 100% rename from resources/commands/system/run-script.sh rename to app/SSH/OS/scripts/run-script.sh diff --git a/app/SSH/OS/scripts/upgrade.sh b/app/SSH/OS/scripts/upgrade.sh new file mode 100755 index 0000000..22e04ae --- /dev/null +++ b/app/SSH/OS/scripts/upgrade.sh @@ -0,0 +1,9 @@ +sudo DEBIAN_FRONTEND=noninteractive apt-get clean +sudo DEBIAN_FRONTEND=noninteractive apt-get update +sudo DEBIAN_FRONTEND=noninteractive apt-get upgrade -y +sudo DEBIAN_FRONTEND=noninteractive apt-get autoremove -y + +# Install Node.js +curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -; +sudo DEBIAN_FRONTEND=noninteractive apt-get update +sudo DEBIAN_FRONTEND=noninteractive apt-get install nodejs -y diff --git a/app/SSH/Services/Database/AbstractDatabase.php b/app/SSH/Services/Database/AbstractDatabase.php new file mode 100755 index 0000000..397be36 --- /dev/null +++ b/app/SSH/Services/Database/AbstractDatabase.php @@ -0,0 +1,32 @@ +service = $service; + $this->server = $service->server; + } + + public function install(): void + { + $version = $this->service->version; + $command = $this->getScript($this->service->name.'/install-'.$version.'.sh'); + $this->server->ssh()->exec($command, 'install-'.$this->service->name.'-'.$version); + $status = $this->server->systemd()->status($this->service->unit); + $this->service->validateInstall($status); + } +} diff --git a/app/Contracts/Database.php b/app/SSH/Services/Database/Database.php similarity index 94% rename from app/Contracts/Database.php rename to app/SSH/Services/Database/Database.php index aabcb24..a5ab82c 100755 --- a/app/Contracts/Database.php +++ b/app/SSH/Services/Database/Database.php @@ -1,6 +1,6 @@ server->ssh()->exec( + $this->getScript('mysql/create.sh', [ + 'name' => $name, + ]), + 'create-database' + ); + } + + public function delete(string $name): void + { + $this->server->ssh()->exec( + $this->getScript('mysql/delete.sh', [ + 'name' => $name, + ]), + 'delete-database' + ); + } + + public function createUser(string $username, string $password, string $host): void + { + $this->server->ssh()->exec( + $this->getScript('mysql/create-user.sh', [ + 'username' => $username, + 'password' => $password, + 'host' => $host, + ]), + 'create-user' + ); + } + + public function deleteUser(string $username, string $host): void + { + $this->server->ssh()->exec( + $this->getScript('mysql/delete-user.sh', [ + 'username' => $username, + 'host' => $host, + ]), + 'delete-user' + ); + } + + public function link(string $username, string $host, array $databases): void + { + $ssh = $this->server->ssh(); + + foreach ($databases as $database) { + $ssh->exec( + $this->getScript('mysql/link.sh', [ + 'username' => $username, + 'host' => $host, + 'database' => $database, + ]), + 'link-user-to-database' + ); + } + } + + public function unlink(string $username, string $host): void + { + $this->server->ssh()->exec( + $this->getScript('mysql/unlink.sh', [ + 'username' => $username, + 'host' => $host, + ]), + 'unlink-user-from-databases' + ); + } + + public function runBackup(BackupFile $backupFile): void + { + // backup + $this->server->ssh()->exec( + $this->getScript('mysql/backup.sh', [ + 'file' => $backupFile->name, + 'database' => $backupFile->backup->database->name, + ]), + 'backup-database' + ); + + // upload to storage + $upload = $backupFile->backup->storage->provider()->ssh($this->server)->upload( + $backupFile->path(), + $backupFile->storagePath(), + ); + + // cleanup + $this->server->ssh()->exec('rm '.$backupFile->name.'.zip'); + + $backupFile->size = $upload['size']; + $backupFile->save(); + } + + public function restoreBackup(BackupFile $backupFile, string $database): void + { + // download + $backupFile->backup->storage->provider()->ssh($this->server)->download( + $backupFile->storagePath(), + $backupFile->name.'.zip', + ); + + $this->server->ssh()->exec( + $this->getScript('mysql/restore.sh', [ + 'database' => $database, + 'file' => $backupFile->name, + ]), + 'restore-database' + ); + } +} diff --git a/resources/commands/database/install-mariadb.sh b/app/SSH/Services/Database/scripts/mariadb/install-10.sh similarity index 62% rename from resources/commands/database/install-mariadb.sh rename to app/SSH/Services/Database/scripts/mariadb/install-10.sh index dd3bc49..8a946d0 100755 --- a/resources/commands/database/install-mariadb.sh +++ b/app/SSH/Services/Database/scripts/mariadb/install-10.sh @@ -5,8 +5,8 @@ chmod +x mariadb_repo_setup sudo DEBIAN_FRONTEND=noninteractive ./mariadb_repo_setup \ --mariadb-server-version="mariadb-10.3" -sudo DEBIAN_FRONTEND=noninteractive apt update +sudo DEBIAN_FRONTEND=noninteractive apt-get update -sudo DEBIAN_FRONTEND=noninteractive apt install mariadb-server mariadb-backup -y +sudo DEBIAN_FRONTEND=noninteractive apt-get install mariadb-server mariadb-backup -y sudo service mysql start diff --git a/resources/commands/database/mysql/backup.sh b/app/SSH/Services/Database/scripts/mysql/backup.sh similarity index 100% rename from resources/commands/database/mysql/backup.sh rename to app/SSH/Services/Database/scripts/mysql/backup.sh diff --git a/resources/commands/database/mysql/create-user.sh b/app/SSH/Services/Database/scripts/mysql/create-user.sh similarity index 100% rename from resources/commands/database/mysql/create-user.sh rename to app/SSH/Services/Database/scripts/mysql/create-user.sh diff --git a/resources/commands/database/mysql/create.sh b/app/SSH/Services/Database/scripts/mysql/create.sh similarity index 100% rename from resources/commands/database/mysql/create.sh rename to app/SSH/Services/Database/scripts/mysql/create.sh diff --git a/resources/commands/database/mysql/delete-user.sh b/app/SSH/Services/Database/scripts/mysql/delete-user.sh similarity index 100% rename from resources/commands/database/mysql/delete-user.sh rename to app/SSH/Services/Database/scripts/mysql/delete-user.sh diff --git a/resources/commands/database/mysql/delete.sh b/app/SSH/Services/Database/scripts/mysql/delete.sh similarity index 100% rename from resources/commands/database/mysql/delete.sh rename to app/SSH/Services/Database/scripts/mysql/delete.sh diff --git a/resources/commands/database/install-mysql.sh b/app/SSH/Services/Database/scripts/mysql/install-5.7.sh similarity index 79% rename from resources/commands/database/install-mysql.sh rename to app/SSH/Services/Database/scripts/mysql/install-5.7.sh index 9d1484c..7348d94 100755 --- a/resources/commands/database/install-mysql.sh +++ b/app/SSH/Services/Database/scripts/mysql/install-5.7.sh @@ -1,4 +1,4 @@ -sudo DEBIAN_FRONTEND=noninteractive apt install mysql-server -y +sudo DEBIAN_FRONTEND=noninteractive apt-get install mysql-server -y sudo service mysql enable diff --git a/resources/commands/database/install-mysql-8.sh b/app/SSH/Services/Database/scripts/mysql/install-8.0.sh similarity index 77% rename from resources/commands/database/install-mysql-8.sh rename to app/SSH/Services/Database/scripts/mysql/install-8.0.sh index 9d8b822..0df07a7 100755 --- a/resources/commands/database/install-mysql-8.sh +++ b/app/SSH/Services/Database/scripts/mysql/install-8.0.sh @@ -2,9 +2,9 @@ wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.22-1_all.deb sudo DEBIAN_FRONTEND=noninteractive dpkg -i mysql-apt-config_0.8.22-1_all.deb -sudo DEBIAN_FRONTEND=noninteractive apt update +sudo DEBIAN_FRONTEND=noninteractive apt-get update -sudo DEBIAN_FRONTEND=noninteractive apt install mysql-server -y +sudo DEBIAN_FRONTEND=noninteractive apt-get install mysql-server -y sudo service mysql enable diff --git a/resources/commands/database/mysql/link.sh b/app/SSH/Services/Database/scripts/mysql/link.sh similarity index 100% rename from resources/commands/database/mysql/link.sh rename to app/SSH/Services/Database/scripts/mysql/link.sh diff --git a/resources/commands/database/mysql/restore.sh b/app/SSH/Services/Database/scripts/mysql/restore.sh similarity index 100% rename from resources/commands/database/mysql/restore.sh rename to app/SSH/Services/Database/scripts/mysql/restore.sh diff --git a/resources/commands/database/mysql/unlink.sh b/app/SSH/Services/Database/scripts/mysql/unlink.sh similarity index 100% rename from resources/commands/database/mysql/unlink.sh rename to app/SSH/Services/Database/scripts/mysql/unlink.sh diff --git a/app/ServiceHandlers/Firewall/AbstractFirewall.php b/app/SSH/Services/Firewall/AbstractFirewall.php similarity index 53% rename from app/ServiceHandlers/Firewall/AbstractFirewall.php rename to app/SSH/Services/Firewall/AbstractFirewall.php index a10673a..19b4724 100755 --- a/app/ServiceHandlers/Firewall/AbstractFirewall.php +++ b/app/SSH/Services/Firewall/AbstractFirewall.php @@ -1,11 +1,11 @@ service->server->ssh()->exec( + $this->getScript('ufw/install-ufw.sh'), + 'install-ufw' + ); + } + + public function addRule(string $type, string $protocol, int $port, string $source, ?string $mask): void + { + $this->service->server->ssh()->exec( + $this->getScript('ufw/add-rule.sh', [ + 'type' => $type, + 'protocol' => $protocol, + 'port' => $port, + 'source' => $source, + 'mask' => $mask || $mask == 0 ? '/'.$mask : '', + ]), + 'add-firewall-rule' + ); + } + + public function removeRule(string $type, string $protocol, int $port, string $source, ?string $mask): void + { + $this->service->server->ssh()->exec( + $this->getScript('ufw/remove-rule.sh', [ + 'type' => $type, + 'protocol' => $protocol, + 'port' => $port, + 'source' => $source, + 'mask' => $mask || $mask == 0 ? '/'.$mask : '', + ]), + 'remove-firewall-rule' + ); + } +} diff --git a/resources/commands/firewall/ufw/add-rule.sh b/app/SSH/Services/Firewall/scripts/ufw/add-rule.sh similarity index 100% rename from resources/commands/firewall/ufw/add-rule.sh rename to app/SSH/Services/Firewall/scripts/ufw/add-rule.sh diff --git a/resources/commands/firewall/ufw/install-ufw.sh b/app/SSH/Services/Firewall/scripts/ufw/install-ufw.sh similarity index 100% rename from resources/commands/firewall/ufw/install-ufw.sh rename to app/SSH/Services/Firewall/scripts/ufw/install-ufw.sh diff --git a/resources/commands/firewall/ufw/remove-rule.sh b/app/SSH/Services/Firewall/scripts/ufw/remove-rule.sh similarity index 100% rename from resources/commands/firewall/ufw/remove-rule.sh rename to app/SSH/Services/Firewall/scripts/ufw/remove-rule.sh diff --git a/app/SSH/Services/PHP/PHP.php b/app/SSH/Services/PHP/PHP.php new file mode 100644 index 0000000..21eb4ab --- /dev/null +++ b/app/SSH/Services/PHP/PHP.php @@ -0,0 +1,88 @@ +service = $service; + } + + public function install(): void + { + $server = $this->service->server; + $server->ssh()->exec( + $this->getScript('install-php.sh', [ + 'version' => $this->service->version, + 'user' => $server->getSshUser(), + ]), + 'install-php-'.$this->service->version + ); + } + + public function uninstall(): void + { + $this->service->server->ssh()->exec( + $this->getScript('uninstall-php.sh', [ + 'version' => $this->service->version, + ]), + 'uninstall-php-'.$this->service->version + ); + } + + public function setDefaultCli(): void + { + $this->service->server->ssh()->exec( + $this->getScript('change-default-php.sh', [ + 'version' => $this->service->version, + ]), + 'change-default-php' + ); + } + + /** + * @throws SSHCommandError + */ + public function installExtension($name): void + { + $result = $this->service->server->ssh()->exec( + $this->getScript('install-php-extension.sh', [ + 'version' => $this->service->version, + 'name' => $name, + ]), + 'install-php-extension-'.$name + ); + $result = Str::substr($result, strpos($result, '[PHP Modules]')); + if (! Str::contains($result, $name)) { + throw new SSHCommandError('Failed to install extension'); + } + } + + public function installComposer(): void + { + $this->service->server->ssh()->exec( + $this->getScript('install-composer.sh'), + 'install-composer' + ); + } + + public function getPHPIni(): string + { + return $this->service->server->ssh()->exec( + $this->getScript('get-php-ini.sh', [ + 'version' => $this->service->version, + ]) + ); + } +} diff --git a/resources/commands/php/change-default-php.sh b/app/SSH/Services/PHP/scripts/change-default-php.sh similarity index 100% rename from resources/commands/php/change-default-php.sh rename to app/SSH/Services/PHP/scripts/change-default-php.sh diff --git a/resources/commands/php/get-php-ini.sh b/app/SSH/Services/PHP/scripts/get-php-ini.sh similarity index 100% rename from resources/commands/php/get-php-ini.sh rename to app/SSH/Services/PHP/scripts/get-php-ini.sh diff --git a/resources/commands/php/install-composer.sh b/app/SSH/Services/PHP/scripts/install-composer.sh similarity index 100% rename from resources/commands/php/install-composer.sh rename to app/SSH/Services/PHP/scripts/install-composer.sh diff --git a/resources/commands/php/install-php-extension.sh b/app/SSH/Services/PHP/scripts/install-php-extension.sh similarity index 55% rename from resources/commands/php/install-php-extension.sh rename to app/SSH/Services/PHP/scripts/install-php-extension.sh index bda498e..726ed97 100644 --- a/resources/commands/php/install-php-extension.sh +++ b/app/SSH/Services/PHP/scripts/install-php-extension.sh @@ -1,4 +1,4 @@ -sudo apt install -y php__version__-__name__ +sudo apt-get install -y php__version__-__name__ sudo service php__version__-fpm restart diff --git a/app/SSH/Services/PHP/scripts/install-php.sh b/app/SSH/Services/PHP/scripts/install-php.sh new file mode 100755 index 0000000..04cbb75 --- /dev/null +++ b/app/SSH/Services/PHP/scripts/install-php.sh @@ -0,0 +1,13 @@ +sudo add-apt-repository ppa:ondrej/php -y + +sudo DEBIAN_FRONTEND=noninteractive apt-get update + +sudo DEBIAN_FRONTEND=noninteractive apt-get install -y php__version__ php__version__-fpm php__version__-mbstring php__version__-mysql php__version__-mcrypt php__version__-gd php__version__-xml php__version__-curl php__version__-gettext php__version__-zip php__version__-bcmath php__version__-soap php__version__-redis php__version__-sqlite3 + +if ! sudo sed -i 's/www-data/__user__/g' /etc/php/__version__/fpm/pool.d/www.conf; then + echo 'VITO_SSH_ERROR' && exit 1 +fi + +sudo service php__version__-fpm enable + +sudo service php__version__-fpm start diff --git a/app/SSH/Services/PHP/scripts/uninstall-php.sh b/app/SSH/Services/PHP/scripts/uninstall-php.sh new file mode 100755 index 0000000..6bb5bde --- /dev/null +++ b/app/SSH/Services/PHP/scripts/uninstall-php.sh @@ -0,0 +1,5 @@ +sudo service php__version__-fpm stop + +if ! sudo DEBIAN_FRONTEND=noninteractive apt-get remove -y php__version__ php__version__-fpm php__version__-mbstring php__version__-mysql php__version__-mcrypt php__version__-gd php__version__-xml php__version__-curl php__version__-gettext php__version__-zip php__version__-bcmath php__version__-soap php__version__-redis php__version__-sqlite3; then + echo 'VITO_SSH_ERROR' && exit 1 +fi diff --git a/resources/commands/php/update-php-settings.sh b/app/SSH/Services/PHP/scripts/update-php-settings.sh similarity index 100% rename from resources/commands/php/update-php-settings.sh rename to app/SSH/Services/PHP/scripts/update-php-settings.sh diff --git a/app/ServiceHandlers/ProcessManager/AbstractProcessManager.php b/app/SSH/Services/ProcessManager/AbstractProcessManager.php similarity index 50% rename from app/ServiceHandlers/ProcessManager/AbstractProcessManager.php rename to app/SSH/Services/ProcessManager/AbstractProcessManager.php index d72c24c..229ba69 100644 --- a/app/ServiceHandlers/ProcessManager/AbstractProcessManager.php +++ b/app/SSH/Services/ProcessManager/AbstractProcessManager.php @@ -1,11 +1,11 @@ service->server->ssh()->exec( + $this->getScript('supervisor/install-supervisor.sh'), + 'install-supervisor' + ); + } + /** * @throws Throwable */ @@ -27,9 +31,9 @@ public function create( ?int $siteId = null ): void { $this->service->server->ssh($user)->exec( - new CreateWorkerCommand( - $id, - $this->generateConfigFile( + $this->getScript('supervisor/create-worker.sh', [ + 'id' => $id, + 'config' => $this->generateConfigFile( $id, $command, $user, @@ -37,8 +41,8 @@ public function create( $autoRestart, $numprocs, $logFile - ) - ), + ), + ]), 'create-worker', $siteId ); @@ -50,7 +54,9 @@ public function create( public function delete(int $id, ?int $siteId = null): void { $this->service->server->ssh()->exec( - new DeleteWorkerCommand($id), + $this->getScript('supervisor/delete-worker.sh', [ + 'id' => $id, + ]), 'delete-worker', $siteId ); @@ -62,7 +68,9 @@ public function delete(int $id, ?int $siteId = null): void public function restart(int $id, ?int $siteId = null): void { $this->service->server->ssh()->exec( - new RestartWorkerCommand($id), + $this->getScript('supervisor/restart-worker.sh', [ + 'id' => $id, + ]), 'restart-worker', $siteId ); @@ -74,7 +82,9 @@ public function restart(int $id, ?int $siteId = null): void public function stop(int $id, ?int $siteId = null): void { $this->service->server->ssh()->exec( - new StopWorkerCommand($id), + $this->getScript('supervisor/stop-worker.sh', [ + 'id' => $id, + ]), 'stop-worker', $siteId ); @@ -86,7 +96,9 @@ public function stop(int $id, ?int $siteId = null): void public function start(int $id, ?int $siteId = null): void { $this->service->server->ssh()->exec( - new StartWorkerCommand($id), + $this->getScript('supervisor/start-worker.sh', [ + 'id' => $id, + ]), 'start-worker', $siteId ); @@ -111,14 +123,14 @@ private function generateConfigFile( int $numprocs, string $logFile ): string { - $config = File::get(resource_path('commands/supervisor/worker.conf')); - $config = Str::replace('__name__', (string) $id, $config); - $config = Str::replace('__command__', $command, $config); - $config = Str::replace('__user__', $user, $config); - $config = Str::replace('__auto_start__', var_export($autoStart, true), $config); - $config = Str::replace('__auto_restart__', var_export($autoRestart, true), $config); - $config = Str::replace('__numprocs__', (string) $numprocs, $config); - - return Str::replace('__log_file__', $logFile, $config); + return $this->getScript('supervisor/worker.conf', [ + 'name' => (string) $id, + 'command' => $command, + 'user' => $user, + 'auto_start' => var_export($autoStart, true), + 'auto_restart' => var_export($autoRestart, true), + 'numprocs' => (string) $numprocs, + 'log_file' => $logFile, + ]); } } diff --git a/resources/commands/supervisor/create-worker.sh b/app/SSH/Services/ProcessManager/scripts/supervisor/create-worker.sh similarity index 100% rename from resources/commands/supervisor/create-worker.sh rename to app/SSH/Services/ProcessManager/scripts/supervisor/create-worker.sh diff --git a/resources/commands/supervisor/delete-worker.sh b/app/SSH/Services/ProcessManager/scripts/supervisor/delete-worker.sh similarity index 100% rename from resources/commands/supervisor/delete-worker.sh rename to app/SSH/Services/ProcessManager/scripts/supervisor/delete-worker.sh diff --git a/resources/commands/supervisor/install-supervisor.sh b/app/SSH/Services/ProcessManager/scripts/supervisor/install-supervisor.sh similarity index 100% rename from resources/commands/supervisor/install-supervisor.sh rename to app/SSH/Services/ProcessManager/scripts/supervisor/install-supervisor.sh diff --git a/resources/commands/supervisor/restart-worker.sh b/app/SSH/Services/ProcessManager/scripts/supervisor/restart-worker.sh similarity index 100% rename from resources/commands/supervisor/restart-worker.sh rename to app/SSH/Services/ProcessManager/scripts/supervisor/restart-worker.sh diff --git a/resources/commands/supervisor/start-worker.sh b/app/SSH/Services/ProcessManager/scripts/supervisor/start-worker.sh similarity index 100% rename from resources/commands/supervisor/start-worker.sh rename to app/SSH/Services/ProcessManager/scripts/supervisor/start-worker.sh diff --git a/resources/commands/supervisor/stop-worker.sh b/app/SSH/Services/ProcessManager/scripts/supervisor/stop-worker.sh similarity index 100% rename from resources/commands/supervisor/stop-worker.sh rename to app/SSH/Services/ProcessManager/scripts/supervisor/stop-worker.sh diff --git a/resources/commands/supervisor/worker.conf b/app/SSH/Services/ProcessManager/scripts/supervisor/worker.conf similarity index 100% rename from resources/commands/supervisor/worker.conf rename to app/SSH/Services/ProcessManager/scripts/supervisor/worker.conf diff --git a/app/SSH/Services/Redis/Redis.php b/app/SSH/Services/Redis/Redis.php new file mode 100644 index 0000000..1a1296b --- /dev/null +++ b/app/SSH/Services/Redis/Redis.php @@ -0,0 +1,24 @@ +service->server->ssh()->exec( + $this->getScript('install.sh'), + 'install-redis' + ); + } +} diff --git a/resources/commands/installation/install-redis.sh b/app/SSH/Services/Redis/scripts/install.sh similarity index 64% rename from resources/commands/installation/install-redis.sh rename to app/SSH/Services/Redis/scripts/install.sh index 1e8c733..85df312 100755 --- a/resources/commands/installation/install-redis.sh +++ b/app/SSH/Services/Redis/scripts/install.sh @@ -1,4 +1,4 @@ -sudo DEBIAN_FRONTEND=noninteractive apt install redis-server -y +sudo DEBIAN_FRONTEND=noninteractive apt-get install redis-server -y sudo sed -i 's/bind 127.0.0.1 ::1/bind 0.0.0.0/g' /etc/redis/redis.conf diff --git a/app/SSH/Services/ServiceInterface.php b/app/SSH/Services/ServiceInterface.php new file mode 100644 index 0000000..23646d0 --- /dev/null +++ b/app/SSH/Services/ServiceInterface.php @@ -0,0 +1,8 @@ +service->server->ssh()->exec( + $this->getScript('nginx/install-nginx.sh', [ + 'config' => $this->getScript('nginx/nginx.conf', [ + 'user' => $this->service->server->getSshUser(), + ]), + ]), + 'install-nginx' + ); + } + + public function createVHost(Site $site): void + { + $this->service->server->ssh()->exec( + $this->getScript('nginx/create-vhost.sh', [ + 'domain' => $site->domain, + 'path' => $site->path, + 'vhost' => $this->generateVhost($site), + ]), + 'create-vhost', + $site->id + ); + } + + public function updateVHost(Site $site, bool $noSSL = false, ?string $vhost = null): void + { + $this->service->server->ssh()->exec( + $this->getScript('nginx/update-vhost.sh', [ + 'domain' => $site->domain, + 'path' => $site->path, + 'vhost' => $this->generateVhost($site, $noSSL), + ]), + 'update-vhost', + $site->id + ); + } + + public function getVHost(Site $site): string + { + return $this->service->server->ssh()->exec( + $this->getScript('nginx/get-vhost.sh', [ + 'domain' => $site->domain, + ]), + ); + } + + public function deleteSite(Site $site): void + { + $this->service->server->ssh()->exec( + $this->getScript('nginx/delete-site.sh', [ + 'domain' => $site->domain, + 'path' => $site->path, + ]), + 'delete-vhost', + $site->id + ); + $this->service->restart(); + } + + public function changePHPVersion(Site $site, $version): void + { + $this->service->server->ssh()->exec( + $this->getScript('nginx/change-php-version.sh', [ + 'domain' => $site->domain, + 'old_version' => $site->php_version, + 'new_version' => $version, + ]), + 'change-php-version', + $site->id + ); + } + + /** + * @throws SSLCreationException + */ + public function setupSSL(Ssl $ssl): void + { + $command = $this->getScript('nginx/create-letsencrypt-ssl.sh', [ + 'email' => $ssl->site->server->creator->email, + 'domain' => $ssl->site->domain, + 'web_directory' => $ssl->site->getWebDirectoryPath(), + ]); + if ($ssl->type == 'custom') { + $command = $this->getScript('nginx/create-custom-ssl.sh', [ + 'path' => $ssl->getCertsDirectoryPath(), + 'certificate' => $ssl->certificate, + 'pk' => $ssl->pk, + 'certificate_path' => $ssl->getCertificatePath(), + 'pk_path' => $ssl->getPkPath(), + ]); + } + $result = $this->service->server->ssh()->exec( + $command, + 'create-ssl', + $ssl->site_id + ); + if (! $ssl->validateSetup($result)) { + throw new SSLCreationException(); + } + + $this->updateVHost($ssl->site); + } + + /** + * @throws Throwable + */ + public function removeSSL(Ssl $ssl): void + { + $this->service->server->ssh()->exec( + 'sudo rm -rf '.$ssl->getCertsDirectoryPath().'*', + 'remove-ssl', + $ssl->site_id + ); + + $this->updateVHost($ssl->site, true); + + $this->service->server->systemd()->restart('nginx'); + } + + protected function generateVhost(Site $site, bool $noSSL = false): string + { + $ssl = $site->activeSsl; + if ($noSSL) { + $ssl = null; + } + $vhost = $this->getScript('nginx/vhost.conf'); + if ($ssl) { + $vhost = $this->getScript('nginx/vhost-ssl.conf'); + } + if ($site->type()->language() === 'php') { + $vhost = $this->getScript('nginx/php-vhost.conf'); + if ($ssl) { + $vhost = $this->getScript('nginx/php-vhost-ssl.conf'); + } + } + if ($site->port) { + $vhost = $this->getScript('nginx/reverse-vhost.conf'); + if ($ssl) { + $vhost = $this->getScript('nginx/reverse-vhost-ssl.conf'); + } + $vhost = Str::replace('__port__', (string) $site->port, $vhost); + } + + $vhost = Str::replace('__domain__', $site->domain, $vhost); + $vhost = Str::replace('__aliases__', $site->getAliasesString(), $vhost); + $vhost = Str::replace('__path__', $site->path, $vhost); + $vhost = Str::replace('__web_directory__', $site->web_directory, $vhost); + + if ($ssl) { + $vhost = Str::replace('__certificate__', $ssl->getCertificatePath(), $vhost); + $vhost = Str::replace('__private_key__', $ssl->getPkPath(), $vhost); + } + + if ($site->php_version) { + $vhost = Str::replace('__php_version__', $site->php_version, $vhost); + } + + return $vhost; + } +} diff --git a/app/Contracts/Webserver.php b/app/SSH/Services/Webserver/Webserver.php similarity index 83% rename from app/Contracts/Webserver.php rename to app/SSH/Services/Webserver/Webserver.php index 39698e8..4137d32 100755 --- a/app/Contracts/Webserver.php +++ b/app/SSH/Services/Webserver/Webserver.php @@ -1,6 +1,6 @@ server->ssh()->exec( + $this->getScript('dropbox/upload.sh', [ + 'src' => $src, + 'dest' => $dest, + 'token' => $this->storageProvider->credentials['token'], + ]), + 'upload-to-dropbox' + ); + + $data = json_decode($upload, true); + + if (isset($data['error'])) { + throw new SSHCommandError('Failed to upload to Dropbox '.$data['error']); + } + + return [ + 'size' => $data['size'] ?? null, + ]; + } + + public function download(string $src, string $dest): void + { + $this->server->ssh()->exec( + $this->getScript('dropbox/download.sh', [ + 'src' => $src, + 'dest' => $dest, + 'token' => $this->storageProvider->credentials['token'], + ]), + 'download-from-dropbox' + ); + } +} diff --git a/app/SSH/Storage/FTP.php b/app/SSH/Storage/FTP.php new file mode 100644 index 0000000..245a055 --- /dev/null +++ b/app/SSH/Storage/FTP.php @@ -0,0 +1,48 @@ +server->ssh()->exec( + $this->getScript('ftp/upload.sh', [ + 'src' => $src, + 'dest' => $this->storageProvider->credentials['path'].'/'.$dest, + 'host' => $this->storageProvider->credentials['host'], + 'port' => $this->storageProvider->credentials['port'], + 'username' => $this->storageProvider->credentials['username'], + 'password' => $this->storageProvider->credentials['password'], + 'ssl' => $this->storageProvider->credentials['ssl'], + 'passive' => $this->storageProvider->credentials['passive'], + ]), + 'upload-to-ftp' + ); + + return [ + 'size' => null, + ]; + } + + public function download(string $src, string $dest): void + { + $this->server->ssh()->exec( + $this->getScript('ftp/download.sh', [ + 'src' => $this->storageProvider->credentials['path'].'/'.$src, + 'dest' => $dest, + 'host' => $this->storageProvider->credentials['host'], + 'port' => $this->storageProvider->credentials['port'], + 'username' => $this->storageProvider->credentials['username'], + 'password' => $this->storageProvider->credentials['password'], + 'ssl' => $this->storageProvider->credentials['ssl'], + 'passive' => $this->storageProvider->credentials['passive'], + ]), + 'download-from-ftp' + ); + } +} diff --git a/app/SSH/Storage/Storage.php b/app/SSH/Storage/Storage.php new file mode 100644 index 0000000..cd54709 --- /dev/null +++ b/app/SSH/Storage/Storage.php @@ -0,0 +1,10 @@ +server->ssh()->exec($command, sprintf('status-%s', $unit)); + } + + public function start(string $unit): string + { + $command = <<server->ssh()->exec($command, sprintf('start-%s', $unit)); + } + + public function stop(string $unit): string + { + $command = <<server->ssh()->exec($command, sprintf('stop-%s', $unit)); + } + + public function restart(string $unit): string + { + $command = <<server->ssh()->exec($command, sprintf('restart-%s', $unit)); + } +} diff --git a/app/SSH/Wordpress/Wordpress.php b/app/SSH/Wordpress/Wordpress.php new file mode 100644 index 0000000..ca48767 --- /dev/null +++ b/app/SSH/Wordpress/Wordpress.php @@ -0,0 +1,31 @@ +server->ssh()->exec( + $this->getScript('install.sh', [ + 'path' => $site->path, + 'domain' => $site->domain, + 'db_name' => $site->type_data['database'], + 'db_user' => $site->type_data['database_user'], + 'db_pass' => $site->type_data['database_password'], + 'db_host' => 'localhost', + 'db_prefix' => 'wp_', + 'username' => $site->type_data['username'], + 'password' => $site->type_data['password'], + 'email' => $site->type_data['email'], + 'title' => $site->type_data['title'], + ]), + 'install-wordpress' + ); + } +} diff --git a/app/SSH/Wordpress/scripts/install.sh b/app/SSH/Wordpress/scripts/install.sh new file mode 100644 index 0000000..0321d66 --- /dev/null +++ b/app/SSH/Wordpress/scripts/install.sh @@ -0,0 +1,27 @@ +if ! curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar; then + echo 'VITO_SSH_ERROR' && exit 1 +fi + +if ! chmod +x wp-cli.phar; then + echo 'VITO_SSH_ERROR' && exit 1 +fi + +if ! sudo mv wp-cli.phar /usr/local/bin/wp; then + echo 'VITO_SSH_ERROR' && exit 1 +fi + +rm -rf __path__ + +if ! wp --path=__path__ core download; then + echo 'VITO_SSH_ERROR' && exit 1 +fi + +if ! wp --path=__path__ core config --dbname='__db_name__' --dbuser='__db_user__' --dbpass='__db_pass__' --dbhost='__db_host__' --dbprefix='__db_prefix__'; then + echo 'VITO_SSH_ERROR' && exit 1 +fi + +if ! wp --path=__path__ core install --url='http://__domain__' --title="__title__" --admin_user='__username__' --admin_password="__password__" --admin_email='__email__'; then + echo 'VITO_SSH_ERROR' && exit 1 +fi + +print "Wordpress installed!" diff --git a/app/SSHCommands/Command.php b/app/SSHCommands/Command.php deleted file mode 100755 index ff7f7c2..0000000 --- a/app/SSHCommands/Command.php +++ /dev/null @@ -1,10 +0,0 @@ -file()) - ->replace('__user__', $this->user) - ->replace('__data__', $this->data) - ->toString(); - } -} diff --git a/app/SSHCommands/Database/BackupDatabaseCommand.php b/app/SSHCommands/Database/BackupDatabaseCommand.php deleted file mode 100644 index 8164c65..0000000 --- a/app/SSHCommands/Database/BackupDatabaseCommand.php +++ /dev/null @@ -1,26 +0,0 @@ -provider))); - } - - public function content(): string - { - return str($this->file()) - ->replace('__database__', $this->database) - ->replace('__file__', $this->fileName) - ->toString(); - } -} diff --git a/app/SSHCommands/Database/CreateCommand.php b/app/SSHCommands/Database/CreateCommand.php deleted file mode 100755 index 054b297..0000000 --- a/app/SSHCommands/Database/CreateCommand.php +++ /dev/null @@ -1,25 +0,0 @@ -provider))); - } - - public function content(): string - { - return str($this->file()) - ->replace('__name__', $this->name) - ->toString(); - } -} diff --git a/app/SSHCommands/Database/CreateUserCommand.php b/app/SSHCommands/Database/CreateUserCommand.php deleted file mode 100755 index f3ff630..0000000 --- a/app/SSHCommands/Database/CreateUserCommand.php +++ /dev/null @@ -1,31 +0,0 @@ -provider))); - } - - public function content(): string - { - return str($this->file()) - ->replace('__username__', $this->username) - ->replace('__password__', $this->password) - ->replace('__host__', $this->host) - ->toString(); - } -} diff --git a/app/SSHCommands/Database/DeleteCommand.php b/app/SSHCommands/Database/DeleteCommand.php deleted file mode 100755 index 91c6bba..0000000 --- a/app/SSHCommands/Database/DeleteCommand.php +++ /dev/null @@ -1,25 +0,0 @@ -provider))); - } - - public function content(): string - { - return str($this->file()) - ->replace('__name__', $this->name) - ->toString(); - } -} diff --git a/app/SSHCommands/Database/DeleteUserCommand.php b/app/SSHCommands/Database/DeleteUserCommand.php deleted file mode 100755 index 89fd652..0000000 --- a/app/SSHCommands/Database/DeleteUserCommand.php +++ /dev/null @@ -1,26 +0,0 @@ -provider))); - } - - public function content(): string - { - return str($this->file()) - ->replace('__username__', $this->username) - ->replace('__host__', $this->host) - ->toString(); - } -} diff --git a/app/SSHCommands/Database/InstallMariadbCommand.php b/app/SSHCommands/Database/InstallMariadbCommand.php deleted file mode 100755 index 42a19ff..0000000 --- a/app/SSHCommands/Database/InstallMariadbCommand.php +++ /dev/null @@ -1,19 +0,0 @@ -file(); - } -} diff --git a/app/SSHCommands/Database/InstallMysqlCommand.php b/app/SSHCommands/Database/InstallMysqlCommand.php deleted file mode 100755 index 3060ec7..0000000 --- a/app/SSHCommands/Database/InstallMysqlCommand.php +++ /dev/null @@ -1,27 +0,0 @@ -version == '8.0') { - return File::get(resource_path('commands/database/install-mysql-8.sh')); - } - - return File::get(resource_path('commands/database/install-mysql.sh')); - } - - public function content(): string - { - return $this->file(); - } -} diff --git a/app/SSHCommands/Database/LinkCommand.php b/app/SSHCommands/Database/LinkCommand.php deleted file mode 100755 index 249a828..0000000 --- a/app/SSHCommands/Database/LinkCommand.php +++ /dev/null @@ -1,31 +0,0 @@ -provider))); - } - - public function content(): string - { - return str($this->file()) - ->replace('__username__', $this->username) - ->replace('__host__', $this->host) - ->replace('__database__', $this->database) - ->toString(); - } -} diff --git a/app/SSHCommands/Database/RestoreDatabaseCommand.php b/app/SSHCommands/Database/RestoreDatabaseCommand.php deleted file mode 100644 index c19ad35..0000000 --- a/app/SSHCommands/Database/RestoreDatabaseCommand.php +++ /dev/null @@ -1,26 +0,0 @@ -provider))); - } - - public function content(): string - { - return str($this->file()) - ->replace('__database__', $this->database) - ->replace('__file__', $this->fileName) - ->toString(); - } -} diff --git a/app/SSHCommands/Database/UnlinkCommand.php b/app/SSHCommands/Database/UnlinkCommand.php deleted file mode 100755 index 91b1b8c..0000000 --- a/app/SSHCommands/Database/UnlinkCommand.php +++ /dev/null @@ -1,29 +0,0 @@ -provider))); - } - - public function content(): string - { - return str($this->file()) - ->replace('__username__', $this->username) - ->replace('__host__', $this->host) - ->toString(); - } -} diff --git a/app/SSHCommands/Firewall/AddRuleCommand.php b/app/SSHCommands/Firewall/AddRuleCommand.php deleted file mode 100755 index bf5e212..0000000 --- a/app/SSHCommands/Firewall/AddRuleCommand.php +++ /dev/null @@ -1,26 +0,0 @@ -provider))); - } -} diff --git a/app/SSHCommands/Firewall/CommandContent.php b/app/SSHCommands/Firewall/CommandContent.php deleted file mode 100755 index 86ea4bd..0000000 --- a/app/SSHCommands/Firewall/CommandContent.php +++ /dev/null @@ -1,17 +0,0 @@ -file()) - ->replace('__type__', $this->type) - ->replace('__protocol__', $this->protocol) - ->replace('__source__', $this->source) - ->replace('__mask__', $this->mask || $this->mask == 0 ? '/'.$this->mask : '') - ->replace('__port__', $this->port) - ->toString(); - } -} diff --git a/app/SSHCommands/Firewall/InstallUfwCommand.php b/app/SSHCommands/Firewall/InstallUfwCommand.php deleted file mode 100755 index 14cce4a..0000000 --- a/app/SSHCommands/Firewall/InstallUfwCommand.php +++ /dev/null @@ -1,19 +0,0 @@ -file(); - } -} diff --git a/app/SSHCommands/Firewall/RemoveRuleCommand.php b/app/SSHCommands/Firewall/RemoveRuleCommand.php deleted file mode 100755 index 2b9a4c5..0000000 --- a/app/SSHCommands/Firewall/RemoveRuleCommand.php +++ /dev/null @@ -1,26 +0,0 @@ -provider))); - } -} diff --git a/app/SSHCommands/Installation/InstallNodejsCommand.php b/app/SSHCommands/Installation/InstallNodejsCommand.php deleted file mode 100755 index 9173fde..0000000 --- a/app/SSHCommands/Installation/InstallNodejsCommand.php +++ /dev/null @@ -1,19 +0,0 @@ -file(); - } -} diff --git a/app/SSHCommands/Installation/InstallRedisCommand.php b/app/SSHCommands/Installation/InstallRedisCommand.php deleted file mode 100755 index 11b7f95..0000000 --- a/app/SSHCommands/Installation/InstallRedisCommand.php +++ /dev/null @@ -1,19 +0,0 @@ -file(); - } -} diff --git a/app/SSHCommands/Installation/InstallRequirementsCommand.php b/app/SSHCommands/Installation/InstallRequirementsCommand.php deleted file mode 100755 index a0f57b4..0000000 --- a/app/SSHCommands/Installation/InstallRequirementsCommand.php +++ /dev/null @@ -1,26 +0,0 @@ -file()) - ->replace('__email__', $this->email) - ->replace('__name__', $this->name) - ->toString(); - } -} diff --git a/app/SSHCommands/Nginx/ChangeNginxPHPVersionCommand.php b/app/SSHCommands/Nginx/ChangeNginxPHPVersionCommand.php deleted file mode 100755 index be03101..0000000 --- a/app/SSHCommands/Nginx/ChangeNginxPHPVersionCommand.php +++ /dev/null @@ -1,27 +0,0 @@ -file()) - ->replace('__domain__', $this->domain) - ->replace('__old_version__', $this->oldVersion) - ->replace('__new_version__', $this->newVersion) - ->toString(); - } -} diff --git a/app/SSHCommands/Nginx/CreateNginxVHostCommand.php b/app/SSHCommands/Nginx/CreateNginxVHostCommand.php deleted file mode 100755 index b13eb53..0000000 --- a/app/SSHCommands/Nginx/CreateNginxVHostCommand.php +++ /dev/null @@ -1,30 +0,0 @@ -file()) - ->replace('__domain__', $this->domain) - ->replace('__path__', $this->path) - ->replace('__vhost__', $this->vhost) - ->toString(); - } -} diff --git a/app/SSHCommands/Nginx/DeleteNginxSiteCommand.php b/app/SSHCommands/Nginx/DeleteNginxSiteCommand.php deleted file mode 100755 index d4dcc3e..0000000 --- a/app/SSHCommands/Nginx/DeleteNginxSiteCommand.php +++ /dev/null @@ -1,26 +0,0 @@ -file()) - ->replace('__domain__', $this->domain) - ->replace('__path__', $this->path) - ->toString(); - } -} diff --git a/app/SSHCommands/Nginx/GetNginxVHostCommand.php b/app/SSHCommands/Nginx/GetNginxVHostCommand.php deleted file mode 100755 index 28dc68b..0000000 --- a/app/SSHCommands/Nginx/GetNginxVHostCommand.php +++ /dev/null @@ -1,26 +0,0 @@ -file()) - ->replace('__domain__', $this->domain) - ->toString(); - } -} diff --git a/app/SSHCommands/Nginx/InstallNginxCommand.php b/app/SSHCommands/Nginx/InstallNginxCommand.php deleted file mode 100755 index 331938a..0000000 --- a/app/SSHCommands/Nginx/InstallNginxCommand.php +++ /dev/null @@ -1,31 +0,0 @@ -file()) - ->replace('__config__', $this->config()) - ->toString(); - } - - protected function config(): string - { - $config = File::get(resource_path('commands/webserver/nginx/nginx.conf')); - - /** TODO: change user to server user */ - return str($config) - ->replace('__user__', config('core.ssh_user')) - ->toString(); - } -} diff --git a/app/SSHCommands/Nginx/UpdateNginxRedirectsCommand.php b/app/SSHCommands/Nginx/UpdateNginxRedirectsCommand.php deleted file mode 100755 index bd8c25d..0000000 --- a/app/SSHCommands/Nginx/UpdateNginxRedirectsCommand.php +++ /dev/null @@ -1,28 +0,0 @@ -file()) - ->replace('__redirects__', addslashes($this->redirects)) - ->replace('__domain__', $this->domain) - ->toString(); - } -} diff --git a/app/SSHCommands/Nginx/UpdateNginxVHostCommand.php b/app/SSHCommands/Nginx/UpdateNginxVHostCommand.php deleted file mode 100755 index f0f321a..0000000 --- a/app/SSHCommands/Nginx/UpdateNginxVHostCommand.php +++ /dev/null @@ -1,30 +0,0 @@ -file()) - ->replace('__domain__', $this->domain) - ->replace('__path__', $this->path) - ->replace('__vhost__', $this->vhost) - ->toString(); - } -} diff --git a/app/SSHCommands/PHP/ChangeDefaultPHPCommand.php b/app/SSHCommands/PHP/ChangeDefaultPHPCommand.php deleted file mode 100755 index df9fc8a..0000000 --- a/app/SSHCommands/PHP/ChangeDefaultPHPCommand.php +++ /dev/null @@ -1,25 +0,0 @@ -file()) - ->replace('__version__', $this->version) - ->toString(); - } -} diff --git a/app/SSHCommands/PHP/GetPHPIniCommand.php b/app/SSHCommands/PHP/GetPHPIniCommand.php deleted file mode 100755 index ef7858d..0000000 --- a/app/SSHCommands/PHP/GetPHPIniCommand.php +++ /dev/null @@ -1,25 +0,0 @@ -file()) - ->replace('__version__', $this->version) - ->toString(); - } -} diff --git a/app/SSHCommands/PHP/InstallComposerCommand.php b/app/SSHCommands/PHP/InstallComposerCommand.php deleted file mode 100755 index 60cc39a..0000000 --- a/app/SSHCommands/PHP/InstallComposerCommand.php +++ /dev/null @@ -1,19 +0,0 @@ -file(); - } -} diff --git a/app/SSHCommands/PHP/InstallPHPCommand.php b/app/SSHCommands/PHP/InstallPHPCommand.php deleted file mode 100755 index d165d3d..0000000 --- a/app/SSHCommands/PHP/InstallPHPCommand.php +++ /dev/null @@ -1,27 +0,0 @@ -file()) - ->replace('__version__', $this->version) - ->replace('__user__', config('core.ssh_user')) - ->toString(); - } -} diff --git a/app/SSHCommands/PHP/InstallPHPExtensionCommand.php b/app/SSHCommands/PHP/InstallPHPExtensionCommand.php deleted file mode 100755 index 331cf58..0000000 --- a/app/SSHCommands/PHP/InstallPHPExtensionCommand.php +++ /dev/null @@ -1,26 +0,0 @@ -file()) - ->replace('__version__', $this->version) - ->replace('__name__', $this->name) - ->toString(); - } -} diff --git a/app/SSHCommands/PHP/UninstallPHPCommand.php b/app/SSHCommands/PHP/UninstallPHPCommand.php deleted file mode 100755 index 1401d1b..0000000 --- a/app/SSHCommands/PHP/UninstallPHPCommand.php +++ /dev/null @@ -1,27 +0,0 @@ -file()) - ->replace('__version__', $this->version) - ->replace('__user__', config('core.ssh_user')) - ->toString(); - } -} diff --git a/app/SSHCommands/PHPMyAdmin/CreateNginxPHPMyAdminVHostCommand.php b/app/SSHCommands/PHPMyAdmin/CreateNginxPHPMyAdminVHostCommand.php deleted file mode 100755 index 508134e..0000000 --- a/app/SSHCommands/PHPMyAdmin/CreateNginxPHPMyAdminVHostCommand.php +++ /dev/null @@ -1,25 +0,0 @@ -file()) - ->replace('__vhost__', $this->vhost) - ->toString(); - } -} diff --git a/app/SSHCommands/PHPMyAdmin/DeleteNginxPHPMyAdminVHostCommand.php b/app/SSHCommands/PHPMyAdmin/DeleteNginxPHPMyAdminVHostCommand.php deleted file mode 100755 index 5a737f5..0000000 --- a/app/SSHCommands/PHPMyAdmin/DeleteNginxPHPMyAdminVHostCommand.php +++ /dev/null @@ -1,25 +0,0 @@ -file()) - ->replace('__path__', $this->path) - ->toString(); - } -} diff --git a/app/SSHCommands/PHPMyAdmin/DownloadPHPMyAdminCommand.php b/app/SSHCommands/PHPMyAdmin/DownloadPHPMyAdminCommand.php deleted file mode 100644 index 15963de..0000000 --- a/app/SSHCommands/PHPMyAdmin/DownloadPHPMyAdminCommand.php +++ /dev/null @@ -1,19 +0,0 @@ -file(); - } -} diff --git a/app/SSHCommands/SSL/CreateCustomSSLCommand.php b/app/SSHCommands/SSL/CreateCustomSSLCommand.php deleted file mode 100755 index 86732b9..0000000 --- a/app/SSHCommands/SSL/CreateCustomSSLCommand.php +++ /dev/null @@ -1,34 +0,0 @@ -file()) - ->replace('__path__', $this->path) - ->replace('__certificate__', $this->certificate) - ->replace('__pk__', $this->pk) - ->replace('__certificate_path__', $this->certificatePath) - ->replace('__pk_path__', $this->pkPath) - ->toString(); - } -} diff --git a/app/SSHCommands/SSL/CreateLetsencryptSSLCommand.php b/app/SSHCommands/SSL/CreateLetsencryptSSLCommand.php deleted file mode 100755 index 8072b6c..0000000 --- a/app/SSHCommands/SSL/CreateLetsencryptSSLCommand.php +++ /dev/null @@ -1,27 +0,0 @@ -file()) - ->replace('__email__', $this->email) - ->replace('__web_directory__', $this->webDirectory) - ->replace('__domain__', $this->domain) - ->toString(); - } -} diff --git a/app/SSHCommands/SSL/InstallCertbotCommand.php b/app/SSHCommands/SSL/InstallCertbotCommand.php deleted file mode 100755 index b13e8f5..0000000 --- a/app/SSHCommands/SSL/InstallCertbotCommand.php +++ /dev/null @@ -1,19 +0,0 @@ -file(); - } -} diff --git a/app/SSHCommands/SSL/RemoveSSLCommand.php b/app/SSHCommands/SSL/RemoveSSLCommand.php deleted file mode 100755 index bf0b382..0000000 --- a/app/SSHCommands/SSL/RemoveSSLCommand.php +++ /dev/null @@ -1,22 +0,0 @@ -path.'*'; - } -} diff --git a/app/SSHCommands/Service/RestartServiceCommand.php b/app/SSHCommands/Service/RestartServiceCommand.php deleted file mode 100644 index 14a5ab3..0000000 --- a/app/SSHCommands/Service/RestartServiceCommand.php +++ /dev/null @@ -1,25 +0,0 @@ -file()) - ->replace('__service__', $this->unit) - ->toString(); - } -} diff --git a/app/SSHCommands/Service/ServiceStatusCommand.php b/app/SSHCommands/Service/ServiceStatusCommand.php deleted file mode 100755 index 1434546..0000000 --- a/app/SSHCommands/Service/ServiceStatusCommand.php +++ /dev/null @@ -1,25 +0,0 @@ -file()) - ->replace('__service__', $this->unit) - ->toString(); - } -} diff --git a/app/SSHCommands/Service/StartServiceCommand.php b/app/SSHCommands/Service/StartServiceCommand.php deleted file mode 100644 index f4b616b..0000000 --- a/app/SSHCommands/Service/StartServiceCommand.php +++ /dev/null @@ -1,25 +0,0 @@ -file()) - ->replace('__service__', $this->unit) - ->toString(); - } -} diff --git a/app/SSHCommands/Service/StopServiceCommand.php b/app/SSHCommands/Service/StopServiceCommand.php deleted file mode 100644 index b9a99bb..0000000 --- a/app/SSHCommands/Service/StopServiceCommand.php +++ /dev/null @@ -1,25 +0,0 @@ -file()) - ->replace('__service__', $this->unit) - ->toString(); - } -} diff --git a/app/SSHCommands/Storage/DownloadFromDropboxCommand.php b/app/SSHCommands/Storage/DownloadFromDropboxCommand.php deleted file mode 100644 index e6ba695..0000000 --- a/app/SSHCommands/Storage/DownloadFromDropboxCommand.php +++ /dev/null @@ -1,27 +0,0 @@ -file()) - ->replace('__src__', $this->src) - ->replace('__dest__', $this->dest) - ->replace('__token__', $this->token) - ->toString(); - } -} diff --git a/app/SSHCommands/Storage/DownloadFromFTPCommand.php b/app/SSHCommands/Storage/DownloadFromFTPCommand.php deleted file mode 100644 index 068df06..0000000 --- a/app/SSHCommands/Storage/DownloadFromFTPCommand.php +++ /dev/null @@ -1,40 +0,0 @@ -file()) - ->replace('__src__', $this->src) - ->replace('__dest__', $this->dest) - ->replace('__host__', $this->host) - ->replace('__port__', $this->port) - ->replace('__username__', $this->username) - ->replace('__password__', $this->password) - ->replace('__ssl__', $this->ssl ? 's' : '') - ->replace('__passive__', $this->passive ? '--ftp-pasv' : '') - ->toString(); - } -} diff --git a/app/SSHCommands/Storage/UploadToDropboxCommand.php b/app/SSHCommands/Storage/UploadToDropboxCommand.php deleted file mode 100644 index 29498f3..0000000 --- a/app/SSHCommands/Storage/UploadToDropboxCommand.php +++ /dev/null @@ -1,27 +0,0 @@ -file()) - ->replace('__src__', $this->src) - ->replace('__dest__', $this->dest) - ->replace('__token__', $this->token) - ->toString(); - } -} diff --git a/app/SSHCommands/Storage/UploadToFTPCommand.php b/app/SSHCommands/Storage/UploadToFTPCommand.php deleted file mode 100644 index d9e556a..0000000 --- a/app/SSHCommands/Storage/UploadToFTPCommand.php +++ /dev/null @@ -1,40 +0,0 @@ -file()) - ->replace('__src__', $this->src) - ->replace('__dest__', $this->dest) - ->replace('__host__', $this->host) - ->replace('__port__', $this->port) - ->replace('__username__', $this->username) - ->replace('__password__', $this->password) - ->replace('__ssl__', $this->ssl ? 's' : '') - ->replace('__passive__', $this->passive ? '--ftp-pasv' : '') - ->toString(); - } -} diff --git a/app/SSHCommands/Supervisor/CreateWorkerCommand.php b/app/SSHCommands/Supervisor/CreateWorkerCommand.php deleted file mode 100644 index 197c7a9..0000000 --- a/app/SSHCommands/Supervisor/CreateWorkerCommand.php +++ /dev/null @@ -1,26 +0,0 @@ -file()) - ->replace('__id__', $this->id) - ->replace('__config__', $this->config) - ->toString(); - } -} diff --git a/app/SSHCommands/Supervisor/DeleteWorkerCommand.php b/app/SSHCommands/Supervisor/DeleteWorkerCommand.php deleted file mode 100644 index 0171772..0000000 --- a/app/SSHCommands/Supervisor/DeleteWorkerCommand.php +++ /dev/null @@ -1,25 +0,0 @@ -file()) - ->replace('__id__', $this->id) - ->toString(); - } -} diff --git a/app/SSHCommands/Supervisor/InstallSupervisorCommand.php b/app/SSHCommands/Supervisor/InstallSupervisorCommand.php deleted file mode 100755 index 8808741..0000000 --- a/app/SSHCommands/Supervisor/InstallSupervisorCommand.php +++ /dev/null @@ -1,19 +0,0 @@ -file(); - } -} diff --git a/app/SSHCommands/Supervisor/RestartWorkerCommand.php b/app/SSHCommands/Supervisor/RestartWorkerCommand.php deleted file mode 100644 index 12a1006..0000000 --- a/app/SSHCommands/Supervisor/RestartWorkerCommand.php +++ /dev/null @@ -1,25 +0,0 @@ -file()) - ->replace('__id__', $this->id) - ->toString(); - } -} diff --git a/app/SSHCommands/Supervisor/StartWorkerCommand.php b/app/SSHCommands/Supervisor/StartWorkerCommand.php deleted file mode 100644 index f2732bf..0000000 --- a/app/SSHCommands/Supervisor/StartWorkerCommand.php +++ /dev/null @@ -1,25 +0,0 @@ -file()) - ->replace('__id__', $this->id) - ->toString(); - } -} diff --git a/app/SSHCommands/Supervisor/StopWorkerCommand.php b/app/SSHCommands/Supervisor/StopWorkerCommand.php deleted file mode 100644 index 32125e0..0000000 --- a/app/SSHCommands/Supervisor/StopWorkerCommand.php +++ /dev/null @@ -1,25 +0,0 @@ -file()) - ->replace('__id__', $this->id) - ->toString(); - } -} diff --git a/app/SSHCommands/System/CreateUserCommand.php b/app/SSHCommands/System/CreateUserCommand.php deleted file mode 100755 index adc7e39..0000000 --- a/app/SSHCommands/System/CreateUserCommand.php +++ /dev/null @@ -1,27 +0,0 @@ -file()) - ->replace('__user__', $this->user) - ->replace('__key__', $this->key) - ->replace('__password__', $this->password) - ->toString(); - } -} diff --git a/app/SSHCommands/System/DeleteSshKeyCommand.php b/app/SSHCommands/System/DeleteSshKeyCommand.php deleted file mode 100755 index 6eb46ca..0000000 --- a/app/SSHCommands/System/DeleteSshKeyCommand.php +++ /dev/null @@ -1,25 +0,0 @@ -file()) - ->replace('__key__', str($this->key)->replace('/', '\/')) - ->toString(); - } -} diff --git a/app/SSHCommands/System/DeploySshKeyCommand.php b/app/SSHCommands/System/DeploySshKeyCommand.php deleted file mode 100755 index fd228d1..0000000 --- a/app/SSHCommands/System/DeploySshKeyCommand.php +++ /dev/null @@ -1,25 +0,0 @@ -file()) - ->replace('__key__', addslashes($this->key)) - ->toString(); - } -} diff --git a/app/SSHCommands/System/EditFileCommand.php b/app/SSHCommands/System/EditFileCommand.php deleted file mode 100644 index 267ae80..0000000 --- a/app/SSHCommands/System/EditFileCommand.php +++ /dev/null @@ -1,26 +0,0 @@ -file()) - ->replace('__path__', $this->path) - ->replace('__content__', $this->content) - ->toString(); - } -} diff --git a/app/SSHCommands/System/GenerateSshKeyCommand.php b/app/SSHCommands/System/GenerateSshKeyCommand.php deleted file mode 100755 index 29077c6..0000000 --- a/app/SSHCommands/System/GenerateSshKeyCommand.php +++ /dev/null @@ -1,25 +0,0 @@ -file()) - ->replace('__name__', $this->name) - ->toString(); - } -} diff --git a/app/SSHCommands/System/GetPublicKeyCommand.php b/app/SSHCommands/System/GetPublicKeyCommand.php deleted file mode 100755 index 2ebad7f..0000000 --- a/app/SSHCommands/System/GetPublicKeyCommand.php +++ /dev/null @@ -1,19 +0,0 @@ -file(); - } -} diff --git a/app/SSHCommands/System/ReadFileCommand.php b/app/SSHCommands/System/ReadFileCommand.php deleted file mode 100644 index 943dd3a..0000000 --- a/app/SSHCommands/System/ReadFileCommand.php +++ /dev/null @@ -1,25 +0,0 @@ -file()) - ->replace('__path__', $this->path) - ->toString(); - } -} diff --git a/app/SSHCommands/System/ReadSshKeyCommand.php b/app/SSHCommands/System/ReadSshKeyCommand.php deleted file mode 100755 index 7a89529..0000000 --- a/app/SSHCommands/System/ReadSshKeyCommand.php +++ /dev/null @@ -1,25 +0,0 @@ -file()) - ->replace('__name__', $this->name) - ->toString(); - } -} diff --git a/app/SSHCommands/System/RebootCommand.php b/app/SSHCommands/System/RebootCommand.php deleted file mode 100644 index 0e0a7da..0000000 --- a/app/SSHCommands/System/RebootCommand.php +++ /dev/null @@ -1,19 +0,0 @@ -file(); - } -} diff --git a/app/SSHCommands/System/RunScriptCommand.php b/app/SSHCommands/System/RunScriptCommand.php deleted file mode 100644 index 97bc947..0000000 --- a/app/SSHCommands/System/RunScriptCommand.php +++ /dev/null @@ -1,26 +0,0 @@ -file()) - ->replace('__path__', $this->path) - ->replace('__script__', $this->script) - ->toString(); - } -} diff --git a/app/SSHCommands/System/UpgradeCommand.php b/app/SSHCommands/System/UpgradeCommand.php deleted file mode 100755 index 20607ad..0000000 --- a/app/SSHCommands/System/UpgradeCommand.php +++ /dev/null @@ -1,19 +0,0 @@ -file(); - } -} diff --git a/app/SSHCommands/Website/CloneRepositoryCommand.php b/app/SSHCommands/Website/CloneRepositoryCommand.php deleted file mode 100755 index 2b28b78..0000000 --- a/app/SSHCommands/Website/CloneRepositoryCommand.php +++ /dev/null @@ -1,33 +0,0 @@ -file()) - ->replace('__repo__', $this->repository) - ->replace('__host__', str($this->repository)->after('@')->before('-')) - ->replace('__branch__', $this->branch) - ->replace('__path__', $this->path) - ->replace('__key__', $this->privateKeyPath) - ->toString(); - } -} diff --git a/app/SSHCommands/Website/ComposerInstallCommand.php b/app/SSHCommands/Website/ComposerInstallCommand.php deleted file mode 100755 index 182536e..0000000 --- a/app/SSHCommands/Website/ComposerInstallCommand.php +++ /dev/null @@ -1,25 +0,0 @@ -file()) - ->replace('__path__', $this->path) - ->toString(); - } -} diff --git a/app/SSHCommands/Website/GetEnvCommand.php b/app/SSHCommands/Website/GetEnvCommand.php deleted file mode 100755 index 0bc439f..0000000 --- a/app/SSHCommands/Website/GetEnvCommand.php +++ /dev/null @@ -1,26 +0,0 @@ -file()) - ->replace('__domain__', $this->domain) - ->toString(); - } -} diff --git a/app/SSHCommands/Website/UpdateBranchCommand.php b/app/SSHCommands/Website/UpdateBranchCommand.php deleted file mode 100644 index 652ae77..0000000 --- a/app/SSHCommands/Website/UpdateBranchCommand.php +++ /dev/null @@ -1,26 +0,0 @@ -file()) - ->replace('__path__', $this->path) - ->replace('__branch__', $this->branch) - ->toString(); - } -} diff --git a/app/SSHCommands/Wordpress/InstallWordpressCommand.php b/app/SSHCommands/Wordpress/InstallWordpressCommand.php deleted file mode 100755 index 3432860..0000000 --- a/app/SSHCommands/Wordpress/InstallWordpressCommand.php +++ /dev/null @@ -1,46 +0,0 @@ -file()) - ->replace('__path__', $this->path) - ->replace('__domain__', $this->domain) - ->replace('__db_name__', $this->dbName) - ->replace('__db_user__', $this->dbUser) - ->replace('__db_pass__', $this->dbPass) - ->replace('__db_host__', $this->dbHost) - ->replace('__db_prefix__', $this->dbPrefix) - ->replace('__username__', $this->username) - ->replace('__password__', $this->password) - ->replace('__title__', $this->title) - ->replace('__email__', $this->email) - ->toString(); - } -} diff --git a/app/SSHCommands/Wordpress/UpdateWordpressCommand.php b/app/SSHCommands/Wordpress/UpdateWordpressCommand.php deleted file mode 100755 index db0bb38..0000000 --- a/app/SSHCommands/Wordpress/UpdateWordpressCommand.php +++ /dev/null @@ -1,37 +0,0 @@ -title) { - $command .= 'wp --path='.$this->path.' option update blogname "'.addslashes($this->title).'"'."\n"; - } - if ($this->url) { - $command .= 'wp --path='.$this->path.' option update siteurl "'.addslashes($this->url).'"'."\n"; - $command .= 'wp --path='.$this->path.' option update home "'.addslashes($this->url).'"'."\n"; - } - - return $command; - } -} diff --git a/app/ServerProviders/AWS.php b/app/ServerProviders/AWS.php index ea8cebf..9d6f933 100755 --- a/app/ServerProviders/AWS.php +++ b/app/ServerProviders/AWS.php @@ -19,7 +19,7 @@ class AWS extends AbstractProvider protected EC2InstanceConnectClient $ec2InstanceConnectClient; - public function createValidationRules(array $input): array + public function createRules(array $input): array { $rules = [ 'os' => 'required|in:'.implode(',', OperatingSystem::getValues()), diff --git a/app/ServerProviders/AbstractProvider.php b/app/ServerProviders/AbstractProvider.php index e6b8f93..c628461 100755 --- a/app/ServerProviders/AbstractProvider.php +++ b/app/ServerProviders/AbstractProvider.php @@ -2,8 +2,8 @@ namespace App\ServerProviders; -use App\Contracts\ServerProvider; use App\Models\Server; +use Illuminate\Filesystem\FilesystemAdapter; use Illuminate\Support\Facades\Storage; abstract class AbstractProvider implements ServerProvider @@ -15,9 +15,9 @@ public function __construct(?Server $server = null) $this->server = $server; } - protected function generateKeyPair(): void + public function generateKeyPair(): void { - /** @var \Illuminate\Filesystem\FilesystemAdapter $storageDisk */ + /** @var FilesystemAdapter $storageDisk */ $storageDisk = Storage::disk(config('core.key_pairs_disk')); generate_key_pair($storageDisk->path((string) $this->server->id)); } diff --git a/app/ServerProviders/Custom.php b/app/ServerProviders/Custom.php index f696dd8..a5fd1cb 100755 --- a/app/ServerProviders/Custom.php +++ b/app/ServerProviders/Custom.php @@ -9,7 +9,7 @@ class Custom extends AbstractProvider { - public function createValidationRules(array $input): array + public function createRules(array $input): array { return [ 'ip' => [ diff --git a/app/ServerProviders/DigitalOcean.php b/app/ServerProviders/DigitalOcean.php index 8e60035..052ff2d 100644 --- a/app/ServerProviders/DigitalOcean.php +++ b/app/ServerProviders/DigitalOcean.php @@ -14,7 +14,7 @@ class DigitalOcean extends AbstractProvider { protected string $apiUrl = 'https://api.digitalocean.com/v2'; - public function createValidationRules(array $input): array + public function createRules(array $input): array { $rules = [ 'os' => 'required|in:'.implode(',', config('core.operating_systems')), diff --git a/app/ServerProviders/Hetzner.php b/app/ServerProviders/Hetzner.php index 6cc42ee..e19971e 100644 --- a/app/ServerProviders/Hetzner.php +++ b/app/ServerProviders/Hetzner.php @@ -13,7 +13,7 @@ class Hetzner extends AbstractProvider { protected string $apiUrl = 'https://api.hetzner.cloud/v1'; - public function createValidationRules(array $input): array + public function createRules(array $input): array { return [ 'os' => 'required|in:'.implode(',', config('core.operating_systems')), diff --git a/app/ServerProviders/Linode.php b/app/ServerProviders/Linode.php index b696c1a..d61e02d 100644 --- a/app/ServerProviders/Linode.php +++ b/app/ServerProviders/Linode.php @@ -13,7 +13,7 @@ class Linode extends AbstractProvider { protected string $apiUrl = 'https://api.linode.com/v4'; - public function createValidationRules($input): array + public function createRules($input): array { $rules = [ 'os' => 'required|in:'.implode(',', config('core.operating_systems')), diff --git a/app/Contracts/ServerProvider.php b/app/ServerProviders/ServerProvider.php similarity index 78% rename from app/Contracts/ServerProvider.php rename to app/ServerProviders/ServerProvider.php index 58d25fa..d717718 100755 --- a/app/Contracts/ServerProvider.php +++ b/app/ServerProviders/ServerProvider.php @@ -1,10 +1,10 @@ 'required|in:'.implode(',', config('core.operating_systems')), diff --git a/app/ServerTypes/AbstractType.php b/app/ServerTypes/AbstractType.php index ba03f57..bf5792a 100755 --- a/app/ServerTypes/AbstractType.php +++ b/app/ServerTypes/AbstractType.php @@ -2,9 +2,8 @@ namespace App\ServerTypes; -use App\Contracts\ServerType; +use App\Enums\ServiceStatus; use App\Models\Server; -use Closure; abstract class AbstractType implements ServerType { @@ -15,12 +14,110 @@ public function __construct(Server $server) $this->server = $server; } - protected function progress(int $percentage, ?string $step = null): Closure + public function install(): void { - return function () use ($percentage, $step) { - $this->server->progress = $percentage; - $this->server->progress_step = $step; - $this->server->save(); - }; + $this->createUser(); + $this->progress(15, 'installing-updates'); + $this->server->os()->upgrade(); + $this->progress(25, 'installing-dependencies'); + $this->server->os()->installDependencies(); + $services = $this->server->services; + $currentProgress = 45; + $progressPerService = (100 - $currentProgress) / count($services); + foreach ($services as $service) { + $currentProgress += $progressPerService; + $this->progress($currentProgress, 'installing- '.$service->name); + $service->handler()->install(); + $service->update(['status' => ServiceStatus::READY]); + if ($service->type == 'php') { + $this->progress($currentProgress, 'installing-composer'); + $service->handler()->installComposer(); + } + } + $this->progress(100, 'finishing'); + } + + protected function createUser(): void + { + $this->server->os()->createUser( + $this->server->authentication['user'], + $this->server->authentication['pass'], + $this->server->sshKey()['public_key'] + ); + $this->server->ssh_user = config('core.ssh_user'); + $this->server->save(); + $this->server->refresh(); + $this->server->public_key = $this->server->os()->getPublicKey($this->server->getSshUser()); + $this->server->save(); + } + + protected function progress(int $percentage, ?string $step = null): void + { + $this->server->progress = $percentage; + $this->server->progress_step = $step; + $this->server->save(); + } + + protected function addWebserver(string $service): void + { + if ($service != 'none') { + $this->server->services()->create([ + 'type' => 'webserver', + 'name' => $service, + 'version' => 'latest', + ]); + } + } + + protected function addDatabase(string $service): void + { + if ($service != 'none') { + $this->server->services()->create([ + 'type' => 'database', + 'name' => config('core.databases_name.'.$service), + 'version' => config('core.databases_version.'.$service), + ]); + } + } + + protected function addPHP(string $version): void + { + if ($version != 'none') { + $this->server->services()->create([ + 'type' => 'php', + 'type_data' => [ + 'extensions' => [], + ], + 'name' => 'php', + 'version' => $version, + ]); + } + } + + protected function addSupervisor(): void + { + $this->server->services()->create([ + 'type' => 'process_manager', + 'name' => 'supervisor', + 'version' => 'latest', + ]); + } + + protected function addRedis(): void + { + $this->server->services()->create([ + 'type' => 'memory_database', + 'name' => 'redis', + 'version' => 'latest', + ]); + } + + protected function addUfw(): void + { + $this->server->services()->create([ + 'type' => 'firewall', + 'name' => 'ufw', + 'version' => 'latest', + ]); } } diff --git a/app/ServerTypes/Database.php b/app/ServerTypes/Database.php index fdbee39..b8e1925 100755 --- a/app/ServerTypes/Database.php +++ b/app/ServerTypes/Database.php @@ -2,19 +2,9 @@ namespace App\ServerTypes; -use App\Facades\Notifier; -use App\Jobs\Installation\Initialize; -use App\Jobs\Installation\InstallRequirements; -use App\Jobs\Installation\Upgrade; -use App\Notifications\ServerInstallationFailed; -use App\Notifications\ServerInstallationSucceed; -use Illuminate\Support\Facades\Bus; -use Illuminate\Support\Facades\Log; -use Throwable; - class Database extends AbstractType { - public function createValidationRules(array $input): array + public function createRules(array $input): array { return [ 'database' => [ @@ -32,99 +22,9 @@ public function data(array $input): array public function createServices(array $input): void { $this->server->services()->forceDelete(); - $this->addDatabase($input['database']); $this->addSupervisor(); $this->addRedis(); $this->addUfw(); } - - public function install(): void - { - $jobs = [ - new Initialize($this->server, $this->server->ssh_user, $this->server->provider === 'custom'), - $this->progress(15, 'Installing Updates'), - new Upgrade($this->server), - $this->progress(25, 'Installing Requirements'), - new InstallRequirements($this->server), - ]; - - $services = $this->server->services; - $currentProgress = 25; - $progressPerService = (100 - $currentProgress) / count($services); - foreach ($services as $service) { - $currentProgress += $progressPerService; - $jobs[] = $this->progress($currentProgress, 'Installing '.$service->name); - $jobs[] = $service->installer(); - } - - $jobs[] = function () { - $this->server->update([ - 'status' => 'ready', - 'progress' => 100, - ]); - Notifier::send($this->server, new ServerInstallationSucceed($this->server)); - }; - - Bus::chain($jobs) - ->catch(function (Throwable $e) { - $this->server->update([ - 'status' => 'installation_failed', - ]); - Notifier::send($this->server, new ServerInstallationFailed($this->server)); - Log::error('server-installation-error', [ - 'error' => (string) $e, - ]); - throw $e; - }) - ->onConnection('ssh-long') - ->dispatch(); - } - - protected function addDatabase(string $service): void - { - if ($service != 'none') { - $this->server->services()->create([ - 'type' => 'database', - 'name' => config('core.databases_name.'.$service), - 'version' => config('core.databases_version.'.$service), - ]); - } - } - - /** - * add supervisor - */ - protected function addSupervisor(): void - { - $this->server->services()->create([ - 'type' => 'process_manager', - 'name' => 'supervisor', - 'version' => 'latest', - ]); - } - - /** - * add supervisor - */ - protected function addRedis(): void - { - $this->server->services()->create([ - 'type' => 'memory_database', - 'name' => 'redis', - 'version' => 'latest', - ]); - } - - /** - * add supervisor - */ - protected function addUfw(): void - { - $this->server->services()->create([ - 'type' => 'firewall', - 'name' => 'ufw', - 'version' => 'latest', - ]); - } } diff --git a/app/ServerTypes/Regular.php b/app/ServerTypes/Regular.php index ac53de5..9ba276a 100755 --- a/app/ServerTypes/Regular.php +++ b/app/ServerTypes/Regular.php @@ -2,23 +2,9 @@ namespace App\ServerTypes; -use App\Enums\ServerStatus; -use App\Facades\Notifier; -use App\Jobs\Installation\Initialize; -use App\Jobs\Installation\InstallCertbot; -use App\Jobs\Installation\InstallComposer; -use App\Jobs\Installation\InstallNodejs; -use App\Jobs\Installation\InstallRequirements; -use App\Jobs\Installation\Upgrade; -use App\Notifications\ServerInstallationFailed; -use App\Notifications\ServerInstallationSucceed; -use Illuminate\Support\Facades\Bus; -use Illuminate\Support\Facades\Log; -use Throwable; - class Regular extends AbstractType { - public function createValidationRules(array $input): array + public function createRules(array $input): array { return [ 'webserver' => [ @@ -44,7 +30,6 @@ public function data(array $input): array public function createServices(array $input): void { $this->server->services()->forceDelete(); - $this->addWebserver($input['webserver']); $this->addDatabase($input['database']); $this->addPHP($input['php']); @@ -52,126 +37,4 @@ public function createServices(array $input): void $this->addRedis(); $this->addUfw(); } - - public function install(): void - { - $jobs = [ - new Initialize($this->server, $this->server->ssh_user), - $this->progress(15, 'Installing Updates'), - new Upgrade($this->server), - $this->progress(25, 'Installing Requirements'), - new InstallRequirements($this->server), - $this->progress(35, 'Installing Node.js'), - new InstallNodejs($this->server), - $this->progress(45, 'Installing Certbot'), - new InstallCertbot($this->server), - ]; - - $services = $this->server->services; - $currentProgress = 45; - $progressPerService = (100 - $currentProgress) / count($services); - foreach ($services as $service) { - $currentProgress += $progressPerService; - $jobs[] = $this->progress($currentProgress, 'Installing '.$service->name); - $jobs[] = $service->installer(); - if ($service->type == 'php') { - $jobs[] = $this->progress($currentProgress, 'Installing Composer'); - $jobs[] = new InstallComposer($this->server); - } - } - - $jobs[] = function () { - $this->server->update([ - 'status' => ServerStatus::READY, - 'progress' => 100, - ]); - Notifier::send($this->server, new ServerInstallationSucceed($this->server)); - }; - - Bus::chain($jobs) - ->catch(function (Throwable $e) { - $this->server->update([ - 'status' => 'installation_failed', - ]); - Notifier::send($this->server, new ServerInstallationFailed($this->server)); - Log::error('server-installation-error', [ - 'error' => (string) $e, - ]); - throw $e; - }) - ->onConnection('ssh-long') - ->dispatch(); - } - - protected function addWebserver(string $service): void - { - if ($service != 'none') { - $this->server->services()->create([ - 'type' => 'webserver', - 'name' => $service, - 'version' => 'latest', - ]); - } - } - - protected function addDatabase(string $service): void - { - if ($service != 'none') { - $this->server->services()->create([ - 'type' => 'database', - 'name' => config('core.databases_name.'.$service), - 'version' => config('core.databases_version.'.$service), - ]); - } - } - - protected function addPHP(string $version): void - { - if ($version != 'none') { - $this->server->services()->create([ - 'type' => 'php', - 'type_data' => [ - 'extensions' => [], - ], - 'name' => 'php', - 'version' => $version, - ]); - } - } - - /** - * add supervisor - */ - protected function addSupervisor(): void - { - $this->server->services()->create([ - 'type' => 'process_manager', - 'name' => 'supervisor', - 'version' => 'latest', - ]); - } - - /** - * add redis - */ - protected function addRedis(): void - { - $this->server->services()->create([ - 'type' => 'memory_database', - 'name' => 'redis', - 'version' => 'latest', - ]); - } - - /** - * add ufw - */ - protected function addUfw(): void - { - $this->server->services()->create([ - 'type' => 'firewall', - 'name' => 'ufw', - 'version' => 'latest', - ]); - } } diff --git a/app/Contracts/ServerType.php b/app/ServerTypes/ServerType.php similarity index 66% rename from app/Contracts/ServerType.php rename to app/ServerTypes/ServerType.php index ba9252a..382bd6f 100755 --- a/app/Contracts/ServerType.php +++ b/app/ServerTypes/ServerType.php @@ -1,10 +1,10 @@ service = $service; - } -} diff --git a/app/ServiceHandlers/Database/Mysql.php b/app/ServiceHandlers/Database/Mysql.php deleted file mode 100755 index dffec82..0000000 --- a/app/ServiceHandlers/Database/Mysql.php +++ /dev/null @@ -1,138 +0,0 @@ -service->server->ssh()->exec( - new CreateCommand('mysql', $name), - 'create-database' - ); - } - - /** - * @throws Throwable - */ - public function delete(string $name): void - { - $this->service->server->ssh()->exec( - new DeleteCommand('mysql', $name), - 'delete-database' - ); - } - - /** - * @throws Throwable - */ - public function createUser(string $username, string $password, string $host): void - { - $this->service->server->ssh()->exec( - new CreateUserCommand('mysql', $username, $password, $host), - 'create-user' - ); - } - - /** - * @throws Throwable - */ - public function deleteUser(string $username, string $host): void - { - $this->service->server->ssh()->exec( - new DeleteUserCommand('mysql', $username, $host), - 'delete-user' - ); - } - - /** - * @throws Throwable - */ - public function link(string $username, string $host, array $databases): void - { - $ssh = $this->service->server->ssh(); - foreach ($databases as $database) { - $ssh->exec( - new LinkCommand('mysql', $username, $host, $database), - 'link-user-to-databases' - ); - } - } - - /** - * @throws Throwable - */ - public function unlink(string $username, string $host): void - { - $this->service->server->ssh()->exec( - new UnlinkCommand('mysql', $username, $host), - 'unlink-user-from-database' - ); - } - - /** - * @throws Throwable - */ - public function runBackup(BackupFile $backupFile): void - { - // backup - $this->service->server->ssh()->exec( - new BackupDatabaseCommand( - 'mysql', - $backupFile->backup->database->name, - $backupFile->name - ), - 'backup-database' - ); - - // upload to storage - $upload = $backupFile->backup->storage->provider()->upload( - $backupFile->backup->server, - $backupFile->path, - $backupFile->storage_path, - ); - - // cleanup - $this->service->server->ssh()->exec('rm '.$backupFile->name.'.zip'); - - $backupFile->size = $upload['size']; - $backupFile->save(); - } - - /** - * @throws Throwable - */ - public function restoreBackup(BackupFile $backupFile, string $database): void - { - // download - $backupFile->backup->storage->provider()->download( - $backupFile->backup->server, - $backupFile->storage_path, - $backupFile->name.'.zip', - ); - - // restore - $this->service->server->ssh()->exec( - new RestoreDatabaseCommand( - 'mysql', - $database, - $backupFile->name - ), - 'restore-database' - ); - } -} diff --git a/app/ServiceHandlers/Firewall/Ufw.php b/app/ServiceHandlers/Firewall/Ufw.php deleted file mode 100755 index d6dd8e7..0000000 --- a/app/ServiceHandlers/Firewall/Ufw.php +++ /dev/null @@ -1,32 +0,0 @@ -service->server->ssh()->exec( - new AddRuleCommand('ufw', $type, $protocol, $port, $source, $mask), - 'add-firewall-rule' - ); - } - - /** - * @throws Throwable - */ - public function removeRule(string $type, string $protocol, int $port, string $source, ?string $mask): void - { - $this->service->server->ssh()->exec( - new RemoveRuleCommand('ufw', $type, $protocol, $port, $source, $mask), - 'remove-firewall-rule' - ); - } -} diff --git a/app/ServiceHandlers/PHP.php b/app/ServiceHandlers/PHP.php deleted file mode 100644 index 1e64c9c..0000000 --- a/app/ServiceHandlers/PHP.php +++ /dev/null @@ -1,30 +0,0 @@ -service = $service; - } - - public function setDefaultCli(): void - { - $this->service->server->ssh()->exec( - new ChangeDefaultPHPCommand($this->service->version), - 'change-default-php' - ); - } - - public function installExtension($name): void - { - dispatch(new InstallPHPExtension($this->service, $name))->onConnection('ssh'); - } -} diff --git a/app/ServiceHandlers/Webserver/AbstractWebserver.php b/app/ServiceHandlers/Webserver/AbstractWebserver.php deleted file mode 100755 index b194ff1..0000000 --- a/app/ServiceHandlers/Webserver/AbstractWebserver.php +++ /dev/null @@ -1,16 +0,0 @@ -service = $service; - } -} diff --git a/app/ServiceHandlers/Webserver/Nginx.php b/app/ServiceHandlers/Webserver/Nginx.php deleted file mode 100755 index c0d93f8..0000000 --- a/app/ServiceHandlers/Webserver/Nginx.php +++ /dev/null @@ -1,206 +0,0 @@ -service->server->ssh()->exec( - new CreateNginxVHostCommand( - $site->domain, - $site->path, - $this->generateVhost($site) - ), - 'create-vhost', - $site->id - ); - } - - /** - * @throws Throwable - */ - public function updateVHost(Site $site, bool $noSSL = false, ?string $vhost = null): void - { - $this->service->server->ssh()->exec( - new UpdateNginxVHostCommand( - $site->domain, - $site->path, - $vhost ?? $this->generateVhost($site, $noSSL) - ), - 'update-vhost', - $site->id - ); - } - - public function getVHost(Site $site): string - { - return $this->service->server->ssh()->exec( - new GetNginxVHostCommand( - $site->domain - ), - 'get-vhost', - $site->id - ); - } - - /** - * @throws Throwable - */ - public function deleteSite(Site $site): void - { - $this->service->server->ssh()->exec( - new DeleteNginxSiteCommand( - $site->domain, - $site->path - ), - 'delete-site', - $site->id - ); - $this->service->restart(); - } - - /** - * @throws Throwable - */ - public function changePHPVersion(Site $site, $version): void - { - $this->service->server->ssh()->exec( - new ChangeNginxPHPVersionCommand($site->domain, $site->php_version, $version), - 'change-php-version', - $site->id - ); - } - - /** - * @throws Throwable - */ - public function setupSSL(Ssl $ssl): void - { - $command = new CreateLetsencryptSSLCommand( - $ssl->site->server->creator->email, - $ssl->site->domain, - $ssl->site->web_directory_path - ); - if ($ssl->type == 'custom') { - $command = new CreateCustomSSLCommand( - $ssl->certs_directory_path, - $ssl->certificate, - $ssl->pk, - $ssl->certificate_path, - $ssl->pk_path, - ); - } - $result = $this->service->server->ssh()->exec( - $command, - 'create-ssl', - $ssl->site_id - ); - if (! $ssl->validateSetup($result)) { - throw new SSLCreationException(); - } - - $this->updateVHost($ssl->site); - } - - /** - * @throws Throwable - */ - public function removeSSL(Ssl $ssl): void - { - $this->service->server->ssh()->exec( - new RemoveSSLCommand($ssl->certs_directory_path), - 'remove-ssl', - $ssl->site_id - ); - - $this->updateVHost($ssl->site, true); - } - - /** - * @throws Throwable - */ - public function updateRedirects(Site $site, array $redirects): void - { - $redirectsPlain = ''; - foreach ($redirects as $redirect) { - $rd = File::get(resource_path('commands/webserver/nginx/redirect.conf')); - $rd = Str::replace('__from__', $redirect->from, $rd); - $rd = Str::replace('__mode__', $redirect->mode, $rd); - $rd = Str::replace('__to__', $redirect->to, $rd); - $redirectsPlain .= $rd."\n"; - } - $result = $this->service->server->ssh()->exec( - new UpdateNginxRedirectsCommand( - $site->domain, - $redirectsPlain, - ), - 'update-redirects', - $site->id - ); - if (Str::contains($result, 'journalctl -xe')) { - throw new ErrorUpdatingRedirects(); - } - } - - protected function generateVhost(Site $site, bool $noSSL = false): string - { - $ssl = $site->activeSsl; - if ($noSSL) { - $ssl = null; - } - $vhost = File::get(resource_path('commands/webserver/nginx/vhost.conf')); - if ($ssl) { - $vhost = File::get(resource_path('commands/webserver/nginx/vhost-ssl.conf')); - } - if ($site->type()->language() === 'php') { - $vhost = File::get(resource_path('commands/webserver/nginx/php-vhost.conf')); - if ($ssl) { - $vhost = File::get(resource_path('commands/webserver/nginx/php-vhost-ssl.conf')); - } - } - if ($site->port) { - $vhost = File::get(resource_path('commands/webserver/nginx/reverse-vhost.conf')); - if ($ssl) { - $vhost = File::get(resource_path('commands/webserver/nginx/reverse-vhost-ssl.conf')); - } - $vhost = Str::replace('__port__', (string) $site->port, $vhost); - } - - $vhost = Str::replace('__domain__', $site->domain, $vhost); - $vhost = Str::replace('__aliases__', $site->aliases_string, $vhost); - $vhost = Str::replace('__path__', $site->path, $vhost); - $vhost = Str::replace('__web_directory__', $site->web_directory, $vhost); - - if ($ssl) { - $vhost = Str::replace('__certificate__', $ssl->certificate_path, $vhost); - $vhost = Str::replace('__private_key__', $ssl->pk_path, $vhost); - } - - if ($site->php_version) { - $vhost = Str::replace('__php_version__', $site->php_version, $vhost); - } - - return $vhost; - } -} diff --git a/app/SiteTypes/AbstractSiteType.php b/app/SiteTypes/AbstractSiteType.php index ce8746a..38998dc 100755 --- a/app/SiteTypes/AbstractSiteType.php +++ b/app/SiteTypes/AbstractSiteType.php @@ -2,10 +2,8 @@ namespace App\SiteTypes; -use App\Contracts\SiteType; -use App\Jobs\Site\DeleteSite; +use App\Exceptions\SourceControlIsNotConnected; use App\Models\Site; -use Closure; abstract class AbstractSiteType implements SiteType { @@ -16,16 +14,25 @@ public function __construct(Site $site) $this->site = $site; } - public function delete(): void + protected function progress(int $percentage): void { - dispatch(new DeleteSite($this->site))->onConnection('ssh'); + $this->site->progress = $percentage; + $this->site->save(); } - protected function progress(int $percentage): Closure + /** + * @throws SourceControlIsNotConnected + */ + protected function deployKey(): void { - return function () use ($percentage) { - $this->site->progress = $percentage; - $this->site->save(); - }; + $os = $this->site->server->os(); + $os->generateSSHKey($this->site->getSshKeyName()); + $this->site->ssh_key = $os->readSSHKey($this->site->getSshKeyName()); + $this->site->save(); + $this->site->sourceControl()->provider()->deployKey( + $this->site->domain.'-key-'.$this->site->id, + $this->site->repository, + $this->site->ssh_key + ); } } diff --git a/app/SiteTypes/PHPBlank.php b/app/SiteTypes/PHPBlank.php index f4cd468..36fa2d3 100755 --- a/app/SiteTypes/PHPBlank.php +++ b/app/SiteTypes/PHPBlank.php @@ -3,10 +3,7 @@ namespace App\SiteTypes; use App\Enums\SiteFeature; -use App\Jobs\Site\CreateVHost; -use Illuminate\Support\Facades\Bus; use Illuminate\Validation\Rule; -use Throwable; class PHPBlank extends PHPSite { @@ -20,7 +17,7 @@ public function supportedFeatures(): array ]; } - public function createValidationRules(array $input): array + public function createRules(array $input): array { return [ 'php_version' => [ @@ -45,23 +42,8 @@ public function data(array $input): array public function install(): void { - $chain = [ - new CreateVHost($this->site), - $this->progress(65), - function () { - $this->site->php()?->restart(); - }, - ]; - - $chain[] = function () { - $this->site->installationFinished(); - }; - - Bus::chain($chain) - ->catch(function (Throwable $e) { - $this->site->installationFailed($e); - }) - ->onConnection('ssh-long') - ->dispatch(); + $this->site->server->webserver()->handler()->createVHost($this->site); + $this->progress(65); + $this->site->php()?->restart(); } } diff --git a/app/SiteTypes/PHPSite.php b/app/SiteTypes/PHPSite.php index ec676d4..b7823e6 100755 --- a/app/SiteTypes/PHPSite.php +++ b/app/SiteTypes/PHPSite.php @@ -3,13 +3,10 @@ namespace App\SiteTypes; use App\Enums\SiteFeature; -use App\Jobs\Site\CloneRepository; -use App\Jobs\Site\ComposerInstall; -use App\Jobs\Site\CreateVHost; -use App\Jobs\Site\DeployKey; -use Illuminate\Support\Facades\Bus; +use App\Exceptions\SourceControlIsNotConnected; +use App\SSH\Composer\Composer; +use App\SSH\Git\Git; use Illuminate\Validation\Rule; -use Throwable; class PHPSite extends AbstractSiteType { @@ -28,7 +25,7 @@ public function supportedFeatures(): array ]; } - public function createValidationRules(array $input): array + public function createRules(array $input): array { return [ 'php_version' => [ @@ -66,37 +63,24 @@ public function data(array $input): array ]; } + /** + * @throws SourceControlIsNotConnected + */ public function install(): void { - $chain = [ - new CreateVHost($this->site), - $this->progress(15), - new DeployKey($this->site), - $this->progress(30), - new CloneRepository($this->site), - $this->progress(65), - function () { - $this->site->php()?->restart(); - }, - ]; - + $this->site->server->webserver()->handler()->createVHost($this->site); + $this->progress(15); + $this->deployKey(); + $this->progress(30); + app(Git::class)->clone($this->site); + $this->progress(65); + $this->site->php()?->restart(); if ($this->site->type_data['composer']) { - $chain[] = new ComposerInstall($this->site); + app(Composer::class)->installDependencies($this->site); } - - $chain[] = function () { - $this->site->installationFinished(); - }; - - Bus::chain($chain) - ->catch(function (Throwable $e) { - $this->site->installationFailed($e); - }) - ->onConnection('ssh-long') - ->dispatch(); } - public function editValidationRules(array $input): array + public function editRules(array $input): array { return []; } diff --git a/app/Contracts/SiteType.php b/app/SiteTypes/SiteType.php similarity index 61% rename from app/Contracts/SiteType.php rename to app/SiteTypes/SiteType.php index 7e82038..a65019d 100755 --- a/app/Contracts/SiteType.php +++ b/app/SiteTypes/SiteType.php @@ -1,6 +1,6 @@ [ @@ -73,7 +68,7 @@ public function createFields(array $input): array public function data(array $input): array { return [ - 'url' => $this->site->url, + 'url' => $this->site->getUrl(), 'title' => $input['title'], 'username' => $input['username'], 'email' => $input['email'], @@ -86,79 +81,37 @@ public function data(array $input): array public function install(): void { - $chain = [ - new CreateVHost($this->site), - $this->progress(15), - function () { - /** @var Database $database */ - $database = app(CreateDatabase::class)->create($this->site->server, [ - 'name' => $this->site->type_data['database'], - ]); - /** @var DatabaseUser $databaseUser */ - $databaseUser = app(CreateDatabaseUser::class)->create($this->site->server, [ - 'username' => $this->site->type_data['database_user'], - 'password' => $this->site->type_data['database_password'], - 'remote' => false, - 'host' => 'localhost', - ], [$database->name]); - app(LinkUser::class)->link($databaseUser, [ - 'databases' => [$database->name], - ]); - }, - $this->progress(50), - new InstallWordpress($this->site), - $this->progress(75), - function () { - $this->site->php()?->restart(); - $this->site->installationFinished(); - }, - ]; - - Bus::chain($chain) - ->catch(function (Throwable $e) { - $this->site->installationFailed($e); - }) - ->onConnection('ssh-long') - ->dispatch(); + $this->site->server->webserver()->handler()->createVHost($this->site); + $this->progress(30); + /** @var Database $database */ + $database = app(CreateDatabase::class)->create($this->site->server, [ + 'name' => $this->site->type_data['database'], + ]); + /** @var DatabaseUser $databaseUser */ + $databaseUser = app(CreateDatabaseUser::class)->create($this->site->server, [ + 'username' => $this->site->type_data['database_user'], + 'password' => $this->site->type_data['database_password'], + 'remote' => false, + 'host' => 'localhost', + ], [$database->name]); + app(LinkUser::class)->link($databaseUser, [ + 'databases' => [$database->name], + ]); + $this->site->php()?->restart(); + $this->progress(60); + app(\App\SSH\Wordpress\Wordpress::class)->install($this->site); } - public function editValidationRules(array $input): array + public function editRules(array $input): array { return [ 'title' => 'required', 'url' => 'required', - // 'email' => 'required|email', ]; } public function edit(): void { - $this->site->status = 'installing'; - $this->site->progress = 90; - $this->site->save(); - $chain = [ - function () { - $this->site->server->ssh()->exec( - new UpdateWordpressCommand( - $this->site->path, - $this->site->type_data['url'], - $this->site->type_data['username'] ?? '', - $this->site->type_data['password'] ?? '', - $this->site->type_data['email'] ?? '', - $this->site->type_data['title'] ?? '', - ), - 'update-wordpress', - $this->site->id - ); - $this->site->installationFinished(); - }, - ]; - - Bus::chain($chain) - ->catch(function (Throwable $e) { - $this->site->installationFailed($e); - }) - ->onConnection('ssh') - ->dispatch(); + // } } diff --git a/app/SourceControlProviders/AbstractSourceControlProvider.php b/app/SourceControlProviders/AbstractSourceControlProvider.php index 86f1087..80b23a1 100755 --- a/app/SourceControlProviders/AbstractSourceControlProvider.php +++ b/app/SourceControlProviders/AbstractSourceControlProvider.php @@ -2,7 +2,6 @@ namespace App\SourceControlProviders; -use App\Contracts\SourceControlProvider; use App\Exceptions\RepositoryNotFound; use App\Exceptions\RepositoryPermissionDenied; use App\Exceptions\SourceControlIsNotConnected; diff --git a/app/Contracts/SourceControlProvider.php b/app/SourceControlProviders/SourceControlProvider.php similarity index 93% rename from app/Contracts/SourceControlProvider.php rename to app/SourceControlProviders/SourceControlProvider.php index cd2a0ca..a8fa6c9 100755 --- a/app/Contracts/SourceControlProvider.php +++ b/app/SourceControlProviders/SourceControlProvider.php @@ -1,6 +1,6 @@ successful(); } - /** - * @throws Throwable - */ - public function upload(Server $server, string $src, string $dest): array + public function ssh(Server $server): Storage { - $upload = $server->ssh()->exec( - new UploadToDropboxCommand( - $src, - $dest, - $this->storageProvider->credentials['token'] - ), - 'upload-to-dropbox' - ); - - $data = json_decode($upload, true); - - if (isset($data['error'])) { - throw new BackupFileException('Failed to upload to Dropbox '.$data['error_summary'] ?? ''); - } - - return [ - 'size' => $data['size'] ?? null, - ]; - } - - /** - * @throws Throwable - */ - public function download(Server $server, string $src, string $dest): void - { - $server->ssh()->exec( - new DownloadFromDropboxCommand( - $src, - $dest, - $this->storageProvider->credentials['token'] - ), - 'download-from-dropbox' - ); + return new \App\SSH\Storage\Dropbox($server, $this->storageProvider); } public function delete(array $paths): void diff --git a/app/StorageProviders/FTP.php b/app/StorageProviders/FTP.php index 93e8f02..88c2a0d 100644 --- a/app/StorageProviders/FTP.php +++ b/app/StorageProviders/FTP.php @@ -3,10 +3,8 @@ namespace App\StorageProviders; use App\Models\Server; -use App\SSHCommands\Storage\DownloadFromFTPCommand; -use App\SSHCommands\Storage\UploadToFTPCommand; +use App\SSH\Storage\Storage; use FTP\Connection; -use Throwable; class FTP extends AbstractStorageProvider { @@ -49,48 +47,9 @@ public function connect(): bool return $isConnected; } - /** - * @throws Throwable - */ - public function upload(Server $server, string $src, string $dest): array + public function ssh(Server $server): Storage { - $server->ssh()->exec( - new UploadToFTPCommand( - $src, - $this->storageProvider->credentials['path'].'/'.$dest, - $this->storageProvider->credentials['host'], - $this->storageProvider->credentials['port'], - $this->storageProvider->credentials['username'], - $this->storageProvider->credentials['password'], - $this->storageProvider->credentials['ssl'], - $this->storageProvider->credentials['passive'], - ), - 'upload-to-ftp' - ); - - return [ - 'size' => null, - ]; - } - - /** - * @throws Throwable - */ - public function download(Server $server, string $src, string $dest): void - { - $server->ssh()->exec( - new DownloadFromFTPCommand( - $this->storageProvider->credentials['path'].'/'.$src, - $dest, - $this->storageProvider->credentials['host'], - $this->storageProvider->credentials['port'], - $this->storageProvider->credentials['username'], - $this->storageProvider->credentials['password'], - $this->storageProvider->credentials['ssl'], - $this->storageProvider->credentials['passive'], - ), - 'download-from-ftp' - ); + return new \App\SSH\Storage\FTP($server, $this->storageProvider); } public function delete(array $paths): void diff --git a/app/Contracts/StorageProvider.php b/app/StorageProviders/StorageProvider.php similarity index 58% rename from app/Contracts/StorageProvider.php rename to app/StorageProviders/StorageProvider.php index fe5643b..06f57f4 100644 --- a/app/Contracts/StorageProvider.php +++ b/app/StorageProviders/StorageProvider.php @@ -1,8 +1,9 @@ output = $output; - - return $this; } - public function assertExecuted(array|string $commands): void + public function exec(string|array $commands, string $log = '', ?int $siteId = null): string { - if (! $this->commands) { - PHPUnit::fail('No commands are executed'); + if ($log) { + $this->setLog($log, $siteId); + } else { + $this->log = null; } - if (! is_array($commands)) { - $commands = [$commands]; - } - $allExecuted = true; - foreach ($commands as $command) { - if (! in_array($command, $commands)) { - $allExecuted = false; - } - } - if (! $allExecuted) { - PHPUnit::fail('The expected commands are not executed'); - } - PHPUnit::assertTrue(true, $allExecuted); - } - public function exec(string|array|SSHCommand $commands, string $log = '', ?int $siteId = null): string - { if (! is_array($commands)) { $commands = [$commands]; } @@ -61,6 +39,49 @@ public function exec(string|array|SSHCommand $commands, string $log = '', ?int $ } } - return 'fake output'; + $output = $this->output ?? 'fake output'; + $this->log?->write($output); + + return $output; + } + + public function assertExecuted(array|string $commands): void + { + if (! $this->commands) { + Assert::fail('No commands are executed'); + } + if (! is_array($commands)) { + $commands = [$commands]; + } + $allExecuted = true; + foreach ($commands as $command) { + if (! in_array($command, $commands)) { + $allExecuted = false; + } + } + if (! $allExecuted) { + Assert::fail('The expected commands are not executed. executed commands: '.implode(', ', $this->commands)); + } + Assert::assertTrue(true, $allExecuted); + } + + public function assertExecutedContains(string $command): void + { + if (! $this->commands) { + Assert::fail('No commands are executed'); + } + $executed = false; + foreach ($this->commands as $executedCommand) { + if (str($executedCommand)->contains($command)) { + $executed = true; + break; + } + } + if (! $executed) { + Assert::fail( + 'The expected command is not executed in the executed commands: '.implode(', ', $this->commands) + ); + } + Assert::assertTrue(true, $executed); } } diff --git a/composer.lock b/composer.lock index 539ef9c..ff190b1 100644 --- a/composer.lock +++ b/composer.lock @@ -62,16 +62,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.300.11", + "version": "3.300.14", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "b1c05a5d3cb429aa5d9ffa69066ce46e3d7aca52" + "reference": "f1e0c37d8403d7097c2c808c184137e6517d54be" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/b1c05a5d3cb429aa5d9ffa69066ce46e3d7aca52", - "reference": "b1c05a5d3cb429aa5d9ffa69066ce46e3d7aca52", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/f1e0c37d8403d7097c2c808c184137e6517d54be", + "reference": "f1e0c37d8403d7097c2c808c184137e6517d54be", "shasum": "" }, "require": { @@ -151,9 +151,9 @@ "support": { "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.300.11" + "source": "https://github.com/aws/aws-sdk-php/tree/3.300.14" }, - "time": "2024-03-05T19:08:14+00:00" + "time": "2024-03-08T19:05:39+00:00" }, { "name": "bacon/bacon-qr-code", @@ -650,16 +650,16 @@ }, { "name": "composer/pcre", - "version": "3.1.1", + "version": "3.1.2", "source": { "type": "git", "url": "https://github.com/composer/pcre.git", - "reference": "00104306927c7a0919b4ced2aaa6782c1e61a3c9" + "reference": "4775f35b2d70865807c89d32c8e7385b86eb0ace" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/00104306927c7a0919b4ced2aaa6782c1e61a3c9", - "reference": "00104306927c7a0919b4ced2aaa6782c1e61a3c9", + "url": "https://api.github.com/repos/composer/pcre/zipball/4775f35b2d70865807c89d32c8e7385b86eb0ace", + "reference": "4775f35b2d70865807c89d32c8e7385b86eb0ace", "shasum": "" }, "require": { @@ -701,7 +701,7 @@ ], "support": { "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.1.1" + "source": "https://github.com/composer/pcre/tree/3.1.2" }, "funding": [ { @@ -717,7 +717,7 @@ "type": "tidelift" } ], - "time": "2023-10-11T07:11:09+00:00" + "time": "2024-03-07T15:38:35+00:00" }, { "name": "dasprid/enum", @@ -2526,16 +2526,16 @@ }, { "name": "league/flysystem", - "version": "3.24.0", + "version": "3.25.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "b25a361508c407563b34fac6f64a8a17a8819675" + "reference": "4c44347133618cccd9b3df1729647a1577b4ad99" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/b25a361508c407563b34fac6f64a8a17a8819675", - "reference": "b25a361508c407563b34fac6f64a8a17a8819675", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/4c44347133618cccd9b3df1729647a1577b4ad99", + "reference": "4c44347133618cccd9b3df1729647a1577b4ad99", "shasum": "" }, "require": { @@ -2563,7 +2563,7 @@ "friendsofphp/php-cs-fixer": "^3.5", "google/cloud-storage": "^1.23", "microsoft/azure-storage-blob": "^1.1", - "phpseclib/phpseclib": "^3.0.34", + "phpseclib/phpseclib": "^3.0.36", "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^9.5.11|^10.0", "sabre/dav": "^4.6.0" @@ -2600,7 +2600,7 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/3.24.0" + "source": "https://github.com/thephpleague/flysystem/tree/3.25.0" }, "funding": [ { @@ -2612,7 +2612,7 @@ "type": "github" } ], - "time": "2024-02-04T12:10:17+00:00" + "time": "2024-03-09T17:06:45+00:00" }, { "name": "league/flysystem-local", @@ -7755,16 +7755,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "10.1.12", + "version": "10.1.13", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "842f72662d6b9edda84c4b6f13885fd9cd53dc63" + "reference": "d51c3aec14896d5e80b354fad58e998d1980f8f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/842f72662d6b9edda84c4b6f13885fd9cd53dc63", - "reference": "842f72662d6b9edda84c4b6f13885fd9cd53dc63", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d51c3aec14896d5e80b354fad58e998d1980f8f8", + "reference": "d51c3aec14896d5e80b354fad58e998d1980f8f8", "shasum": "" }, "require": { @@ -7821,7 +7821,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.12" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.13" }, "funding": [ { @@ -7829,7 +7829,7 @@ "type": "github" } ], - "time": "2024-03-02T07:22:05+00:00" + "time": "2024-03-09T16:54:15+00:00" }, { "name": "phpunit/php-file-iterator", @@ -8076,16 +8076,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.11", + "version": "10.5.12", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "0d968f6323deb3dbfeba5bfd4929b9415eb7a9a4" + "reference": "41a9886b85ac7bf3929853baf96b95361cd69d2b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0d968f6323deb3dbfeba5bfd4929b9415eb7a9a4", - "reference": "0d968f6323deb3dbfeba5bfd4929b9415eb7a9a4", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/41a9886b85ac7bf3929853baf96b95361cd69d2b", + "reference": "41a9886b85ac7bf3929853baf96b95361cd69d2b", "shasum": "" }, "require": { @@ -8157,7 +8157,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.11" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.12" }, "funding": [ { @@ -8173,7 +8173,7 @@ "type": "tidelift" } ], - "time": "2024-02-25T14:05:00+00:00" + "time": "2024-03-09T12:04:07+00:00" }, { "name": "sebastian/cli-parser", diff --git a/config/core.php b/config/core.php index 5a45541..de10c36 100755 --- a/config/core.php +++ b/config/core.php @@ -1,15 +1,5 @@ env('SSH_USER', 'vito'), 'ssh_public_key_name' => env('SSH_PUBLIC_KEY_NAME', 'ssh-public.key'), 'ssh_private_key_name' => env('SSH_PRIVATE_KEY_NAME', 'ssh-private.pem'), - 'logs_disk' => env('SERVER_LOGS_DISK', 'server-logs-local'), // should to be FilesystemAdapter storage - 'key_pairs_disk' => env('KEY_PAIRS_DISK', 'key-pairs-local'), // should to be FilesystemAdapter storage + 'logs_disk' => env('SERVER_LOGS_DISK', 'server-logs'), // should be FilesystemAdapter storage + 'key_pairs_disk' => env('KEY_PAIRS_DISK', 'key-pairs'), // should be FilesystemAdapter storage /* * General @@ -130,24 +122,11 @@ /* * Service */ - 'service_installers' => [ - 'nginx' => InstallNginx::class, - 'mysql' => InstallMysql::class, - 'mariadb' => InstallMariadb::class, - 'php' => InstallPHP::class, - 'redis' => InstallRedis::class, - 'supervisor' => InstallSupervisor::class, - 'ufw' => InstallUfw::class, - 'phpmyadmin' => InstallPHPMyAdmin::class, - ], - 'service_uninstallers' => [ - 'phpmyadmin' => UninstallPHPMyAdmin::class, - 'php' => UninstallPHP::class, - ], 'service_handlers' => [ 'nginx' => Nginx::class, 'mysql' => Mysql::class, - 'mariadb' => Mysql::class, + 'mariadb' => Mariadb::class, + 'redis' => Redis::class, 'php' => PHP::class, 'ufw' => Ufw::class, 'supervisor' => Supervisor::class, diff --git a/config/filesystems.php b/config/filesystems.php index c2e46c0..06fbd5a 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -57,12 +57,12 @@ 'throw' => false, ], - 'key-pairs-local' => [ + 'key-pairs' => [ 'driver' => 'local', 'root' => storage_path('app/key-pairs'), ], - 'server-logs-local' => [ + 'server-logs' => [ 'driver' => 'local', 'root' => storage_path('app/server-logs'), ], diff --git a/config/log-viewer.php b/config/log-viewer.php index 1f6f9a9..4f16f60 100644 --- a/config/log-viewer.php +++ b/config/log-viewer.php @@ -85,7 +85,7 @@ 'api_middleware' => [ \Opcodes\LogViewer\Http\Middleware\EnsureFrontendRequestsAreStateful::class, - 'auth', + \Opcodes\LogViewer\Http\Middleware\AuthorizeLogViewer::class, ], /* diff --git a/config/queue.php b/config/queue.php index 0f7d124..410319b 100755 --- a/config/queue.php +++ b/config/queue.php @@ -54,15 +54,7 @@ 'ssh' => [ 'driver' => 'database', 'table' => 'jobs', - 'queue' => 'ssh', - 'timeout' => 240, - 'retry_after' => 600, - ], - - 'ssh-long' => [ - 'driver' => 'database', - 'table' => 'jobs', - 'queue' => 'ssh-long', + 'queue' => 'default', 'timeout' => 600, 'retry_after' => 600, ], @@ -82,7 +74,7 @@ 'failed' => [ 'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'), - 'database' => env('DB_CONNECTION', 'mysql'), + 'database' => env('DB_CONNECTION', 'sqlite'), 'table' => 'failed_jobs', ], diff --git a/database/factories/ServerLogFactory.php b/database/factories/ServerLogFactory.php index ddfc118..d1bb9e8 100644 --- a/database/factories/ServerLogFactory.php +++ b/database/factories/ServerLogFactory.php @@ -14,7 +14,7 @@ public function definition(): array return [ 'type' => 'test-log', 'name' => 'test.log', - 'disk' => 'server-logs-local', + 'disk' => 'server-logs', ]; } } diff --git a/database/factories/StorageProviderFactory.php b/database/factories/StorageProviderFactory.php index 32a28f9..447cc71 100644 --- a/database/factories/StorageProviderFactory.php +++ b/database/factories/StorageProviderFactory.php @@ -12,7 +12,9 @@ public function definition(): array return [ 'profile' => $this->faker->word(), 'provider' => $this->faker->randomElement(\App\Enums\StorageProvider::getValues()), - 'credentials' => [], + 'credentials' => [ + 'token' => 'test-token', + ], 'user_id' => User::factory(), ]; } diff --git a/docker-compose.yml b/docker-compose.yml index b6b06e0..cbe46ae 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,8 +1,7 @@ -version: '3' services: - vito: + app: build: - context: ./docker/8.1 + context: ./vendor/laravel/sail/runtimes/8.2 dockerfile: Dockerfile args: WWWGROUP: '${WWWGROUP}' @@ -17,63 +16,33 @@ services: LARAVEL_SAIL: 1 XDEBUG_MODE: '${SAIL_XDEBUG_MODE:-off}' XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}' + IGNITION_LOCAL_SITES_PATH: '${PWD}' volumes: - '.:/var/www/html' networks: - - sail - depends_on: - - mysql - - redis + - vito + depends_on: { } worker: build: - context: ./docker/8.1 + context: ./vendor/laravel/sail/runtimes/8.2 dockerfile: Dockerfile args: WWWGROUP: '${WWWGROUP}' image: vito/app - command: php artisan queue:listen --timeout=600 --queue=default,ssh,ssh-long - extra_hosts: - - 'host.docker.internal:host-gateway' environment: WWWUSER: '${WWWUSER}' LARAVEL_SAIL: 1 XDEBUG_MODE: '${SAIL_XDEBUG_MODE:-off}' XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}' + IGNITION_LOCAL_SITES_PATH: '${PWD}' + command: 'php artisan queue:listen --sleep=3 --tries=1 --timeout=0' + restart: unless-stopped volumes: - '.:/var/www/html' networks: - - sail - restart: unless-stopped + - vito depends_on: - - mysql - - redis - mysql: - image: 'mysql/mysql-server:8.0' - ports: - - '${FORWARD_DB_PORT:-3306}:3306' - environment: - MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}' - MYSQL_ROOT_HOST: "%" - MYSQL_DATABASE: '${DB_DATABASE}' - MYSQL_USER: '${DB_USERNAME}' - MYSQL_PASSWORD: '${DB_PASSWORD}' - MYSQL_ALLOW_EMPTY_PASSWORD: 1 - volumes: - - 'sail-mysql:/var/lib/mysql' - - './vendor/laravel/sail/database/mysql/create-testing-database.sh:/docker-entrypoint-initdb.d/10-create-testing-database.sh' - networks: - - sail - healthcheck: - test: [ "CMD", "mysqladmin", "ping", "-p${DB_PASSWORD}" ] - retries: 3 - timeout: 5s - redis: - image: redis:latest - networks: - - sail + - app networks: - sail: + vito: driver: bridge -volumes: - sail-mysql: - driver: local diff --git a/docker/8.1/Dockerfile b/docker/8.1/Dockerfile deleted file mode 100644 index 725c9d9..0000000 --- a/docker/8.1/Dockerfile +++ /dev/null @@ -1,61 +0,0 @@ -FROM ubuntu:22.04 - -LABEL maintainer="Taylor Otwell" - -ARG WWWGROUP -ARG NODE_VERSION=18 -ARG POSTGRES_VERSION=15 - -WORKDIR /var/www/html - -ENV DEBIAN_FRONTEND noninteractive -ENV TZ=UTC - -RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone - -RUN apt-get update \ - && mkdir -p /etc/apt/keyrings \ - && apt-get install -y gnupg gosu curl ca-certificates zip unzip git supervisor sqlite3 libcap2-bin libpng-dev python2 dnsutils librsvg2-bin fswatch \ - && curl -sS 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x14aa40ec0831756756d7f66c4f4ea0aae5267a6c' | gpg --dearmor | tee /usr/share/keyrings/ppa_ondrej_php.gpg > /dev/null \ - && echo "deb [signed-by=/usr/share/keyrings/ppa_ondrej_php.gpg] https://ppa.launchpadcontent.net/ondrej/php/ubuntu jammy main" > /etc/apt/sources.list.d/ppa_ondrej_php.list \ - && apt-get update \ - && apt-get install -y php8.1-cli php8.1-dev \ - php8.1-pgsql php8.1-sqlite3 php8.1-gd php8.1-imagick \ - php8.1-curl \ - php8.1-imap php8.1-mysql php8.1-mbstring php8.1-ssh2\ - php8.1-xml php8.1-zip php8.1-bcmath php8.1-soap \ - php8.1-intl php8.1-readline \ - php8.1-ldap \ - php8.1-msgpack php8.1-igbinary php8.1-redis php8.1-swoole \ - php8.1-memcached php8.1-pcov php8.1-xdebug \ - && curl -sLS https://getcomposer.org/installer | php -- --install-dir=/usr/bin/ --filename=composer \ - && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \ - && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_VERSION.x nodistro main" > /etc/apt/sources.list.d/nodesource.list \ - && apt-get update \ - && apt-get install -y nodejs \ - && npm install -g npm \ - && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | tee /usr/share/keyrings/yarn.gpg >/dev/null \ - && echo "deb [signed-by=/usr/share/keyrings/yarn.gpg] https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ - && curl -sS https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /usr/share/keyrings/pgdg.gpg >/dev/null \ - && echo "deb [signed-by=/usr/share/keyrings/pgdg.gpg] http://apt.postgresql.org/pub/repos/apt jammy-pgdg main" > /etc/apt/sources.list.d/pgdg.list \ - && apt-get update \ - && apt-get install -y yarn \ - && apt-get install -y mysql-client \ - && apt-get install -y postgresql-client-$POSTGRES_VERSION \ - && apt-get -y autoremove \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* - -RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.1 - -RUN groupadd --force -g $WWWGROUP sail -RUN useradd -ms /bin/bash --no-user-group -g $WWWGROUP -u 1337 sail - -COPY start-container /usr/local/bin/start-container -COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf -COPY php.ini /etc/php/8.1/cli/conf.d/99-sail.ini -RUN chmod +x /usr/local/bin/start-container - -EXPOSE 8000 - -ENTRYPOINT ["start-container"] diff --git a/docker/8.1/php.ini b/docker/8.1/php.ini deleted file mode 100644 index 66d04d5..0000000 --- a/docker/8.1/php.ini +++ /dev/null @@ -1,4 +0,0 @@ -[PHP] -post_max_size = 100M -upload_max_filesize = 100M -variables_order = EGPCS diff --git a/docker/8.1/start-container b/docker/8.1/start-container deleted file mode 100644 index b864399..0000000 --- a/docker/8.1/start-container +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash - -if [ ! -z "$WWWUSER" ]; then - usermod -u $WWWUSER sail -fi - -if [ ! -d /.composer ]; then - mkdir /.composer -fi - -chmod -R ugo+rw /.composer - -if [ $# -gt 0 ]; then - exec gosu $WWWUSER "$@" -else - exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf -fi diff --git a/docker/8.1/supervisord.conf b/docker/8.1/supervisord.conf deleted file mode 100644 index 9d28479..0000000 --- a/docker/8.1/supervisord.conf +++ /dev/null @@ -1,14 +0,0 @@ -[supervisord] -nodaemon=true -user=root -logfile=/var/log/supervisor/supervisord.log -pidfile=/var/run/supervisord.pid - -[program:php] -command=/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan serve --host=0.0.0.0 --port=80 -user=sail -environment=LARAVEL_SAIL="1" -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 diff --git a/phpunit.xml b/phpunit.xml index 2d8b792..1e628e6 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -16,7 +16,7 @@ - + diff --git a/public/build/assets/app-5db42bc3.css b/public/build/assets/app-5db42bc3.css deleted file mode 100644 index c4d4248..0000000 --- a/public/build/assets/app-5db42bc3.css +++ /dev/null @@ -1 +0,0 @@ -.toast-title{font-weight:700}.toast-message{-ms-word-wrap:break-word;word-wrap:break-word}.toast-message a,.toast-message label{color:#fff}.toast-message a:hover{color:#ccc;text-decoration:none}.toast-close-button{position:relative;right:-.3em;top:-.3em;float:right;font-size:20px;font-weight:700;color:#fff;-webkit-text-shadow:0 1px 0 #ffffff;text-shadow:0 1px 0 #ffffff;opacity:.8;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=80);filter:alpha(opacity=80);line-height:1}.toast-close-button:hover,.toast-close-button:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.4;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=40);filter:alpha(opacity=40)}.rtl .toast-close-button{left:-.3em;float:left;right:.3em}button.toast-close-button{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.toast-top-center{top:0;right:0;width:100%}.toast-bottom-center{bottom:0;right:0;width:100%}.toast-top-full-width{top:0;right:0;width:100%}.toast-bottom-full-width{bottom:0;right:0;width:100%}.toast-top-left{top:12px;left:12px}.toast-top-right{top:12px;right:12px}.toast-bottom-right{right:12px;bottom:12px}.toast-bottom-left{bottom:12px;left:12px}#toast-container{position:fixed;z-index:999999;pointer-events:none}#toast-container *{box-sizing:border-box}#toast-container>div{border-radius:.5rem;position:relative;pointer-events:auto;overflow:hidden;margin:0 0 6px;padding:15px 15px 15px 50px;width:300px;background-position:15px center;background-repeat:no-repeat;color:#fff}#toast-container>div.rtl{direction:rtl;padding:15px 50px 15px 15px;background-position:right 15px center}#toast-container>div:hover{cursor:pointer;--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}#toast-container>.toast-info{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=)!important}#toast-container>.toast-error{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=)!important}#toast-container>.toast-success{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==)!important}#toast-container>.toast-warning{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=)!important}#toast-container.toast-top-center>div,#toast-container.toast-bottom-center>div{width:300px;margin-left:auto;margin-right:auto}#toast-container.toast-top-full-width>div,#toast-container.toast-bottom-full-width>div{width:96%;margin-left:auto;margin-right:auto}.toast{background-color:#030303}.toast-success{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity))}.toast-error{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity))}.toast-info{--tw-bg-opacity: 1;background-color:rgb(99 102 241 / var(--tw-bg-opacity))}.toast-warning{--tw-bg-opacity: 1;background-color:rgb(234 179 8 / var(--tw-bg-opacity))}.toast-progress{position:absolute;left:0;bottom:0;height:4px;background-color:#000;opacity:.4;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=40);filter:alpha(opacity=40)}@media all and (max-width: 240px){#toast-container>div{padding:8px 8px 8px 50px;width:11em}#toast-container>div.rtl{padding:8px 50px 8px 8px}#toast-container .toast-close-button{right:-.2em;top:-.2em}#toast-container .rtl .toast-close-button{left:-.2em;right:.2em}}@media all and (min-width: 241px) and (max-width: 480px){#toast-container>div{padding:8px 8px 8px 50px;width:18em}#toast-container>div.rtl{padding:8px 50px 8px 8px}#toast-container .toast-close-button{right:-.2em;top:-.2em}#toast-container .rtl .toast-close-button{left:-.2em;right:.2em}}@media all and (min-width: 481px) and (max-width: 768px){#toast-container>div{padding:15px 15px 15px 50px;width:25em}#toast-container>div.rtl{padding:15px 50px 15px 15px}}/*! tailwindcss v3.3.1 | MIT License | https://tailwindcss.com*/*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e2e8f0}:before,:after{--tw-content: ""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Figtree,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#94a3b8}input::placeholder,textarea::placeholder{opacity:1;color:#94a3b8}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}[type=text],[type=email],[type=url],[type=password],[type=number],[type=date],[type=datetime-local],[type=month],[type=search],[type=tel],[type=time],[type=week],[multiple],textarea,select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#64748b;border-width:1px;border-radius:0;padding:.5rem .75rem;font-size:1rem;line-height:1.5rem;--tw-shadow: 0 0 #0000}[type=text]:focus,[type=email]:focus,[type=url]:focus,[type=password]:focus,[type=number]:focus,[type=date]:focus,[type=datetime-local]:focus,[type=month]:focus,[type=search]:focus,[type=tel]:focus,[type=time]:focus,[type=week]:focus,[multiple]:focus,textarea:focus,select:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: #2563eb;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);border-color:#2563eb}input::-moz-placeholder,textarea::-moz-placeholder{color:#64748b;opacity:1}input::placeholder,textarea::placeholder{color:#64748b;opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em}::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field{padding-top:0;padding-bottom:0}select{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%2364748b' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact;print-color-adjust:exact}[multiple]{background-image:initial;background-position:initial;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset;print-color-adjust:unset}[type=checkbox],[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:0;-webkit-print-color-adjust:exact;print-color-adjust:exact;display:inline-block;vertical-align:middle;background-origin:border-box;-webkit-user-select:none;-moz-user-select:none;user-select:none;flex-shrink:0;height:1rem;width:1rem;color:#2563eb;background-color:#fff;border-color:#64748b;border-width:1px;--tw-shadow: 0 0 #0000}[type=checkbox]{border-radius:0}[type=radio]{border-radius:100%}[type=checkbox]:focus,[type=radio]:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 2px;--tw-ring-offset-color: #fff;--tw-ring-color: #2563eb;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}[type=checkbox]:checked,[type=radio]:checked{border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:center;background-repeat:no-repeat}[type=checkbox]:checked{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e")}[type=radio]:checked{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e")}[type=checkbox]:checked:hover,[type=checkbox]:checked:focus,[type=radio]:checked:hover,[type=radio]:checked:focus{border-color:transparent;background-color:currentColor}[type=checkbox]:indeterminate{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e");border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:center;background-repeat:no-repeat}[type=checkbox]:indeterminate:hover,[type=checkbox]:indeterminate:focus{border-color:transparent;background-color:currentColor}[type=file]{background:unset;border-color:inherit;border-width:0;border-radius:0;padding:0;font-size:unset;line-height:inherit}[type=file]:focus{outline:1px solid ButtonText;outline:1px auto -webkit-focus-ring-color}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.container{width:100%}@media (min-width: 640px){.container{max-width:640px}}@media (min-width: 768px){.container{max-width:768px}}@media (min-width: 1024px){.container{max-width:1024px}}@media (min-width: 1280px){.container{max-width:1280px}}@media (min-width: 1536px){.container{max-width:1536px}}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{top:0;right:0;bottom:0;left:0}.inset-y-0{top:0;bottom:0}.left-0{left:0}.right-0{right:0}.top-0{top:0}.top-1{top:.25rem}.z-0{z-index:0}.z-10{z-index:10}.z-50{z-index:50}.float-right{float:right}.mx-5{margin-left:1.25rem;margin-right:1.25rem}.mx-auto{margin-left:auto;margin-right:auto}.-ml-px{margin-left:-1px}.-mt-px{margin-top:-1px}.mb-1{margin-bottom:.25rem}.mb-10{margin-bottom:2.5rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.mb-6{margin-bottom:1.5rem}.ml-1{margin-left:.25rem}.ml-12{margin-left:3rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.ml-4{margin-left:1rem}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mt-1{margin-top:.25rem}.mt-10{margin-top:2.5rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.mt-6{margin-top:1.5rem}.mt-8{margin-top:2rem}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-10{height:2.5rem}.h-16{height:4rem}.h-20{height:5rem}.h-4{height:1rem}.h-48{height:12rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-96{height:24rem}.max-h-\[350px\]{max-height:350px}.min-h-screen{min-height:100vh}.w-1{width:.25rem}.w-10{width:2.5rem}.w-4{width:1rem}.w-48{width:12rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-64{width:16rem}.w-7{width:1.75rem}.w-8{width:2rem}.w-9{width:2.25rem}.w-auto{width:auto}.w-full{width:100%}.w-max{width:-moz-max-content;width:max-content}.min-w-full{min-width:100%}.min-w-max{min-width:-moz-max-content;min-width:max-content}.max-w-6xl{max-width:72rem}.max-w-7xl{max-width:80rem}.max-w-full{max-width:100%}.max-w-xl{max-width:36rem}.flex-1{flex:1 1 0%}.flex-none{flex:none}.flex-grow{flex-grow:1}.origin-top{transform-origin:top}.origin-top-left{transform-origin:top left}.origin-top-right{transform-origin:top right}.translate-y-0{--tw-translate-y: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-4{--tw-translate-y: 1rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-100{--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-95{--tw-scale-x: .95;--tw-scale-y: .95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-items-center{justify-items:center}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-5{gap:1.25rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-10>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(2.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2.5rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-y-hidden{overflow-y:hidden}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-line{white-space:pre-line}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-sm{border-radius:.125rem}.rounded-b-md{border-bottom-right-radius:.375rem;border-bottom-left-radius:.375rem}.rounded-l-md{border-top-left-radius:.375rem;border-bottom-left-radius:.375rem}.rounded-r-md{border-top-right-radius:.375rem;border-bottom-right-radius:.375rem}.rounded-t-md{border-top-left-radius:.375rem;border-top-right-radius:.375rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-l{border-left-width:1px}.border-l-4{border-left-width:4px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-gray-100{--tw-border-opacity: 1;border-color:rgb(241 245 249 / var(--tw-border-opacity))}.border-gray-200{--tw-border-opacity: 1;border-color:rgb(226 232 240 / var(--tw-border-opacity))}.border-gray-300{--tw-border-opacity: 1;border-color:rgb(203 213 225 / var(--tw-border-opacity))}.border-gray-400{--tw-border-opacity: 1;border-color:rgb(148 163 184 / var(--tw-border-opacity))}.border-primary-200{--tw-border-opacity: 1;border-color:rgb(199 210 254 / var(--tw-border-opacity))}.border-primary-400{--tw-border-opacity: 1;border-color:rgb(129 140 248 / var(--tw-border-opacity))}.border-primary-600{--tw-border-opacity: 1;border-color:rgb(79 70 229 / var(--tw-border-opacity))}.border-transparent{border-color:transparent}.border-yellow-500{--tw-border-opacity: 1;border-color:rgb(234 179 8 / var(--tw-border-opacity))}.border-t-transparent{border-top-color:transparent}.bg-gray-100{--tw-bg-opacity: 1;background-color:rgb(241 245 249 / var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity: 1;background-color:rgb(226 232 240 / var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity))}.bg-gray-500{--tw-bg-opacity: 1;background-color:rgb(100 116 139 / var(--tw-bg-opacity))}.bg-gray-800{--tw-bg-opacity: 1;background-color:rgb(30 41 59 / var(--tw-bg-opacity))}.bg-gray-900{--tw-bg-opacity: 1;background-color:rgb(15 23 42 / var(--tw-bg-opacity))}.bg-green-50{--tw-bg-opacity: 1;background-color:rgb(240 253 244 / var(--tw-bg-opacity))}.bg-primary-200{--tw-bg-opacity: 1;background-color:rgb(199 210 254 / var(--tw-bg-opacity))}.bg-primary-50{--tw-bg-opacity: 1;background-color:rgb(238 242 255 / var(--tw-bg-opacity))}.bg-primary-500{--tw-bg-opacity: 1;background-color:rgb(99 102 241 / var(--tw-bg-opacity))}.bg-primary-600{--tw-bg-opacity: 1;background-color:rgb(79 70 229 / var(--tw-bg-opacity))}.bg-red-50{--tw-bg-opacity: 1;background-color:rgb(254 242 242 / var(--tw-bg-opacity))}.bg-red-600{--tw-bg-opacity: 1;background-color:rgb(220 38 38 / var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity))}.bg-yellow-100{--tw-bg-opacity: 1;background-color:rgb(254 249 195 / var(--tw-bg-opacity))}.bg-yellow-50{--tw-bg-opacity: 1;background-color:rgb(254 252 232 / var(--tw-bg-opacity))}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.p-7{padding:1.75rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-8{padding-left:2rem;padding-right:2rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.pb-1{padding-bottom:.25rem}.pb-2{padding-bottom:.5rem}.pl-3{padding-left:.75rem}.pr-10{padding-right:2.5rem}.pr-2{padding-right:.5rem}.pr-3{padding-right:.75rem}.pr-4{padding-right:1rem}.pt-1{padding-top:.25rem}.pt-3{padding-top:.75rem}.pt-6{padding-top:1.5rem}.pt-8{padding-top:2rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-middle{vertical-align:middle}.font-sans{font-family:Figtree,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji"}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-extrabold{font-weight:800}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.capitalize{text-transform:capitalize}.leading-4{line-height:1rem}.leading-5{line-height:1.25rem}.leading-7{line-height:1.75rem}.tracking-wider{letter-spacing:.05em}.text-black{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity))}.text-gray-100{--tw-text-opacity: 1;color:rgb(241 245 249 / var(--tw-text-opacity))}.text-gray-200{--tw-text-opacity: 1;color:rgb(226 232 240 / var(--tw-text-opacity))}.text-gray-300{--tw-text-opacity: 1;color:rgb(203 213 225 / var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity))}.text-gray-50{--tw-text-opacity: 1;color:rgb(248 250 252 / var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity: 1;color:rgb(71 85 105 / var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity: 1;color:rgb(51 65 85 / var(--tw-text-opacity))}.text-gray-800{--tw-text-opacity: 1;color:rgb(30 41 59 / var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity: 1;color:rgb(15 23 42 / var(--tw-text-opacity))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity))}.text-green-600{--tw-text-opacity: 1;color:rgb(22 163 74 / var(--tw-text-opacity))}.text-indigo-600{--tw-text-opacity: 1;color:rgb(79 70 229 / var(--tw-text-opacity))}.text-primary-500{--tw-text-opacity: 1;color:rgb(99 102 241 / var(--tw-text-opacity))}.text-primary-600{--tw-text-opacity: 1;color:rgb(79 70 229 / var(--tw-text-opacity))}.text-primary-700{--tw-text-opacity: 1;color:rgb(67 56 202 / var(--tw-text-opacity))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity))}.text-red-600{--tw-text-opacity: 1;color:rgb(220 38 38 / var(--tw-text-opacity))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.text-yellow-500{--tw-text-opacity: 1;color:rgb(234 179 8 / var(--tw-text-opacity))}.text-yellow-700{--tw-text-opacity: 1;color:rgb(161 98 7 / var(--tw-text-opacity))}.underline{text-decoration-line:underline}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-25{opacity:.25}.opacity-75{opacity:.75}.shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-none{--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline-0{outline-width:0px}.ring-1{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-black{--tw-ring-opacity: 1;--tw-ring-color: rgb(0 0 0 / var(--tw-ring-opacity))}.ring-gray-300{--tw-ring-opacity: 1;--tw-ring-color: rgb(203 213 225 / var(--tw-ring-opacity))}.ring-opacity-5{--tw-ring-opacity: .05}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-100{transition-duration:.1s}.duration-150{transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-75{transition-duration:75ms}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}[x-cloak]{display:none!important}.hover\:border-gray-300:hover{--tw-border-opacity: 1;border-color:rgb(203 213 225 / var(--tw-border-opacity))}.hover\:bg-gray-100:hover{--tw-bg-opacity: 1;background-color:rgb(241 245 249 / var(--tw-bg-opacity))}.hover\:bg-gray-50:hover{--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity))}.hover\:bg-gray-900:hover{--tw-bg-opacity: 1;background-color:rgb(15 23 42 / var(--tw-bg-opacity))}.hover\:bg-primary-600:hover{--tw-bg-opacity: 1;background-color:rgb(79 70 229 / var(--tw-bg-opacity))}.hover\:bg-primary-700:hover{--tw-bg-opacity: 1;background-color:rgb(67 56 202 / var(--tw-bg-opacity))}.hover\:bg-red-500:hover{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity))}.hover\:text-gray-400:hover{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity))}.hover\:text-gray-500:hover{--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity))}.hover\:text-gray-700:hover{--tw-text-opacity: 1;color:rgb(51 65 85 / var(--tw-text-opacity))}.hover\:text-gray-800:hover{--tw-text-opacity: 1;color:rgb(30 41 59 / var(--tw-text-opacity))}.hover\:text-gray-900:hover{--tw-text-opacity: 1;color:rgb(15 23 42 / var(--tw-text-opacity))}.hover\:text-white:hover{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.hover\:opacity-50:hover{opacity:.5}.focus\:z-10:focus{z-index:10}.focus\:border-blue-300:focus{--tw-border-opacity: 1;border-color:rgb(147 197 253 / var(--tw-border-opacity))}.focus\:border-gray-300:focus{--tw-border-opacity: 1;border-color:rgb(203 213 225 / var(--tw-border-opacity))}.focus\:border-primary-300:focus{--tw-border-opacity: 1;border-color:rgb(165 180 252 / var(--tw-border-opacity))}.focus\:border-primary-500:focus{--tw-border-opacity: 1;border-color:rgb(99 102 241 / var(--tw-border-opacity))}.focus\:border-primary-700:focus{--tw-border-opacity: 1;border-color:rgb(67 56 202 / var(--tw-border-opacity))}.focus\:border-red-700:focus{--tw-border-opacity: 1;border-color:rgb(185 28 28 / var(--tw-border-opacity))}.focus\:bg-gray-100:focus{--tw-bg-opacity: 1;background-color:rgb(241 245 249 / var(--tw-bg-opacity))}.focus\:bg-gray-50:focus{--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity))}.focus\:bg-primary-100:focus{--tw-bg-opacity: 1;background-color:rgb(224 231 255 / var(--tw-bg-opacity))}.focus\:text-gray-700:focus{--tw-text-opacity: 1;color:rgb(51 65 85 / var(--tw-text-opacity))}.focus\:text-gray-800:focus{--tw-text-opacity: 1;color:rgb(30 41 59 / var(--tw-text-opacity))}.focus\:text-primary-800:focus{--tw-text-opacity: 1;color:rgb(55 48 163 / var(--tw-text-opacity))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-1:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-gray-400:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(148 163 184 / var(--tw-ring-opacity))}.focus\:ring-gray-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(100 116 139 / var(--tw-ring-opacity))}.focus\:ring-gray-700:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(51 65 85 / var(--tw-ring-opacity))}.focus\:ring-indigo-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(99 102 241 / var(--tw-ring-opacity))}.focus\:ring-primary-200:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(199 210 254 / var(--tw-ring-opacity))}.focus\:ring-primary-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(99 102 241 / var(--tw-ring-opacity))}.focus\:ring-red-200:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(254 202 202 / var(--tw-ring-opacity))}.focus\:ring-opacity-50:focus{--tw-ring-opacity: .5}.focus\:ring-offset-2:focus{--tw-ring-offset-width: 2px}.active\:bg-gray-100:active{--tw-bg-opacity: 1;background-color:rgb(241 245 249 / var(--tw-bg-opacity))}.active\:bg-primary-700:active{--tw-bg-opacity: 1;background-color:rgb(67 56 202 / var(--tw-bg-opacity))}.active\:bg-red-600:active{--tw-bg-opacity: 1;background-color:rgb(220 38 38 / var(--tw-bg-opacity))}.active\:text-gray-500:active{--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity))}.active\:text-gray-700:active{--tw-text-opacity: 1;color:rgb(51 65 85 / var(--tw-text-opacity))}.disabled\:opacity-25:disabled{opacity:.25}:is(.dark .dark\:border-r-2){border-right-width:2px}:is(.dark .dark\:border-gray-500){--tw-border-opacity: 1;border-color:rgb(100 116 139 / var(--tw-border-opacity))}:is(.dark .dark\:border-gray-600){--tw-border-opacity: 1;border-color:rgb(71 85 105 / var(--tw-border-opacity))}:is(.dark .dark\:border-gray-700){--tw-border-opacity: 1;border-color:rgb(51 65 85 / var(--tw-border-opacity))}:is(.dark .dark\:border-gray-800){--tw-border-opacity: 1;border-color:rgb(30 41 59 / var(--tw-border-opacity))}:is(.dark .dark\:border-gray-900){--tw-border-opacity: 1;border-color:rgb(15 23 42 / var(--tw-border-opacity))}:is(.dark .dark\:border-primary-600){--tw-border-opacity: 1;border-color:rgb(79 70 229 / var(--tw-border-opacity))}:is(.dark .dark\:border-t-transparent){border-top-color:transparent}:is(.dark .dark\:border-opacity-20){--tw-border-opacity: .2}:is(.dark .dark\:bg-gray-500){--tw-bg-opacity: 1;background-color:rgb(100 116 139 / var(--tw-bg-opacity))}:is(.dark .dark\:bg-gray-700){--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity))}:is(.dark .dark\:bg-gray-800){--tw-bg-opacity: 1;background-color:rgb(30 41 59 / var(--tw-bg-opacity))}:is(.dark .dark\:bg-gray-800\/50){background-color:#1e293b80}:is(.dark .dark\:bg-gray-900){--tw-bg-opacity: 1;background-color:rgb(15 23 42 / var(--tw-bg-opacity))}:is(.dark .dark\:bg-green-500){--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity))}:is(.dark .dark\:bg-primary-500){--tw-bg-opacity: 1;background-color:rgb(99 102 241 / var(--tw-bg-opacity))}:is(.dark .dark\:bg-primary-900\/50){background-color:#312e8180}:is(.dark .dark\:bg-red-500){--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity))}:is(.dark .dark\:bg-yellow-500){--tw-bg-opacity: 1;background-color:rgb(234 179 8 / var(--tw-bg-opacity))}:is(.dark .dark\:bg-opacity-10){--tw-bg-opacity: .1}:is(.dark .dark\:bg-opacity-20){--tw-bg-opacity: .2}:is(.dark .dark\:bg-opacity-30){--tw-bg-opacity: .3}:is(.dark .dark\:bg-opacity-70){--tw-bg-opacity: .7}:is(.dark .dark\:text-gray-100){--tw-text-opacity: 1;color:rgb(241 245 249 / var(--tw-text-opacity))}:is(.dark .dark\:text-gray-200){--tw-text-opacity: 1;color:rgb(226 232 240 / var(--tw-text-opacity))}:is(.dark .dark\:text-gray-300){--tw-text-opacity: 1;color:rgb(203 213 225 / var(--tw-text-opacity))}:is(.dark .dark\:text-gray-400){--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity))}:is(.dark .dark\:text-green-400){--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity))}:is(.dark .dark\:text-primary-300){--tw-text-opacity: 1;color:rgb(165 180 252 / var(--tw-text-opacity))}:is(.dark .dark\:text-red-400){--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity))}:is(.dark .dark\:text-white){--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}:is(.dark .dark\:text-yellow-500){--tw-text-opacity: 1;color:rgb(234 179 8 / var(--tw-text-opacity))}:is(.dark .dark\:hover\:border-gray-600:hover){--tw-border-opacity: 1;border-color:rgb(71 85 105 / var(--tw-border-opacity))}:is(.dark .dark\:hover\:border-gray-700:hover){--tw-border-opacity: 1;border-color:rgb(51 65 85 / var(--tw-border-opacity))}:is(.dark .dark\:hover\:bg-gray-700:hover){--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity))}:is(.dark .dark\:hover\:bg-gray-800:hover){--tw-bg-opacity: 1;background-color:rgb(30 41 59 / var(--tw-bg-opacity))}:is(.dark .dark\:hover\:text-gray-100:hover){--tw-text-opacity: 1;color:rgb(241 245 249 / var(--tw-text-opacity))}:is(.dark .dark\:hover\:text-gray-200:hover){--tw-text-opacity: 1;color:rgb(226 232 240 / var(--tw-text-opacity))}:is(.dark .dark\:hover\:text-gray-300:hover){--tw-text-opacity: 1;color:rgb(203 213 225 / var(--tw-text-opacity))}:is(.dark .dark\:focus\:border-gray-600:focus){--tw-border-opacity: 1;border-color:rgb(71 85 105 / var(--tw-border-opacity))}:is(.dark .dark\:focus\:border-gray-700:focus){--tw-border-opacity: 1;border-color:rgb(51 65 85 / var(--tw-border-opacity))}:is(.dark .dark\:focus\:border-primary-300:focus){--tw-border-opacity: 1;border-color:rgb(165 180 252 / var(--tw-border-opacity))}:is(.dark .dark\:focus\:border-primary-600:focus){--tw-border-opacity: 1;border-color:rgb(79 70 229 / var(--tw-border-opacity))}:is(.dark .dark\:focus\:border-primary-700:focus){--tw-border-opacity: 1;border-color:rgb(67 56 202 / var(--tw-border-opacity))}:is(.dark .dark\:focus\:bg-gray-700:focus){--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity))}:is(.dark .dark\:focus\:bg-primary-900:focus){--tw-bg-opacity: 1;background-color:rgb(49 46 129 / var(--tw-bg-opacity))}:is(.dark .dark\:focus\:text-gray-200:focus){--tw-text-opacity: 1;color:rgb(226 232 240 / var(--tw-text-opacity))}:is(.dark .dark\:focus\:text-gray-300:focus){--tw-text-opacity: 1;color:rgb(203 213 225 / var(--tw-text-opacity))}:is(.dark .dark\:focus\:text-primary-200:focus){--tw-text-opacity: 1;color:rgb(199 210 254 / var(--tw-text-opacity))}:is(.dark .dark\:focus\:ring-indigo-600:focus){--tw-ring-opacity: 1;--tw-ring-color: rgb(79 70 229 / var(--tw-ring-opacity))}:is(.dark .dark\:focus\:ring-primary-600:focus){--tw-ring-opacity: 1;--tw-ring-color: rgb(79 70 229 / var(--tw-ring-opacity))}:is(.dark .dark\:focus\:ring-primary-700:focus){--tw-ring-opacity: 1;--tw-ring-color: rgb(67 56 202 / var(--tw-ring-opacity))}:is(.dark .dark\:focus\:ring-opacity-40:focus){--tw-ring-opacity: .4}:is(.dark .dark\:focus\:ring-offset-gray-800:focus){--tw-ring-offset-color: #1e293b}@media (min-width: 640px){.sm\:mx-auto{margin-left:auto;margin-right:auto}.sm\:flex{display:flex}.sm\:hidden{display:none}.sm\:w-full{width:100%}.sm\:max-w-2xl{max-width:42rem}.sm\:max-w-3xl{max-width:48rem}.sm\:max-w-4xl{max-width:56rem}.sm\:max-w-lg{max-width:32rem}.sm\:max-w-md{max-width:28rem}.sm\:max-w-sm{max-width:24rem}.sm\:max-w-xl{max-width:36rem}.sm\:flex-1{flex:1 1 0%}.sm\:translate-y-0{--tw-translate-y: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.sm\:scale-100{--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.sm\:scale-95{--tw-scale-x: .95;--tw-scale-y: .95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.sm\:items-center{align-items:center}.sm\:justify-start{justify-content:flex-start}.sm\:justify-center{justify-content:center}.sm\:justify-between{justify-content:space-between}.sm\:rounded-md{border-radius:.375rem}.sm\:rounded-bl-md{border-bottom-left-radius:.375rem}.sm\:rounded-br-md{border-bottom-right-radius:.375rem}.sm\:rounded-tl-md{border-top-left-radius:.375rem}.sm\:rounded-tr-md{border-top-right-radius:.375rem}.sm\:p-6{padding:1.5rem}.sm\:px-0{padding-left:0;padding-right:0}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:pt-0{padding-top:0}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}@media (min-width: 768px){.md\:col-span-1{grid-column:span 1 / span 1}.md\:block{display:block}.md\:justify-start{justify-content:flex-start}.md\:text-left{text-align:left}}@media (min-width: 1024px){.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:px-8{padding-left:2rem;padding-right:2rem}} diff --git a/public/build/assets/app-e2e337dc.css b/public/build/assets/app-e2e337dc.css new file mode 100644 index 0000000..9aea730 --- /dev/null +++ b/public/build/assets/app-e2e337dc.css @@ -0,0 +1 @@ +.toast-title{font-weight:700}.toast-message{-ms-word-wrap:break-word;word-wrap:break-word}.toast-message a,.toast-message label{color:#fff}.toast-message a:hover{color:#ccc;text-decoration:none}.toast-close-button{position:relative;right:-.3em;top:-.3em;float:right;font-size:20px;font-weight:700;color:#fff;-webkit-text-shadow:0 1px 0 #ffffff;text-shadow:0 1px 0 #ffffff;opacity:.8;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=80);filter:alpha(opacity=80);line-height:1}.toast-close-button:hover,.toast-close-button:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.4;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=40);filter:alpha(opacity=40)}.rtl .toast-close-button{left:-.3em;float:left;right:.3em}button.toast-close-button{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.toast-top-center{top:0;right:0;width:100%}.toast-bottom-center{bottom:0;right:0;width:100%}.toast-top-full-width{top:0;right:0;width:100%}.toast-bottom-full-width{bottom:0;right:0;width:100%}.toast-top-left{top:12px;left:12px}.toast-top-right{top:12px;right:12px}.toast-bottom-right{right:12px;bottom:12px}.toast-bottom-left{bottom:12px;left:12px}#toast-container{position:fixed;z-index:999999;pointer-events:none}#toast-container *{box-sizing:border-box}#toast-container>div{border-radius:.5rem;position:relative;pointer-events:auto;overflow:hidden;margin:0 0 6px;padding:15px 15px 15px 50px;width:300px;background-position:15px center;background-repeat:no-repeat;color:#fff}#toast-container>div.rtl{direction:rtl;padding:15px 50px 15px 15px;background-position:right 15px center}#toast-container>div:hover{cursor:pointer;--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}#toast-container>.toast-info{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=)!important}#toast-container>.toast-error{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=)!important}#toast-container>.toast-success{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==)!important}#toast-container>.toast-warning{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=)!important}#toast-container.toast-top-center>div,#toast-container.toast-bottom-center>div{width:300px;margin-left:auto;margin-right:auto}#toast-container.toast-top-full-width>div,#toast-container.toast-bottom-full-width>div{width:96%;margin-left:auto;margin-right:auto}.toast{background-color:#030303}.toast-success{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity))}.toast-error{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity))}.toast-info{--tw-bg-opacity: 1;background-color:rgb(99 102 241 / var(--tw-bg-opacity))}.toast-warning{--tw-bg-opacity: 1;background-color:rgb(234 179 8 / var(--tw-bg-opacity))}.toast-progress{position:absolute;left:0;bottom:0;height:4px;background-color:#000;opacity:.4;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=40);filter:alpha(opacity=40)}@media all and (max-width: 240px){#toast-container>div{padding:8px 8px 8px 50px;width:11em}#toast-container>div.rtl{padding:8px 50px 8px 8px}#toast-container .toast-close-button{right:-.2em;top:-.2em}#toast-container .rtl .toast-close-button{left:-.2em;right:.2em}}@media all and (min-width: 241px) and (max-width: 480px){#toast-container>div{padding:8px 8px 8px 50px;width:18em}#toast-container>div.rtl{padding:8px 50px 8px 8px}#toast-container .toast-close-button{right:-.2em;top:-.2em}#toast-container .rtl .toast-close-button{left:-.2em;right:.2em}}@media all and (min-width: 481px) and (max-width: 768px){#toast-container>div{padding:15px 15px 15px 50px;width:25em}#toast-container>div.rtl{padding:15px 50px 15px 15px}}/*! tailwindcss v3.3.1 | MIT License | https://tailwindcss.com*/*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e2e8f0}:before,:after{--tw-content: ""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Figtree,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#94a3b8}input::placeholder,textarea::placeholder{opacity:1;color:#94a3b8}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}[type=text],[type=email],[type=url],[type=password],[type=number],[type=date],[type=datetime-local],[type=month],[type=search],[type=tel],[type=time],[type=week],[multiple],textarea,select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#64748b;border-width:1px;border-radius:0;padding:.5rem .75rem;font-size:1rem;line-height:1.5rem;--tw-shadow: 0 0 #0000}[type=text]:focus,[type=email]:focus,[type=url]:focus,[type=password]:focus,[type=number]:focus,[type=date]:focus,[type=datetime-local]:focus,[type=month]:focus,[type=search]:focus,[type=tel]:focus,[type=time]:focus,[type=week]:focus,[multiple]:focus,textarea:focus,select:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: #2563eb;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);border-color:#2563eb}input::-moz-placeholder,textarea::-moz-placeholder{color:#64748b;opacity:1}input::placeholder,textarea::placeholder{color:#64748b;opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em}::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field{padding-top:0;padding-bottom:0}select{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%2364748b' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact;print-color-adjust:exact}[multiple]{background-image:initial;background-position:initial;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset;print-color-adjust:unset}[type=checkbox],[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:0;-webkit-print-color-adjust:exact;print-color-adjust:exact;display:inline-block;vertical-align:middle;background-origin:border-box;-webkit-user-select:none;-moz-user-select:none;user-select:none;flex-shrink:0;height:1rem;width:1rem;color:#2563eb;background-color:#fff;border-color:#64748b;border-width:1px;--tw-shadow: 0 0 #0000}[type=checkbox]{border-radius:0}[type=radio]{border-radius:100%}[type=checkbox]:focus,[type=radio]:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 2px;--tw-ring-offset-color: #fff;--tw-ring-color: #2563eb;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}[type=checkbox]:checked,[type=radio]:checked{border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:center;background-repeat:no-repeat}[type=checkbox]:checked{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e")}[type=radio]:checked{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e")}[type=checkbox]:checked:hover,[type=checkbox]:checked:focus,[type=radio]:checked:hover,[type=radio]:checked:focus{border-color:transparent;background-color:currentColor}[type=checkbox]:indeterminate{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e");border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:center;background-repeat:no-repeat}[type=checkbox]:indeterminate:hover,[type=checkbox]:indeterminate:focus{border-color:transparent;background-color:currentColor}[type=file]{background:unset;border-color:inherit;border-width:0;border-radius:0;padding:0;font-size:unset;line-height:inherit}[type=file]:focus{outline:1px solid ButtonText;outline:1px auto -webkit-focus-ring-color}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.container{width:100%}@media (min-width: 640px){.container{max-width:640px}}@media (min-width: 768px){.container{max-width:768px}}@media (min-width: 1024px){.container{max-width:1024px}}@media (min-width: 1280px){.container{max-width:1280px}}@media (min-width: 1536px){.container{max-width:1536px}}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{top:0;right:0;bottom:0;left:0}.inset-y-0{top:0;bottom:0}.left-0{left:0}.right-0{right:0}.top-0{top:0}.top-1{top:.25rem}.z-0{z-index:0}.z-10{z-index:10}.z-50{z-index:50}.float-right{float:right}.mx-5{margin-left:1.25rem;margin-right:1.25rem}.mx-auto{margin-left:auto;margin-right:auto}.-ml-px{margin-left:-1px}.-mt-px{margin-top:-1px}.mb-1{margin-bottom:.25rem}.mb-10{margin-bottom:2.5rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.mb-6{margin-bottom:1.5rem}.ml-1{margin-left:.25rem}.ml-12{margin-left:3rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.ml-4{margin-left:1rem}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mt-1{margin-top:.25rem}.mt-10{margin-top:2.5rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.mt-6{margin-top:1.5rem}.mt-8{margin-top:2rem}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-10{height:2.5rem}.h-16{height:4rem}.h-20{height:5rem}.h-4{height:1rem}.h-48{height:12rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-\[500px\]{height:500px}.h-full{height:100%}.max-h-\[350px\]{max-height:350px}.max-h-screen{max-height:100vh}.min-h-\[400px\]{min-height:400px}.min-h-screen{min-height:100vh}.w-1{width:.25rem}.w-10{width:2.5rem}.w-4{width:1rem}.w-48{width:12rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-64{width:16rem}.w-7{width:1.75rem}.w-8{width:2rem}.w-9{width:2.25rem}.w-auto{width:auto}.w-full{width:100%}.w-max{width:-moz-max-content;width:max-content}.min-w-full{min-width:100%}.min-w-max{min-width:-moz-max-content;min-width:max-content}.max-w-6xl{max-width:72rem}.max-w-7xl{max-width:80rem}.max-w-\[500px\]{max-width:500px}.max-w-full{max-width:100%}.max-w-xl{max-width:36rem}.flex-1{flex:1 1 0%}.flex-none{flex:none}.flex-grow{flex-grow:1}.origin-top{transform-origin:top}.origin-top-left{transform-origin:top left}.origin-top-right{transform-origin:top right}.translate-y-0{--tw-translate-y: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-4{--tw-translate-y: 1rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-100{--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-95{--tw-scale-x: .95;--tw-scale-y: .95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-items-center{justify-items:center}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-5{gap:1.25rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-10>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(2.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2.5rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-y-hidden{overflow-y:hidden}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-line{white-space:pre-line}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-sm{border-radius:.125rem}.rounded-b-md{border-bottom-right-radius:.375rem;border-bottom-left-radius:.375rem}.rounded-l-md{border-top-left-radius:.375rem;border-bottom-left-radius:.375rem}.rounded-r-md{border-top-right-radius:.375rem;border-bottom-right-radius:.375rem}.rounded-t-md{border-top-left-radius:.375rem;border-top-right-radius:.375rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-l{border-left-width:1px}.border-l-4{border-left-width:4px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-gray-100{--tw-border-opacity: 1;border-color:rgb(241 245 249 / var(--tw-border-opacity))}.border-gray-200{--tw-border-opacity: 1;border-color:rgb(226 232 240 / var(--tw-border-opacity))}.border-gray-300{--tw-border-opacity: 1;border-color:rgb(203 213 225 / var(--tw-border-opacity))}.border-gray-400{--tw-border-opacity: 1;border-color:rgb(148 163 184 / var(--tw-border-opacity))}.border-gray-500{--tw-border-opacity: 1;border-color:rgb(100 116 139 / var(--tw-border-opacity))}.border-green-500{--tw-border-opacity: 1;border-color:rgb(34 197 94 / var(--tw-border-opacity))}.border-primary-200{--tw-border-opacity: 1;border-color:rgb(199 210 254 / var(--tw-border-opacity))}.border-primary-400{--tw-border-opacity: 1;border-color:rgb(129 140 248 / var(--tw-border-opacity))}.border-primary-600{--tw-border-opacity: 1;border-color:rgb(79 70 229 / var(--tw-border-opacity))}.border-red-500{--tw-border-opacity: 1;border-color:rgb(239 68 68 / var(--tw-border-opacity))}.border-transparent{border-color:transparent}.border-yellow-500{--tw-border-opacity: 1;border-color:rgb(234 179 8 / var(--tw-border-opacity))}.border-t-transparent{border-top-color:transparent}.border-opacity-50{--tw-border-opacity: .5}.bg-gray-100{--tw-bg-opacity: 1;background-color:rgb(241 245 249 / var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity: 1;background-color:rgb(226 232 240 / var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity))}.bg-gray-500{--tw-bg-opacity: 1;background-color:rgb(100 116 139 / var(--tw-bg-opacity))}.bg-gray-800{--tw-bg-opacity: 1;background-color:rgb(30 41 59 / var(--tw-bg-opacity))}.bg-gray-900{--tw-bg-opacity: 1;background-color:rgb(15 23 42 / var(--tw-bg-opacity))}.bg-green-50{--tw-bg-opacity: 1;background-color:rgb(240 253 244 / var(--tw-bg-opacity))}.bg-primary-200{--tw-bg-opacity: 1;background-color:rgb(199 210 254 / var(--tw-bg-opacity))}.bg-primary-50{--tw-bg-opacity: 1;background-color:rgb(238 242 255 / var(--tw-bg-opacity))}.bg-primary-500{--tw-bg-opacity: 1;background-color:rgb(99 102 241 / var(--tw-bg-opacity))}.bg-primary-600{--tw-bg-opacity: 1;background-color:rgb(79 70 229 / var(--tw-bg-opacity))}.bg-red-50{--tw-bg-opacity: 1;background-color:rgb(254 242 242 / var(--tw-bg-opacity))}.bg-red-600{--tw-bg-opacity: 1;background-color:rgb(220 38 38 / var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity))}.bg-yellow-100{--tw-bg-opacity: 1;background-color:rgb(254 249 195 / var(--tw-bg-opacity))}.bg-yellow-50{--tw-bg-opacity: 1;background-color:rgb(254 252 232 / var(--tw-bg-opacity))}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.p-7{padding:1.75rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-8{padding-left:2rem;padding-right:2rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.pb-1{padding-bottom:.25rem}.pb-2{padding-bottom:.5rem}.pl-3{padding-left:.75rem}.pr-10{padding-right:2.5rem}.pr-2{padding-right:.5rem}.pr-3{padding-right:.75rem}.pr-4{padding-right:1rem}.pt-1{padding-top:.25rem}.pt-3{padding-top:.75rem}.pt-6{padding-top:1.5rem}.pt-8{padding-top:2rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-middle{vertical-align:middle}.font-sans{font-family:Figtree,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji"}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-extrabold{font-weight:800}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.capitalize{text-transform:capitalize}.leading-4{line-height:1rem}.leading-5{line-height:1.25rem}.leading-7{line-height:1.75rem}.tracking-wider{letter-spacing:.05em}.text-black{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity))}.text-gray-100{--tw-text-opacity: 1;color:rgb(241 245 249 / var(--tw-text-opacity))}.text-gray-200{--tw-text-opacity: 1;color:rgb(226 232 240 / var(--tw-text-opacity))}.text-gray-300{--tw-text-opacity: 1;color:rgb(203 213 225 / var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity))}.text-gray-50{--tw-text-opacity: 1;color:rgb(248 250 252 / var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity: 1;color:rgb(71 85 105 / var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity: 1;color:rgb(51 65 85 / var(--tw-text-opacity))}.text-gray-800{--tw-text-opacity: 1;color:rgb(30 41 59 / var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity: 1;color:rgb(15 23 42 / var(--tw-text-opacity))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity))}.text-green-600{--tw-text-opacity: 1;color:rgb(22 163 74 / var(--tw-text-opacity))}.text-indigo-600{--tw-text-opacity: 1;color:rgb(79 70 229 / var(--tw-text-opacity))}.text-primary-500{--tw-text-opacity: 1;color:rgb(99 102 241 / var(--tw-text-opacity))}.text-primary-600{--tw-text-opacity: 1;color:rgb(79 70 229 / var(--tw-text-opacity))}.text-primary-700{--tw-text-opacity: 1;color:rgb(67 56 202 / var(--tw-text-opacity))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity))}.text-red-600{--tw-text-opacity: 1;color:rgb(220 38 38 / var(--tw-text-opacity))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.text-yellow-500{--tw-text-opacity: 1;color:rgb(234 179 8 / var(--tw-text-opacity))}.text-yellow-700{--tw-text-opacity: 1;color:rgb(161 98 7 / var(--tw-text-opacity))}.underline{text-decoration-line:underline}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-25{opacity:.25}.opacity-75{opacity:.75}.shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-none{--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline-0{outline-width:0px}.ring-1{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-black{--tw-ring-opacity: 1;--tw-ring-color: rgb(0 0 0 / var(--tw-ring-opacity))}.ring-gray-300{--tw-ring-opacity: 1;--tw-ring-color: rgb(203 213 225 / var(--tw-ring-opacity))}.ring-opacity-5{--tw-ring-opacity: .05}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-100{transition-duration:.1s}.duration-150{transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-75{transition-duration:75ms}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}[x-cloak]{display:none!important}.hover\:border-gray-300:hover{--tw-border-opacity: 1;border-color:rgb(203 213 225 / var(--tw-border-opacity))}.hover\:bg-gray-100:hover{--tw-bg-opacity: 1;background-color:rgb(241 245 249 / var(--tw-bg-opacity))}.hover\:bg-gray-50:hover{--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity))}.hover\:bg-gray-900:hover{--tw-bg-opacity: 1;background-color:rgb(15 23 42 / var(--tw-bg-opacity))}.hover\:bg-primary-600:hover{--tw-bg-opacity: 1;background-color:rgb(79 70 229 / var(--tw-bg-opacity))}.hover\:bg-primary-700:hover{--tw-bg-opacity: 1;background-color:rgb(67 56 202 / var(--tw-bg-opacity))}.hover\:bg-red-500:hover{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity))}.hover\:text-gray-400:hover{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity))}.hover\:text-gray-500:hover{--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity))}.hover\:text-gray-700:hover{--tw-text-opacity: 1;color:rgb(51 65 85 / var(--tw-text-opacity))}.hover\:text-gray-800:hover{--tw-text-opacity: 1;color:rgb(30 41 59 / var(--tw-text-opacity))}.hover\:text-gray-900:hover{--tw-text-opacity: 1;color:rgb(15 23 42 / var(--tw-text-opacity))}.hover\:text-white:hover{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.hover\:opacity-50:hover{opacity:.5}.focus\:z-10:focus{z-index:10}.focus\:border-blue-300:focus{--tw-border-opacity: 1;border-color:rgb(147 197 253 / var(--tw-border-opacity))}.focus\:border-gray-300:focus{--tw-border-opacity: 1;border-color:rgb(203 213 225 / var(--tw-border-opacity))}.focus\:border-primary-300:focus{--tw-border-opacity: 1;border-color:rgb(165 180 252 / var(--tw-border-opacity))}.focus\:border-primary-500:focus{--tw-border-opacity: 1;border-color:rgb(99 102 241 / var(--tw-border-opacity))}.focus\:border-primary-700:focus{--tw-border-opacity: 1;border-color:rgb(67 56 202 / var(--tw-border-opacity))}.focus\:border-red-700:focus{--tw-border-opacity: 1;border-color:rgb(185 28 28 / var(--tw-border-opacity))}.focus\:bg-gray-100:focus{--tw-bg-opacity: 1;background-color:rgb(241 245 249 / var(--tw-bg-opacity))}.focus\:bg-gray-50:focus{--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity))}.focus\:bg-primary-100:focus{--tw-bg-opacity: 1;background-color:rgb(224 231 255 / var(--tw-bg-opacity))}.focus\:text-gray-700:focus{--tw-text-opacity: 1;color:rgb(51 65 85 / var(--tw-text-opacity))}.focus\:text-gray-800:focus{--tw-text-opacity: 1;color:rgb(30 41 59 / var(--tw-text-opacity))}.focus\:text-primary-800:focus{--tw-text-opacity: 1;color:rgb(55 48 163 / var(--tw-text-opacity))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-1:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-gray-400:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(148 163 184 / var(--tw-ring-opacity))}.focus\:ring-gray-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(100 116 139 / var(--tw-ring-opacity))}.focus\:ring-gray-700:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(51 65 85 / var(--tw-ring-opacity))}.focus\:ring-indigo-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(99 102 241 / var(--tw-ring-opacity))}.focus\:ring-primary-200:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(199 210 254 / var(--tw-ring-opacity))}.focus\:ring-primary-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(99 102 241 / var(--tw-ring-opacity))}.focus\:ring-red-200:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(254 202 202 / var(--tw-ring-opacity))}.focus\:ring-opacity-50:focus{--tw-ring-opacity: .5}.focus\:ring-offset-2:focus{--tw-ring-offset-width: 2px}.active\:bg-gray-100:active{--tw-bg-opacity: 1;background-color:rgb(241 245 249 / var(--tw-bg-opacity))}.active\:bg-primary-700:active{--tw-bg-opacity: 1;background-color:rgb(67 56 202 / var(--tw-bg-opacity))}.active\:bg-red-600:active{--tw-bg-opacity: 1;background-color:rgb(220 38 38 / var(--tw-bg-opacity))}.active\:text-gray-500:active{--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity))}.active\:text-gray-700:active{--tw-text-opacity: 1;color:rgb(51 65 85 / var(--tw-text-opacity))}.disabled\:opacity-25:disabled{opacity:.25}:is([dir=rtl] .rtl\:flex-row-reverse){flex-direction:row-reverse}:is(.dark .dark\:border-r-2){border-right-width:2px}:is(.dark .dark\:border-gray-500){--tw-border-opacity: 1;border-color:rgb(100 116 139 / var(--tw-border-opacity))}:is(.dark .dark\:border-gray-600){--tw-border-opacity: 1;border-color:rgb(71 85 105 / var(--tw-border-opacity))}:is(.dark .dark\:border-gray-700){--tw-border-opacity: 1;border-color:rgb(51 65 85 / var(--tw-border-opacity))}:is(.dark .dark\:border-gray-800){--tw-border-opacity: 1;border-color:rgb(30 41 59 / var(--tw-border-opacity))}:is(.dark .dark\:border-gray-900){--tw-border-opacity: 1;border-color:rgb(15 23 42 / var(--tw-border-opacity))}:is(.dark .dark\:border-primary-600){--tw-border-opacity: 1;border-color:rgb(79 70 229 / var(--tw-border-opacity))}:is(.dark .dark\:border-t-transparent){border-top-color:transparent}:is(.dark .dark\:border-opacity-20){--tw-border-opacity: .2}:is(.dark .dark\:bg-gray-500){--tw-bg-opacity: 1;background-color:rgb(100 116 139 / var(--tw-bg-opacity))}:is(.dark .dark\:bg-gray-700){--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity))}:is(.dark .dark\:bg-gray-800){--tw-bg-opacity: 1;background-color:rgb(30 41 59 / var(--tw-bg-opacity))}:is(.dark .dark\:bg-gray-800\/50){background-color:#1e293b80}:is(.dark .dark\:bg-gray-900){--tw-bg-opacity: 1;background-color:rgb(15 23 42 / var(--tw-bg-opacity))}:is(.dark .dark\:bg-green-500){--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity))}:is(.dark .dark\:bg-primary-500){--tw-bg-opacity: 1;background-color:rgb(99 102 241 / var(--tw-bg-opacity))}:is(.dark .dark\:bg-primary-900\/50){background-color:#312e8180}:is(.dark .dark\:bg-red-500){--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity))}:is(.dark .dark\:bg-yellow-500){--tw-bg-opacity: 1;background-color:rgb(234 179 8 / var(--tw-bg-opacity))}:is(.dark .dark\:bg-opacity-10){--tw-bg-opacity: .1}:is(.dark .dark\:bg-opacity-20){--tw-bg-opacity: .2}:is(.dark .dark\:bg-opacity-30){--tw-bg-opacity: .3}:is(.dark .dark\:bg-opacity-70){--tw-bg-opacity: .7}:is(.dark .dark\:text-gray-100){--tw-text-opacity: 1;color:rgb(241 245 249 / var(--tw-text-opacity))}:is(.dark .dark\:text-gray-200){--tw-text-opacity: 1;color:rgb(226 232 240 / var(--tw-text-opacity))}:is(.dark .dark\:text-gray-300){--tw-text-opacity: 1;color:rgb(203 213 225 / var(--tw-text-opacity))}:is(.dark .dark\:text-gray-400){--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity))}:is(.dark .dark\:text-gray-600){--tw-text-opacity: 1;color:rgb(71 85 105 / var(--tw-text-opacity))}:is(.dark .dark\:text-green-400){--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity))}:is(.dark .dark\:text-primary-300){--tw-text-opacity: 1;color:rgb(165 180 252 / var(--tw-text-opacity))}:is(.dark .dark\:text-red-400){--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity))}:is(.dark .dark\:text-white){--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}:is(.dark .dark\:text-yellow-500){--tw-text-opacity: 1;color:rgb(234 179 8 / var(--tw-text-opacity))}:is(.dark .dark\:hover\:border-gray-600:hover){--tw-border-opacity: 1;border-color:rgb(71 85 105 / var(--tw-border-opacity))}:is(.dark .dark\:hover\:border-gray-700:hover){--tw-border-opacity: 1;border-color:rgb(51 65 85 / var(--tw-border-opacity))}:is(.dark .dark\:hover\:bg-gray-700:hover){--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity))}:is(.dark .dark\:hover\:bg-gray-800:hover){--tw-bg-opacity: 1;background-color:rgb(30 41 59 / var(--tw-bg-opacity))}:is(.dark .dark\:hover\:text-gray-100:hover){--tw-text-opacity: 1;color:rgb(241 245 249 / var(--tw-text-opacity))}:is(.dark .dark\:hover\:text-gray-200:hover){--tw-text-opacity: 1;color:rgb(226 232 240 / var(--tw-text-opacity))}:is(.dark .dark\:hover\:text-gray-300:hover){--tw-text-opacity: 1;color:rgb(203 213 225 / var(--tw-text-opacity))}:is(.dark .dark\:focus\:border-blue-700:focus){--tw-border-opacity: 1;border-color:rgb(29 78 216 / var(--tw-border-opacity))}:is(.dark .dark\:focus\:border-blue-800:focus){--tw-border-opacity: 1;border-color:rgb(30 64 175 / var(--tw-border-opacity))}:is(.dark .dark\:focus\:border-gray-600:focus){--tw-border-opacity: 1;border-color:rgb(71 85 105 / var(--tw-border-opacity))}:is(.dark .dark\:focus\:border-gray-700:focus){--tw-border-opacity: 1;border-color:rgb(51 65 85 / var(--tw-border-opacity))}:is(.dark .dark\:focus\:border-primary-300:focus){--tw-border-opacity: 1;border-color:rgb(165 180 252 / var(--tw-border-opacity))}:is(.dark .dark\:focus\:border-primary-600:focus){--tw-border-opacity: 1;border-color:rgb(79 70 229 / var(--tw-border-opacity))}:is(.dark .dark\:focus\:border-primary-700:focus){--tw-border-opacity: 1;border-color:rgb(67 56 202 / var(--tw-border-opacity))}:is(.dark .dark\:focus\:bg-gray-700:focus){--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity))}:is(.dark .dark\:focus\:bg-primary-900:focus){--tw-bg-opacity: 1;background-color:rgb(49 46 129 / var(--tw-bg-opacity))}:is(.dark .dark\:focus\:text-gray-200:focus){--tw-text-opacity: 1;color:rgb(226 232 240 / var(--tw-text-opacity))}:is(.dark .dark\:focus\:text-gray-300:focus){--tw-text-opacity: 1;color:rgb(203 213 225 / var(--tw-text-opacity))}:is(.dark .dark\:focus\:text-primary-200:focus){--tw-text-opacity: 1;color:rgb(199 210 254 / var(--tw-text-opacity))}:is(.dark .dark\:focus\:ring-indigo-600:focus){--tw-ring-opacity: 1;--tw-ring-color: rgb(79 70 229 / var(--tw-ring-opacity))}:is(.dark .dark\:focus\:ring-primary-600:focus){--tw-ring-opacity: 1;--tw-ring-color: rgb(79 70 229 / var(--tw-ring-opacity))}:is(.dark .dark\:focus\:ring-primary-700:focus){--tw-ring-opacity: 1;--tw-ring-color: rgb(67 56 202 / var(--tw-ring-opacity))}:is(.dark .dark\:focus\:ring-opacity-40:focus){--tw-ring-opacity: .4}:is(.dark .dark\:focus\:ring-offset-gray-800:focus){--tw-ring-offset-color: #1e293b}:is(.dark .dark\:active\:bg-gray-700:active){--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity))}:is(.dark .dark\:active\:text-gray-300:active){--tw-text-opacity: 1;color:rgb(203 213 225 / var(--tw-text-opacity))}@media (min-width: 640px){.sm\:mx-auto{margin-left:auto;margin-right:auto}.sm\:flex{display:flex}.sm\:hidden{display:none}.sm\:w-full{width:100%}.sm\:max-w-2xl{max-width:42rem}.sm\:max-w-3xl{max-width:48rem}.sm\:max-w-4xl{max-width:56rem}.sm\:max-w-lg{max-width:32rem}.sm\:max-w-md{max-width:28rem}.sm\:max-w-sm{max-width:24rem}.sm\:max-w-xl{max-width:36rem}.sm\:flex-1{flex:1 1 0%}.sm\:translate-y-0{--tw-translate-y: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.sm\:scale-100{--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.sm\:scale-95{--tw-scale-x: .95;--tw-scale-y: .95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.sm\:items-center{align-items:center}.sm\:justify-start{justify-content:flex-start}.sm\:justify-center{justify-content:center}.sm\:justify-between{justify-content:space-between}.sm\:rounded-md{border-radius:.375rem}.sm\:rounded-bl-md{border-bottom-left-radius:.375rem}.sm\:rounded-br-md{border-bottom-right-radius:.375rem}.sm\:rounded-tl-md{border-top-left-radius:.375rem}.sm\:rounded-tr-md{border-top-right-radius:.375rem}.sm\:p-6{padding:1.5rem}.sm\:px-0{padding-left:0;padding-right:0}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:pt-0{padding-top:0}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}@media (min-width: 768px){.md\:col-span-1{grid-column:span 1 / span 1}.md\:block{display:block}.md\:justify-start{justify-content:flex-start}.md\:text-left{text-align:left}}@media (min-width: 1024px){.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:px-5{padding-left:1.25rem;padding-right:1.25rem}.lg\:px-8{padding-left:2rem;padding-right:2rem}} diff --git a/public/build/manifest.json b/public/build/manifest.json index b874ef5..d574542 100644 --- a/public/build/manifest.json +++ b/public/build/manifest.json @@ -1,6 +1,6 @@ { "resources/css/app.css": { - "file": "assets/app-5db42bc3.css", + "file": "assets/app-e2e337dc.css", "isEntry": true, "src": "resources/css/app.css" }, diff --git a/public/static/libs/ace/ace.js b/public/static/libs/ace/ace.js old mode 100755 new mode 100644 index eccca70..4e53d3b --- a/public/static/libs/ace/ace.js +++ b/public/static/libs/ace/ace.js @@ -1,4 +1,21372 @@ -(function(){function o(n){var i=e;n&&(e[n]||(e[n]={}),i=e[n]);if(!i.define||!i.define.packaged)t.original=i.define,i.define=t,i.define.packaged=!0;if(!i.require||!i.require.packaged)r.original=i.require,i.require=r,i.require.packaged=!0}var ACE_NAMESPACE = "ace",e=function(){return this}();!e&&typeof window!="undefined"&&(e=window);if(!ACE_NAMESPACE&&typeof requirejs!="undefined")return;var t=function(e,n,r){if(typeof e!="string"){t.original?t.original.apply(this,arguments):(console.error("dropping module because define wasn't a string."),console.trace());return}arguments.length==2&&(r=n),t.modules[e]||(t.payloads[e]=r,t.modules[e]=null)};t.modules={},t.payloads={};var n=function(e,t,n){if(typeof t=="string"){var i=s(e,t);if(i!=undefined)return n&&n(),i}else if(Object.prototype.toString.call(t)==="[object Array]"){var o=[];for(var u=0,a=t.length;u1&&u(t,"")>-1&&(a=RegExp(this.source,r.replace.call(o(this),"g","")),r.replace.call(e.slice(t.index),a,function(){for(var e=1;et.index&&this.lastIndex--}return t},s||(RegExp.prototype.test=function(e){var t=r.exec.call(this,e);return t&&this.global&&!t[0].length&&this.lastIndex>t.index&&this.lastIndex--,!!t})}),ace.define("ace/lib/es5-shim",["require","exports","module"],function(e,t,n){function r(){}function w(e){try{return Object.defineProperty(e,"sentinel",{}),"sentinel"in e}catch(t){}}function H(e){return e=+e,e!==e?e=0:e!==0&&e!==1/0&&e!==-1/0&&(e=(e>0||-1)*Math.floor(Math.abs(e))),e}function B(e){var t=typeof e;return e===null||t==="undefined"||t==="boolean"||t==="number"||t==="string"}function j(e){var t,n,r;if(B(e))return e;n=e.valueOf;if(typeof n=="function"){t=n.call(e);if(B(t))return t}r=e.toString;if(typeof r=="function"){t=r.call(e);if(B(t))return t}throw new TypeError}Function.prototype.bind||(Function.prototype.bind=function(t){var n=this;if(typeof n!="function")throw new TypeError("Function.prototype.bind called on incompatible "+n);var i=u.call(arguments,1),s=function(){if(this instanceof s){var e=n.apply(this,i.concat(u.call(arguments)));return Object(e)===e?e:this}return n.apply(t,i.concat(u.call(arguments)))};return n.prototype&&(r.prototype=n.prototype,s.prototype=new r,r.prototype=null),s});var i=Function.prototype.call,s=Array.prototype,o=Object.prototype,u=s.slice,a=i.bind(o.toString),f=i.bind(o.hasOwnProperty),l,c,h,p,d;if(d=f(o,"__defineGetter__"))l=i.bind(o.__defineGetter__),c=i.bind(o.__defineSetter__),h=i.bind(o.__lookupGetter__),p=i.bind(o.__lookupSetter__);if([1,2].splice(0).length!=2)if(!function(){function e(e){var t=new Array(e+2);return t[0]=t[1]=0,t}var t=[],n;t.splice.apply(t,e(20)),t.splice.apply(t,e(26)),n=t.length,t.splice(5,0,"XXX"),n+1==t.length;if(n+1==t.length)return!0}())Array.prototype.splice=function(e,t){var n=this.length;e>0?e>n&&(e=n):e==void 0?e=0:e<0&&(e=Math.max(n+e,0)),e+ta)for(h=l;h--;)this[f+h]=this[a+h];if(s&&e===c)this.length=c,this.push.apply(this,i);else{this.length=c+s;for(h=0;h>>0;if(a(t)!="[object Function]")throw new TypeError;while(++s>>0,s=Array(i),o=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var u=0;u>>0,s=[],o,u=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var f=0;f>>0,s=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var o=0;o>>0,s=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var o=0;o>>0;if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");if(!i&&arguments.length==1)throw new TypeError("reduce of empty array with no initial value");var s=0,o;if(arguments.length>=2)o=arguments[1];else do{if(s in r){o=r[s++];break}if(++s>=i)throw new TypeError("reduce of empty array with no initial value")}while(!0);for(;s>>0;if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");if(!i&&arguments.length==1)throw new TypeError("reduceRight of empty array with no initial value");var s,o=i-1;if(arguments.length>=2)s=arguments[1];else do{if(o in r){s=r[o--];break}if(--o<0)throw new TypeError("reduceRight of empty array with no initial value")}while(!0);do o in this&&(s=t.call(void 0,s,r[o],o,n));while(o--);return s});if(!Array.prototype.indexOf||[0,1].indexOf(1,2)!=-1)Array.prototype.indexOf=function(t){var n=g&&a(this)=="[object String]"?this.split(""):F(this),r=n.length>>>0;if(!r)return-1;var i=0;arguments.length>1&&(i=H(arguments[1])),i=i>=0?i:Math.max(0,r+i);for(;i>>0;if(!r)return-1;var i=r-1;arguments.length>1&&(i=Math.min(i,H(arguments[1]))),i=i>=0?i:r-Math.abs(i);for(;i>=0;i--)if(i in n&&t===n[i])return i;return-1};Object.getPrototypeOf||(Object.getPrototypeOf=function(t){return t.__proto__||(t.constructor?t.constructor.prototype:o)});if(!Object.getOwnPropertyDescriptor){var y="Object.getOwnPropertyDescriptor called on a non-object: ";Object.getOwnPropertyDescriptor=function(t,n){if(typeof t!="object"&&typeof t!="function"||t===null)throw new TypeError(y+t);if(!f(t,n))return;var r,i,s;r={enumerable:!0,configurable:!0};if(d){var u=t.__proto__;t.__proto__=o;var i=h(t,n),s=p(t,n);t.__proto__=u;if(i||s)return i&&(r.get=i),s&&(r.set=s),r}return r.value=t[n],r}}Object.getOwnPropertyNames||(Object.getOwnPropertyNames=function(t){return Object.keys(t)});if(!Object.create){var b;Object.prototype.__proto__===null?b=function(){return{__proto__:null}}:b=function(){var e={};for(var t in e)e[t]=null;return e.constructor=e.hasOwnProperty=e.propertyIsEnumerable=e.isPrototypeOf=e.toLocaleString=e.toString=e.valueOf=e.__proto__=null,e},Object.create=function(t,n){var r;if(t===null)r=b();else{if(typeof t!="object")throw new TypeError("typeof prototype["+typeof t+"] != 'object'");var i=function(){};i.prototype=t,r=new i,r.__proto__=t}return n!==void 0&&Object.defineProperties(r,n),r}}if(Object.defineProperty){var E=w({}),S=typeof document=="undefined"||w(document.createElement("div"));if(!E||!S)var x=Object.defineProperty}if(!Object.defineProperty||x){var T="Property description must be an object: ",N="Object.defineProperty called on non-object: ",C="getters & setters can not be defined on this javascript engine";Object.defineProperty=function(t,n,r){if(typeof t!="object"&&typeof t!="function"||t===null)throw new TypeError(N+t);if(typeof r!="object"&&typeof r!="function"||r===null)throw new TypeError(T+r);if(x)try{return x.call(Object,t,n,r)}catch(i){}if(f(r,"value"))if(d&&(h(t,n)||p(t,n))){var s=t.__proto__;t.__proto__=o,delete t[n],t[n]=r.value,t.__proto__=s}else t[n]=r.value;else{if(!d)throw new TypeError(C);f(r,"get")&&l(t,n,r.get),f(r,"set")&&c(t,n,r.set)}return t}}Object.defineProperties||(Object.defineProperties=function(t,n){for(var r in n)f(n,r)&&Object.defineProperty(t,r,n[r]);return t}),Object.seal||(Object.seal=function(t){return t}),Object.freeze||(Object.freeze=function(t){return t});try{Object.freeze(function(){})}catch(k){Object.freeze=function(t){return function(n){return typeof n=="function"?n:t(n)}}(Object.freeze)}Object.preventExtensions||(Object.preventExtensions=function(t){return t}),Object.isSealed||(Object.isSealed=function(t){return!1}),Object.isFrozen||(Object.isFrozen=function(t){return!1}),Object.isExtensible||(Object.isExtensible=function(t){if(Object(t)===t)throw new TypeError;var n="";while(f(t,n))n+="?";t[n]=!0;var r=f(t,n);return delete t[n],r});if(!Object.keys){var L=!0,A=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],O=A.length;for(var M in{toString:null})L=!1;Object.keys=function I(e){if(typeof e!="object"&&typeof e!="function"||e===null)throw new TypeError("Object.keys called on a non-object");var I=[];for(var t in e)f(e,t)&&I.push(t);if(L)for(var n=0,r=O;n=0?parseFloat((s.match(/(?:MSIE |Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]):parseFloat((s.match(/(?:Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]),t.isOldIE=t.isIE&&t.isIE<9,t.isGecko=t.isMozilla=s.match(/ Gecko\/\d+/),t.isOpera=typeof opera=="object"&&Object.prototype.toString.call(window.opera)=="[object Opera]",t.isWebKit=parseFloat(s.split("WebKit/")[1])||undefined,t.isChrome=parseFloat(s.split(" Chrome/")[1])||undefined,t.isEdge=parseFloat(s.split(" Edge/")[1])||undefined,t.isAIR=s.indexOf("AdobeAIR")>=0,t.isIPad=s.indexOf("iPad")>=0,t.isAndroid=s.indexOf("Android")>=0,t.isChromeOS=s.indexOf(" CrOS ")>=0,t.isIOS=/iPad|iPhone|iPod/.test(s)&&!window.MSStream,t.isIOS&&(t.isMac=!0),t.isMobile=t.isIPad||t.isAndroid}),ace.define("ace/lib/dom",["require","exports","module","ace/lib/useragent"],function(e,t,n){"use strict";var r=e("./useragent"),i="http://www.w3.org/1999/xhtml";t.buildDom=function o(e,t,n){if(typeof e=="string"&&e){var r=document.createTextNode(e);return t&&t.appendChild(r),r}if(!Array.isArray(e))return e;if(typeof e[0]!="string"||!e[0]){var i=[];for(var s=0;s=1.5:!0;if(typeof document!="undefined"){var s=document.createElement("div");t.HI_DPI&&s.style.transform!==undefined&&(t.HAS_CSS_TRANSFORMS=!0),!r.isEdge&&typeof s.style.animationName!="undefined"&&(t.HAS_CSS_ANIMATION=!0),s=null}t.HAS_CSS_TRANSFORMS?t.translate=function(e,t,n){e.style.transform="translate("+Math.round(t)+"px, "+Math.round(n)+"px)"}:t.translate=function(e,t,n){e.style.top=Math.round(n)+"px",e.style.left=Math.round(t)+"px"}}),ace.define("ace/lib/oop",["require","exports","module"],function(e,t,n){"use strict";t.inherits=function(e,t){e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}})},t.mixin=function(e,t){for(var n in t)e[n]=t[n];return e},t.implement=function(e,n){t.mixin(e,n)}}),ace.define("ace/lib/keys",["require","exports","module","ace/lib/oop"],function(e,t,n){"use strict";var r=e("./oop"),i=function(){var e={MODIFIER_KEYS:{16:"Shift",17:"Ctrl",18:"Alt",224:"Meta"},KEY_MODS:{ctrl:1,alt:2,option:2,shift:4,"super":8,meta:8,command:8,cmd:8},FUNCTION_KEYS:{8:"Backspace",9:"Tab",13:"Return",19:"Pause",27:"Esc",32:"Space",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"Left",38:"Up",39:"Right",40:"Down",44:"Print",45:"Insert",46:"Delete",96:"Numpad0",97:"Numpad1",98:"Numpad2",99:"Numpad3",100:"Numpad4",101:"Numpad5",102:"Numpad6",103:"Numpad7",104:"Numpad8",105:"Numpad9","-13":"NumpadEnter",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"Numlock",145:"Scrolllock"},PRINTABLE_KEYS:{32:" ",48:"0",49:"1",50:"2",51:"3",52:"4",53:"5",54:"6",55:"7",56:"8",57:"9",59:";",61:"=",65:"a",66:"b",67:"c",68:"d",69:"e",70:"f",71:"g",72:"h",73:"i",74:"j",75:"k",76:"l",77:"m",78:"n",79:"o",80:"p",81:"q",82:"r",83:"s",84:"t",85:"u",86:"v",87:"w",88:"x",89:"y",90:"z",107:"+",109:"-",110:".",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'",111:"/",106:"*"}},t,n;for(n in e.FUNCTION_KEYS)t=e.FUNCTION_KEYS[n].toLowerCase(),e[t]=parseInt(n,10);for(n in e.PRINTABLE_KEYS)t=e.PRINTABLE_KEYS[n].toLowerCase(),e[t]=parseInt(n,10);return r.mixin(e,e.MODIFIER_KEYS),r.mixin(e,e.PRINTABLE_KEYS),r.mixin(e,e.FUNCTION_KEYS),e.enter=e["return"],e.escape=e.esc,e.del=e["delete"],e[173]="-",function(){var t=["cmd","ctrl","alt","shift"];for(var n=Math.pow(2,t.length);n--;)e.KEY_MODS[n]=t.filter(function(t){return n&e.KEY_MODS[t]}).join("-")+"-"}(),e.KEY_MODS[0]="",e.KEY_MODS[-1]="input-",e}();r.mixin(t,i),t.keyCodeToString=function(e){var t=i[e];return typeof t!="string"&&(t=String.fromCharCode(e)),t.toLowerCase()}}),ace.define("ace/lib/event",["require","exports","module","ace/lib/keys","ace/lib/useragent"],function(e,t,n){"use strict";function a(e,t,n){var a=u(t);if(!i.isMac&&s){t.getModifierState&&(t.getModifierState("OS")||t.getModifierState("Win"))&&(a|=8);if(s.altGr){if((3&a)==3)return;s.altGr=0}if(n===18||n===17){var f="location"in t?t.location:t.keyLocation;if(n===17&&f===1)s[n]==1&&(o=t.timeStamp);else if(n===18&&a===3&&f===2){var l=t.timeStamp-o;l<50&&(s.altGr=!0)}}}n in r.MODIFIER_KEYS&&(n=-1),a&8&&n>=91&&n<=93&&(n=-1);if(!a&&n===13){var f="location"in t?t.location:t.keyLocation;if(f===3){e(t,a,-n);if(t.defaultPrevented)return}}if(i.isChromeOS&&a&8){e(t,a,n);if(t.defaultPrevented)return;a&=-9}return!!a||n in r.FUNCTION_KEYS||n in r.PRINTABLE_KEYS?e(t,a,n):!1}function f(){s=Object.create(null)}var r=e("./keys"),i=e("./useragent"),s=null,o=0;t.addListener=function(e,t,n){if(e.addEventListener)return e.addEventListener(t,n,!1);if(e.attachEvent){var r=function(){n.call(e,window.event)};n._wrapper=r,e.attachEvent("on"+t,r)}},t.removeListener=function(e,t,n){if(e.removeEventListener)return e.removeEventListener(t,n,!1);e.detachEvent&&e.detachEvent("on"+t,n._wrapper||n)},t.stopEvent=function(e){return t.stopPropagation(e),t.preventDefault(e),!1},t.stopPropagation=function(e){e.stopPropagation?e.stopPropagation():e.cancelBubble=!0},t.preventDefault=function(e){e.preventDefault?e.preventDefault():e.returnValue=!1},t.getButton=function(e){return e.type=="dblclick"?0:e.type=="contextmenu"||i.isMac&&e.ctrlKey&&!e.altKey&&!e.shiftKey?2:e.preventDefault?e.button:{1:0,2:2,4:1}[e.button]},t.capture=function(e,n,r){function i(e){n&&n(e),r&&r(e),t.removeListener(document,"mousemove",n,!0),t.removeListener(document,"mouseup",i,!0),t.removeListener(document,"dragstart",i,!0)}return t.addListener(document,"mousemove",n,!0),t.addListener(document,"mouseup",i,!0),t.addListener(document,"dragstart",i,!0),i},t.addMouseWheelListener=function(e,n){"onmousewheel"in e?t.addListener(e,"mousewheel",function(e){var t=8;e.wheelDeltaX!==undefined?(e.wheelX=-e.wheelDeltaX/t,e.wheelY=-e.wheelDeltaY/t):(e.wheelX=0,e.wheelY=-e.wheelDelta/t),n(e)}):"onwheel"in e?t.addListener(e,"wheel",function(e){var t=.35;switch(e.deltaMode){case e.DOM_DELTA_PIXEL:e.wheelX=e.deltaX*t||0,e.wheelY=e.deltaY*t||0;break;case e.DOM_DELTA_LINE:case e.DOM_DELTA_PAGE:e.wheelX=(e.deltaX||0)*5,e.wheelY=(e.deltaY||0)*5}n(e)}):t.addListener(e,"DOMMouseScroll",function(e){e.axis&&e.axis==e.HORIZONTAL_AXIS?(e.wheelX=(e.detail||0)*5,e.wheelY=0):(e.wheelX=0,e.wheelY=(e.detail||0)*5),n(e)})},t.addMultiMouseDownListener=function(e,n,r,s){function c(e){t.getButton(e)!==0?o=0:e.detail>1?(o++,o>4&&(o=1)):o=1;if(i.isIE){var c=Math.abs(e.clientX-u)>5||Math.abs(e.clientY-a)>5;if(!f||c)o=1;f&&clearTimeout(f),f=setTimeout(function(){f=null},n[o-1]||600),o==1&&(u=e.clientX,a=e.clientY)}e._clicks=o,r[s]("mousedown",e);if(o>4)o=0;else if(o>1)return r[s](l[o],e)}function h(e){o=2,f&&clearTimeout(f),f=setTimeout(function(){f=null},n[o-1]||600),r[s]("mousedown",e),r[s](l[o],e)}var o=0,u,a,f,l={2:"dblclick",3:"tripleclick",4:"quadclick"};Array.isArray(e)||(e=[e]),e.forEach(function(e){t.addListener(e,"mousedown",c),i.isOldIE&&t.addListener(e,"dblclick",h)})};var u=!i.isMac||!i.isOpera||"KeyboardEvent"in window?function(e){return 0|(e.ctrlKey?1:0)|(e.altKey?2:0)|(e.shiftKey?4:0)|(e.metaKey?8:0)}:function(e){return 0|(e.metaKey?1:0)|(e.altKey?2:0)|(e.shiftKey?4:0)|(e.ctrlKey?8:0)};t.getModifierString=function(e){return r.KEY_MODS[u(e)]},t.addCommandKeyListener=function(e,n){var r=t.addListener;if(i.isOldGecko||i.isOpera&&!("KeyboardEvent"in window)){var o=null;r(e,"keydown",function(e){o=e.keyCode}),r(e,"keypress",function(e){return a(n,e,o)})}else{var u=null;r(e,"keydown",function(e){s[e.keyCode]=(s[e.keyCode]||0)+1;var t=a(n,e,e.keyCode);return u=e.defaultPrevented,t}),r(e,"keypress",function(e){u&&(e.ctrlKey||e.altKey||e.shiftKey||e.metaKey)&&(t.stopEvent(e),u=null)}),r(e,"keyup",function(e){s[e.keyCode]=null}),s||(f(),r(window,"focus",f))}};if(typeof window=="object"&&window.postMessage&&!i.isOldIE){var l=1;t.nextTick=function(e,n){n=n||window;var r="zero-timeout-message-"+l++,i=function(s){s.data==r&&(t.stopPropagation(s),t.removeListener(n,"message",i),e())};t.addListener(n,"message",i),n.postMessage(r,"*")}}t.$idleBlocked=!1,t.onIdle=function(e,n){return setTimeout(function r(){t.$idleBlocked?setTimeout(r,100):e()},n)},t.$idleBlockId=null,t.blockIdle=function(e){t.$idleBlockId&&clearTimeout(t.$idleBlockId),t.$idleBlocked=!0,t.$idleBlockId=setTimeout(function(){t.$idleBlocked=!1},e||100)},t.nextFrame=typeof window=="object"&&(window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame||window.oRequestAnimationFrame),t.nextFrame?t.nextFrame=t.nextFrame.bind(window):t.nextFrame=function(e){setTimeout(e,17)}}),ace.define("ace/range",["require","exports","module"],function(e,t,n){"use strict";var r=function(e,t){return e.row-t.row||e.column-t.column},i=function(e,t,n,r){this.start={row:e,column:t},this.end={row:n,column:r}};(function(){this.isEqual=function(e){return this.start.row===e.start.row&&this.end.row===e.end.row&&this.start.column===e.start.column&&this.end.column===e.end.column},this.toString=function(){return"Range: ["+this.start.row+"/"+this.start.column+"] -> ["+this.end.row+"/"+this.end.column+"]"},this.contains=function(e,t){return this.compare(e,t)==0},this.compareRange=function(e){var t,n=e.end,r=e.start;return t=this.compare(n.row,n.column),t==1?(t=this.compare(r.row,r.column),t==1?2:t==0?1:0):t==-1?-2:(t=this.compare(r.row,r.column),t==-1?-1:t==1?42:0)},this.comparePoint=function(e){return this.compare(e.row,e.column)},this.containsRange=function(e){return this.comparePoint(e.start)==0&&this.comparePoint(e.end)==0},this.intersects=function(e){var t=this.compareRange(e);return t==-1||t==0||t==1},this.isEnd=function(e,t){return this.end.row==e&&this.end.column==t},this.isStart=function(e,t){return this.start.row==e&&this.start.column==t},this.setStart=function(e,t){typeof e=="object"?(this.start.column=e.column,this.start.row=e.row):(this.start.row=e,this.start.column=t)},this.setEnd=function(e,t){typeof e=="object"?(this.end.column=e.column,this.end.row=e.row):(this.end.row=e,this.end.column=t)},this.inside=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)||this.isStart(e,t)?!1:!0:!1},this.insideStart=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)?!1:!0:!1},this.insideEnd=function(e,t){return this.compare(e,t)==0?this.isStart(e,t)?!1:!0:!1},this.compare=function(e,t){return!this.isMultiLine()&&e===this.start.row?tthis.end.column?1:0:ethis.end.row?1:this.start.row===e?t>=this.start.column?0:-1:this.end.row===e?t<=this.end.column?0:1:0},this.compareStart=function(e,t){return this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.compareEnd=function(e,t){return this.end.row==e&&this.end.column==t?1:this.compare(e,t)},this.compareInside=function(e,t){return this.end.row==e&&this.end.column==t?1:this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.clipRows=function(e,t){if(this.end.row>t)var n={row:t+1,column:0};else if(this.end.rowt)var r={row:t+1,column:0};else if(this.start.row0){t&1&&(n+=e);if(t>>=1)e+=e}return n};var r=/^\s\s*/,i=/\s\s*$/;t.stringTrimLeft=function(e){return e.replace(r,"")},t.stringTrimRight=function(e){return e.replace(i,"")},t.copyObject=function(e){var t={};for(var n in e)t[n]=e[n];return t},t.copyArray=function(e){var t=[];for(var n=0,r=e.length;n63,l=400,c=e("../lib/keys"),h=c.KEY_MODS,p=i.isIOS,d=p?/\s/:/\n/,v=function(e,t){function W(){x=!0,n.blur(),n.focus(),x=!1}function V(e){e.keyCode==27&&n.value.lengthC&&T[s]=="\n")o=c.end;else if(rC&&T.slice(0,s).split("\n").length>2)o=c.down;else if(s>C&&T[s-1]==" ")o=c.right,u=h.option;else if(s>C||s==C&&C!=N&&r==s)o=c.right;r!==s&&(u|=h.shift),o&&(t.onCommandKey(null,u,o),N=r,C=s,A(""))};document.addEventListener("selectionchange",s),t.on("destroy",function(){document.removeEventListener("selectionchange",s)})}var n=s.createElement("textarea");n.className="ace_text-input",n.setAttribute("wrap","off"),n.setAttribute("autocorrect","off"),n.setAttribute("autocapitalize","off"),n.setAttribute("spellcheck",!1),n.style.opacity="0",e.insertBefore(n,e.firstChild);var v=!1,m=!1,g=!1,y=!1,b="",w=!0,E=!1;i.isMobile||(n.style.fontSize="1px");var S=!1,x=!1,T="",N=0,C=0;try{var k=document.activeElement===n}catch(L){}r.addListener(n,"blur",function(e){if(x)return;t.onBlur(e),k=!1}),r.addListener(n,"focus",function(e){if(x)return;k=!0;if(i.isEdge)try{if(!document.hasFocus())return}catch(e){}t.onFocus(e),i.isEdge?setTimeout(A):A()}),this.$focusScroll=!1,this.focus=function(){if(b||f||this.$focusScroll=="browser")return n.focus({preventScroll:!0});var e=n.style.top;n.style.position="fixed",n.style.top="0px";try{var t=n.getBoundingClientRect().top!=0}catch(r){return}var i=[];if(t){var s=n.parentElement;while(s&&s.nodeType==1)i.push(s),s.setAttribute("ace_nocontext",!0),!s.parentElement&&s.getRootNode?s=s.getRootNode().host:s=s.parentElement}n.focus({preventScroll:!0}),t&&i.forEach(function(e){e.removeAttribute("ace_nocontext")}),setTimeout(function(){n.style.position="",n.style.top=="0px"&&(n.style.top=e)},0)},this.blur=function(){n.blur()},this.isFocused=function(){return k},t.on("beforeEndOperation",function(){if(t.curOp&&t.curOp.command.name=="insertstring")return;g&&(T=n.value="",z()),A()});var A=p?function(e){if(!k||v&&!e||y)return;e||(e="");var r="\n ab"+e+"cde fg\n";r!=n.value&&(n.value=T=r);var i=4,s=4+(e.length||(t.selection.isEmpty()?0:1));(N!=i||C!=s)&&n.setSelectionRange(i,s),N=i,C=s}:function(){if(g||y)return;if(!k&&!D)return;g=!0;var e=t.selection,r=e.getRange(),i=e.cursor.row,s=r.start.column,o=r.end.column,u=t.session.getLine(i);if(r.start.row!=i){var a=t.session.getLine(i-1);s=r.start.rowi+1?f.length:o,o+=u.length+1,u=u+"\n"+f}u.length>l&&(s=T.length&&e.value===T&&T&&e.selectionEnd!==C},M=function(e){if(g)return;v?v=!1:O(n)&&(t.selectAll(),A())},_=null;this.setInputHandler=function(e){_=e},this.getInputHandler=function(){return _};var D=!1,P=function(e,r){D&&(D=!1);if(m)return A(),e&&t.onPaste(e),m=!1,"";var i=n.selectionStart,s=n.selectionEnd,o=N,u=T.length-C,a=e,f=e.length-i,l=e.length-s,c=0;while(o>0&&T[c]==e[c])c++,o--;a=a.slice(c),c=1;while(u>0&&T.length-c>N-1&&T[T.length-c]==e[e.length-c])c++,u--;return f-=c-1,l-=c-1,a=a.slice(0,a.length-c+1),!r&&f==a.length&&!o&&!u&&!l?"":(y=!0,a&&!o&&!u&&!f&&!l||S?t.onTextInput(a):t.onTextInput(a,{extendLeft:o,extendRight:u,restoreStart:f,restoreEnd:l}),y=!1,T=e,N=i,C=s,a)},H=function(e){if(g)return U();var t=n.value,r=P(t,!0);(t.length>l+100||d.test(r))&&A()},B=function(e,t,n){var r=e.clipboardData||window.clipboardData;if(!r||u)return;var i=a||n?"Text":"text/plain";try{return t?r.setData(i,t)!==!1:r.getData(i)}catch(e){if(!n)return B(e,t,!0)}},j=function(e,i){var s=t.getCopyText();if(!s)return r.preventDefault(e);B(e,s)?(p&&(A(s),v=s,setTimeout(function(){v=!1},10)),i?t.onCut():t.onCopy(),r.preventDefault(e)):(v=!0,n.value=s,n.select(),setTimeout(function(){v=!1,A(),i?t.onCut():t.onCopy()}))},F=function(e){j(e,!0)},I=function(e){j(e,!1)},q=function(e){var s=B(e);typeof s=="string"?(s&&t.onPaste(s,e),i.isIE&&setTimeout(A),r.preventDefault(e)):(n.value="",m=!0)};r.addCommandKeyListener(n,t.onCommandKey.bind(t)),r.addListener(n,"select",M),r.addListener(n,"input",H),r.addListener(n,"cut",F),r.addListener(n,"copy",I),r.addListener(n,"paste",q),(!("oncut"in n)||!("oncopy"in n)||!("onpaste"in n))&&r.addListener(e,"keydown",function(e){if(i.isMac&&!e.metaKey||!e.ctrlKey)return;switch(e.keyCode){case 67:I(e);break;case 86:q(e);break;case 88:F(e)}});var R=function(e){if(g||!t.onCompositionStart||t.$readOnly)return;g={};if(S)return;setTimeout(U,0),t.on("mousedown",W);var r=t.getSelectionRange();r.end.row=r.start.row,r.end.column=r.start.column,g.markerRange=r,g.selectionStart=N,t.onCompositionStart(g),g.useTextareaForIME?(n.value="",T="",N=0,C=0):(n.msGetInputContext&&(g.context=n.msGetInputContext()),n.getInputContext&&(g.context=n.getInputContext()))},U=function(){if(!g||!t.onCompositionUpdate||t.$readOnly)return;if(S)return W();if(g.useTextareaForIME)t.onCompositionUpdate(n.value);else{var e=n.value;P(e),g.markerRange&&(g.context&&(g.markerRange.start.column=g.selectionStart=g.context.compositionStartOffset),g.markerRange.end.column=g.markerRange.start.column+C-g.selectionStart)}},z=function(e){if(!t.onCompositionEnd||t.$readOnly)return;g=!1,t.onCompositionEnd(),t.off("mousedown",W),e&&H()},X=o.delayedCall(U,50).schedule.bind(null,null);r.addListener(n,"compositionstart",R),r.addListener(n,"compositionupdate",U),r.addListener(n,"keyup",V),r.addListener(n,"keydown",X),r.addListener(n,"compositionend",z),this.getElement=function(){return n},this.setCommandMode=function(e){S=e,n.readOnly=!1},this.setReadOnly=function(e){S||(n.readOnly=e)},this.setCopyWithEmptySelection=function(e){E=e},this.onContextMenu=function(e){D=!0,A(),t._emit("nativecontextmenu",{target:t,domEvent:e}),this.moveToMouse(e,!0)},this.moveToMouse=function(e,o){b||(b=n.style.cssText),n.style.cssText=(o?"z-index:100000;":"")+(i.isIE?"opacity:0.1;":"")+"text-indent: -"+(N+C)*t.renderer.characterWidth*.5+"px;";var u=t.container.getBoundingClientRect(),a=s.computedStyle(t.container),f=u.top+(parseInt(a.borderTopWidth)||0),l=u.left+(parseInt(u.borderLeftWidth)||0),c=u.bottom-f-n.clientHeight-2,h=function(e){s.translate(n,e.clientX-l-2,Math.min(e.clientY-f-2,c))};h(e);if(e.type!="mousedown")return;t.renderer.$isMousePressed=!0,clearTimeout($),i.isWin&&r.capture(t.container,h,J)},this.onContextMenuClose=J;var $,K=function(e){t.textInput.onContextMenu(e),J()};r.addListener(n,"mouseup",K),r.addListener(n,"mousedown",function(e){e.preventDefault(),J()}),r.addListener(t.renderer.scroller,"contextmenu",K),r.addListener(n,"contextmenu",K),p&&Q(e,t,n)};t.TextInput=v}),ace.define("ace/mouse/default_handlers",["require","exports","module","ace/lib/useragent"],function(e,t,n){"use strict";function o(e){e.$clickSelection=null;var t=e.editor;t.setDefaultHandler("mousedown",this.onMouseDown.bind(e)),t.setDefaultHandler("dblclick",this.onDoubleClick.bind(e)),t.setDefaultHandler("tripleclick",this.onTripleClick.bind(e)),t.setDefaultHandler("quadclick",this.onQuadClick.bind(e)),t.setDefaultHandler("mousewheel",this.onMouseWheel.bind(e));var n=["select","startSelect","selectEnd","selectAllEnd","selectByWordsEnd","selectByLinesEnd","dragWait","dragWaitEnd","focusWait"];n.forEach(function(t){e[t]=this[t]},this),e.selectByLines=this.extendSelectionBy.bind(e,"getLineRange"),e.selectByWords=this.extendSelectionBy.bind(e,"getWordRange")}function u(e,t,n,r){return Math.sqrt(Math.pow(n-e,2)+Math.pow(r-t,2))}function a(e,t){if(e.start.row==e.end.row)var n=2*t.column-e.start.column-e.end.column;else if(e.start.row==e.end.row-1&&!e.start.column&&!e.end.column)var n=t.column-4;else var n=2*t.row-e.start.row-e.end.row;return n<0?{cursor:e.start,anchor:e.end}:{cursor:e.end,anchor:e.start}}var r=e("../lib/useragent"),i=0,s=550;(function(){this.onMouseDown=function(e){var t=e.inSelection(),n=e.getDocumentPosition();this.mousedownEvent=e;var i=this.editor,s=e.getButton();if(s!==0){var o=i.getSelectionRange(),u=o.isEmpty();(u||s==1)&&i.selection.moveToPosition(n),s==2&&(i.textInput.onContextMenu(e.domEvent),r.isMozilla||e.preventDefault());return}this.mousedownEvent.time=Date.now();if(t&&!i.isFocused()){i.focus();if(this.$focusTimeout&&!this.$clickSelection&&!i.inMultiSelectMode){this.setState("focusWait"),this.captureMouse(e);return}}return this.captureMouse(e),this.startSelect(n,e.domEvent._clicks>1),e.preventDefault()},this.startSelect=function(e,t){e=e||this.editor.renderer.screenToTextCoordinates(this.x,this.y);var n=this.editor;if(!this.mousedownEvent)return;this.mousedownEvent.getShiftKey()?n.selection.selectToPosition(e):t||n.selection.moveToPosition(e),t||this.select(),n.renderer.scroller.setCapture&&n.renderer.scroller.setCapture(),n.setStyle("ace_selecting"),this.setState("select")},this.select=function(){var e,t=this.editor,n=t.renderer.screenToTextCoordinates(this.x,this.y);if(this.$clickSelection){var r=this.$clickSelection.comparePoint(n);if(r==-1)e=this.$clickSelection.end;else if(r==1)e=this.$clickSelection.start;else{var i=a(this.$clickSelection,n);n=i.cursor,e=i.anchor}t.selection.setSelectionAnchor(e.row,e.column)}t.selection.selectToPosition(n),t.renderer.scrollCursorIntoView()},this.extendSelectionBy=function(e){var t,n=this.editor,r=n.renderer.screenToTextCoordinates(this.x,this.y),i=n.selection[e](r.row,r.column);if(this.$clickSelection){var s=this.$clickSelection.comparePoint(i.start),o=this.$clickSelection.comparePoint(i.end);if(s==-1&&o<=0){t=this.$clickSelection.end;if(i.end.row!=r.row||i.end.column!=r.column)r=i.start}else if(o==1&&s>=0){t=this.$clickSelection.start;if(i.start.row!=r.row||i.start.column!=r.column)r=i.end}else if(s==-1&&o==1)r=i.end,t=i.start;else{var u=a(this.$clickSelection,r);r=u.cursor,t=u.anchor}n.selection.setSelectionAnchor(t.row,t.column)}n.selection.selectToPosition(r),n.renderer.scrollCursorIntoView()},this.selectEnd=this.selectAllEnd=this.selectByWordsEnd=this.selectByLinesEnd=function(){this.$clickSelection=null,this.editor.unsetStyle("ace_selecting"),this.editor.renderer.scroller.releaseCapture&&this.editor.renderer.scroller.releaseCapture()},this.focusWait=function(){var e=u(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y),t=Date.now();(e>i||t-this.mousedownEvent.time>this.$focusTimeout)&&this.startSelect(this.mousedownEvent.getDocumentPosition())},this.onDoubleClick=function(e){var t=e.getDocumentPosition(),n=this.editor,r=n.session,i=r.getBracketRange(t);i?(i.isEmpty()&&(i.start.column--,i.end.column++),this.setState("select")):(i=n.selection.getWordRange(t.row,t.column),this.setState("selectByWords")),this.$clickSelection=i,this.select()},this.onTripleClick=function(e){var t=e.getDocumentPosition(),n=this.editor;this.setState("selectByLines");var r=n.getSelectionRange();r.isMultiLine()&&r.contains(t.row,t.column)?(this.$clickSelection=n.selection.getLineRange(r.start.row),this.$clickSelection.end=n.selection.getLineRange(r.end.row).end):this.$clickSelection=n.selection.getLineRange(t.row),this.select()},this.onQuadClick=function(e){var t=this.editor;t.selectAll(),this.$clickSelection=t.getSelectionRange(),this.setState("selectAll")},this.onMouseWheel=function(e){if(e.getAccelKey())return;e.getShiftKey()&&e.wheelY&&!e.wheelX&&(e.wheelX=e.wheelY,e.wheelY=0);var t=this.editor;this.$lastScroll||(this.$lastScroll={t:0,vx:0,vy:0,allowed:0});var n=this.$lastScroll,r=e.domEvent.timeStamp,i=r-n.t,o=i?e.wheelX/i:n.vx,u=i?e.wheelY/i:n.vy;i=1&&t.renderer.isScrollableBy(e.wheelX*e.speed,0)&&(f=!0),a<=1&&t.renderer.isScrollableBy(0,e.wheelY*e.speed)&&(f=!0);if(f)n.allowed=r;else if(r-n.allowedt.session.documentToScreenRow(l.row,l.column))return c()}if(f==s)return;f=s.text.join("
"),i.setHtml(f),i.show(),t._signal("showGutterTooltip",i),t.on("mousewheel",c);if(e.$tooltipFollowsMouse)h(u);else{var p=u.domEvent.target,d=p.getBoundingClientRect(),v=i.getElement().style;v.left=d.right+"px",v.top=d.bottom+"px"}}function c(){o&&(o=clearTimeout(o)),f&&(i.hide(),f=null,t._signal("hideGutterTooltip",i),t.removeEventListener("mousewheel",c))}function h(e){i.setPosition(e.x,e.y)}var t=e.editor,n=t.renderer.$gutterLayer,i=new a(t.container);e.editor.setDefaultHandler("guttermousedown",function(r){if(!t.isFocused()||r.getButton()!=0)return;var i=n.getRegion(r);if(i=="foldWidgets")return;var s=r.getDocumentPosition().row,o=t.session.selection;if(r.getShiftKey())o.selectTo(s,0);else{if(r.domEvent.detail==2)return t.selectAll(),r.preventDefault();e.$clickSelection=t.selection.getLineRange(s)}return e.setState("selectByLines"),e.captureMouse(r),r.preventDefault()});var o,u,f;e.editor.setDefaultHandler("guttermousemove",function(t){var n=t.domEvent.target||t.domEvent.srcElement;if(r.hasCssClass(n,"ace_fold-widget"))return c();f&&e.$tooltipFollowsMouse&&h(t),u=t;if(o)return;o=setTimeout(function(){o=null,u&&!e.isMousePressed?l():c()},50)}),s.addListener(t.renderer.$gutter,"mouseout",function(e){u=null;if(!f||o)return;o=setTimeout(function(){o=null,c()},50)}),t.on("changeSession",c)}function a(e){o.call(this,e)}var r=e("../lib/dom"),i=e("../lib/oop"),s=e("../lib/event"),o=e("../tooltip").Tooltip;i.inherits(a,o),function(){this.setPosition=function(e,t){var n=window.innerWidth||document.documentElement.clientWidth,r=window.innerHeight||document.documentElement.clientHeight,i=this.getWidth(),s=this.getHeight();e+=15,t+=15,e+i>n&&(e-=e+i-n),t+s>r&&(t-=20+s),o.prototype.setPosition.call(this,e,t)}}.call(a.prototype),t.GutterHandler=u}),ace.define("ace/mouse/mouse_event",["require","exports","module","ace/lib/event","ace/lib/useragent"],function(e,t,n){"use strict";var r=e("../lib/event"),i=e("../lib/useragent"),s=t.MouseEvent=function(e,t){this.domEvent=e,this.editor=t,this.x=this.clientX=e.clientX,this.y=this.clientY=e.clientY,this.$pos=null,this.$inSelection=null,this.propagationStopped=!1,this.defaultPrevented=!1};(function(){this.stopPropagation=function(){r.stopPropagation(this.domEvent),this.propagationStopped=!0},this.preventDefault=function(){r.preventDefault(this.domEvent),this.defaultPrevented=!0},this.stop=function(){this.stopPropagation(),this.preventDefault()},this.getDocumentPosition=function(){return this.$pos?this.$pos:(this.$pos=this.editor.renderer.screenToTextCoordinates(this.clientX,this.clientY),this.$pos)},this.inSelection=function(){if(this.$inSelection!==null)return this.$inSelection;var e=this.editor,t=e.getSelectionRange();if(t.isEmpty())this.$inSelection=!1;else{var n=this.getDocumentPosition();this.$inSelection=t.contains(n.row,n.column)}return this.$inSelection},this.getButton=function(){return r.getButton(this.domEvent)},this.getShiftKey=function(){return this.domEvent.shiftKey},this.getAccelKey=i.isMac?function(){return this.domEvent.metaKey}:function(){return this.domEvent.ctrlKey}}).call(s.prototype)}),ace.define("ace/mouse/dragdrop_handler",["require","exports","module","ace/lib/dom","ace/lib/event","ace/lib/useragent"],function(e,t,n){"use strict";function f(e){function T(e,n){var r=Date.now(),i=!n||e.row!=n.row,s=!n||e.column!=n.column;if(!S||i||s)t.moveCursorToPosition(e),S=r,x={x:p,y:d};else{var o=l(x.x,x.y,p,d);o>a?S=null:r-S>=u&&(t.renderer.scrollCursorIntoView(),S=null)}}function N(e,n){var r=Date.now(),i=t.renderer.layerConfig.lineHeight,s=t.renderer.layerConfig.characterWidth,u=t.renderer.scroller.getBoundingClientRect(),a={x:{left:p-u.left,right:u.right-p},y:{top:d-u.top,bottom:u.bottom-d}},f=Math.min(a.x.left,a.x.right),l=Math.min(a.y.top,a.y.bottom),c={row:e.row,column:e.column};f/s<=2&&(c.column+=a.x.left=o&&t.renderer.scrollCursorIntoView(c):E=r:E=null}function C(){var e=g;g=t.renderer.screenToTextCoordinates(p,d),T(g,e),N(g,e)}function k(){m=t.selection.toOrientedRange(),h=t.session.addMarker(m,"ace_selection",t.getSelectionStyle()),t.clearSelection(),t.isFocused()&&t.renderer.$cursorLayer.setBlinking(!1),clearInterval(v),C(),v=setInterval(C,20),y=0,i.addListener(document,"mousemove",O)}function L(){clearInterval(v),t.session.removeMarker(h),h=null,t.selection.fromOrientedRange(m),t.isFocused()&&!w&&t.$resetCursorStyle(),m=null,g=null,y=0,E=null,S=null,i.removeListener(document,"mousemove",O)}function O(){A==null&&(A=setTimeout(function(){A!=null&&h&&L()},20))}function M(e){var t=e.types;return!t||Array.prototype.some.call(t,function(e){return e=="text/plain"||e=="Text"})}function _(e){var t=["copy","copymove","all","uninitialized"],n=["move","copymove","linkmove","all","uninitialized"],r=s.isMac?e.altKey:e.ctrlKey,i="uninitialized";try{i=e.dataTransfer.effectAllowed.toLowerCase()}catch(e){}var o="none";return r&&t.indexOf(i)>=0?o="copy":n.indexOf(i)>=0?o="move":t.indexOf(i)>=0&&(o="copy"),o}var t=e.editor,n=r.createElement("img");n.src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==",s.isOpera&&(n.style.cssText="width:1px;height:1px;position:fixed;top:0;left:0;z-index:2147483647;opacity:0;");var f=["dragWait","dragWaitEnd","startDrag","dragReadyEnd","onMouseDrag"];f.forEach(function(t){e[t]=this[t]},this),t.addEventListener("mousedown",this.onMouseDown.bind(e));var c=t.container,h,p,d,v,m,g,y=0,b,w,E,S,x;this.onDragStart=function(e){if(this.cancelDrag||!c.draggable){var r=this;return setTimeout(function(){r.startSelect(),r.captureMouse(e)},0),e.preventDefault()}m=t.getSelectionRange();var i=e.dataTransfer;i.effectAllowed=t.getReadOnly()?"copy":"copyMove",s.isOpera&&(t.container.appendChild(n),n.scrollTop=0),i.setDragImage&&i.setDragImage(n,0,0),s.isOpera&&t.container.removeChild(n),i.clearData(),i.setData("Text",t.session.getTextRange()),w=!0,this.setState("drag")},this.onDragEnd=function(e){c.draggable=!1,w=!1,this.setState(null);if(!t.getReadOnly()){var n=e.dataTransfer.dropEffect;!b&&n=="move"&&t.session.remove(t.getSelectionRange()),t.$resetCursorStyle()}this.editor.unsetStyle("ace_dragging"),this.editor.renderer.setCursorStyle("")},this.onDragEnter=function(e){if(t.getReadOnly()||!M(e.dataTransfer))return;return p=e.clientX,d=e.clientY,h||k(),y++,e.dataTransfer.dropEffect=b=_(e),i.preventDefault(e)},this.onDragOver=function(e){if(t.getReadOnly()||!M(e.dataTransfer))return;return p=e.clientX,d=e.clientY,h||(k(),y++),A!==null&&(A=null),e.dataTransfer.dropEffect=b=_(e),i.preventDefault(e)},this.onDragLeave=function(e){y--;if(y<=0&&h)return L(),b=null,i.preventDefault(e)},this.onDrop=function(e){if(!g)return;var n=e.dataTransfer;if(w)switch(b){case"move":m.contains(g.row,g.column)?m={start:g,end:g}:m=t.moveText(m,g);break;case"copy":m=t.moveText(m,g,!0)}else{var r=n.getData("Text");m={start:g,end:t.session.insert(g,r)},t.focus(),b=null}return L(),i.preventDefault(e)},i.addListener(c,"dragstart",this.onDragStart.bind(e)),i.addListener(c,"dragend",this.onDragEnd.bind(e)),i.addListener(c,"dragenter",this.onDragEnter.bind(e)),i.addListener(c,"dragover",this.onDragOver.bind(e)),i.addListener(c,"dragleave",this.onDragLeave.bind(e)),i.addListener(c,"drop",this.onDrop.bind(e));var A=null}function l(e,t,n,r){return Math.sqrt(Math.pow(n-e,2)+Math.pow(r-t,2))}var r=e("../lib/dom"),i=e("../lib/event"),s=e("../lib/useragent"),o=200,u=200,a=5;(function(){this.dragWait=function(){var e=Date.now()-this.mousedownEvent.time;e>this.editor.getDragDelay()&&this.startDrag()},this.dragWaitEnd=function(){var e=this.editor.container;e.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()),this.selectEnd()},this.dragReadyEnd=function(e){this.editor.$resetCursorStyle(),this.editor.unsetStyle("ace_dragging"),this.editor.renderer.setCursorStyle(""),this.dragWaitEnd()},this.startDrag=function(){this.cancelDrag=!1;var e=this.editor,t=e.container;t.draggable=!0,e.renderer.$cursorLayer.setBlinking(!1),e.setStyle("ace_dragging");var n=s.isWin?"default":"move";e.renderer.setCursorStyle(n),this.setState("dragReady")},this.onMouseDrag=function(e){var t=this.editor.container;if(s.isIE&&this.state=="dragReady"){var n=l(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y);n>3&&t.dragDrop()}if(this.state==="dragWait"){var n=l(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y);n>0&&(t.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()))}},this.onMouseDown=function(e){if(!this.$dragEnabled)return;this.mousedownEvent=e;var t=this.editor,n=e.inSelection(),r=e.getButton(),i=e.domEvent.detail||1;if(i===1&&r===0&&n){if(e.editor.inMultiSelectMode&&(e.getAccelKey()||e.getShiftKey()))return;this.mousedownEvent.time=Date.now();var o=e.domEvent.target||e.domEvent.srcElement;"unselectable"in o&&(o.unselectable="on");if(t.getDragDelay()){if(s.isWebKit){this.cancelDrag=!0;var u=t.container;u.draggable=!0}this.setState("dragWait")}else this.startDrag();this.captureMouse(e,this.onMouseDrag.bind(this)),e.defaultPrevented=!0}}}).call(f.prototype),t.DragdropHandler=f}),ace.define("ace/mouse/touch_handler",["require","exports","module","ace/mouse/mouse_event"],function(e,t,n){"use strict";var r=e("./mouse_event").MouseEvent;t.addTouchListeners=function(e,t){function m(){a=null,clearTimeout(a),t.selection.isEmpty()&&t.selection.moveToPosition(c),n="wait"}function g(){a=null,clearTimeout(a),t.selection.moveToPosition(c);var e=h>=2?t.selection.getLineRange(c.row):t.session.getBracketRange(c);e&&!e.isEmpty()?t.selection.setRange(e):t.selection.selectWord(),n="wait"}function y(){l+=60,f=setInterval(function(){l--<=0&&(clearInterval(f),f=null),Math.abs(p)<.01&&(p=0),Math.abs(d)<.01&&(d=0),l<20&&(p=.9*p),l<20&&(d=.9*d),t.renderer.scrollBy(10*p,10*d)},10)}var n="scroll",i,s,o,u,a,f,l=0,c,h=0,p=0,d=0,v;e.addEventListener("contextmenu",function(e){if(!v)return;var n=t.textInput.getElement();n.focus()}),e.addEventListener("touchstart",function(e){var f=e.touches;if(a||f.length>1){clearTimeout(a),a=null,n="zoom";return}v=t.$mouseHandler.isMousePressed=!0;var y=f[0];i=y.clientX,s=y.clientY,p=d=0,e.clientX=y.clientX,e.clientY=y.clientY;var b=e.timeStamp;u=b;var w=new r(e,t);c=w.getDocumentPosition();if(b-o<500&&f.length==1&&!l)h++,e.preventDefault(),e.button=0,g();else{h=0,a=setTimeout(m,450);var E=t.selection.cursor,S=t.selection.isEmpty()?E:t.selection.anchor,x=t.renderer.$cursorLayer.getPixelPosition(E,!0),T=t.renderer.$cursorLayer.getPixelPosition(S,!0),N=t.renderer.scroller.getBoundingClientRect(),C=t.renderer.layerConfig.lineHeight,k=t.renderer.layerConfig.lineHeight,L=function(e,t){return e/=k,t=t/C-.75,e*e+t*t},A=L(e.clientX-N.left-x.left,e.clientY-N.top-x.top),O=L(e.clientX-N.left-T.left,e.clientY-N.top-T.top);A<3.5&&O<3.5&&(n=A>O?"cursor":"anchor"),O<3.5?n="anchor":A<3.5?n="cursor":n="scroll"}o=b}),e.addEventListener("touchend",function(e){v=t.$mouseHandler.isMousePressed=!1,f&&clearInterval(f),n=="zoom"?(n="",l=0):a?(t.selection.moveToPosition(c),l=0):n=="scroll"&&(y(),e.preventDefault()),clearTimeout(a),a=null}),e.addEventListener("touchmove",function(e){a&&(clearTimeout(a),a=null);var o=e.touches;if(o.length>1||n=="zoom")return;var f=o[0],l=i-f.clientX,c=s-f.clientY;if(n=="wait"){if(!(l*l+c*c>4))return e.preventDefault();n="cursor"}i=f.clientX,s=f.clientY,e.clientX=f.clientX,e.clientY=f.clientY;var h=e.timeStamp,v=h-u;u=h;if(n=="scroll"){var m=new r(e,t);m.speed=1,m.wheelX=l,m.wheelY=c,10*Math.abs(l)1&&(i=n[n.length-2]);var o=a[t+"Path"];return o==null?o=a.basePath:r=="/"&&(t=r=""),o&&o.slice(-1)!="/"&&(o+="/"),o+t+r+i+this.get("suffix")},t.setModuleUrl=function(e,t){return a.$moduleUrls[e]=t},t.$loading={},t.loadModule=function(n,r){var i,o;Array.isArray(n)&&(o=n[0],n=n[1]);try{i=e(n)}catch(u){}if(i&&!t.$loading[n])return r&&r(i);t.$loading[n]||(t.$loading[n]=[]),t.$loading[n].push(r);if(t.$loading[n].length>1)return;var a=function(){e([n],function(e){t._emit("load.module",{name:n,module:e});var r=t.$loading[n];t.$loading[n]=null,r.forEach(function(t){t&&t(e)})})};if(!t.get("packaged"))return a();s.loadScript(t.moduleUrl(n,o),a),f()};var f=function(){!a.basePath&&!a.workerPath&&!a.modePath&&!a.themePath&&!Object.keys(a.$moduleUrls).length&&(console.error("Unable to infer path to ace from script src,","use ace.config.set('basePath', 'path') to enable dynamic loading of modes and themes","or with webpack use ace/webpack-resolver"),f=function(){})};t.init=l,t.version="1.4.5"}),ace.define("ace/mouse/mouse_handler",["require","exports","module","ace/lib/event","ace/lib/useragent","ace/mouse/default_handlers","ace/mouse/default_gutter_handler","ace/mouse/mouse_event","ace/mouse/dragdrop_handler","ace/mouse/touch_handler","ace/config"],function(e,t,n){"use strict";var r=e("../lib/event"),i=e("../lib/useragent"),s=e("./default_handlers").DefaultHandlers,o=e("./default_gutter_handler").GutterHandler,u=e("./mouse_event").MouseEvent,a=e("./dragdrop_handler").DragdropHandler,f=e("./touch_handler").addTouchListeners,l=e("../config"),c=function(e){var t=this;this.editor=e,new s(this),new o(this),new a(this);var n=function(t){var n=!document.hasFocus||!document.hasFocus()||!e.isFocused()&&document.activeElement==(e.textInput&&e.textInput.getElement());n&&window.focus(),e.focus()},u=e.renderer.getMouseEventTarget();r.addListener(u,"click",this.onMouseEvent.bind(this,"click")),r.addListener(u,"mousemove",this.onMouseMove.bind(this,"mousemove")),r.addMultiMouseDownListener([u,e.renderer.scrollBarV&&e.renderer.scrollBarV.inner,e.renderer.scrollBarH&&e.renderer.scrollBarH.inner,e.textInput&&e.textInput.getElement()].filter(Boolean),[400,300,250],this,"onMouseEvent"),r.addMouseWheelListener(e.container,this.onMouseWheel.bind(this,"mousewheel")),f(e.container,e);var l=e.renderer.$gutter;r.addListener(l,"mousedown",this.onMouseEvent.bind(this,"guttermousedown")),r.addListener(l,"click",this.onMouseEvent.bind(this,"gutterclick")),r.addListener(l,"dblclick",this.onMouseEvent.bind(this,"gutterdblclick")),r.addListener(l,"mousemove",this.onMouseEvent.bind(this,"guttermousemove")),r.addListener(u,"mousedown",n),r.addListener(l,"mousedown",n),i.isIE&&e.renderer.scrollBarV&&(r.addListener(e.renderer.scrollBarV.element,"mousedown",n),r.addListener(e.renderer.scrollBarH.element,"mousedown",n)),e.on("mousemove",function(n){if(t.state||t.$dragDelay||!t.$dragEnabled)return;var r=e.renderer.screenToTextCoordinates(n.x,n.y),i=e.session.selection.getRange(),s=e.renderer;!i.isEmpty()&&i.insideStart(r.row,r.column)?s.setCursorStyle("default"):s.setCursorStyle("")})};(function(){this.onMouseEvent=function(e,t){this.editor._emit(e,new u(t,this.editor))},this.onMouseMove=function(e,t){var n=this.editor._eventRegistry&&this.editor._eventRegistry.mousemove;if(!n||!n.length)return;this.editor._emit(e,new u(t,this.editor))},this.onMouseWheel=function(e,t){var n=new u(t,this.editor);n.speed=this.$scrollSpeed*2,n.wheelX=t.wheelX,n.wheelY=t.wheelY,this.editor._emit(e,n)},this.setState=function(e){this.state=e},this.captureMouse=function(e,t){this.x=e.x,this.y=e.y,this.isMousePressed=!0;var n=this.editor,s=this.editor.renderer;s.$isMousePressed=!0;var o=this,a=function(e){if(!e)return;if(i.isWebKit&&!e.which&&o.releaseMouse)return o.releaseMouse();o.x=e.clientX,o.y=e.clientY,t&&t(e),o.mouseEvent=new u(e,o.editor),o.$mouseMoved=!0},f=function(e){n.off("beforeEndOperation",c),clearInterval(h),l(),o[o.state+"End"]&&o[o.state+"End"](e),o.state="",o.isMousePressed=s.$isMousePressed=!1,s.$keepTextAreaAtCursor&&s.$moveTextAreaToCursor(),o.$onCaptureMouseMove=o.releaseMouse=null,e&&o.onMouseEvent("mouseup",e),n.endOperation()},l=function(){o[o.state]&&o[o.state](),o.$mouseMoved=!1};if(i.isOldIE&&e.domEvent.type=="dblclick")return setTimeout(function(){f(e)});var c=function(e){if(!o.releaseMouse)return;n.curOp.command.name&&n.curOp.selectionChanged&&(o[o.state+"End"]&&o[o.state+"End"](),o.state="",o.releaseMouse())};n.on("beforeEndOperation",c),n.startOperation({command:{name:"mouse"}}),o.$onCaptureMouseMove=a,o.releaseMouse=r.capture(this.editor.container,a,f);var h=setInterval(l,20)},this.releaseMouse=null,this.cancelContextMenu=function(){var e=function(t){if(t&&t.domEvent&&t.domEvent.type!="contextmenu")return;this.editor.off("nativecontextmenu",e),t&&t.domEvent&&r.stopEvent(t.domEvent)}.bind(this);setTimeout(e,10),this.editor.on("nativecontextmenu",e)}}).call(c.prototype),l.defineOptions(c.prototype,"mouseHandler",{scrollSpeed:{initialValue:2},dragDelay:{initialValue:i.isMac?150:0},dragEnabled:{initialValue:!0},focusTimeout:{initialValue:0},tooltipFollowsMouse:{initialValue:!0}}),t.MouseHandler=c}),ace.define("ace/mouse/fold_handler",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";function i(e){e.on("click",function(t){var n=t.getDocumentPosition(),i=e.session,s=i.getFoldAt(n.row,n.column,1);s&&(t.getAccelKey()?i.removeFold(s):i.expandFold(s),t.stop());var o=t.domEvent&&t.domEvent.target;o&&r.hasCssClass(o,"ace_inline_button")&&r.hasCssClass(o,"ace_toggle_wrap")&&(i.setOption("wrap",!i.getUseWrapMode()),e.renderer.scrollCursorIntoView())}),e.on("gutterclick",function(t){var n=e.renderer.$gutterLayer.getRegion(t);if(n=="foldWidgets"){var r=t.getDocumentPosition().row,i=e.session;i.foldWidgets&&i.foldWidgets[r]&&e.session.onFoldWidgetClick(r,t),e.isFocused()||e.focus(),t.stop()}}),e.on("gutterdblclick",function(t){var n=e.renderer.$gutterLayer.getRegion(t);if(n=="foldWidgets"){var r=t.getDocumentPosition().row,i=e.session,s=i.getParentFoldRangeData(r,!0),o=s.range||s.firstRange;if(o){r=o.start.row;var u=i.getFoldAt(r,i.getLine(r).length,1);u?i.removeFold(u):(i.addFold("...",o),e.renderer.scrollCursorIntoView({row:o.start.row,column:0}))}t.stop()}})}var r=e("../lib/dom");t.FoldHandler=i}),ace.define("ace/keyboard/keybinding",["require","exports","module","ace/lib/keys","ace/lib/event"],function(e,t,n){"use strict";var r=e("../lib/keys"),i=e("../lib/event"),s=function(e){this.$editor=e,this.$data={editor:e},this.$handlers=[],this.setDefaultHandler(e.commands)};(function(){this.setDefaultHandler=function(e){this.removeKeyboardHandler(this.$defaultHandler),this.$defaultHandler=e,this.addKeyboardHandler(e,0)},this.setKeyboardHandler=function(e){var t=this.$handlers;if(t[t.length-1]==e)return;while(t[t.length-1]&&t[t.length-1]!=this.$defaultHandler)this.removeKeyboardHandler(t[t.length-1]);this.addKeyboardHandler(e,1)},this.addKeyboardHandler=function(e,t){if(!e)return;typeof e=="function"&&!e.handleKeyboard&&(e.handleKeyboard=e);var n=this.$handlers.indexOf(e);n!=-1&&this.$handlers.splice(n,1),t==undefined?this.$handlers.push(e):this.$handlers.splice(t,0,e),n==-1&&e.attach&&e.attach(this.$editor)},this.removeKeyboardHandler=function(e){var t=this.$handlers.indexOf(e);return t==-1?!1:(this.$handlers.splice(t,1),e.detach&&e.detach(this.$editor),!0)},this.getKeyboardHandler=function(){return this.$handlers[this.$handlers.length-1]},this.getStatusText=function(){var e=this.$data,t=e.editor;return this.$handlers.map(function(n){return n.getStatusText&&n.getStatusText(t,e)||""}).filter(Boolean).join(" ")},this.$callKeyboardHandlers=function(e,t,n,r){var s,o=!1,u=this.$editor.commands;for(var a=this.$handlers.length;a--;){s=this.$handlers[a].handleKeyboard(this.$data,e,t,n,r);if(!s||!s.command)continue;s.command=="null"?o=!0:o=u.exec(s.command,this.$editor,s.args,r),o&&r&&e!=-1&&s.passEvent!=1&&s.command.passEvent!=1&&i.stopEvent(r);if(o)break}return!o&&e==-1&&(s={command:"insertstring"},o=u.exec("insertstring",this.$editor,t)),o&&this.$editor._signal&&this.$editor._signal("keyboardActivity",s),o},this.onCommandKey=function(e,t,n){var i=r.keyCodeToString(n);this.$callKeyboardHandlers(t,i,n,e)},this.onTextInput=function(e){this.$callKeyboardHandlers(-1,e)}}).call(s.prototype),t.KeyBinding=s}),ace.define("ace/lib/bidiutil",["require","exports","module"],function(e,t,n){"use strict";function F(e,t,n,r){var i=s?d:p,c=null,h=null,v=null,m=0,g=null,y=null,b=-1,w=null,E=null,T=[];if(!r)for(w=0,r=[];w0)if(g==16){for(w=b;w-1){for(w=b;w=0;C--){if(r[C]!=N)break;t[C]=s}}}function I(e,t,n){if(o=e){u=i+1;while(u=e)u++;for(a=i,l=u-1;a=t.length||(o=n[r-1])!=b&&o!=w||(c=t[r+1])!=b&&c!=w)return E;return u&&(c=w),c==o?c:E;case k:o=r>0?n[r-1]:S;if(o==b&&r+10&&n[r-1]==b)return b;if(u)return E;p=r+1,h=t.length;while(p=1425&&d<=2303||d==64286;o=t[p];if(v&&(o==y||o==T))return y}if(r<1||(o=t[r-1])==S)return E;return n[r-1];case S:return u=!1,f=!0,s;case x:return l=!0,E;case O:case M:case D:case P:case _:u=!1;case H:return E}}function R(e){var t=e.charCodeAt(0),n=t>>8;return n==0?t>191?g:B[t]:n==5?/[\u0591-\u05f4]/.test(e)?y:g:n==6?/[\u0610-\u061a\u064b-\u065f\u06d6-\u06e4\u06e7-\u06ed]/.test(e)?A:/[\u0660-\u0669\u066b-\u066c]/.test(e)?w:t==1642?L:/[\u06f0-\u06f9]/.test(e)?b:T:n==32&&t<=8287?j[t&255]:n==254?t>=65136?T:E:E}function U(e){return e>="\u064b"&&e<="\u0655"}var r=["\u0621","\u0641"],i=["\u063a","\u064a"],s=0,o=0,u=!1,a=!1,f=!1,l=!1,c=!1,h=!1,p=[[0,3,0,1,0,0,0],[0,3,0,1,2,2,0],[0,3,0,17,2,0,1],[0,3,5,5,4,1,0],[0,3,21,21,4,0,1],[0,3,5,5,4,2,0]],d=[[2,0,1,1,0,1,0],[2,0,1,1,0,2,0],[2,0,2,1,3,2,0],[2,0,2,33,3,1,1]],v=0,m=1,g=0,y=1,b=2,w=3,E=4,S=5,x=6,T=7,N=8,C=9,k=10,L=11,A=12,O=13,M=14,_=15,D=16,P=17,H=18,B=[H,H,H,H,H,H,H,H,H,x,S,x,N,S,H,H,H,H,H,H,H,H,H,H,H,H,H,H,S,S,S,x,N,E,E,L,L,L,E,E,E,E,E,k,C,k,C,C,b,b,b,b,b,b,b,b,b,b,C,E,E,E,E,E,E,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,E,E,E,E,E,E,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,E,E,E,E,H,H,H,H,H,H,S,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,C,E,L,L,L,L,E,E,E,E,g,E,E,H,E,E,L,L,b,b,E,g,E,E,E,b,g,E,E,E,E,E],j=[N,N,N,N,N,N,N,N,N,N,N,H,H,H,g,y,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,N,S,O,M,_,D,P,C,L,L,L,L,L,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,C,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,N];t.L=g,t.R=y,t.EN=b,t.ON_R=3,t.AN=4,t.R_H=5,t.B=6,t.RLE=7,t.DOT="\u00b7",t.doBidiReorder=function(e,n,r){if(e.length<2)return{};var i=e.split(""),o=new Array(i.length),u=new Array(i.length),a=[];s=r?m:v,F(i,a,i.length,n);for(var f=0;fT&&n[f]0&&i[f-1]==="\u0644"&&/\u0622|\u0623|\u0625|\u0627/.test(i[f])&&(a[f-1]=a[f]=t.R_H,f++);i[i.length-1]===t.DOT&&(a[i.length-1]=t.B),i[0]==="\u202b"&&(a[0]=t.RLE);for(var f=0;f=0&&(e=this.session.$docRowCache[n])}return e},this.getSplitIndex=function(){var e=0,t=this.session.$screenRowCache;if(t.length){var n,r=this.session.$getRowCacheIndex(t,this.currentRow);while(this.currentRow-e>0){n=this.session.$getRowCacheIndex(t,this.currentRow-e-1);if(n!==r)break;r=n,e++}}else e=this.currentRow;return e},this.updateRowLine=function(e,t){e===undefined&&(e=this.getDocumentRow());var n=e===this.session.getLength()-1,s=n?this.EOF:this.EOL;this.wrapIndent=0,this.line=this.session.getLine(e),this.isRtlDir=this.$isRtl||this.line.charAt(0)===this.RLE;if(this.session.$useWrapMode){var o=this.session.$wrapData[e];o&&(t===undefined&&(t=this.getSplitIndex()),t>0&&o.length?(this.wrapIndent=o.indent,this.wrapOffset=this.wrapIndent*this.charWidths[r.L],this.line=tt?this.session.getOverwrite()?e:e-1:t,i=r.getVisualFromLogicalIdx(n,this.bidiMap),s=this.bidiMap.bidiLevels,o=0;!this.session.getOverwrite()&&e<=t&&s[i]%2!==0&&i++;for(var u=0;ut&&s[i]%2===0&&(o+=this.charWidths[s[i]]),this.wrapIndent&&(o+=this.isRtlDir?-1*this.wrapOffset:this.wrapOffset),this.isRtlDir&&(o+=this.rtlLineOffset),o},this.getSelections=function(e,t){var n=this.bidiMap,r=n.bidiLevels,i,s=[],o=0,u=Math.min(e,t)-this.wrapIndent,a=Math.max(e,t)-this.wrapIndent,f=!1,l=!1,c=0;this.wrapIndent&&(o+=this.isRtlDir?-1*this.wrapOffset:this.wrapOffset);for(var h,p=0;p=u&&hn+s/2){n+=s;if(r===i.length-1){s=0;break}s=this.charWidths[i[++r]]}return r>0&&i[r-1]%2!==0&&i[r]%2===0?(e0&&i[r-1]%2===0&&i[r]%2!==0?t=1+(e>n?this.bidiMap.logicalFromVisual[r]:this.bidiMap.logicalFromVisual[r-1]):this.isRtlDir&&r===i.length-1&&s===0&&i[r-1]%2===0||!this.isRtlDir&&r===0&&i[r]%2!==0?t=1+this.bidiMap.logicalFromVisual[r]:(r>0&&i[r-1]%2!==0&&s!==0&&r--,t=this.bidiMap.logicalFromVisual[r]),t===0&&this.isRtlDir&&t++,t+this.wrapIndent}}).call(o.prototype),t.BidiHandler=o}),ace.define("ace/selection",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/lib/event_emitter","ace/range"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/lang"),s=e("./lib/event_emitter").EventEmitter,o=e("./range").Range,u=function(e){this.session=e,this.doc=e.getDocument(),this.clearSelection(),this.cursor=this.lead=this.doc.createAnchor(0,0),this.anchor=this.doc.createAnchor(0,0),this.$silent=!1;var t=this;this.cursor.on("change",function(e){t.$cursorChanged=!0,t.$silent||t._emit("changeCursor"),!t.$isEmpty&&!t.$silent&&t._emit("changeSelection"),!t.$keepDesiredColumnOnChange&&e.old.column!=e.value.column&&(t.$desiredColumn=null)}),this.anchor.on("change",function(){t.$anchorChanged=!0,!t.$isEmpty&&!t.$silent&&t._emit("changeSelection")})};(function(){r.implement(this,s),this.isEmpty=function(){return this.$isEmpty||this.anchor.row==this.lead.row&&this.anchor.column==this.lead.column},this.isMultiLine=function(){return!this.$isEmpty&&this.anchor.row!=this.cursor.row},this.getCursor=function(){return this.lead.getPosition()},this.setSelectionAnchor=function(e,t){this.$isEmpty=!1,this.anchor.setPosition(e,t)},this.getAnchor=this.getSelectionAnchor=function(){return this.$isEmpty?this.getSelectionLead():this.anchor.getPosition()},this.getSelectionLead=function(){return this.lead.getPosition()},this.isBackwards=function(){var e=this.anchor,t=this.lead;return e.row>t.row||e.row==t.row&&e.column>t.column},this.getRange=function(){var e=this.anchor,t=this.lead;return this.$isEmpty?o.fromPoints(t,t):this.isBackwards()?o.fromPoints(t,e):o.fromPoints(e,t)},this.clearSelection=function(){this.$isEmpty||(this.$isEmpty=!0,this._emit("changeSelection"))},this.selectAll=function(){this.$setSelection(0,0,Number.MAX_VALUE,Number.MAX_VALUE)},this.setRange=this.setSelectionRange=function(e,t){var n=t?e.end:e.start,r=t?e.start:e.end;this.$setSelection(n.row,n.column,r.row,r.column)},this.$setSelection=function(e,t,n,r){var i=this.$isEmpty,s=this.inMultiSelectMode;this.$silent=!0,this.$cursorChanged=this.$anchorChanged=!1,this.anchor.setPosition(e,t),this.cursor.setPosition(n,r),this.$isEmpty=!o.comparePoints(this.anchor,this.cursor),this.$silent=!1,this.$cursorChanged&&this._emit("changeCursor"),(this.$cursorChanged||this.$anchorChanged||i!=this.$isEmpty||s)&&this._emit("changeSelection")},this.$moveSelection=function(e){var t=this.lead;this.$isEmpty&&this.setSelectionAnchor(t.row,t.column),e.call(this)},this.selectTo=function(e,t){this.$moveSelection(function(){this.moveCursorTo(e,t)})},this.selectToPosition=function(e){this.$moveSelection(function(){this.moveCursorToPosition(e)})},this.moveTo=function(e,t){this.clearSelection(),this.moveCursorTo(e,t)},this.moveToPosition=function(e){this.clearSelection(),this.moveCursorToPosition(e)},this.selectUp=function(){this.$moveSelection(this.moveCursorUp)},this.selectDown=function(){this.$moveSelection(this.moveCursorDown)},this.selectRight=function(){this.$moveSelection(this.moveCursorRight)},this.selectLeft=function(){this.$moveSelection(this.moveCursorLeft)},this.selectLineStart=function(){this.$moveSelection(this.moveCursorLineStart)},this.selectLineEnd=function(){this.$moveSelection(this.moveCursorLineEnd)},this.selectFileEnd=function(){this.$moveSelection(this.moveCursorFileEnd)},this.selectFileStart=function(){this.$moveSelection(this.moveCursorFileStart)},this.selectWordRight=function(){this.$moveSelection(this.moveCursorWordRight)},this.selectWordLeft=function(){this.$moveSelection(this.moveCursorWordLeft)},this.getWordRange=function(e,t){if(typeof t=="undefined"){var n=e||this.lead;e=n.row,t=n.column}return this.session.getWordRange(e,t)},this.selectWord=function(){this.setSelectionRange(this.getWordRange())},this.selectAWord=function(){var e=this.getCursor(),t=this.session.getAWordRange(e.row,e.column);this.setSelectionRange(t)},this.getLineRange=function(e,t){var n=typeof e=="number"?e:this.lead.row,r,i=this.session.getFoldLine(n);return i?(n=i.start.row,r=i.end.row):r=n,t===!0?new o(n,0,r,this.session.getLine(r).length):new o(n,0,r+1,0)},this.selectLine=function(){this.setSelectionRange(this.getLineRange())},this.moveCursorUp=function(){this.moveCursorBy(-1,0)},this.moveCursorDown=function(){this.moveCursorBy(1,0)},this.wouldMoveIntoSoftTab=function(e,t,n){var r=e.column,i=e.column+t;return n<0&&(r=e.column-t,i=e.column),this.session.isTabStop(e)&&this.doc.getLine(e.row).slice(r,i).split(" ").length-1==t},this.moveCursorLeft=function(){var e=this.lead.getPosition(),t;if(t=this.session.getFoldAt(e.row,e.column,-1))this.moveCursorTo(t.start.row,t.start.column);else if(e.column===0)e.row>0&&this.moveCursorTo(e.row-1,this.doc.getLine(e.row-1).length);else{var n=this.session.getTabSize();this.wouldMoveIntoSoftTab(e,n,-1)&&!this.session.getNavigateWithinSoftTabs()?this.moveCursorBy(0,-n):this.moveCursorBy(0,-1)}},this.moveCursorRight=function(){var e=this.lead.getPosition(),t;if(t=this.session.getFoldAt(e.row,e.column,1))this.moveCursorTo(t.end.row,t.end.column);else if(this.lead.column==this.doc.getLine(this.lead.row).length)this.lead.row0&&(t.column=r)}}this.moveCursorTo(t.row,t.column)},this.moveCursorFileEnd=function(){var e=this.doc.getLength()-1,t=this.doc.getLine(e).length;this.moveCursorTo(e,t)},this.moveCursorFileStart=function(){this.moveCursorTo(0,0)},this.moveCursorLongWordRight=function(){var e=this.lead.row,t=this.lead.column,n=this.doc.getLine(e),r=n.substring(t);this.session.nonTokenRe.lastIndex=0,this.session.tokenRe.lastIndex=0;var i=this.session.getFoldAt(e,t,1);if(i){this.moveCursorTo(i.end.row,i.end.column);return}this.session.nonTokenRe.exec(r)&&(t+=this.session.nonTokenRe.lastIndex,this.session.nonTokenRe.lastIndex=0,r=n.substring(t));if(t>=n.length){this.moveCursorTo(e,n.length),this.moveCursorRight(),e0&&this.moveCursorWordLeft();return}this.session.tokenRe.exec(s)&&(t-=this.session.tokenRe.lastIndex,this.session.tokenRe.lastIndex=0),this.moveCursorTo(e,t)},this.$shortWordEndIndex=function(e){var t=0,n,r=/\s/,i=this.session.tokenRe;i.lastIndex=0;if(this.session.tokenRe.exec(e))t=this.session.tokenRe.lastIndex;else{while((n=e[t])&&r.test(n))t++;if(t<1){i.lastIndex=0;while((n=e[t])&&!i.test(n)){i.lastIndex=0,t++;if(r.test(n)){if(t>2){t--;break}while((n=e[t])&&r.test(n))t++;if(t>2)break}}}}return i.lastIndex=0,t},this.moveCursorShortWordRight=function(){var e=this.lead.row,t=this.lead.column,n=this.doc.getLine(e),r=n.substring(t),i=this.session.getFoldAt(e,t,1);if(i)return this.moveCursorTo(i.end.row,i.end.column);if(t==n.length){var s=this.doc.getLength();do e++,r=this.doc.getLine(e);while(e0&&/^\s*$/.test(r));t=r.length,/\s+$/.test(r)||(r="")}var s=i.stringReverse(r),o=this.$shortWordEndIndex(s);return this.moveCursorTo(e,t-o)},this.moveCursorWordRight=function(){this.session.$selectLongWords?this.moveCursorLongWordRight():this.moveCursorShortWordRight()},this.moveCursorWordLeft=function(){this.session.$selectLongWords?this.moveCursorLongWordLeft():this.moveCursorShortWordLeft()},this.moveCursorBy=function(e,t){var n=this.session.documentToScreenPosition(this.lead.row,this.lead.column),r;t===0&&(e!==0&&(this.session.$bidiHandler.isBidiRow(n.row,this.lead.row)?(r=this.session.$bidiHandler.getPosLeft(n.column),n.column=Math.round(r/this.session.$bidiHandler.charWidths[0])):r=n.column*this.session.$bidiHandler.charWidths[0]),this.$desiredColumn?n.column=this.$desiredColumn:this.$desiredColumn=n.column);var i=this.session.screenToDocumentPosition(n.row+e,n.column,r);e!==0&&t===0&&i.row===this.lead.row&&i.column===this.lead.column&&this.session.lineWidgets&&this.session.lineWidgets[i.row]&&(i.row>0||e>0)&&i.row++,this.moveCursorTo(i.row,i.column+t,t===0)},this.moveCursorToPosition=function(e){this.moveCursorTo(e.row,e.column)},this.moveCursorTo=function(e,t,n){var r=this.session.getFoldAt(e,t,1);r&&(e=r.start.row,t=r.start.column),this.$keepDesiredColumnOnChange=!0;var i=this.session.getLine(e);/[\uDC00-\uDFFF]/.test(i.charAt(t))&&i.charAt(t-1)&&(this.lead.row==e&&this.lead.column==t+1?t-=1:t+=1),this.lead.setPosition(e,t),this.$keepDesiredColumnOnChange=!1,n||(this.$desiredColumn=null)},this.moveCursorToScreen=function(e,t,n){var r=this.session.screenToDocumentPosition(e,t);this.moveCursorTo(r.row,r.column,n)},this.detach=function(){this.lead.detach(),this.anchor.detach(),this.session=this.doc=null},this.fromOrientedRange=function(e){this.setSelectionRange(e,e.cursor==e.start),this.$desiredColumn=e.desiredColumn||this.$desiredColumn},this.toOrientedRange=function(e){var t=this.getRange();return e?(e.start.column=t.start.column,e.start.row=t.start.row,e.end.column=t.end.column,e.end.row=t.end.row):e=t,e.cursor=this.isBackwards()?e.start:e.end,e.desiredColumn=this.$desiredColumn,e},this.getRangeOfMovements=function(e){var t=this.getCursor();try{e(this);var n=this.getCursor();return o.fromPoints(t,n)}catch(r){return o.fromPoints(t,t)}finally{this.moveCursorToPosition(t)}},this.toJSON=function(){if(this.rangeCount)var e=this.ranges.map(function(e){var t=e.clone();return t.isBackwards=e.cursor==e.start,t});else{var e=this.getRange();e.isBackwards=this.isBackwards()}return e},this.fromJSON=function(e){if(e.start==undefined){if(this.rangeList&&e.length>1){this.toSingleRange(e[0]);for(var t=e.length;t--;){var n=o.fromPoints(e[t].start,e[t].end);e[t].isBackwards&&(n.cursor=n.start),this.addRange(n,!0)}return}e=e[0]}this.rangeList&&this.toSingleRange(e),this.setSelectionRange(e,e.isBackwards)},this.isEqual=function(e){if((e.length||this.rangeCount)&&e.length!=this.rangeCount)return!1;if(!e.length||!this.ranges)return this.getRange().isEqual(e);for(var t=this.ranges.length;t--;)if(!this.ranges[t].isEqual(e[t]))return!1;return!0}}).call(u.prototype),t.Selection=u}),ace.define("ace/tokenizer",["require","exports","module","ace/config"],function(e,t,n){"use strict";var r=e("./config"),i=2e3,s=function(e){this.states=e,this.regExps={},this.matchMappings={};for(var t in this.states){var n=this.states[t],r=[],i=0,s=this.matchMappings[t]={defaultToken:"text"},o="g",u=[];for(var a=0;a1?f.onMatch=this.$applyToken:f.onMatch=f.token),c>1&&(/\\\d/.test(f.regex)?l=f.regex.replace(/\\([0-9]+)/g,function(e,t){return"\\"+(parseInt(t,10)+i+1)}):(c=1,l=this.removeCapturingGroups(f.regex)),!f.splitRegex&&typeof f.token!="string"&&u.push(f)),s[i]=a,i+=c,r.push(l),f.onMatch||(f.onMatch=null)}r.length||(s[0]=0,r.push("$")),u.forEach(function(e){e.splitRegex=this.createSplitterRegexp(e.regex,o)},this),this.regExps[t]=new RegExp("("+r.join(")|(")+")|($)",o)}};(function(){this.$setMaxTokenCount=function(e){i=e|0},this.$applyToken=function(e){var t=this.splitRegex.exec(e).slice(1),n=this.token.apply(this,t);if(typeof n=="string")return[{type:n,value:e}];var r=[];for(var i=0,s=n.length;il){var g=e.substring(l,m-v.length);h.type==p?h.value+=g:(h.type&&f.push(h),h={type:p,value:g})}for(var y=0;yi){c>2*e.length&&this.reportError("infinite loop with in ace tokenizer",{startState:t,line:e});while(l1&&n[0]!==r&&n.unshift("#tmp",r),{tokens:f,state:n.length?n:r}},this.reportError=r.reportError}).call(s.prototype),t.Tokenizer=s}),ace.define("ace/mode/text_highlight_rules",["require","exports","module","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../lib/lang"),i=function(){this.$rules={start:[{token:"empty_line",regex:"^$"},{defaultToken:"text"}]}};(function(){this.addRules=function(e,t){if(!t){for(var n in e)this.$rules[n]=e[n];return}for(var n in e){var r=e[n];for(var i=0;i=this.$rowTokens.length){this.$row+=1,e||(e=this.$session.getLength());if(this.$row>=e)return this.$row=e-1,null;this.$rowTokens=this.$session.getTokens(this.$row),this.$tokenIndex=0}return this.$rowTokens[this.$tokenIndex]},this.getCurrentToken=function(){return this.$rowTokens[this.$tokenIndex]},this.getCurrentTokenRow=function(){return this.$row},this.getCurrentTokenColumn=function(){var e=this.$rowTokens,t=this.$tokenIndex,n=e[t].start;if(n!==undefined)return n;n=0;while(t>0)t-=1,n+=e[t].value.length;return n},this.getCurrentTokenPosition=function(){return{row:this.$row,column:this.getCurrentTokenColumn()}},this.getCurrentTokenRange=function(){var e=this.$rowTokens[this.$tokenIndex],t=this.getCurrentTokenColumn();return new r(this.$row,t,this.$row,t+e.value.length)}}).call(i.prototype),t.TokenIterator=i}),ace.define("ace/mode/behaviour/cstyle",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),u=["text","paren.rparen","punctuation.operator"],a=["text","paren.rparen","punctuation.operator","comment"],f,l={},c={'"':'"',"'":"'"},h=function(e){var t=-1;e.multiSelect&&(t=e.selection.index,l.rangeCount!=e.multiSelect.rangeCount&&(l={rangeCount:e.multiSelect.rangeCount}));if(l[t])return f=l[t];f=l[t]={autoInsertedBrackets:0,autoInsertedRow:-1,autoInsertedLineEnd:"",maybeInsertedBrackets:0,maybeInsertedRow:-1,maybeInsertedLineStart:"",maybeInsertedLineEnd:""}},p=function(e,t,n,r){var i=e.end.row-e.start.row;return{text:n+t+r,selection:[0,e.start.column+1,i,e.end.column+(i?0:1)]}},d=function(e){this.add("braces","insertion",function(t,n,r,i,s){var u=r.getCursorPosition(),a=i.doc.getLine(u.row);if(s=="{"){h(r);var l=r.getSelectionRange(),c=i.doc.getTextRange(l);if(c!==""&&c!=="{"&&r.getWrapBehavioursEnabled())return p(l,c,"{","}");if(d.isSaneInsertion(r,i))return/[\]\}\)]/.test(a[u.column])||r.inMultiSelectMode||e&&e.braces?(d.recordAutoInsert(r,i,"}"),{text:"{}",selection:[1,1]}):(d.recordMaybeInsert(r,i,"{"),{text:"{",selection:[1,1]})}else if(s=="}"){h(r);var v=a.substring(u.column,u.column+1);if(v=="}"){var m=i.$findOpeningBracket("}",{column:u.column+1,row:u.row});if(m!==null&&d.isAutoInsertedClosing(u,a,s))return d.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}else{if(s=="\n"||s=="\r\n"){h(r);var g="";d.isMaybeInsertedClosing(u,a)&&(g=o.stringRepeat("}",f.maybeInsertedBrackets),d.clearMaybeInsertedClosing());var v=a.substring(u.column,u.column+1);if(v==="}"){var y=i.findMatchingBracket({row:u.row,column:u.column+1},"}");if(!y)return null;var b=this.$getIndent(i.getLine(y.row))}else{if(!g){d.clearMaybeInsertedClosing();return}var b=this.$getIndent(a)}var w=b+i.getTabString();return{text:"\n"+w+"\n"+b+g,selection:[1,w.length,1,w.length]}}d.clearMaybeInsertedClosing()}}),this.add("braces","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="{"){h(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.end.column,i.end.column+1);if(u=="}")return i.end.column++,i;f.maybeInsertedBrackets--}}),this.add("parens","insertion",function(e,t,n,r,i){if(i=="("){h(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return p(s,o,"(",")");if(d.isSaneInsertion(n,r))return d.recordAutoInsert(n,r,")"),{text:"()",selection:[1,1]}}else if(i==")"){h(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f==")"){var l=r.$findOpeningBracket(")",{column:u.column+1,row:u.row});if(l!==null&&d.isAutoInsertedClosing(u,a,i))return d.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("parens","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="("){h(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==")")return i.end.column++,i}}),this.add("brackets","insertion",function(e,t,n,r,i){if(i=="["){h(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return p(s,o,"[","]");if(d.isSaneInsertion(n,r))return d.recordAutoInsert(n,r,"]"),{text:"[]",selection:[1,1]}}else if(i=="]"){h(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f=="]"){var l=r.$findOpeningBracket("]",{column:u.column+1,row:u.row});if(l!==null&&d.isAutoInsertedClosing(u,a,i))return d.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("brackets","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="["){h(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u=="]")return i.end.column++,i}}),this.add("string_dquotes","insertion",function(e,t,n,r,i){var s=r.$mode.$quotes||c;if(i.length==1&&s[i]){if(this.lineCommentStart&&this.lineCommentStart.indexOf(i)!=-1)return;h(n);var o=i,u=n.getSelectionRange(),a=r.doc.getTextRange(u);if(a!==""&&(a.length!=1||!s[a])&&n.getWrapBehavioursEnabled())return p(u,a,o,o);if(!a){var f=n.getCursorPosition(),l=r.doc.getLine(f.row),d=l.substring(f.column-1,f.column),v=l.substring(f.column,f.column+1),m=r.getTokenAt(f.row,f.column),g=r.getTokenAt(f.row,f.column+1);if(d=="\\"&&m&&/escape/.test(m.type))return null;var y=m&&/string|escape/.test(m.type),b=!g||/string|escape/.test(g.type),w;if(v==o)w=y!==b,w&&/string\.end/.test(g.type)&&(w=!1);else{if(y&&!b)return null;if(y&&b)return null;var E=r.$mode.tokenRe;E.lastIndex=0;var S=E.test(d);E.lastIndex=0;var x=E.test(d);if(S||x)return null;if(v&&!/[\s;,.})\]\\]/.test(v))return null;var T=l[f.column-2];if(!(d!=o||T!=o&&!E.test(T)))return null;w=!0}return{text:w?o+o:"",selection:[1,1]}}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.$mode.$quotes||c,o=r.doc.getTextRange(i);if(!i.isMultiLine()&&s.hasOwnProperty(o)){h(n);var u=r.doc.getLine(i.start.row),a=u.substring(i.start.column+1,i.start.column+2);if(a==o)return i.end.column++,i}})};d.isSaneInsertion=function(e,t){var n=e.getCursorPosition(),r=new s(t,n.row,n.column);if(!this.$matchTokenType(r.getCurrentToken()||"text",u)){var i=new s(t,n.row,n.column+1);if(!this.$matchTokenType(i.getCurrentToken()||"text",u))return!1}return r.stepForward(),r.getCurrentTokenRow()!==n.row||this.$matchTokenType(r.getCurrentToken()||"text",a)},d.$matchTokenType=function(e,t){return t.indexOf(e.type||e)>-1},d.recordAutoInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isAutoInsertedClosing(r,i,f.autoInsertedLineEnd[0])||(f.autoInsertedBrackets=0),f.autoInsertedRow=r.row,f.autoInsertedLineEnd=n+i.substr(r.column),f.autoInsertedBrackets++},d.recordMaybeInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isMaybeInsertedClosing(r,i)||(f.maybeInsertedBrackets=0),f.maybeInsertedRow=r.row,f.maybeInsertedLineStart=i.substr(0,r.column)+n,f.maybeInsertedLineEnd=i.substr(r.column),f.maybeInsertedBrackets++},d.isAutoInsertedClosing=function(e,t,n){return f.autoInsertedBrackets>0&&e.row===f.autoInsertedRow&&n===f.autoInsertedLineEnd[0]&&t.substr(e.column)===f.autoInsertedLineEnd},d.isMaybeInsertedClosing=function(e,t){return f.maybeInsertedBrackets>0&&e.row===f.maybeInsertedRow&&t.substr(e.column)===f.maybeInsertedLineEnd&&t.substr(0,e.column)==f.maybeInsertedLineStart},d.popAutoInsertedClosing=function(){f.autoInsertedLineEnd=f.autoInsertedLineEnd.substr(1),f.autoInsertedBrackets--},d.clearMaybeInsertedClosing=function(){f&&(f.maybeInsertedBrackets=0,f.maybeInsertedRow=-1)},r.inherits(d,i),t.CstyleBehaviour=d}),ace.define("ace/unicode",["require","exports","module"],function(e,t,n){"use strict";var r=[48,9,8,25,5,0,2,25,48,0,11,0,5,0,6,22,2,30,2,457,5,11,15,4,8,0,2,0,18,116,2,1,3,3,9,0,2,2,2,0,2,19,2,82,2,138,2,4,3,155,12,37,3,0,8,38,10,44,2,0,2,1,2,1,2,0,9,26,6,2,30,10,7,61,2,9,5,101,2,7,3,9,2,18,3,0,17,58,3,100,15,53,5,0,6,45,211,57,3,18,2,5,3,11,3,9,2,1,7,6,2,2,2,7,3,1,3,21,2,6,2,0,4,3,3,8,3,1,3,3,9,0,5,1,2,4,3,11,16,2,2,5,5,1,3,21,2,6,2,1,2,1,2,1,3,0,2,4,5,1,3,2,4,0,8,3,2,0,8,15,12,2,2,8,2,2,2,21,2,6,2,1,2,4,3,9,2,2,2,2,3,0,16,3,3,9,18,2,2,7,3,1,3,21,2,6,2,1,2,4,3,8,3,1,3,2,9,1,5,1,2,4,3,9,2,0,17,1,2,5,4,2,2,3,4,1,2,0,2,1,4,1,4,2,4,11,5,4,4,2,2,3,3,0,7,0,15,9,18,2,2,7,2,2,2,22,2,9,2,4,4,7,2,2,2,3,8,1,2,1,7,3,3,9,19,1,2,7,2,2,2,22,2,9,2,4,3,8,2,2,2,3,8,1,8,0,2,3,3,9,19,1,2,7,2,2,2,22,2,15,4,7,2,2,2,3,10,0,9,3,3,9,11,5,3,1,2,17,4,23,2,8,2,0,3,6,4,0,5,5,2,0,2,7,19,1,14,57,6,14,2,9,40,1,2,0,3,1,2,0,3,0,7,3,2,6,2,2,2,0,2,0,3,1,2,12,2,2,3,4,2,0,2,5,3,9,3,1,35,0,24,1,7,9,12,0,2,0,2,0,5,9,2,35,5,19,2,5,5,7,2,35,10,0,58,73,7,77,3,37,11,42,2,0,4,328,2,3,3,6,2,0,2,3,3,40,2,3,3,32,2,3,3,6,2,0,2,3,3,14,2,56,2,3,3,66,5,0,33,15,17,84,13,619,3,16,2,25,6,74,22,12,2,6,12,20,12,19,13,12,2,2,2,1,13,51,3,29,4,0,5,1,3,9,34,2,3,9,7,87,9,42,6,69,11,28,4,11,5,11,11,39,3,4,12,43,5,25,7,10,38,27,5,62,2,28,3,10,7,9,14,0,89,75,5,9,18,8,13,42,4,11,71,55,9,9,4,48,83,2,2,30,14,230,23,280,3,5,3,37,3,5,3,7,2,0,2,0,2,0,2,30,3,52,2,6,2,0,4,2,2,6,4,3,3,5,5,12,6,2,2,6,67,1,20,0,29,0,14,0,17,4,60,12,5,0,4,11,18,0,5,0,3,9,2,0,4,4,7,0,2,0,2,0,2,3,2,10,3,3,6,4,5,0,53,1,2684,46,2,46,2,132,7,6,15,37,11,53,10,0,17,22,10,6,2,6,2,6,2,6,2,6,2,6,2,6,2,6,2,31,48,0,470,1,36,5,2,4,6,1,5,85,3,1,3,2,2,89,2,3,6,40,4,93,18,23,57,15,513,6581,75,20939,53,1164,68,45,3,268,4,27,21,31,3,13,13,1,2,24,9,69,11,1,38,8,3,102,3,1,111,44,25,51,13,68,12,9,7,23,4,0,5,45,3,35,13,28,4,64,15,10,39,54,10,13,3,9,7,22,4,1,5,66,25,2,227,42,2,1,3,9,7,11171,13,22,5,48,8453,301,3,61,3,105,39,6,13,4,6,11,2,12,2,4,2,0,2,1,2,1,2,107,34,362,19,63,3,53,41,11,5,15,17,6,13,1,25,2,33,4,2,134,20,9,8,25,5,0,2,25,12,88,4,5,3,5,3,5,3,2],i=0,s=[];for(var o=0;o2?r%f!=f-1:r%f==0}}var E=Infinity;w(function(e,t){var n=e.search(/\S/);n!==-1?(ne.length&&(E=e.length)}),u==Infinity&&(u=E,s=!1,o=!1),l&&u%f!=0&&(u=Math.floor(u/f)*f),w(o?m:v)},this.toggleBlockComment=function(e,t,n,r){var i=this.blockComment;if(!i)return;!i.start&&i[0]&&(i=i[0]);var s=new f(t,r.row,r.column),o=s.getCurrentToken(),u=t.selection,a=t.selection.toOrientedRange(),c,h;if(o&&/comment/.test(o.type)){var p,d;while(o&&/comment/.test(o.type)){var v=o.value.indexOf(i.start);if(v!=-1){var m=s.getCurrentTokenRow(),g=s.getCurrentTokenColumn()+v;p=new l(m,g,m,g+i.start.length);break}o=s.stepBackward()}var s=new f(t,r.row,r.column),o=s.getCurrentToken();while(o&&/comment/.test(o.type)){var v=o.value.indexOf(i.end);if(v!=-1){var m=s.getCurrentTokenRow(),g=s.getCurrentTokenColumn()+v;d=new l(m,g,m,g+i.end.length);break}o=s.stepForward()}d&&t.remove(d),p&&(t.remove(p),c=p.start.row,h=-i.start.length)}else h=i.start.length,c=n.start.row,t.insert(n.end,i.end),t.insert(n.start,i.start);a.start.row==c&&(a.start.column+=h),a.end.row==c&&(a.end.column+=h),t.selection.fromOrientedRange(a)},this.getNextLineIndent=function(e,t,n){return this.$getIndent(t)},this.checkOutdent=function(e,t,n){return!1},this.autoOutdent=function(e,t,n){},this.$getIndent=function(e){return e.match(/^\s*/)[0]},this.createWorker=function(e){return null},this.createModeDelegates=function(e){this.$embeds=[],this.$modes={};for(var t in e)if(e[t]){var n=e[t],i=n.prototype.$id,s=r.$modes[i];s||(r.$modes[i]=s=new n),r.$modes[t]||(r.$modes[t]=s),this.$embeds.push(t),this.$modes[t]=s}var o=["toggleBlockComment","toggleCommentLines","getNextLineIndent","checkOutdent","autoOutdent","transformAction","getCompletions"];for(var t=0;t=0&&t.row=0&&t.column<=e[t.row].length}function s(e,t){t.action!="insert"&&t.action!="remove"&&r(t,"delta.action must be 'insert' or 'remove'"),t.lines instanceof Array||r(t,"delta.lines must be an Array"),(!t.start||!t.end)&&r(t,"delta.start/end must be an present");var n=t.start;i(e,t.start)||r(t,"delta.start must be contained in document");var s=t.end;t.action=="remove"&&!i(e,s)&&r(t,"delta.end must contained in document for 'remove' actions");var o=s.row-n.row,u=s.column-(o==0?n.column:0);(o!=t.lines.length-1||t.lines[o].length!=u)&&r(t,"delta.range must match delta lines")}t.applyDelta=function(e,t,n){var r=t.start.row,i=t.start.column,s=e[r]||"";switch(t.action){case"insert":var o=t.lines;if(o.length===1)e[r]=s.substring(0,i)+t.lines[0]+s.substring(i);else{var u=[r,1].concat(t.lines);e.splice.apply(e,u),e[r]=s.substring(0,i)+e[r],e[r+t.lines.length-1]+=s.substring(i)}break;case"remove":var a=t.end.column,f=t.end.row;r===f?e[r]=s.substring(0,i)+s.substring(a):e.splice(r,f-r+1,s.substring(0,i)+e[f].substring(a))}}}),ace.define("ace/anchor",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/event_emitter").EventEmitter,s=t.Anchor=function(e,t,n){this.$onChange=this.onChange.bind(this),this.attach(e),typeof n=="undefined"?this.setPosition(t.row,t.column):this.setPosition(t,n)};(function(){function e(e,t,n){var r=n?e.column<=t.column:e.columnthis.row)return;var n=t(e,{row:this.row,column:this.column},this.$insertRight);this.setPosition(n.row,n.column,!0)},this.setPosition=function(e,t,n){var r;n?r={row:e,column:t}:r=this.$clipPositionToDocument(e,t);if(this.row==r.row&&this.column==r.column)return;var i={row:this.row,column:this.column};this.row=r.row,this.column=r.column,this._signal("change",{old:i,value:r})},this.detach=function(){this.document.removeEventListener("change",this.$onChange)},this.attach=function(e){this.document=e||this.document,this.document.on("change",this.$onChange)},this.$clipPositionToDocument=function(e,t){var n={};return e>=this.document.getLength()?(n.row=Math.max(0,this.document.getLength()-1),n.column=this.document.getLine(n.row).length):e<0?(n.row=0,n.column=0):(n.row=e,n.column=Math.min(this.document.getLine(n.row).length,Math.max(0,t))),t<0&&(n.column=0),n}}).call(s.prototype)}),ace.define("ace/document",["require","exports","module","ace/lib/oop","ace/apply_delta","ace/lib/event_emitter","ace/range","ace/anchor"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./apply_delta").applyDelta,s=e("./lib/event_emitter").EventEmitter,o=e("./range").Range,u=e("./anchor").Anchor,a=function(e){this.$lines=[""],e.length===0?this.$lines=[""]:Array.isArray(e)?this.insertMergedLines({row:0,column:0},e):this.insert({row:0,column:0},e)};(function(){r.implement(this,s),this.setValue=function(e){var t=this.getLength()-1;this.remove(new o(0,0,t,this.getLine(t).length)),this.insert({row:0,column:0},e)},this.getValue=function(){return this.getAllLines().join(this.getNewLineCharacter())},this.createAnchor=function(e,t){return new u(this,e,t)},"aaa".split(/a/).length===0?this.$split=function(e){return e.replace(/\r\n|\r/g,"\n").split("\n")}:this.$split=function(e){return e.split(/\r\n|\r|\n/)},this.$detectNewLine=function(e){var t=e.match(/^.*?(\r\n|\r|\n)/m);this.$autoNewLine=t?t[1]:"\n",this._signal("changeNewLineMode")},this.getNewLineCharacter=function(){switch(this.$newLineMode){case"windows":return"\r\n";case"unix":return"\n";default:return this.$autoNewLine||"\n"}},this.$autoNewLine="",this.$newLineMode="auto",this.setNewLineMode=function(e){if(this.$newLineMode===e)return;this.$newLineMode=e,this._signal("changeNewLineMode")},this.getNewLineMode=function(){return this.$newLineMode},this.isNewLine=function(e){return e=="\r\n"||e=="\r"||e=="\n"},this.getLine=function(e){return this.$lines[e]||""},this.getLines=function(e,t){return this.$lines.slice(e,t+1)},this.getAllLines=function(){return this.getLines(0,this.getLength())},this.getLength=function(){return this.$lines.length},this.getTextRange=function(e){return this.getLinesForRange(e).join(this.getNewLineCharacter())},this.getLinesForRange=function(e){var t;if(e.start.row===e.end.row)t=[this.getLine(e.start.row).substring(e.start.column,e.end.column)];else{t=this.getLines(e.start.row,e.end.row),t[0]=(t[0]||"").substring(e.start.column);var n=t.length-1;e.end.row-e.start.row==n&&(t[n]=t[n].substring(0,e.end.column))}return t},this.insertLines=function(e,t){return console.warn("Use of document.insertLines is deprecated. Use the insertFullLines method instead."),this.insertFullLines(e,t)},this.removeLines=function(e,t){return console.warn("Use of document.removeLines is deprecated. Use the removeFullLines method instead."),this.removeFullLines(e,t)},this.insertNewLine=function(e){return console.warn("Use of document.insertNewLine is deprecated. Use insertMergedLines(position, ['', '']) instead."),this.insertMergedLines(e,["",""])},this.insert=function(e,t){return this.getLength()<=1&&this.$detectNewLine(t),this.insertMergedLines(e,this.$split(t))},this.insertInLine=function(e,t){var n=this.clippedPos(e.row,e.column),r=this.pos(e.row,e.column+t.length);return this.applyDelta({start:n,end:r,action:"insert",lines:[t]},!0),this.clonePos(r)},this.clippedPos=function(e,t){var n=this.getLength();e===undefined?e=n:e<0?e=0:e>=n&&(e=n-1,t=undefined);var r=this.getLine(e);return t==undefined&&(t=r.length),t=Math.min(Math.max(t,0),r.length),{row:e,column:t}},this.clonePos=function(e){return{row:e.row,column:e.column}},this.pos=function(e,t){return{row:e,column:t}},this.$clipPosition=function(e){var t=this.getLength();return e.row>=t?(e.row=Math.max(0,t-1),e.column=this.getLine(t-1).length):(e.row=Math.max(0,e.row),e.column=Math.min(Math.max(e.column,0),this.getLine(e.row).length)),e},this.insertFullLines=function(e,t){e=Math.min(Math.max(e,0),this.getLength());var n=0;e0,r=t=0&&this.applyDelta({start:this.pos(e,this.getLine(e).length),end:this.pos(e+1,0),action:"remove",lines:["",""]})},this.replace=function(e,t){e instanceof o||(e=o.fromPoints(e.start,e.end));if(t.length===0&&e.isEmpty())return e.start;if(t==this.getTextRange(e))return e.end;this.remove(e);var n;return t?n=this.insert(e.start,t):n=e.start,n},this.applyDeltas=function(e){for(var t=0;t=0;t--)this.revertDelta(e[t])},this.applyDelta=function(e,t){var n=e.action=="insert";if(n?e.lines.length<=1&&!e.lines[0]:!o.comparePoints(e.start,e.end))return;n&&e.lines.length>2e4?this.$splitAndapplyLargeDelta(e,2e4):(i(this.$lines,e,t),this._signal("change",e))},this.$splitAndapplyLargeDelta=function(e,t){var n=e.lines,r=n.length-t+1,i=e.start.row,s=e.start.column;for(var o=0,u=0;o20){n.running=setTimeout(n.$worker,20);break}}n.currentLine=t,r==-1&&(r=t),s<=r&&n.fireUpdateEvent(s,r)}};(function(){r.implement(this,i),this.setTokenizer=function(e){this.tokenizer=e,this.lines=[],this.states=[],this.start(0)},this.setDocument=function(e){this.doc=e,this.lines=[],this.states=[],this.stop()},this.fireUpdateEvent=function(e,t){var n={first:e,last:t};this._signal("update",{data:n})},this.start=function(e){this.currentLine=Math.min(e||0,this.currentLine,this.doc.getLength()),this.lines.splice(this.currentLine,this.lines.length),this.states.splice(this.currentLine,this.states.length),this.stop(),this.running=setTimeout(this.$worker,700)},this.scheduleStart=function(){this.running||(this.running=setTimeout(this.$worker,700))},this.$updateOnChange=function(e){var t=e.start.row,n=e.end.row-t;if(n===0)this.lines[t]=null;else if(e.action=="remove")this.lines.splice(t,n+1,null),this.states.splice(t,n+1,null);else{var r=Array(n+1);r.unshift(t,1),this.lines.splice.apply(this.lines,r),this.states.splice.apply(this.states,r)}this.currentLine=Math.min(t,this.currentLine,this.doc.getLength()),this.stop()},this.stop=function(){this.running&&clearTimeout(this.running),this.running=!1},this.getTokens=function(e){return this.lines[e]||this.$tokenizeRow(e)},this.getState=function(e){return this.currentLine==e&&this.$tokenizeRow(e),this.states[e]||"start"},this.$tokenizeRow=function(e){var t=this.doc.getLine(e),n=this.states[e-1],r=this.tokenizer.getLineTokens(t,n,e);return this.states[e]+""!=r.state+""?(this.states[e]=r.state,this.lines[e+1]=null,this.currentLine>e+1&&(this.currentLine=e+1)):this.currentLine==e&&(this.currentLine=e+1),this.lines[e]=r.tokens}}).call(s.prototype),t.BackgroundTokenizer=s}),ace.define("ace/search_highlight",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"],function(e,t,n){"use strict";var r=e("./lib/lang"),i=e("./lib/oop"),s=e("./range").Range,o=function(e,t,n){this.setRegexp(e),this.clazz=t,this.type=n||"text"};(function(){this.MAX_RANGES=500,this.setRegexp=function(e){if(this.regExp+""==e+"")return;this.regExp=e,this.cache=[]},this.update=function(e,t,n,i){if(!this.regExp)return;var o=i.firstRow,u=i.lastRow;for(var a=o;a<=u;a++){var f=this.cache[a];f==null&&(f=r.getMatchOffsets(n.getLine(a),this.regExp),f.length>this.MAX_RANGES&&(f=f.slice(0,this.MAX_RANGES)),f=f.map(function(e){return new s(a,e.offset,a,e.offset+e.length)}),this.cache[a]=f.length?f:"");for(var l=f.length;l--;)t.drawSingleLineMarker(e,f[l].toScreenRange(n),this.clazz,i)}}}).call(o.prototype),t.SearchHighlight=o}),ace.define("ace/edit_session/fold_line",["require","exports","module","ace/range"],function(e,t,n){"use strict";function i(e,t){this.foldData=e,Array.isArray(t)?this.folds=t:t=this.folds=[t];var n=t[t.length-1];this.range=new r(t[0].start.row,t[0].start.column,n.end.row,n.end.column),this.start=this.range.start,this.end=this.range.end,this.folds.forEach(function(e){e.setFoldLine(this)},this)}var r=e("../range").Range;(function(){this.shiftRow=function(e){this.start.row+=e,this.end.row+=e,this.folds.forEach(function(t){t.start.row+=e,t.end.row+=e})},this.addFold=function(e){if(e.sameRow){if(e.start.rowthis.endRow)throw new Error("Can't add a fold to this FoldLine as it has no connection");this.folds.push(e),this.folds.sort(function(e,t){return-e.range.compareEnd(t.start.row,t.start.column)}),this.range.compareEnd(e.start.row,e.start.column)>0?(this.end.row=e.end.row,this.end.column=e.end.column):this.range.compareStart(e.end.row,e.end.column)<0&&(this.start.row=e.start.row,this.start.column=e.start.column)}else if(e.start.row==this.end.row)this.folds.push(e),this.end.row=e.end.row,this.end.column=e.end.column;else{if(e.end.row!=this.start.row)throw new Error("Trying to add fold to FoldRow that doesn't have a matching row");this.folds.unshift(e),this.start.row=e.start.row,this.start.column=e.start.column}e.foldLine=this},this.containsRow=function(e){return e>=this.start.row&&e<=this.end.row},this.walk=function(e,t,n){var r=0,i=this.folds,s,o,u,a=!0;t==null&&(t=this.end.row,n=this.end.column);for(var f=0;f0)continue;var a=i(e,o.start);return u===0?t&&a!==0?-s-2:s:a>0||a===0&&!t?s:-s-1}return-s-1},this.add=function(e){var t=!e.isEmpty(),n=this.pointIndex(e.start,t);n<0&&(n=-n-1);var r=this.pointIndex(e.end,t,n);return r<0?r=-r-1:r++,this.ranges.splice(n,r-n,e)},this.addList=function(e){var t=[];for(var n=e.length;n--;)t.push.apply(t,this.add(e[n]));return t},this.substractPoint=function(e){var t=this.pointIndex(e);if(t>=0)return this.ranges.splice(t,1)},this.merge=function(){var e=[],t=this.ranges;t=t.sort(function(e,t){return i(e.start,t.start)});var n=t[0],r;for(var s=1;s=0},this.containsPoint=function(e){return this.pointIndex(e)>=0},this.rangeAtPoint=function(e){var t=this.pointIndex(e);if(t>=0)return this.ranges[t]},this.clipRows=function(e,t){var n=this.ranges;if(n[0].start.row>t||n[n.length-1].start.row=r)break}if(e.action=="insert"){var f=i-r,l=-t.column+n.column;for(;or)break;a.start.row==r&&a.start.column>=t.column&&(a.start.column!=t.column||!this.$insertRight)&&(a.start.column+=l,a.start.row+=f);if(a.end.row==r&&a.end.column>=t.column){if(a.end.column==t.column&&this.$insertRight)continue;a.end.column==t.column&&l>0&&oa.start.column&&a.end.column==s[o+1].start.column&&(a.end.column-=l),a.end.column+=l,a.end.row+=f}}}else{var f=r-i,l=t.column-n.column;for(;oi)break;if(a.end.rowt.column)a.end.column=t.column,a.end.row=t.row}else a.end.column+=l,a.end.row+=f;else a.end.row>i&&(a.end.row+=f);if(a.start.rowt.column)a.start.column=t.column,a.start.row=t.row}else a.start.column+=l,a.start.row+=f;else a.start.row>i&&(a.start.row+=f)}}if(f!=0&&o=e)return i;if(i.end.row>e)return null}return null},this.getNextFoldLine=function(e,t){var n=this.$foldData,r=0;t&&(r=n.indexOf(t)),r==-1&&(r=0);for(r;r=e)return i}return null},this.getFoldedRowCount=function(e,t){var n=this.$foldData,r=t-e+1;for(var i=0;i=t){u=e?r-=t-u:r=0);break}o>=e&&(u>=e?r-=o-u:r-=o-e+1)}return r},this.$addFoldLine=function(e){return this.$foldData.push(e),this.$foldData.sort(function(e,t){return e.start.row-t.start.row}),e},this.addFold=function(e,t){var n=this.$foldData,r=!1,o;e instanceof s?o=e:(o=new s(t,e),o.collapseChildren=t.collapseChildren),this.$clipRangeToDocument(o.range);var u=o.start.row,a=o.start.column,f=o.end.row,l=o.end.column,c=this.getFoldAt(u,a,1),h=this.getFoldAt(f,l,-1);if(c&&h==c)return c.addSubFold(o);c&&!c.range.isStart(u,a)&&this.removeFold(c),h&&!h.range.isEnd(f,l)&&this.removeFold(h);var p=this.getFoldsInRange(o.range);p.length>0&&(this.removeFolds(p),p.forEach(function(e){o.addSubFold(e)}));for(var d=0;d0&&this.foldAll(e.start.row+1,e.end.row,e.collapseChildren-1),e.subFolds=[]},this.expandFolds=function(e){e.forEach(function(e){this.expandFold(e)},this)},this.unfold=function(e,t){var n,i;e==null?(n=new r(0,0,this.getLength(),0),t=!0):typeof e=="number"?n=new r(e,0,e,this.getLine(e).length):"row"in e?n=r.fromPoints(e,e):n=e,i=this.getFoldsInRangeList(n);if(t)this.removeFolds(i);else{var s=i;while(s.length)this.expandFolds(s),s=this.getFoldsInRangeList(n)}if(i.length)return i},this.isRowFolded=function(e,t){return!!this.getFoldLine(e,t)},this.getRowFoldEnd=function(e,t){var n=this.getFoldLine(e,t);return n?n.end.row:e},this.getRowFoldStart=function(e,t){var n=this.getFoldLine(e,t);return n?n.start.row:e},this.getFoldDisplayLine=function(e,t,n,r,i){r==null&&(r=e.start.row),i==null&&(i=0),t==null&&(t=e.end.row),n==null&&(n=this.getLine(t).length);var s=this.doc,o="";return e.walk(function(e,t,n,u){if(tl)break}while(s&&a.test(s.type));s=i.stepBackward()}else s=i.getCurrentToken();return f.end.row=i.getCurrentTokenRow(),f.end.column=i.getCurrentTokenColumn()+s.value.length-2,f}},this.foldAll=function(e,t,n){n==undefined&&(n=1e5);var r=this.foldWidgets;if(!r)return;t=t||this.getLength(),e=e||0;for(var i=e;i=e){i=s.end.row;try{var o=this.addFold("...",s);o&&(o.collapseChildren=n)}catch(u){}}}},this.$foldStyles={manual:1,markbegin:1,markbeginend:1},this.$foldStyle="markbegin",this.setFoldStyle=function(e){if(!this.$foldStyles[e])throw new Error("invalid fold style: "+e+"["+Object.keys(this.$foldStyles).join(", ")+"]");if(this.$foldStyle==e)return;this.$foldStyle=e,e=="manual"&&this.unfold();var t=this.$foldMode;this.$setFolding(null),this.$setFolding(t)},this.$setFolding=function(e){if(this.$foldMode==e)return;this.$foldMode=e,this.off("change",this.$updateFoldWidgets),this.off("tokenizerUpdate",this.$tokenizerUpdateFoldWidgets),this._signal("changeAnnotation");if(!e||this.$foldStyle=="manual"){this.foldWidgets=null;return}this.foldWidgets=[],this.getFoldWidget=e.getFoldWidget.bind(e,this,this.$foldStyle),this.getFoldWidgetRange=e.getFoldWidgetRange.bind(e,this,this.$foldStyle),this.$updateFoldWidgets=this.updateFoldWidgets.bind(this),this.$tokenizerUpdateFoldWidgets=this.tokenizerUpdateFoldWidgets.bind(this),this.on("change",this.$updateFoldWidgets),this.on("tokenizerUpdate",this.$tokenizerUpdateFoldWidgets)},this.getParentFoldRangeData=function(e,t){var n=this.foldWidgets;if(!n||t&&n[e])return{};var r=e-1,i;while(r>=0){var s=n[r];s==null&&(s=n[r]=this.getFoldWidget(r));if(s=="start"){var o=this.getFoldWidgetRange(r);i||(i=o);if(o&&o.end.row>=e)break}r--}return{range:r!==-1&&o,firstRange:i}},this.onFoldWidgetClick=function(e,t){t=t.domEvent;var n={children:t.shiftKey,all:t.ctrlKey||t.metaKey,siblings:t.altKey},r=this.$toggleFoldWidget(e,n);if(!r){var i=t.target||t.srcElement;i&&/ace_fold-widget/.test(i.className)&&(i.className+=" ace_invalid")}},this.$toggleFoldWidget=function(e,t){if(!this.getFoldWidget)return;var n=this.getFoldWidget(e),r=this.getLine(e),i=n==="end"?-1:1,s=this.getFoldAt(e,i===-1?0:r.length,i);if(s)return t.children||t.all?this.removeFold(s):this.expandFold(s),s;var o=this.getFoldWidgetRange(e,!0);if(o&&!o.isMultiLine()){s=this.getFoldAt(o.start.row,o.start.column,1);if(s&&o.isEqual(s.range))return this.removeFold(s),s}if(t.siblings){var u=this.getParentFoldRangeData(e);if(u.range)var a=u.range.start.row+1,f=u.range.end.row;this.foldAll(a,f,t.all?1e4:0)}else t.children?(f=o?o.end.row:this.getLength(),this.foldAll(e+1,f,t.all?1e4:0)):o&&(t.all&&(o.collapseChildren=1e4),this.addFold("...",o));return o},this.toggleFoldWidget=function(e){var t=this.selection.getCursor().row;t=this.getRowFoldStart(t);var n=this.$toggleFoldWidget(t,{});if(n)return;var r=this.getParentFoldRangeData(t,!0);n=r.range||r.firstRange;if(n){t=n.start.row;var i=this.getFoldAt(t,this.getLine(t).length,1);i?this.removeFold(i):this.addFold("...",n)}},this.updateFoldWidgets=function(e){var t=e.start.row,n=e.end.row-t;if(n===0)this.foldWidgets[t]=null;else if(e.action=="remove")this.foldWidgets.splice(t,n+1,null);else{var r=Array(n+1);r.unshift(t,1),this.foldWidgets.splice.apply(this.foldWidgets,r)}},this.tokenizerUpdateFoldWidgets=function(e){var t=e.data;t.first!=t.last&&this.foldWidgets.length>t.first&&this.foldWidgets.splice(t.first,this.foldWidgets.length)}}var r=e("../range").Range,i=e("./fold_line").FoldLine,s=e("./fold").Fold,o=e("../token_iterator").TokenIterator;t.Folding=u}),ace.define("ace/edit_session/bracket_match",["require","exports","module","ace/token_iterator","ace/range"],function(e,t,n){"use strict";function s(){this.findMatchingBracket=function(e,t){if(e.column==0)return null;var n=t||this.getLine(e.row).charAt(e.column-1);if(n=="")return null;var r=n.match(/([\(\[\{])|([\)\]\}])/);return r?r[1]?this.$findClosingBracket(r[1],e):this.$findOpeningBracket(r[2],e):null},this.getBracketRange=function(e){var t=this.getLine(e.row),n=!0,r,s=t.charAt(e.column-1),o=s&&s.match(/([\(\[\{])|([\)\]\}])/);o||(s=t.charAt(e.column),e={row:e.row,column:e.column+1},o=s&&s.match(/([\(\[\{])|([\)\]\}])/),n=!1);if(!o)return null;if(o[1]){var u=this.$findClosingBracket(o[1],e);if(!u)return null;r=i.fromPoints(e,u),n||(r.end.column++,r.start.column--),r.cursor=r.end}else{var u=this.$findOpeningBracket(o[2],e);if(!u)return null;r=i.fromPoints(u,e),n||(r.start.column++,r.end.column--),r.cursor=r.start}return r},this.$brackets={")":"(","(":")","]":"[","[":"]","{":"}","}":"{","<":">",">":"<"},this.$findOpeningBracket=function(e,t,n){var i=this.$brackets[e],s=1,o=new r(this,t.row,t.column),u=o.getCurrentToken();u||(u=o.stepForward());if(!u)return;n||(n=new RegExp("(\\.?"+u.type.replace(".","\\.").replace("rparen",".paren").replace(/\b(?:end)\b/,"(?:start|begin|end)")+")+"));var a=t.column-o.getCurrentTokenColumn()-2,f=u.value;for(;;){while(a>=0){var l=f.charAt(a);if(l==i){s-=1;if(s==0)return{row:o.getCurrentTokenRow(),column:a+o.getCurrentTokenColumn()}}else l==e&&(s+=1);a-=1}do u=o.stepBackward();while(u&&!n.test(u.type));if(u==null)break;f=u.value,a=f.length-1}return null},this.$findClosingBracket=function(e,t,n){var i=this.$brackets[e],s=1,o=new r(this,t.row,t.column),u=o.getCurrentToken();u||(u=o.stepForward());if(!u)return;n||(n=new RegExp("(\\.?"+u.type.replace(".","\\.").replace("lparen",".paren").replace(/\b(?:start|begin)\b/,"(?:start|begin|end)")+")+"));var a=t.column-o.getCurrentTokenColumn();for(;;){var f=u.value,l=f.length;while(a=4352&&e<=4447||e>=4515&&e<=4519||e>=4602&&e<=4607||e>=9001&&e<=9002||e>=11904&&e<=11929||e>=11931&&e<=12019||e>=12032&&e<=12245||e>=12272&&e<=12283||e>=12288&&e<=12350||e>=12353&&e<=12438||e>=12441&&e<=12543||e>=12549&&e<=12589||e>=12593&&e<=12686||e>=12688&&e<=12730||e>=12736&&e<=12771||e>=12784&&e<=12830||e>=12832&&e<=12871||e>=12880&&e<=13054||e>=13056&&e<=19903||e>=19968&&e<=42124||e>=42128&&e<=42182||e>=43360&&e<=43388||e>=44032&&e<=55203||e>=55216&&e<=55238||e>=55243&&e<=55291||e>=63744&&e<=64255||e>=65040&&e<=65049||e>=65072&&e<=65106||e>=65108&&e<=65126||e>=65128&&e<=65131||e>=65281&&e<=65376||e>=65504&&e<=65510}r.implement(this,u),this.setDocument=function(e){this.doc&&this.doc.removeListener("change",this.$onChange),this.doc=e,e.on("change",this.$onChange),this.bgTokenizer&&this.bgTokenizer.setDocument(this.getDocument()),this.resetCaches()},this.getDocument=function(){return this.doc},this.$resetRowCache=function(e){if(!e){this.$docRowCache=[],this.$screenRowCache=[];return}var t=this.$docRowCache.length,n=this.$getRowCacheIndex(this.$docRowCache,e)+1;t>n&&(this.$docRowCache.splice(n,t),this.$screenRowCache.splice(n,t))},this.$getRowCacheIndex=function(e,t){var n=0,r=e.length-1;while(n<=r){var i=n+r>>1,s=e[i];if(t>s)n=i+1;else{if(!(t=t)break}return r=n[s],r?(r.index=s,r.start=i-r.value.length,r):null},this.setUndoManager=function(e){this.$undoManager=e,this.$informUndoManager&&this.$informUndoManager.cancel();if(e){var t=this;e.addSession(this),this.$syncInformUndoManager=function(){t.$informUndoManager.cancel(),t.mergeUndoDeltas=!1},this.$informUndoManager=i.delayedCall(this.$syncInformUndoManager)}else this.$syncInformUndoManager=function(){}},this.markUndoGroup=function(){this.$syncInformUndoManager&&this.$syncInformUndoManager()},this.$defaultUndoManager={undo:function(){},redo:function(){},reset:function(){},add:function(){},addSelection:function(){},startNewGroup:function(){},addSession:function(){}},this.getUndoManager=function(){return this.$undoManager||this.$defaultUndoManager},this.getTabString=function(){return this.getUseSoftTabs()?i.stringRepeat(" ",this.getTabSize()):" "},this.setUseSoftTabs=function(e){this.setOption("useSoftTabs",e)},this.getUseSoftTabs=function(){return this.$useSoftTabs&&!this.$mode.$indentWithTabs},this.setTabSize=function(e){this.setOption("tabSize",e)},this.getTabSize=function(){return this.$tabSize},this.isTabStop=function(e){return this.$useSoftTabs&&e.column%this.$tabSize===0},this.setNavigateWithinSoftTabs=function(e){this.setOption("navigateWithinSoftTabs",e)},this.getNavigateWithinSoftTabs=function(){return this.$navigateWithinSoftTabs},this.$overwrite=!1,this.setOverwrite=function(e){this.setOption("overwrite",e)},this.getOverwrite=function(){return this.$overwrite},this.toggleOverwrite=function(){this.setOverwrite(!this.$overwrite)},this.addGutterDecoration=function(e,t){this.$decorations[e]||(this.$decorations[e]=""),this.$decorations[e]+=" "+t,this._signal("changeBreakpoint",{})},this.removeGutterDecoration=function(e,t){this.$decorations[e]=(this.$decorations[e]||"").replace(" "+t,""),this._signal("changeBreakpoint",{})},this.getBreakpoints=function(){return this.$breakpoints},this.setBreakpoints=function(e){this.$breakpoints=[];for(var t=0;t0&&(r=!!n.charAt(t-1).match(this.tokenRe)),r||(r=!!n.charAt(t).match(this.tokenRe));if(r)var i=this.tokenRe;else if(/^\s+$/.test(n.slice(t-1,t+1)))var i=/\s/;else var i=this.nonTokenRe;var s=t;if(s>0){do s--;while(s>=0&&n.charAt(s).match(i));s++}var o=t;while(oe&&(e=t.screenWidth)}),this.lineWidgetWidth=e},this.$computeWidth=function(e){if(this.$modified||e){this.$modified=!1;if(this.$useWrapMode)return this.screenWidth=this.$wrapLimit;var t=this.doc.getAllLines(),n=this.$rowLengthCache,r=0,i=0,s=this.$foldData[i],o=s?s.start.row:Infinity,u=t.length;for(var a=0;ao){a=s.end.row+1;if(a>=u)break;s=this.$foldData[i++],o=s?s.start.row:Infinity}n[a]==null&&(n[a]=this.$getStringScreenWidth(t[a])[0]),n[a]>r&&(r=n[a])}this.screenWidth=r}},this.getLine=function(e){return this.doc.getLine(e)},this.getLines=function(e,t){return this.doc.getLines(e,t)},this.getLength=function(){return this.doc.getLength()},this.getTextRange=function(e){return this.doc.getTextRange(e||this.selection.getRange())},this.insert=function(e,t){return this.doc.insert(e,t)},this.remove=function(e){return this.doc.remove(e)},this.removeFullLines=function(e,t){return this.doc.removeFullLines(e,t)},this.undoChanges=function(e,t){if(!e.length)return;this.$fromUndo=!0;for(var n=e.length-1;n!=-1;n--){var r=e[n];r.action=="insert"||r.action=="remove"?this.doc.revertDelta(r):r.folds&&this.addFolds(r.folds)}!t&&this.$undoSelect&&(e.selectionBefore?this.selection.fromJSON(e.selectionBefore):this.selection.setRange(this.$getUndoSelection(e,!0))),this.$fromUndo=!1},this.redoChanges=function(e,t){if(!e.length)return;this.$fromUndo=!0;for(var n=0;ne.end.column&&(s.start.column+=u),s.end.row==e.end.row&&s.end.column>e.end.column&&(s.end.column+=u)),o&&s.start.row>=e.end.row&&(s.start.row+=o,s.end.row+=o)}s.end=this.insert(s.start,r);if(i.length){var a=e.start,f=s.start,o=f.row-a.row,u=f.column-a.column;this.addFolds(i.map(function(e){return e=e.clone(),e.start.row==a.row&&(e.start.column+=u),e.end.row==a.row&&(e.end.column+=u),e.start.row+=o,e.end.row+=o,e}))}return s},this.indentRows=function(e,t,n){n=n.replace(/\t/g,this.getTabString());for(var r=e;r<=t;r++)this.doc.insertInLine({row:r,column:0},n)},this.outdentRows=function(e){var t=e.collapseRows(),n=new l(0,0,0,0),r=this.getTabSize();for(var i=t.start.row;i<=t.end.row;++i){var s=this.getLine(i);n.start.row=i,n.end.row=i;for(var o=0;o0){var r=this.getRowFoldEnd(t+n);if(r>this.doc.getLength()-1)return 0;var i=r-t}else{e=this.$clipRowToDocument(e),t=this.$clipRowToDocument(t);var i=t-e+1}var s=new l(e,0,t,Number.MAX_VALUE),o=this.getFoldsInRange(s).map(function(e){return e=e.clone(),e.start.row+=i,e.end.row+=i,e}),u=n==0?this.doc.getLines(e,t):this.doc.removeFullLines(e,t);return this.doc.insertFullLines(e+i,u),o.length&&this.addFolds(o),i},this.moveLinesUp=function(e,t){return this.$moveLines(e,t,-1)},this.moveLinesDown=function(e,t){return this.$moveLines(e,t,1)},this.duplicateLines=function(e,t){return this.$moveLines(e,t,0)},this.$clipRowToDocument=function(e){return Math.max(0,Math.min(e,this.doc.getLength()-1))},this.$clipColumnToRow=function(e,t){return t<0?0:Math.min(this.doc.getLine(e).length,t)},this.$clipPositionToDocument=function(e,t){t=Math.max(0,t);if(e<0)e=0,t=0;else{var n=this.doc.getLength();e>=n?(e=n-1,t=this.doc.getLine(n-1).length):t=Math.min(this.doc.getLine(e).length,t)}return{row:e,column:t}},this.$clipRangeToDocument=function(e){e.start.row<0?(e.start.row=0,e.start.column=0):e.start.column=this.$clipColumnToRow(e.start.row,e.start.column);var t=this.doc.getLength()-1;return e.end.row>t?(e.end.row=t,e.end.column=this.doc.getLine(t).length):e.end.column=this.$clipColumnToRow(e.end.row,e.end.column),e},this.$wrapLimit=80,this.$useWrapMode=!1,this.$wrapLimitRange={min:null,max:null},this.setUseWrapMode=function(e){if(e!=this.$useWrapMode){this.$useWrapMode=e,this.$modified=!0,this.$resetRowCache(0);if(e){var t=this.getLength();this.$wrapData=Array(t),this.$updateWrapData(0,t-1)}this._signal("changeWrapMode")}},this.getUseWrapMode=function(){return this.$useWrapMode},this.setWrapLimitRange=function(e,t){if(this.$wrapLimitRange.min!==e||this.$wrapLimitRange.max!==t)this.$wrapLimitRange={min:e,max:t},this.$modified=!0,this.$bidiHandler.markAsDirty(),this.$useWrapMode&&this._signal("changeWrapMode")},this.adjustWrapLimit=function(e,t){var n=this.$wrapLimitRange;n.max<0&&(n={min:t,max:t});var r=this.$constrainWrapLimit(e,n.min,n.max);return r!=this.$wrapLimit&&r>1?(this.$wrapLimit=r,this.$modified=!0,this.$useWrapMode&&(this.$updateWrapData(0,this.getLength()-1),this.$resetRowCache(0),this._signal("changeWrapLimit")),!0):!1},this.$constrainWrapLimit=function(e,t,n){return t&&(e=Math.max(t,e)),n&&(e=Math.min(n,e)),e},this.getWrapLimit=function(){return this.$wrapLimit},this.setWrapLimit=function(e){this.setWrapLimitRange(e,e)},this.getWrapLimitRange=function(){return{min:this.$wrapLimitRange.min,max:this.$wrapLimitRange.max}},this.$updateInternalDataOnChange=function(e){var t=this.$useWrapMode,n=e.action,r=e.start,i=e.end,s=r.row,o=i.row,u=o-s,a=null;this.$updating=!0;if(u!=0)if(n==="remove"){this[t?"$wrapData":"$rowLengthCache"].splice(s,u);var f=this.$foldData;a=this.getFoldsInRange(e),this.removeFolds(a);var l=this.getFoldLine(i.row),c=0;if(l){l.addRemoveChars(i.row,i.column,r.column-i.column),l.shiftRow(-u);var h=this.getFoldLine(s);h&&h!==l&&(h.merge(l),l=h),c=f.indexOf(l)+1}for(c;c=i.row&&l.shiftRow(-u)}o=s}else{var p=Array(u);p.unshift(s,0);var d=t?this.$wrapData:this.$rowLengthCache;d.splice.apply(d,p);var f=this.$foldData,l=this.getFoldLine(s),c=0;if(l){var v=l.range.compareInside(r.row,r.column);v==0?(l=l.split(r.row,r.column),l&&(l.shiftRow(u),l.addRemoveChars(o,0,i.column-r.column))):v==-1&&(l.addRemoveChars(s,0,i.column-r.column),l.shiftRow(u)),c=f.indexOf(l)+1}for(c;c=s&&l.shiftRow(u)}}else{u=Math.abs(e.start.column-e.end.column),n==="remove"&&(a=this.getFoldsInRange(e),this.removeFolds(a),u=-u);var l=this.getFoldLine(s);l&&l.addRemoveChars(s,r.column,u)}return t&&this.$wrapData.length!=this.doc.getLength()&&console.error("doc.getLength() and $wrapData.length have to be the same!"),this.$updating=!1,t?this.$updateWrapData(s,o):this.$updateRowLengthCache(s,o),a},this.$updateRowLengthCache=function(e,t,n){this.$rowLengthCache[e]=null,this.$rowLengthCache[t]=null},this.$updateWrapData=function(e,t){var r=this.doc.getAllLines(),i=this.getTabSize(),o=this.$wrapData,u=this.$wrapLimit,a,f,l=e;t=Math.min(t,r.length-1);while(l<=t)f=this.getFoldLine(l,f),f?(a=[],f.walk(function(e,t,i,o){var u;if(e!=null){u=this.$getDisplayTokens(e,a.length),u[0]=n;for(var f=1;fr-b){var w=f+r-b;if(e[w-1]>=c&&e[w]>=c){y(w);continue}if(e[w]==n||e[w]==s){for(w;w!=f-1;w--)if(e[w]==n)break;if(w>f){y(w);continue}w=f+r;for(w;w>2)),f-1);while(w>E&&e[w]E&&e[w]E&&e[w]==a)w--}else while(w>E&&e[w]E){y(++w);continue}w=f+r,e[w]==t&&w--,y(w-b)}return o},this.$getDisplayTokens=function(n,r){var i=[],s;r=r||0;for(var o=0;o39&&u<48||u>57&&u<64?i.push(a):u>=4352&&m(u)?i.push(e,t):i.push(e)}return i},this.$getStringScreenWidth=function(e,t,n){if(t==0)return[0,0];t==null&&(t=Infinity),n=n||0;var r,i;for(i=0;i=4352&&m(r)?n+=2:n+=1;if(n>t)break}return[n,i]},this.lineWidgets=null,this.getRowLength=function(e){if(this.lineWidgets)var t=this.lineWidgets[e]&&this.lineWidgets[e].rowCount||0;else t=0;return!this.$useWrapMode||!this.$wrapData[e]?1+t:this.$wrapData[e].length+1+t},this.getRowLineCount=function(e){return!this.$useWrapMode||!this.$wrapData[e]?1:this.$wrapData[e].length+1},this.getRowWrapIndent=function(e){if(this.$useWrapMode){var t=this.screenToDocumentPosition(e,Number.MAX_VALUE),n=this.$wrapData[t.row];return n.length&&n[0]=0)var u=f[l],i=this.$docRowCache[l],h=e>f[c-1];else var h=!c;var p=this.getLength()-1,d=this.getNextFoldLine(i),v=d?d.start.row:Infinity;while(u<=e){a=this.getRowLength(i);if(u+a>e||i>=p)break;u+=a,i++,i>v&&(i=d.end.row+1,d=this.getNextFoldLine(i,d),v=d?d.start.row:Infinity),h&&(this.$docRowCache.push(i),this.$screenRowCache.push(u))}if(d&&d.start.row<=i)r=this.getFoldDisplayLine(d),i=d.start.row;else{if(u+a<=e||i>p)return{row:p,column:this.getLine(p).length};r=this.getLine(i),d=null}var m=0,g=Math.floor(e-u);if(this.$useWrapMode){var y=this.$wrapData[i];y&&(o=y[g],g>0&&y.length&&(m=y.indent,s=y[g-1]||y[y.length-1],r=r.substring(s)))}return n!==undefined&&this.$bidiHandler.isBidiRow(u+g,i,g)&&(t=this.$bidiHandler.offsetToCol(n)),s+=this.$getStringScreenWidth(r,t-m)[1],this.$useWrapMode&&s>=o&&(s=o-1),d?d.idxToPosition(s):{row:i,column:s}},this.documentToScreenPosition=function(e,t){if(typeof t=="undefined")var n=this.$clipPositionToDocument(e.row,e.column);else n=this.$clipPositionToDocument(e,t);e=n.row,t=n.column;var r=0,i=null,s=null;s=this.getFoldAt(e,t,1),s&&(e=s.start.row,t=s.start.column);var o,u=0,a=this.$docRowCache,f=this.$getRowCacheIndex(a,e),l=a.length;if(l&&f>=0)var u=a[f],r=this.$screenRowCache[f],c=e>a[l-1];else var c=!l;var h=this.getNextFoldLine(u),p=h?h.start.row:Infinity;while(u=p){o=h.end.row+1;if(o>e)break;h=this.getNextFoldLine(o,h),p=h?h.start.row:Infinity}else o=u+1;r+=this.getRowLength(u),u=o,c&&(this.$docRowCache.push(u),this.$screenRowCache.push(r))}var d="";h&&u>=p?(d=this.getFoldDisplayLine(h,e,t),i=h.start.row):(d=this.getLine(e).substring(0,t),i=e);var v=0;if(this.$useWrapMode){var m=this.$wrapData[i];if(m){var g=0;while(d.length>=m[g])r++,g++;d=d.substring(m[g-1]||0,d.length),v=g>0?m.indent:0}}return{row:r,column:v+this.$getStringScreenWidth(d)[0]}},this.documentToScreenColumn=function(e,t){return this.documentToScreenPosition(e,t).column},this.documentToScreenRow=function(e,t){return this.documentToScreenPosition(e,t).row},this.getScreenLength=function(){var e=0,t=null;if(!this.$useWrapMode){e=this.getLength();var n=this.$foldData;for(var r=0;ro&&(s=t.end.row+1,t=this.$foldData[r++],o=t?t.start.row:Infinity)}}return this.lineWidgets&&(e+=this.$getWidgetScreenLength()),e},this.$setFontMetrics=function(e){if(!this.$enableVarChar)return;this.$getStringScreenWidth=function(t,n,r){if(n===0)return[0,0];n||(n=Infinity),r=r||0;var i,s;for(s=0;sn)break}return[r,s]}},this.destroy=function(){this.bgTokenizer&&(this.bgTokenizer.setDocument(null),this.bgTokenizer=null),this.$stopWorker()},this.isFullWidth=m}.call(d.prototype),e("./edit_session/folding").Folding.call(d.prototype),e("./edit_session/bracket_match").BracketMatch.call(d.prototype),o.defineOptions(d.prototype,"session",{wrap:{set:function(e){!e||e=="off"?e=!1:e=="free"?e=!0:e=="printMargin"?e=-1:typeof e=="string"&&(e=parseInt(e,10)||!1);if(this.$wrap==e)return;this.$wrap=e;if(!e)this.setUseWrapMode(!1);else{var t=typeof e=="number"?e:null;this.setWrapLimitRange(t,t),this.setUseWrapMode(!0)}},get:function(){return this.getUseWrapMode()?this.$wrap==-1?"printMargin":this.getWrapLimitRange().min?this.$wrap:"free":"off"},handlesSet:!0},wrapMethod:{set:function(e){e=e=="auto"?this.$mode.type!="text":e!="text",e!=this.$wrapAsCode&&(this.$wrapAsCode=e,this.$useWrapMode&&(this.$useWrapMode=!1,this.setUseWrapMode(!0)))},initialValue:"auto"},indentedSoftWrap:{set:function(){this.$useWrapMode&&(this.$useWrapMode=!1,this.setUseWrapMode(!0))},initialValue:!0},firstLineNumber:{set:function(){this._signal("changeBreakpoint")},initialValue:1},useWorker:{set:function(e){this.$useWorker=e,this.$stopWorker(),e&&this.$startWorker()},initialValue:!0},useSoftTabs:{initialValue:!0},tabSize:{set:function(e){e=parseInt(e),e>0&&this.$tabSize!==e&&(this.$modified=!0,this.$rowLengthCache=[],this.$tabSize=e,this._signal("changeTabSize"))},initialValue:4,handlesSet:!0},navigateWithinSoftTabs:{initialValue:!1},foldStyle:{set:function(e){this.setFoldStyle(e)},handlesSet:!0},overwrite:{set:function(e){this._signal("changeOverwrite")},initialValue:!1},newLineMode:{set:function(e){this.doc.setNewLineMode(e)},get:function(){return this.doc.getNewLineMode()},handlesSet:!0},mode:{set:function(e){this.setMode(e)},get:function(){return this.$modeId},handlesSet:!0}}),t.EditSession=d}),ace.define("ace/search",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"],function(e,t,n){"use strict";function u(e,t){function n(e){return/\w/.test(e)||t.regExp?"\\b":""}return n(e[0])+e+n(e[e.length-1])}var r=e("./lib/lang"),i=e("./lib/oop"),s=e("./range").Range,o=function(){this.$options={}};(function(){this.set=function(e){return i.mixin(this.$options,e),this},this.getOptions=function(){return r.copyObject(this.$options)},this.setOptions=function(e){this.$options=e},this.find=function(e){var t=this.$options,n=this.$matchIterator(e,t);if(!n)return!1;var r=null;return n.forEach(function(e,n,i,o){return r=new s(e,n,i,o),n==o&&t.start&&t.start.start&&t.skipCurrent!=0&&r.isEqual(t.start)?(r=null,!1):!0}),r},this.findAll=function(e){var t=this.$options;if(!t.needle)return[];this.$assembleRegExp(t);var n=t.range,i=n?e.getLines(n.start.row,n.end.row):e.doc.getAllLines(),o=[],u=t.re;if(t.$isMultiLine){var a=u.length,f=i.length-a,l;e:for(var c=u.offset||0;c<=f;c++){for(var h=0;hv)continue;o.push(l=new s(c,v,c+a-1,m)),a>2&&(c=c+a-2)}}else for(var g=0;gE&&o[h].end.row==n.end.row)h--;o=o.slice(g,h+1);for(g=0,h=o.length;g=u;n--)if(c(n,Number.MAX_VALUE,e))return;if(t.wrap==0)return;for(n=a,u=o.row;n>=u;n--)if(c(n,Number.MAX_VALUE,e))return};else var f=function(e){var n=o.row;if(c(n,o.column,e))return;for(n+=1;n<=a;n++)if(c(n,0,e))return;if(t.wrap==0)return;for(n=u,a=o.row;n<=a;n++)if(c(n,0,e))return};if(t.$isMultiLine)var l=n.length,c=function(t,i,s){var o=r?t-l+1:t;if(o<0)return;var u=e.getLine(o),a=u.search(n[0]);if(!r&&ai)return;if(s(o,a,o+l-1,c))return!0};else if(r)var c=function(t,r,i){var s=e.getLine(t),o=[],u,a=0;n.lastIndex=0;while(u=n.exec(s)){var f=u[0].length;a=u.index;if(!f){if(a>=s.length)break;n.lastIndex=a+=1}if(u.index+f>r)break;o.push(u.index,f)}for(var l=o.length-1;l>=0;l-=2){var c=o[l-1],f=o[l];if(i(t,c,t,c+f))return!0}};else var c=function(t,r,i){var s=e.getLine(t),o,u;n.lastIndex=r;while(u=n.exec(s)){var a=u[0].length;o=u.index;if(i(t,o,t,o+a))return!0;if(!a){n.lastIndex=o+=1;if(o>=s.length)return!1}}};return{forEach:f}}}).call(o.prototype),t.Search=o}),ace.define("ace/keyboard/hash_handler",["require","exports","module","ace/lib/keys","ace/lib/useragent"],function(e,t,n){"use strict";function o(e,t){this.platform=t||(i.isMac?"mac":"win"),this.commands={},this.commandKeyBinding={},this.addCommands(e),this.$singleCommand=!0}function u(e,t){o.call(this,e,t),this.$singleCommand=!1}var r=e("../lib/keys"),i=e("../lib/useragent"),s=r.KEY_MODS;u.prototype=o.prototype,function(){function e(e){return typeof e=="object"&&e.bindKey&&e.bindKey.position||(e.isDefault?-100:0)}this.addCommand=function(e){this.commands[e.name]&&this.removeCommand(e),this.commands[e.name]=e,e.bindKey&&this._buildKeyHash(e)},this.removeCommand=function(e,t){var n=e&&(typeof e=="string"?e:e.name);e=this.commands[n],t||delete this.commands[n];var r=this.commandKeyBinding;for(var i in r){var s=r[i];if(s==e)delete r[i];else if(Array.isArray(s)){var o=s.indexOf(e);o!=-1&&(s.splice(o,1),s.length==1&&(r[i]=s[0]))}}},this.bindKey=function(e,t,n){typeof e=="object"&&e&&(n==undefined&&(n=e.position),e=e[this.platform]);if(!e)return;if(typeof t=="function")return this.addCommand({exec:t,bindKey:e,name:t.name||e});e.split("|").forEach(function(e){var r="";if(e.indexOf(" ")!=-1){var i=e.split(/\s+/);e=i.pop(),i.forEach(function(e){var t=this.parseKeys(e),n=s[t.hashId]+t.key;r+=(r?" ":"")+n,this._addCommandToBinding(r,"chainKeys")},this),r+=" "}var o=this.parseKeys(e),u=s[o.hashId]+o.key;this._addCommandToBinding(r+u,t,n)},this)},this._addCommandToBinding=function(t,n,r){var i=this.commandKeyBinding,s;if(!n)delete i[t];else if(!i[t]||this.$singleCommand)i[t]=n;else{Array.isArray(i[t])?(s=i[t].indexOf(n))!=-1&&i[t].splice(s,1):i[t]=[i[t]],typeof r!="number"&&(r=e(n));var o=i[t];for(s=0;sr)break}o.splice(s,0,n)}},this.addCommands=function(e){e&&Object.keys(e).forEach(function(t){var n=e[t];if(!n)return;if(typeof n=="string")return this.bindKey(n,t);typeof n=="function"&&(n={exec:n});if(typeof n!="object")return;n.name||(n.name=t),this.addCommand(n)},this)},this.removeCommands=function(e){Object.keys(e).forEach(function(t){this.removeCommand(e[t])},this)},this.bindKeys=function(e){Object.keys(e).forEach(function(t){this.bindKey(t,e[t])},this)},this._buildKeyHash=function(e){this.bindKey(e.bindKey,e)},this.parseKeys=function(e){var t=e.toLowerCase().split(/[\-\+]([\-\+])?/).filter(function(e){return e}),n=t.pop(),i=r[n];if(r.FUNCTION_KEYS[i])n=r.FUNCTION_KEYS[i].toLowerCase();else{if(!t.length)return{key:n,hashId:-1};if(t.length==1&&t[0]=="shift")return{key:n.toUpperCase(),hashId:-1}}var s=0;for(var o=t.length;o--;){var u=r.KEY_MODS[t[o]];if(u==null)return typeof console!="undefined"&&console.error("invalid modifier "+t[o]+" in "+e),!1;s|=u}return{key:n,hashId:s}},this.findKeyCommand=function(t,n){var r=s[t]+n;return this.commandKeyBinding[r]},this.handleKeyboard=function(e,t,n,r){if(r<0)return;var i=s[t]+n,o=this.commandKeyBinding[i];e.$keyChain&&(e.$keyChain+=" "+i,o=this.commandKeyBinding[e.$keyChain]||o);if(o)if(o=="chainKeys"||o[o.length-1]=="chainKeys")return e.$keyChain=e.$keyChain||i,{command:"null"};if(e.$keyChain)if(!!t&&t!=4||n.length!=1){if(t==-1||r>0)e.$keyChain=""}else e.$keyChain=e.$keyChain.slice(0,-i.length-1);return{command:o}},this.getStatusText=function(e,t){return t.$keyChain||""}}.call(o.prototype),t.HashHandler=o,t.MultiHashHandler=u}),ace.define("ace/commands/command_manager",["require","exports","module","ace/lib/oop","ace/keyboard/hash_handler","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../keyboard/hash_handler").MultiHashHandler,s=e("../lib/event_emitter").EventEmitter,o=function(e,t){i.call(this,t,e),this.byName=this.commands,this.setDefaultHandler("exec",function(e){return e.command.exec(e.editor,e.args||{})})};r.inherits(o,i),function(){r.implement(this,s),this.exec=function(e,t,n){if(Array.isArray(e)){for(var r=e.length;r--;)if(this.exec(e[r],t,n))return!0;return!1}typeof e=="string"&&(e=this.commands[e]);if(!e)return!1;if(t&&t.$readOnly&&!e.readOnly)return!1;if(this.$checkCommandState!=0&&e.isAvailable&&!e.isAvailable(t))return!1;var i={editor:t,command:e,args:n};return i.returnValue=this._emit("exec",i),this._signal("afterExec",i),i.returnValue===!1?!1:!0},this.toggleRecording=function(e){if(this.$inReplay)return;return e&&e._emit("changeStatus"),this.recording?(this.macro.pop(),this.removeEventListener("exec",this.$addCommandToMacro),this.macro.length||(this.macro=this.oldMacro),this.recording=!1):(this.$addCommandToMacro||(this.$addCommandToMacro=function(e){this.macro.push([e.command,e.args])}.bind(this)),this.oldMacro=this.macro,this.macro=[],this.on("exec",this.$addCommandToMacro),this.recording=!0)},this.replay=function(e){if(this.$inReplay||!this.macro)return;if(this.recording)return this.toggleRecording(e);try{this.$inReplay=!0,this.macro.forEach(function(t){typeof t=="string"?this.exec(t,e):this.exec(t[0],e,t[1])},this)}finally{this.$inReplay=!1}},this.trimMacro=function(e){return e.map(function(e){return typeof e[0]!="string"&&(e[0]=e[0].name),e[1]||(e=e[0]),e})}}.call(o.prototype),t.CommandManager=o}),ace.define("ace/commands/default_commands",["require","exports","module","ace/lib/lang","ace/config","ace/range"],function(e,t,n){"use strict";function o(e,t){return{win:e,mac:t}}var r=e("../lib/lang"),i=e("../config"),s=e("../range").Range;t.commands=[{name:"showSettingsMenu",bindKey:o("Ctrl-,","Command-,"),exec:function(e){i.loadModule("ace/ext/settings_menu",function(t){t.init(e),e.showSettingsMenu()})},readOnly:!0},{name:"goToNextError",bindKey:o("Alt-E","F4"),exec:function(e){i.loadModule("./ext/error_marker",function(t){t.showErrorMarker(e,1)})},scrollIntoView:"animate",readOnly:!0},{name:"goToPreviousError",bindKey:o("Alt-Shift-E","Shift-F4"),exec:function(e){i.loadModule("./ext/error_marker",function(t){t.showErrorMarker(e,-1)})},scrollIntoView:"animate",readOnly:!0},{name:"selectall",description:"Select all",bindKey:o("Ctrl-A","Command-A"),exec:function(e){e.selectAll()},readOnly:!0},{name:"centerselection",description:"Center selection",bindKey:o(null,"Ctrl-L"),exec:function(e){e.centerSelection()},readOnly:!0},{name:"gotoline",description:"Go to line...",bindKey:o("Ctrl-L","Command-L"),exec:function(e,t){typeof t=="number"&&!isNaN(t)&&e.gotoLine(t),e.prompt({$type:"gotoLine"})},readOnly:!0},{name:"fold",bindKey:o("Alt-L|Ctrl-F1","Command-Alt-L|Command-F1"),exec:function(e){e.session.toggleFold(!1)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"unfold",bindKey:o("Alt-Shift-L|Ctrl-Shift-F1","Command-Alt-Shift-L|Command-Shift-F1"),exec:function(e){e.session.toggleFold(!0)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"toggleFoldWidget",bindKey:o("F2","F2"),exec:function(e){e.session.toggleFoldWidget()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"toggleParentFoldWidget",bindKey:o("Alt-F2","Alt-F2"),exec:function(e){e.session.toggleFoldWidget(!0)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"foldall",description:"Fold all",bindKey:o(null,"Ctrl-Command-Option-0"),exec:function(e){e.session.foldAll()},scrollIntoView:"center",readOnly:!0},{name:"foldOther",description:"Fold other",bindKey:o("Alt-0","Command-Option-0"),exec:function(e){e.session.foldAll(),e.session.unfold(e.selection.getAllRanges())},scrollIntoView:"center",readOnly:!0},{name:"unfoldall",description:"Unfold all",bindKey:o("Alt-Shift-0","Command-Option-Shift-0"),exec:function(e){e.session.unfold()},scrollIntoView:"center",readOnly:!0},{name:"findnext",description:"Find next",bindKey:o("Ctrl-K","Command-G"),exec:function(e){e.findNext()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"findprevious",description:"Find previous",bindKey:o("Ctrl-Shift-K","Command-Shift-G"),exec:function(e){e.findPrevious()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"selectOrFindNext",description:"Select or find next",bindKey:o("Alt-K","Ctrl-G"),exec:function(e){e.selection.isEmpty()?e.selection.selectWord():e.findNext()},readOnly:!0},{name:"selectOrFindPrevious",description:"Select or find previous",bindKey:o("Alt-Shift-K","Ctrl-Shift-G"),exec:function(e){e.selection.isEmpty()?e.selection.selectWord():e.findPrevious()},readOnly:!0},{name:"find",description:"Find",bindKey:o("Ctrl-F","Command-F"),exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e)})},readOnly:!0},{name:"overwrite",description:"Overwrite",bindKey:"Insert",exec:function(e){e.toggleOverwrite()},readOnly:!0},{name:"selecttostart",description:"Select to start",bindKey:o("Ctrl-Shift-Home","Command-Shift-Home|Command-Shift-Up"),exec:function(e){e.getSelection().selectFileStart()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"gotostart",description:"Go to start",bindKey:o("Ctrl-Home","Command-Home|Command-Up"),exec:function(e){e.navigateFileStart()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"selectup",description:"Select up",bindKey:o("Shift-Up","Shift-Up|Ctrl-Shift-P"),exec:function(e){e.getSelection().selectUp()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"golineup",description:"Go line up",bindKey:o("Up","Up|Ctrl-P"),exec:function(e,t){e.navigateUp(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttoend",description:"Select to end",bindKey:o("Ctrl-Shift-End","Command-Shift-End|Command-Shift-Down"),exec:function(e){e.getSelection().selectFileEnd()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"gotoend",description:"Go to end",bindKey:o("Ctrl-End","Command-End|Command-Down"),exec:function(e){e.navigateFileEnd()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"selectdown",description:"Select down",bindKey:o("Shift-Down","Shift-Down|Ctrl-Shift-N"),exec:function(e){e.getSelection().selectDown()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"golinedown",description:"Go line down",bindKey:o("Down","Down|Ctrl-N"),exec:function(e,t){e.navigateDown(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectwordleft",description:"Select word left",bindKey:o("Ctrl-Shift-Left","Option-Shift-Left"),exec:function(e){e.getSelection().selectWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotowordleft",description:"Go to word left",bindKey:o("Ctrl-Left","Option-Left"),exec:function(e){e.navigateWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttolinestart",description:"Select to line start",bindKey:o("Alt-Shift-Left","Command-Shift-Left|Ctrl-Shift-A"),exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotolinestart",description:"Go to line start",bindKey:o("Alt-Left|Home","Command-Left|Home|Ctrl-A"),exec:function(e){e.navigateLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectleft",description:"Select left",bindKey:o("Shift-Left","Shift-Left|Ctrl-Shift-B"),exec:function(e){e.getSelection().selectLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotoleft",description:"Go to left",bindKey:o("Left","Left|Ctrl-B"),exec:function(e,t){e.navigateLeft(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectwordright",description:"Select word right",bindKey:o("Ctrl-Shift-Right","Option-Shift-Right"),exec:function(e){e.getSelection().selectWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotowordright",description:"Go to word right",bindKey:o("Ctrl-Right","Option-Right"),exec:function(e){e.navigateWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttolineend",description:"Select to line end",bindKey:o("Alt-Shift-Right","Command-Shift-Right|Shift-End|Ctrl-Shift-E"),exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotolineend",description:"Go to line end",bindKey:o("Alt-Right|End","Command-Right|End|Ctrl-E"),exec:function(e){e.navigateLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectright",description:"Select right",bindKey:o("Shift-Right","Shift-Right"),exec:function(e){e.getSelection().selectRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotoright",description:"Go to right",bindKey:o("Right","Right|Ctrl-F"),exec:function(e,t){e.navigateRight(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectpagedown",description:"Select page down",bindKey:"Shift-PageDown",exec:function(e){e.selectPageDown()},readOnly:!0},{name:"pagedown",description:"Page down",bindKey:o(null,"Option-PageDown"),exec:function(e){e.scrollPageDown()},readOnly:!0},{name:"gotopagedown",description:"Go to page down",bindKey:o("PageDown","PageDown|Ctrl-V"),exec:function(e){e.gotoPageDown()},readOnly:!0},{name:"selectpageup",description:"Select page up",bindKey:"Shift-PageUp",exec:function(e){e.selectPageUp()},readOnly:!0},{name:"pageup",description:"Page up",bindKey:o(null,"Option-PageUp"),exec:function(e){e.scrollPageUp()},readOnly:!0},{name:"gotopageup",description:"Go to page up",bindKey:"PageUp",exec:function(e){e.gotoPageUp()},readOnly:!0},{name:"scrollup",description:"Scroll up",bindKey:o("Ctrl-Up",null),exec:function(e){e.renderer.scrollBy(0,-2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"scrolldown",description:"Scroll down",bindKey:o("Ctrl-Down",null),exec:function(e){e.renderer.scrollBy(0,2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"selectlinestart",description:"Select line start",bindKey:"Shift-Home",exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectlineend",description:"Select line end",bindKey:"Shift-End",exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"togglerecording",description:"Toggle recording",bindKey:o("Ctrl-Alt-E","Command-Option-E"),exec:function(e){e.commands.toggleRecording(e)},readOnly:!0},{name:"replaymacro",description:"Replay macro",bindKey:o("Ctrl-Shift-E","Command-Shift-E"),exec:function(e){e.commands.replay(e)},readOnly:!0},{name:"jumptomatching",description:"Jump to matching",bindKey:o("Ctrl-P","Ctrl-P"),exec:function(e){e.jumpToMatching()},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"selecttomatching",description:"Select to matching",bindKey:o("Ctrl-Shift-P","Ctrl-Shift-P"),exec:function(e){e.jumpToMatching(!0)},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"expandToMatching",description:"Expand to matching",bindKey:o("Ctrl-Shift-M","Ctrl-Shift-M"),exec:function(e){e.jumpToMatching(!0,!0)},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"passKeysToBrowser",description:"Pass keys to browser",bindKey:o(null,null),exec:function(){},passEvent:!0,readOnly:!0},{name:"copy",description:"Copy",exec:function(e){},readOnly:!0},{name:"cut",description:"Cut",exec:function(e){var t=e.$copyWithEmptySelection&&e.selection.isEmpty(),n=t?e.selection.getLineRange():e.selection.getRange();e._emit("cut",n),n.isEmpty()||e.session.remove(n),e.clearSelection()},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"paste",description:"Paste",exec:function(e,t){e.$handlePaste(t)},scrollIntoView:"cursor"},{name:"removeline",description:"Remove line",bindKey:o("Ctrl-D","Command-D"),exec:function(e){e.removeLines()},scrollIntoView:"cursor",multiSelectAction:"forEachLine"},{name:"duplicateSelection",description:"Duplicate selection",bindKey:o("Ctrl-Shift-D","Command-Shift-D"),exec:function(e){e.duplicateSelection()},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"sortlines",description:"Sort lines",bindKey:o("Ctrl-Alt-S","Command-Alt-S"),exec:function(e){e.sortLines()},scrollIntoView:"selection",multiSelectAction:"forEachLine"},{name:"togglecomment",description:"Toggle comment",bindKey:o("Ctrl-/","Command-/"),exec:function(e){e.toggleCommentLines()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"toggleBlockComment",description:"Toggle block comment",bindKey:o("Ctrl-Shift-/","Command-Shift-/"),exec:function(e){e.toggleBlockComment()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"modifyNumberUp",description:"Modify number up",bindKey:o("Ctrl-Shift-Up","Alt-Shift-Up"),exec:function(e){e.modifyNumber(1)},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"modifyNumberDown",description:"Modify number down",bindKey:o("Ctrl-Shift-Down","Alt-Shift-Down"),exec:function(e){e.modifyNumber(-1)},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"replace",description:"Replace",bindKey:o("Ctrl-H","Command-Option-F"),exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e,!0)})}},{name:"undo",description:"Undo",bindKey:o("Ctrl-Z","Command-Z"),exec:function(e){e.undo()}},{name:"redo",description:"Redo",bindKey:o("Ctrl-Shift-Z|Ctrl-Y","Command-Shift-Z|Command-Y"),exec:function(e){e.redo()}},{name:"copylinesup",description:"Copy lines up",bindKey:o("Alt-Shift-Up","Command-Option-Up"),exec:function(e){e.copyLinesUp()},scrollIntoView:"cursor"},{name:"movelinesup",description:"Move lines up",bindKey:o("Alt-Up","Option-Up"),exec:function(e){e.moveLinesUp()},scrollIntoView:"cursor"},{name:"copylinesdown",description:"Copy lines down",bindKey:o("Alt-Shift-Down","Command-Option-Down"),exec:function(e){e.copyLinesDown()},scrollIntoView:"cursor"},{name:"movelinesdown",description:"Move lines down",bindKey:o("Alt-Down","Option-Down"),exec:function(e){e.moveLinesDown()},scrollIntoView:"cursor"},{name:"del",description:"Delete",bindKey:o("Delete","Delete|Ctrl-D|Shift-Delete"),exec:function(e){e.remove("right")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"backspace",description:"Backspace",bindKey:o("Shift-Backspace|Backspace","Ctrl-Backspace|Shift-Backspace|Backspace|Ctrl-H"),exec:function(e){e.remove("left")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"cut_or_delete",description:"Cut or delete",bindKey:o("Shift-Delete",null),exec:function(e){if(!e.selection.isEmpty())return!1;e.remove("left")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolinestart",description:"Remove to line start",bindKey:o("Alt-Backspace","Command-Backspace"),exec:function(e){e.removeToLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolineend",description:"Remove to line end",bindKey:o("Alt-Delete","Ctrl-K|Command-Delete"),exec:function(e){e.removeToLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolinestarthard",description:"Remove to line start hard",bindKey:o("Ctrl-Shift-Backspace",null),exec:function(e){var t=e.selection.getRange();t.start.column=0,e.session.remove(t)},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolineendhard",description:"Remove to line end hard",bindKey:o("Ctrl-Shift-Delete",null),exec:function(e){var t=e.selection.getRange();t.end.column=Number.MAX_VALUE,e.session.remove(t)},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removewordleft",description:"Remove word left",bindKey:o("Ctrl-Backspace","Alt-Backspace|Ctrl-Alt-Backspace"),exec:function(e){e.removeWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removewordright",description:"Remove word right",bindKey:o("Ctrl-Delete","Alt-Delete"),exec:function(e){e.removeWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"outdent",description:"Outdent",bindKey:o("Shift-Tab","Shift-Tab"),exec:function(e){e.blockOutdent()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"indent",description:"Indent",bindKey:o("Tab","Tab"),exec:function(e){e.indent()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"blockoutdent",description:"Block outdent",bindKey:o("Ctrl-[","Ctrl-["),exec:function(e){e.blockOutdent()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"blockindent",description:"Block indent",bindKey:o("Ctrl-]","Ctrl-]"),exec:function(e){e.blockIndent()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"insertstring",description:"Insert string",exec:function(e,t){e.insert(t)},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"inserttext",description:"Insert text",exec:function(e,t){e.insert(r.stringRepeat(t.text||"",t.times||1))},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"splitline",description:"Split line",bindKey:o(null,"Ctrl-O"),exec:function(e){e.splitLine()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"transposeletters",description:"Transpose letters",bindKey:o("Alt-Shift-X","Ctrl-T"),exec:function(e){e.transposeLetters()},multiSelectAction:function(e){e.transposeSelections(1)},scrollIntoView:"cursor"},{name:"touppercase",description:"To uppercase",bindKey:o("Ctrl-U","Ctrl-U"),exec:function(e){e.toUpperCase()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"tolowercase",description:"To lowercase",bindKey:o("Ctrl-Shift-U","Ctrl-Shift-U"),exec:function(e){e.toLowerCase()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"expandtoline",description:"Expand to line",bindKey:o("Ctrl-Shift-L","Command-Shift-L"),exec:function(e){var t=e.selection.getRange();t.start.column=t.end.column=0,t.end.row++,e.selection.setRange(t,!1)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"joinlines",description:"Join lines",bindKey:o(null,null),exec:function(e){var t=e.selection.isBackwards(),n=t?e.selection.getSelectionLead():e.selection.getSelectionAnchor(),i=t?e.selection.getSelectionAnchor():e.selection.getSelectionLead(),o=e.session.doc.getLine(n.row).length,u=e.session.doc.getTextRange(e.selection.getRange()),a=u.replace(/\n\s*/," ").length,f=e.session.doc.getLine(n.row);for(var l=n.row+1;l<=i.row+1;l++){var c=r.stringTrimLeft(r.stringTrimRight(e.session.doc.getLine(l)));c.length!==0&&(c=" "+c),f+=c}i.row+10?(e.selection.moveCursorTo(n.row,n.column),e.selection.selectTo(n.row,n.column+a)):(o=e.session.doc.getLine(n.row).length>o?o+1:o,e.selection.moveCursorTo(n.row,o))},multiSelectAction:"forEach",readOnly:!0},{name:"invertSelection",description:"Invert selection",bindKey:o(null,null),exec:function(e){var t=e.session.doc.getLength()-1,n=e.session.doc.getLine(t).length,r=e.selection.rangeList.ranges,i=[];r.length<1&&(r=[e.selection.getRange()]);for(var o=0;o=i.lastRow||r.end.row<=i.firstRow)&&this.renderer.scrollSelectionIntoView(this.selection.anchor,this.selection.lead);break;default:}n=="animate"&&this.renderer.animateScrolling(this.curOp.scrollTop)}var s=this.selection.toJSON();this.curOp.selectionAfter=s,this.$lastSel=this.selection.toJSON(),this.session.getUndoManager().addSelection(s),this.prevOp=this.curOp,this.curOp=null}},this.$mergeableCommands=["backspace","del","insertstring"],this.$historyTracker=function(e){if(!this.$mergeUndoDeltas)return;var t=this.prevOp,n=this.$mergeableCommands,r=t.command&&e.command.name==t.command.name;if(e.command.name=="insertstring"){var i=e.args;this.mergeNextCommand===undefined&&(this.mergeNextCommand=!0),r=r&&this.mergeNextCommand&&(!/\s/.test(i)||/\s/.test(t.args)),this.mergeNextCommand=!0}else r=r&&n.indexOf(e.command.name)!==-1;this.$mergeUndoDeltas!="always"&&Date.now()-this.sequenceStartTime>2e3&&(r=!1),r?this.session.mergeUndoDeltas=!0:n.indexOf(e.command.name)!==-1&&(this.sequenceStartTime=Date.now())},this.setKeyboardHandler=function(e,t){if(e&&typeof e=="string"&&e!="ace"){this.$keybindingId=e;var n=this;g.loadModule(["keybinding",e],function(r){n.$keybindingId==e&&n.keyBinding.setKeyboardHandler(r&&r.handler),t&&t()})}else this.$keybindingId=null,this.keyBinding.setKeyboardHandler(e),t&&t()},this.getKeyboardHandler=function(){return this.keyBinding.getKeyboardHandler()},this.setSession=function(e){if(this.session==e)return;this.curOp&&this.endOperation(),this.curOp={};var t=this.session;if(t){this.session.off("change",this.$onDocumentChange),this.session.off("changeMode",this.$onChangeMode),this.session.off("tokenizerUpdate",this.$onTokenizerUpdate),this.session.off("changeTabSize",this.$onChangeTabSize),this.session.off("changeWrapLimit",this.$onChangeWrapLimit),this.session.off("changeWrapMode",this.$onChangeWrapMode),this.session.off("changeFold",this.$onChangeFold),this.session.off("changeFrontMarker",this.$onChangeFrontMarker),this.session.off("changeBackMarker",this.$onChangeBackMarker),this.session.off("changeBreakpoint",this.$onChangeBreakpoint),this.session.off("changeAnnotation",this.$onChangeAnnotation),this.session.off("changeOverwrite",this.$onCursorChange),this.session.off("changeScrollTop",this.$onScrollTopChange),this.session.off("changeScrollLeft",this.$onScrollLeftChange);var n=this.session.getSelection();n.off("changeCursor",this.$onCursorChange),n.off("changeSelection",this.$onSelectionChange)}this.session=e,e?(this.$onDocumentChange=this.onDocumentChange.bind(this),e.on("change",this.$onDocumentChange),this.renderer.setSession(e),this.$onChangeMode=this.onChangeMode.bind(this),e.on("changeMode",this.$onChangeMode),this.$onTokenizerUpdate=this.onTokenizerUpdate.bind(this),e.on("tokenizerUpdate",this.$onTokenizerUpdate),this.$onChangeTabSize=this.renderer.onChangeTabSize.bind(this.renderer),e.on("changeTabSize",this.$onChangeTabSize),this.$onChangeWrapLimit=this.onChangeWrapLimit.bind(this),e.on("changeWrapLimit",this.$onChangeWrapLimit),this.$onChangeWrapMode=this.onChangeWrapMode.bind(this),e.on("changeWrapMode",this.$onChangeWrapMode),this.$onChangeFold=this.onChangeFold.bind(this),e.on("changeFold",this.$onChangeFold),this.$onChangeFrontMarker=this.onChangeFrontMarker.bind(this),this.session.on("changeFrontMarker",this.$onChangeFrontMarker),this.$onChangeBackMarker=this.onChangeBackMarker.bind(this),this.session.on("changeBackMarker",this.$onChangeBackMarker),this.$onChangeBreakpoint=this.onChangeBreakpoint.bind(this),this.session.on("changeBreakpoint",this.$onChangeBreakpoint),this.$onChangeAnnotation=this.onChangeAnnotation.bind(this),this.session.on("changeAnnotation",this.$onChangeAnnotation),this.$onCursorChange=this.onCursorChange.bind(this),this.session.on("changeOverwrite",this.$onCursorChange),this.$onScrollTopChange=this.onScrollTopChange.bind(this),this.session.on("changeScrollTop",this.$onScrollTopChange),this.$onScrollLeftChange=this.onScrollLeftChange.bind(this),this.session.on("changeScrollLeft",this.$onScrollLeftChange),this.selection=e.getSelection(),this.selection.on("changeCursor",this.$onCursorChange),this.$onSelectionChange=this.onSelectionChange.bind(this),this.selection.on("changeSelection",this.$onSelectionChange),this.onChangeMode(),this.onCursorChange(),this.onScrollTopChange(),this.onScrollLeftChange(),this.onSelectionChange(),this.onChangeFrontMarker(),this.onChangeBackMarker(),this.onChangeBreakpoint(),this.onChangeAnnotation(),this.session.getUseWrapMode()&&this.renderer.adjustWrapLimit(),this.renderer.updateFull()):(this.selection=null,this.renderer.setSession(e)),this._signal("changeSession",{session:e,oldSession:t}),this.curOp=null,t&&t._signal("changeEditor",{oldEditor:this}),e&&e._signal("changeEditor",{editor:this}),e&&e.bgTokenizer&&e.bgTokenizer.scheduleStart()},this.getSession=function(){return this.session},this.setValue=function(e,t){return this.session.doc.setValue(e),t?t==1?this.navigateFileEnd():t==-1&&this.navigateFileStart():this.selectAll(),e},this.getValue=function(){return this.session.getValue()},this.getSelection=function(){return this.selection},this.resize=function(e){this.renderer.onResize(e)},this.setTheme=function(e,t){this.renderer.setTheme(e,t)},this.getTheme=function(){return this.renderer.getTheme()},this.setStyle=function(e){this.renderer.setStyle(e)},this.unsetStyle=function(e){this.renderer.unsetStyle(e)},this.getFontSize=function(){return this.getOption("fontSize")||i.computedStyle(this.container).fontSize},this.setFontSize=function(e){this.setOption("fontSize",e)},this.$highlightBrackets=function(){this.session.$bracketHighlight&&(this.session.removeMarker(this.session.$bracketHighlight),this.session.$bracketHighlight=null);if(this.$highlightPending)return;var e=this;this.$highlightPending=!0,setTimeout(function(){e.$highlightPending=!1;var t=e.session;if(!t||!t.bgTokenizer)return;var n=t.findMatchingBracket(e.getCursorPosition());if(n)var r=new p(n.row,n.column,n.row,n.column+1);else if(t.$mode.getMatching)var r=t.$mode.getMatching(e.session);r&&(t.$bracketHighlight=t.addMarker(r,"ace_bracket","text"))},50)},this.$highlightTags=function(){if(this.$highlightTagPending)return;var e=this;this.$highlightTagPending=!0,setTimeout(function(){e.$highlightTagPending=!1;var t=e.session;if(!t||!t.bgTokenizer)return;var n=e.getCursorPosition(),r=new y(e.session,n.row,n.column),i=r.getCurrentToken();if(!i||!/\b(?:tag-open|tag-name)/.test(i.type)){t.removeMarker(t.$tagHighlight),t.$tagHighlight=null;return}if(i.type.indexOf("tag-open")!=-1){i=r.stepForward();if(!i)return}var s=i.value,o=0,u=r.stepBackward();if(u.value=="<"){do u=i,i=r.stepForward(),i&&i.value===s&&i.type.indexOf("tag-name")!==-1&&(u.value==="<"?o++:u.value==="=0)}else{do i=u,u=r.stepBackward(),i&&i.value===s&&i.type.indexOf("tag-name")!==-1&&(u.value==="<"?o++:u.value==="1)&&(t=!1)}if(e.$highlightLineMarker&&!t)e.removeMarker(e.$highlightLineMarker.id),e.$highlightLineMarker=null;else if(!e.$highlightLineMarker&&t){var n=new p(t.row,t.column,t.row,Infinity);n.id=e.addMarker(n,"ace_active-line","screenLine"),e.$highlightLineMarker=n}else t&&(e.$highlightLineMarker.start.row=t.row,e.$highlightLineMarker.end.row=t.row,e.$highlightLineMarker.start.column=t.column,e._signal("changeBackMarker"))},this.onSelectionChange=function(e){var t=this.session;t.$selectionMarker&&t.removeMarker(t.$selectionMarker),t.$selectionMarker=null;if(!this.selection.isEmpty()){var n=this.selection.getRange(),r=this.getSelectionStyle();t.$selectionMarker=t.addMarker(n,"ace_selection",r)}else this.$updateHighlightActiveLine();var i=this.$highlightSelectedWord&&this.$getSelectionHighLightRegexp();this.session.highlight(i),this._signal("changeSelection")},this.$getSelectionHighLightRegexp=function(){var e=this.session,t=this.getSelectionRange();if(t.isEmpty()||t.isMultiLine())return;var n=t.start.column,r=t.end.column,i=e.getLine(t.start.row),s=i.substring(n,r);if(s.length>5e3||!/[\w\d]/.test(s))return;var o=this.$search.$assembleRegExp({wholeWord:!0,caseSensitive:!0,needle:s}),u=i.substring(n-1,r+1);if(!o.test(u))return;return o},this.onChangeFrontMarker=function(){this.renderer.updateFrontMarkers()},this.onChangeBackMarker=function(){this.renderer.updateBackMarkers()},this.onChangeBreakpoint=function(){this.renderer.updateBreakpoints()},this.onChangeAnnotation=function(){this.renderer.setAnnotations(this.session.getAnnotations())},this.onChangeMode=function(e){this.renderer.updateText(),this._emit("changeMode",e)},this.onChangeWrapLimit=function(){this.renderer.updateFull()},this.onChangeWrapMode=function(){this.renderer.onResize(!0)},this.onChangeFold=function(){this.$updateHighlightActiveLine(),this.renderer.updateFull()},this.getSelectedText=function(){return this.session.getTextRange(this.getSelectionRange())},this.getCopyText=function(){var e=this.getSelectedText(),t=this.session.doc.getNewLineCharacter(),n=!1;if(!e&&this.$copyWithEmptySelection){n=!0;var r=this.selection.getAllRanges();for(var i=0;is.length||i.length<2||!i[1])return this.commands.exec("insertstring",this,t);for(var o=s.length;o--;){var u=s[o];u.isEmpty()||r.remove(u),r.insert(u.start,i[o])}}},this.execCommand=function(e,t){return this.commands.exec(e,this,t)},this.insert=function(e,t){var n=this.session,r=n.getMode(),i=this.getCursorPosition();if(this.getBehavioursEnabled()&&!t){var s=r.transformAction(n.getState(i.row),"insertion",this,n,e);s&&(e!==s.text&&(this.inVirtualSelectionMode||(this.session.mergeUndoDeltas=!1,this.mergeNextCommand=!1)),e=s.text)}e==" "&&(e=this.session.getTabString());if(!this.selection.isEmpty()){var o=this.getSelectionRange();i=this.session.remove(o),this.clearSelection()}else if(this.session.getOverwrite()&&e.indexOf("\n")==-1){var o=new p.fromPoints(i,i);o.end.column+=e.length,this.session.remove(o)}if(e=="\n"||e=="\r\n"){var u=n.getLine(i.row);if(i.column>u.search(/\S|$/)){var a=u.substr(i.column).search(/\S|$/);n.doc.removeInLine(i.row,i.column,i.column+a)}}this.clearSelection();var f=i.column,l=n.getState(i.row),u=n.getLine(i.row),c=r.checkOutdent(l,u,e),h=n.insert(i,e);s&&s.selection&&(s.selection.length==2?this.selection.setSelectionRange(new p(i.row,f+s.selection[0],i.row,f+s.selection[1])):this.selection.setSelectionRange(new p(i.row+s.selection[0],s.selection[1],i.row+s.selection[2],s.selection[3])));if(n.getDocument().isNewLine(e)){var d=r.getNextLineIndent(l,u.slice(0,i.column),n.getTabString());n.insert({row:i.row+1,column:0},d)}c&&r.autoOutdent(l,n,i.row)},this.onTextInput=function(e,t){if(!t)return this.keyBinding.onTextInput(e);this.startOperation({command:{name:"insertstring"}});var n=this.applyComposition.bind(this,e,t);this.selection.rangeCount?this.forEachSelection(n):n(),this.endOperation()},this.applyComposition=function(e,t){if(t.extendLeft||t.extendRight){var n=this.selection.getRange();n.start.column-=t.extendLeft,n.end.column+=t.extendRight,this.selection.setRange(n),!e&&!n.isEmpty()&&this.remove()}(e||!this.selection.isEmpty())&&this.insert(e,!0);if(t.restoreStart||t.restoreEnd){var n=this.selection.getRange();n.start.column-=t.restoreStart,n.end.column-=t.restoreEnd,this.selection.setRange(n)}},this.onCommandKey=function(e,t,n){this.keyBinding.onCommandKey(e,t,n)},this.setOverwrite=function(e){this.session.setOverwrite(e)},this.getOverwrite=function(){return this.session.getOverwrite()},this.toggleOverwrite=function(){this.session.toggleOverwrite()},this.setScrollSpeed=function(e){this.setOption("scrollSpeed",e)},this.getScrollSpeed=function(){return this.getOption("scrollSpeed")},this.setDragDelay=function(e){this.setOption("dragDelay",e)},this.getDragDelay=function(){return this.getOption("dragDelay")},this.setSelectionStyle=function(e){this.setOption("selectionStyle",e)},this.getSelectionStyle=function(){return this.getOption("selectionStyle")},this.setHighlightActiveLine=function(e){this.setOption("highlightActiveLine",e)},this.getHighlightActiveLine=function(){return this.getOption("highlightActiveLine")},this.setHighlightGutterLine=function(e){this.setOption("highlightGutterLine",e)},this.getHighlightGutterLine=function(){return this.getOption("highlightGutterLine")},this.setHighlightSelectedWord=function(e){this.setOption("highlightSelectedWord",e)},this.getHighlightSelectedWord=function(){return this.$highlightSelectedWord},this.setAnimatedScroll=function(e){this.renderer.setAnimatedScroll(e)},this.getAnimatedScroll=function(){return this.renderer.getAnimatedScroll()},this.setShowInvisibles=function(e){this.renderer.setShowInvisibles(e)},this.getShowInvisibles=function(){return this.renderer.getShowInvisibles()},this.setDisplayIndentGuides=function(e){this.renderer.setDisplayIndentGuides(e)},this.getDisplayIndentGuides=function(){return this.renderer.getDisplayIndentGuides()},this.setShowPrintMargin=function(e){this.renderer.setShowPrintMargin(e)},this.getShowPrintMargin=function(){return this.renderer.getShowPrintMargin()},this.setPrintMarginColumn=function(e){this.renderer.setPrintMarginColumn(e)},this.getPrintMarginColumn=function(){return this.renderer.getPrintMarginColumn()},this.setReadOnly=function(e){this.setOption("readOnly",e)},this.getReadOnly=function(){return this.getOption("readOnly")},this.setBehavioursEnabled=function(e){this.setOption("behavioursEnabled",e)},this.getBehavioursEnabled=function(){return this.getOption("behavioursEnabled")},this.setWrapBehavioursEnabled=function(e){this.setOption("wrapBehavioursEnabled",e)},this.getWrapBehavioursEnabled=function(){return this.getOption("wrapBehavioursEnabled")},this.setShowFoldWidgets=function(e){this.setOption("showFoldWidgets",e)},this.getShowFoldWidgets=function(){return this.getOption("showFoldWidgets")},this.setFadeFoldWidgets=function(e){this.setOption("fadeFoldWidgets",e)},this.getFadeFoldWidgets=function(){return this.getOption("fadeFoldWidgets")},this.remove=function(e){this.selection.isEmpty()&&(e=="left"?this.selection.selectLeft():this.selection.selectRight());var t=this.getSelectionRange();if(this.getBehavioursEnabled()){var n=this.session,r=n.getState(t.start.row),i=n.getMode().transformAction(r,"deletion",this,n,t);if(t.end.column===0){var s=n.getTextRange(t);if(s[s.length-1]=="\n"){var o=n.getLine(t.end.row);/^\s+$/.test(o)&&(t.end.column=o.length)}}i&&(t=i)}this.session.remove(t),this.clearSelection()},this.removeWordRight=function(){this.selection.isEmpty()&&this.selection.selectWordRight(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeWordLeft=function(){this.selection.isEmpty()&&this.selection.selectWordLeft(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineStart=function(){this.selection.isEmpty()&&this.selection.selectLineStart(),this.selection.isEmpty()&&this.selection.selectLeft(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineEnd=function(){this.selection.isEmpty()&&this.selection.selectLineEnd();var e=this.getSelectionRange();e.start.column==e.end.column&&e.start.row==e.end.row&&(e.end.column=0,e.end.row++),this.session.remove(e),this.clearSelection()},this.splitLine=function(){this.selection.isEmpty()||(this.session.remove(this.getSelectionRange()),this.clearSelection());var e=this.getCursorPosition();this.insert("\n"),this.moveCursorToPosition(e)},this.transposeLetters=function(){if(!this.selection.isEmpty())return;var e=this.getCursorPosition(),t=e.column;if(t===0)return;var n=this.session.getLine(e.row),r,i;tt.toLowerCase()?1:0});var i=new p(0,0,0,0);for(var r=e.first;r<=e.last;r++){var s=t.getLine(r);i.start.row=r,i.end.row=r,i.end.column=s.length,t.replace(i,n[r-e.first])}},this.toggleCommentLines=function(){var e=this.session.getState(this.getCursorPosition().row),t=this.$getSelectedRows();this.session.getMode().toggleCommentLines(e,this.session,t.first,t.last)},this.toggleBlockComment=function(){var e=this.getCursorPosition(),t=this.session.getState(e.row),n=this.getSelectionRange();this.session.getMode().toggleBlockComment(t,this.session,n,e)},this.getNumberAt=function(e,t){var n=/[\-]?[0-9]+(?:\.[0-9]+)?/g;n.lastIndex=0;var r=this.session.getLine(e);while(n.lastIndex=t){var s={value:i[0],start:i.index,end:i.index+i[0].length};return s}}return null},this.modifyNumber=function(e){var t=this.selection.getCursor().row,n=this.selection.getCursor().column,r=new p(t,n-1,t,n),i=this.session.getTextRange(r);if(!isNaN(parseFloat(i))&&isFinite(i)){var s=this.getNumberAt(t,n);if(s){var o=s.value.indexOf(".")>=0?s.start+s.value.indexOf(".")+1:s.end,u=s.start+s.value.length-o,a=parseFloat(s.value);a*=Math.pow(10,u),o!==s.end&&n=u&&o<=a&&(n=t,f.selection.clearSelection(),f.moveCursorTo(e,u+r),f.selection.selectTo(e,a+r)),u=a});var l=this.$toggleWordPairs,c;for(var h=0;hp+1)break;p=d.last}l--,u=this.session.$moveLines(h,p,t?0:e),t&&e==-1&&(c=l+1);while(c<=l)o[c].moveBy(u,0),c++;t||(u=0),a+=u}i.fromOrientedRange(i.ranges[0]),i.rangeList.attach(this.session),this.inVirtualSelectionMode=!1}},this.$getSelectedRows=function(e){return e=(e||this.getSelectionRange()).collapseRows(),{first:this.session.getRowFoldStart(e.start.row),last:this.session.getRowFoldEnd(e.end.row)}},this.onCompositionStart=function(e){this.renderer.showComposition(e)},this.onCompositionUpdate=function(e){this.renderer.setCompositionText(e)},this.onCompositionEnd=function(){this.renderer.hideComposition()},this.getFirstVisibleRow=function(){return this.renderer.getFirstVisibleRow()},this.getLastVisibleRow=function(){return this.renderer.getLastVisibleRow()},this.isRowVisible=function(e){return e>=this.getFirstVisibleRow()&&e<=this.getLastVisibleRow()},this.isRowFullyVisible=function(e){return e>=this.renderer.getFirstFullyVisibleRow()&&e<=this.renderer.getLastFullyVisibleRow()},this.$getVisibleRowCount=function(){return this.renderer.getScrollBottomRow()-this.renderer.getScrollTopRow()+1},this.$moveByPage=function(e,t){var n=this.renderer,r=this.renderer.layerConfig,i=e*Math.floor(r.height/r.lineHeight);t===!0?this.selection.$moveSelection(function(){this.moveCursorBy(i,0)}):t===!1&&(this.selection.moveCursorBy(i,0),this.selection.clearSelection());var s=n.scrollTop;n.scrollBy(0,i*r.lineHeight),t!=null&&n.scrollCursorIntoView(null,.5),n.animateScrolling(s)},this.selectPageDown=function(){this.$moveByPage(1,!0)},this.selectPageUp=function(){this.$moveByPage(-1,!0)},this.gotoPageDown=function(){this.$moveByPage(1,!1)},this.gotoPageUp=function(){this.$moveByPage(-1,!1)},this.scrollPageDown=function(){this.$moveByPage(1)},this.scrollPageUp=function(){this.$moveByPage(-1)},this.scrollToRow=function(e){this.renderer.scrollToRow(e)},this.scrollToLine=function(e,t,n,r){this.renderer.scrollToLine(e,t,n,r)},this.centerSelection=function(){var e=this.getSelectionRange(),t={row:Math.floor(e.start.row+(e.end.row-e.start.row)/2),column:Math.floor(e.start.column+(e.end.column-e.start.column)/2)};this.renderer.alignCursor(t,.5)},this.getCursorPosition=function(){return this.selection.getCursor()},this.getCursorPositionScreen=function(){return this.session.documentToScreenPosition(this.getCursorPosition())},this.getSelectionRange=function(){return this.selection.getRange()},this.selectAll=function(){this.selection.selectAll()},this.clearSelection=function(){this.selection.clearSelection()},this.moveCursorTo=function(e,t){this.selection.moveCursorTo(e,t)},this.moveCursorToPosition=function(e){this.selection.moveCursorToPosition(e)},this.jumpToMatching=function(e,t){var n=this.getCursorPosition(),r=new y(this.session,n.row,n.column),i=r.getCurrentToken(),s=i||r.stepForward();if(!s)return;var o,u=!1,a={},f=n.column-s.start,l,c={")":"(","(":"(","]":"[","[":"[","{":"{","}":"{"};do{if(s.value.match(/[{}()\[\]]/g))for(;f=0;--s)this.$tryReplace(n[s],e)&&r++;return this.selection.setSelectionRange(i),r},this.$tryReplace=function(e,t){var n=this.session.getTextRange(e);return t=this.$search.replace(n,t),t!==null?(e.end=this.session.replace(e,t),e):null},this.getLastSearchOptions=function(){return this.$search.getOptions()},this.find=function(e,t,n){t||(t={}),typeof e=="string"||e instanceof RegExp?t.needle=e:typeof e=="object"&&r.mixin(t,e);var i=this.selection.getRange();t.needle==null&&(e=this.session.getTextRange(i)||this.$search.$options.needle,e||(i=this.session.getWordRange(i.start.row,i.start.column),e=this.session.getTextRange(i)),this.$search.set({needle:e})),this.$search.set(t),t.start||this.$search.set({start:i});var s=this.$search.find(this.session);if(t.preventScroll)return s;if(s)return this.revealRange(s,n),s;t.backwards?i.start=i.end:i.end=i.start,this.selection.setRange(i)},this.findNext=function(e,t){this.find({skipCurrent:!0,backwards:!1},e,t)},this.findPrevious=function(e,t){this.find(e,{skipCurrent:!0,backwards:!0},t)},this.revealRange=function(e,t){this.session.unfold(e),this.selection.setSelectionRange(e);var n=this.renderer.scrollTop;this.renderer.scrollSelectionIntoView(e.start,e.end,.5),t!==!1&&this.renderer.animateScrolling(n)},this.undo=function(){this.session.getUndoManager().undo(this.session),this.renderer.scrollCursorIntoView(null,.5)},this.redo=function(){this.session.getUndoManager().redo(this.session),this.renderer.scrollCursorIntoView(null,.5)},this.destroy=function(){this.renderer.destroy(),this._signal("destroy",this),this.session&&this.session.destroy()},this.setAutoScrollEditorIntoView=function(e){if(!e)return;var t,n=this,r=!1;this.$scrollAnchor||(this.$scrollAnchor=document.createElement("div"));var i=this.$scrollAnchor;i.style.cssText="position:absolute",this.container.insertBefore(i,this.container.firstChild);var s=this.on("changeSelection",function(){r=!0}),o=this.renderer.on("beforeRender",function(){r&&(t=n.renderer.container.getBoundingClientRect())}),u=this.renderer.on("afterRender",function(){if(r&&t&&(n.isFocused()||n.searchBox&&n.searchBox.isFocused())){var e=n.renderer,s=e.$cursorLayer.$pixelPos,o=e.layerConfig,u=s.top-o.offset;s.top>=0&&u+t.top<0?r=!0:s.topwindow.innerHeight?r=!1:r=null,r!=null&&(i.style.top=u+"px",i.style.left=s.left+"px",i.style.height=o.lineHeight+"px",i.scrollIntoView(r)),r=t=null}});this.setAutoScrollEditorIntoView=function(e){if(e)return;delete this.setAutoScrollEditorIntoView,this.off("changeSelection",s),this.renderer.off("afterRender",u),this.renderer.off("beforeRender",o)}},this.$resetCursorStyle=function(){var e=this.$cursorStyle||"ace",t=this.renderer.$cursorLayer;if(!t)return;t.setSmoothBlinking(/smooth/.test(e)),t.isBlinking=!this.$readOnly&&e!="wide",i.setCssClass(t.element,"ace_slim-cursors",/slim/.test(e))},this.prompt=function(e,t,n){var r=this;g.loadModule("./ext/prompt",function(i){i.prompt(r,e,t,n)})}}.call(w.prototype),g.defineOptions(w.prototype,"editor",{selectionStyle:{set:function(e){this.onSelectionChange(),this._signal("changeSelectionStyle",{data:e})},initialValue:"line"},highlightActiveLine:{set:function(){this.$updateHighlightActiveLine()},initialValue:!0},highlightSelectedWord:{set:function(e){this.$onSelectionChange()},initialValue:!0},readOnly:{set:function(e){this.textInput.setReadOnly(e),this.$resetCursorStyle()},initialValue:!1},copyWithEmptySelection:{set:function(e){this.textInput.setCopyWithEmptySelection(e)},initialValue:!1},cursorStyle:{set:function(e){this.$resetCursorStyle()},values:["ace","slim","smooth","wide"],initialValue:"ace"},mergeUndoDeltas:{values:[!1,!0,"always"],initialValue:!0},behavioursEnabled:{initialValue:!0},wrapBehavioursEnabled:{initialValue:!0},autoScrollEditorIntoView:{set:function(e){this.setAutoScrollEditorIntoView(e)}},keyboardHandler:{set:function(e){this.setKeyboardHandler(e)},get:function(){return this.$keybindingId},handlesSet:!0},value:{set:function(e){this.session.setValue(e)},get:function(){return this.getValue()},handlesSet:!0,hidden:!0},session:{set:function(e){this.setSession(e)},get:function(){return this.session},handlesSet:!0,hidden:!0},showLineNumbers:{set:function(e){this.renderer.$gutterLayer.setShowLineNumbers(e),this.renderer.$loop.schedule(this.renderer.CHANGE_GUTTER),e&&this.$relativeLineNumbers?E.attach(this):E.detach(this)},initialValue:!0},relativeLineNumbers:{set:function(e){this.$showLineNumbers&&e?E.attach(this):E.detach(this)}},hScrollBarAlwaysVisible:"renderer",vScrollBarAlwaysVisible:"renderer",highlightGutterLine:"renderer",animatedScroll:"renderer",showInvisibles:"renderer",showPrintMargin:"renderer",printMarginColumn:"renderer",printMargin:"renderer",fadeFoldWidgets:"renderer",showFoldWidgets:"renderer",displayIndentGuides:"renderer",showGutter:"renderer",fontSize:"renderer",fontFamily:"renderer",maxLines:"renderer",minLines:"renderer",scrollPastEnd:"renderer",fixedWidthGutter:"renderer",theme:"renderer",hasCssTransforms:"renderer",maxPixelHeight:"renderer",useTextareaForIME:"renderer",scrollSpeed:"$mouseHandler",dragDelay:"$mouseHandler",dragEnabled:"$mouseHandler",focusTimeout:"$mouseHandler",tooltipFollowsMouse:"$mouseHandler",firstLineNumber:"session",overwrite:"session",newLineMode:"session",useWorker:"session",useSoftTabs:"session",navigateWithinSoftTabs:"session",tabSize:"session",wrap:"session",indentedSoftWrap:"session",foldStyle:"session",mode:"session"});var E={getText:function(e,t){return(Math.abs(e.selection.lead.row-t)||t+1+(t<9?"\u00b7":""))+""},getWidth:function(e,t,n){return Math.max(t.toString().length,(n.lastRow+1).toString().length,2)*n.characterWidth},update:function(e,t){t.renderer.$loop.schedule(t.renderer.CHANGE_GUTTER)},attach:function(e){e.renderer.$gutterLayer.$renderer=this,e.on("changeSelection",this.update),this.update(null,e)},detach:function(e){e.renderer.$gutterLayer.$renderer==this&&(e.renderer.$gutterLayer.$renderer=null),e.off("changeSelection",this.update),this.update(null,e)}};t.Editor=w}),ace.define("ace/undomanager",["require","exports","module","ace/range"],function(e,t,n){"use strict";function i(e,t){for(var n=t;n--;){var r=e[n];if(r&&!r[0].ignore){while(n0){a.row+=i,a.column+=a.row==r.row?s:0;continue}!t&&l<=0&&(a.row=n.row,a.column=n.column,l===0&&(a.bias=1))}}function f(e){return{row:e.row,column:e.column}}function l(e){return{start:f(e.start),end:f(e.end),action:e.action,lines:e.lines.slice()}}function c(e){e=e||this;if(Array.isArray(e))return e.map(c).join("\n");var t="";e.action?(t=e.action=="insert"?"+":"-",t+="["+e.lines+"]"):e.value&&(Array.isArray(e.value)?t=e.value.map(h).join("\n"):t=h(e.value)),e.start&&(t+=h(e));if(e.id||e.rev)t+=" ("+(e.id||e.rev)+")";return t}function h(e){return e.start.row+":"+e.start.column+"=>"+e.end.row+":"+e.end.column}function p(e,t){var n=e.action=="insert",r=t.action=="insert";if(n&&r)if(o(t.start,e.end)>=0)m(t,e,-1);else{if(!(o(t.start,e.start)<=0))return null;m(e,t,1)}else if(n&&!r)if(o(t.start,e.end)>=0)m(t,e,-1);else{if(!(o(t.end,e.start)<=0))return null;m(e,t,-1)}else if(!n&&r)if(o(t.start,e.start)>=0)m(t,e,1);else{if(!(o(t.start,e.start)<=0))return null;m(e,t,1)}else if(!n&&!r)if(o(t.start,e.start)>=0)m(t,e,1);else{if(!(o(t.end,e.start)<=0))return null;m(e,t,-1)}return[t,e]}function d(e,t){for(var n=e.length;n--;)for(var r=0;r=0?m(e,t,-1):o(e.start,t.start)<=0?m(t,e,1):(m(e,s.fromPoints(t.start,e.start),-1),m(t,e,1));else if(!n&&r)o(t.start,e.end)>=0?m(t,e,-1):o(t.start,e.start)<=0?m(e,t,1):(m(t,s.fromPoints(e.start,t.start),-1),m(e,t,1));else if(!n&&!r)if(o(t.start,e.end)>=0)m(t,e,-1);else{if(!(o(t.end,e.start)<=0)){var i,u;return o(e.start,t.start)<0&&(i=e,e=y(e,t.start)),o(e.end,t.end)>0&&(u=y(e,t.end)),g(t.end,e.start,e.end,-1),u&&!i&&(e.lines=u.lines,e.start=u.start,e.end=u.end,u=e),[t,i,u].filter(Boolean)}m(e,t,-1)}return[t,e]}function m(e,t,n){g(e.start,t.start,t.end,n),g(e.end,t.start,t.end,n)}function g(e,t,n,r){e.row==(r==1?t:n).row&&(e.column+=r*(n.column-t.column)),e.row+=r*(n.row-t.row)}function y(e,t){var n=e.lines,r=e.end;e.end=f(t);var i=e.end.row-e.start.row,s=n.splice(i,n.length),o=i?t.column:t.column-e.start.column;n.push(s[0].substring(0,o)),s[0]=s[0].substr(o);var u={start:f(t),end:r,lines:s,action:e.action};return u}function b(e,t){t=l(t);for(var n=e.length;n--;){var r=e[n];for(var i=0;i0},this.canRedo=function(){return this.$redoStack.length>0},this.bookmark=function(e){e==undefined&&(e=this.$rev),this.mark=e},this.isAtBookmark=function(){return this.$rev===this.mark},this.toJSON=function(){},this.fromJSON=function(){},this.hasUndo=this.canUndo,this.hasRedo=this.canRedo,this.isClean=this.isAtBookmark,this.markClean=this.bookmark,this.$prettyPrint=function(e){return e?c(e):c(this.$undoStack)+"\n---\n"+c(this.$redoStack)}}).call(r.prototype);var s=e("./range").Range,o=s.comparePoints,u=s.comparePoints;t.UndoManager=r}),ace.define("ace/layer/lines",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";var r=e("../lib/dom"),i=function(e,t){this.element=e,this.canvasHeight=t||5e5,this.element.style.height=this.canvasHeight*2+"px",this.cells=[],this.cellCache=[],this.$offsetCoefficient=0};(function(){this.moveContainer=function(e){r.translate(this.element,0,-(e.firstRowScreen*e.lineHeight%this.canvasHeight)-e.offset*this.$offsetCoefficient)},this.pageChanged=function(e,t){return Math.floor(e.firstRowScreen*e.lineHeight/this.canvasHeight)!==Math.floor(t.firstRowScreen*t.lineHeight/this.canvasHeight)},this.computeLineTop=function(e,t,n){var r=t.firstRowScreen*t.lineHeight,i=Math.floor(r/this.canvasHeight),s=n.documentToScreenRow(e,0)*t.lineHeight;return s-i*this.canvasHeight},this.computeLineHeight=function(e,t,n){return t.lineHeight*n.getRowLength(e)},this.getLength=function(){return this.cells.length},this.get=function(e){return this.cells[e]},this.shift=function(){this.$cacheCell(this.cells.shift())},this.pop=function(){this.$cacheCell(this.cells.pop())},this.push=function(e){if(Array.isArray(e)){this.cells.push.apply(this.cells,e);var t=r.createFragment(this.element);for(var n=0;ns&&(a=i.end.row+1,i=t.getNextFoldLine(a,i),s=i?i.start.row:Infinity);if(a>r){while(this.$lines.getLength()>u+1)this.$lines.pop();break}o=this.$lines.get(++u),o?o.row=a:(o=this.$lines.createCell(a,e,this.session,f),this.$lines.push(o)),this.$renderCell(o,e,i,a),a++}this._signal("afterRender"),this.$updateGutterWidth(e)},this.$updateGutterWidth=function(e){var t=this.session,n=t.gutterRenderer||this.$renderer,r=t.$firstLineNumber,i=this.$lines.last()?this.$lines.last().text:"";if(this.$fixedWidth||t.$useWrapMode)i=t.getLength()+r-1;var s=n?n.getWidth(t,i,e):i.toString().length*e.characterWidth,o=this.$padding||this.$computePadding();s+=o.left+o.right,s!==this.gutterWidth&&!isNaN(s)&&(this.gutterWidth=s,this.element.parentNode.style.width=this.element.style.width=Math.ceil(this.gutterWidth)+"px",this._signal("changeGutterWidth",s))},this.$updateCursorRow=function(){if(!this.$highlightGutterLine)return;var e=this.session.selection.getCursor();if(this.$cursorRow===e.row)return;this.$cursorRow=e.row},this.updateLineHighlight=function(){if(!this.$highlightGutterLine)return;var e=this.session.selection.cursor.row;this.$cursorRow=e;if(this.$cursorCell&&this.$cursorCell.row==e)return;this.$cursorCell&&(this.$cursorCell.element.className=this.$cursorCell.element.className.replace("ace_gutter-active-line ",""));var t=this.$lines.cells;this.$cursorCell=null;for(var n=0;n=this.$cursorRow){if(r.row>this.$cursorRow){var i=this.session.getFoldLine(this.$cursorRow);if(!(n>0&&i&&i.start.row==t[n-1].row))break;r=t[n-1]}r.element.className="ace_gutter-active-line "+r.element.className,this.$cursorCell=r;break}}},this.scrollLines=function(e){var t=this.config;this.config=e,this.$updateCursorRow();if(this.$lines.pageChanged(t,e))return this.update(e);this.$lines.moveContainer(e);var n=Math.min(e.lastRow+e.gutterOffset,this.session.getLength()-1),r=this.oldLastRow;this.oldLastRow=n;if(!t||r0;i--)this.$lines.shift();if(r>n)for(var i=this.session.getFoldedRowCount(n+1,r);i>0;i--)this.$lines.pop();e.firstRowr&&this.$lines.push(this.$renderLines(e,r+1,n)),this.updateLineHighlight(),this._signal("afterRender"),this.$updateGutterWidth(e)},this.$renderLines=function(e,t,n){var r=[],i=t,s=this.session.getNextFoldLine(i),o=s?s.start.row:Infinity;for(;;){i>o&&(i=s.end.row+1,s=this.session.getNextFoldLine(i,s),o=s?s.start.row:Infinity);if(i>n)break;var u=this.$lines.createCell(i,e,this.session,f);this.$renderCell(u,e,s,i),r.push(u),i++}return r},this.$renderCell=function(e,t,n,i){var s=e.element,o=this.session,u=s.childNodes[0],a=s.childNodes[1],f=o.$firstLineNumber,l=o.$breakpoints,c=o.$decorations,h=o.gutterRenderer||this.$renderer,p=this.$showFoldWidgets&&o.foldWidgets,d=n?n.start.row:Number.MAX_VALUE,v="ace_gutter-cell ";this.$highlightGutterLine&&(i==this.$cursorRow||n&&i=d&&this.$cursorRow<=n.end.row)&&(v+="ace_gutter-active-line ",this.$cursorCell!=e&&(this.$cursorCell&&(this.$cursorCell.element.className=this.$cursorCell.element.className.replace("ace_gutter-active-line ","")),this.$cursorCell=e)),l[i]&&(v+=l[i]),c[i]&&(v+=c[i]),this.$annotations[i]&&(v+=this.$annotations[i].className),s.className!=v&&(s.className=v);if(p){var m=p[i];m==null&&(m=p[i]=o.getFoldWidget(i))}if(m){var v="ace_fold-widget ace_"+m;m=="start"&&i==d&&in.right-t.right)return"foldWidgets"}}).call(a.prototype),t.Gutter=a}),ace.define("ace/layer/marker",["require","exports","module","ace/range","ace/lib/dom"],function(e,t,n){"use strict";var r=e("../range").Range,i=e("../lib/dom"),s=function(e){this.element=i.createElement("div"),this.element.className="ace_layer ace_marker-layer",e.appendChild(this.element)};(function(){function e(e,t,n,r){return(e?1:0)|(t?2:0)|(n?4:0)|(r?8:0)}this.$padding=0,this.setPadding=function(e){this.$padding=e},this.setSession=function(e){this.session=e},this.setMarkers=function(e){this.markers=e},this.elt=function(e,t){var n=this.i!=-1&&this.element.childNodes[this.i];n?this.i++:(n=document.createElement("div"),this.element.appendChild(n),this.i=-1),n.style.cssText=t,n.className=e},this.update=function(e){if(!e)return;this.config=e,this.i=0;var t;for(var n in this.markers){var r=this.markers[n];if(!r.range){r.update(t,this,this.session,e);continue}var i=r.range.clipRows(e.firstRow,e.lastRow);if(i.isEmpty())continue;i=i.toScreenRange(this.session);if(r.renderer){var s=this.$getTop(i.start.row,e),o=this.$padding+i.start.column*e.characterWidth;r.renderer(t,i,o,s,e)}else r.type=="fullLine"?this.drawFullLineMarker(t,i,r.clazz,e):r.type=="screenLine"?this.drawScreenLineMarker(t,i,r.clazz,e):i.isMultiLine()?r.type=="text"?this.drawTextMarker(t,i,r.clazz,e):this.drawMultiLineMarker(t,i,r.clazz,e):this.drawSingleLineMarker(t,i,r.clazz+" ace_start"+" ace_br15",e)}if(this.i!=-1)while(this.ip,l==f),s,l==f?0:1,o)},this.drawMultiLineMarker=function(e,t,n,r,i){var s=this.$padding,o=r.lineHeight,u=this.$getTop(t.start.row,r),a=s+t.start.column*r.characterWidth;i=i||"";if(this.session.$bidiHandler.isBidiRow(t.start.row)){var f=t.clone();f.end.row=f.start.row,f.end.column=this.session.getLine(f.start.row).length,this.drawBidiSingleLineMarker(e,f,n+" ace_br1 ace_start",r,null,i)}else this.elt(n+" ace_br1 ace_start","height:"+o+"px;"+"right:0;"+"top:"+u+"px;left:"+a+"px;"+(i||""));if(this.session.$bidiHandler.isBidiRow(t.end.row)){var f=t.clone();f.start.row=f.end.row,f.start.column=0,this.drawBidiSingleLineMarker(e,f,n+" ace_br12",r,null,i)}else{u=this.$getTop(t.end.row,r);var l=t.end.column*r.characterWidth;this.elt(n+" ace_br12","height:"+o+"px;"+"width:"+l+"px;"+"top:"+u+"px;"+"left:"+s+"px;"+(i||""))}o=(t.end.row-t.start.row-1)*r.lineHeight;if(o<=0)return;u=this.$getTop(t.start.row+1,r);var c=(t.start.column?1:0)|(t.end.column?0:8);this.elt(n+(c?" ace_br"+c:""),"height:"+o+"px;"+"right:0;"+"top:"+u+"px;"+"left:"+s+"px;"+(i||""))},this.drawSingleLineMarker=function(e,t,n,r,i,s){if(this.session.$bidiHandler.isBidiRow(t.start.row))return this.drawBidiSingleLineMarker(e,t,n,r,i,s);var o=r.lineHeight,u=(t.end.column+(i||0)-t.start.column)*r.characterWidth,a=this.$getTop(t.start.row,r),f=this.$padding+t.start.column*r.characterWidth;this.elt(n,"height:"+o+"px;"+"width:"+u+"px;"+"top:"+a+"px;"+"left:"+f+"px;"+(s||""))},this.drawBidiSingleLineMarker=function(e,t,n,r,i,s){var o=r.lineHeight,u=this.$getTop(t.start.row,r),a=this.$padding,f=this.session.$bidiHandler.getSelections(t.start.column,t.end.column);f.forEach(function(e){this.elt(n,"height:"+o+"px;"+"width:"+e.width+(i||0)+"px;"+"top:"+u+"px;"+"left:"+(a+e.left)+"px;"+(s||""))},this)},this.drawFullLineMarker=function(e,t,n,r,i){var s=this.$getTop(t.start.row,r),o=r.lineHeight;t.start.row!=t.end.row&&(o+=this.$getTop(t.end.row,r)-s),this.elt(n,"height:"+o+"px;"+"top:"+s+"px;"+"left:0;right:0;"+(i||""))},this.drawScreenLineMarker=function(e,t,n,r,i){var s=this.$getTop(t.start.row,r),o=r.lineHeight;this.elt(n,"height:"+o+"px;"+"top:"+s+"px;"+"left:0;right:0;"+(i||""))}}).call(s.prototype),t.Marker=s}),ace.define("ace/layer/text",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/layer/lines","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/dom"),s=e("../lib/lang"),o=e("./lines").Lines,u=e("../lib/event_emitter").EventEmitter,a=function(e){this.dom=i,this.element=this.dom.createElement("div"),this.element.className="ace_layer ace_text-layer",e.appendChild(this.element),this.$updateEolChar=this.$updateEolChar.bind(this),this.$lines=new o(this.element)};(function(){r.implement(this,u),this.EOF_CHAR="\u00b6",this.EOL_CHAR_LF="\u00ac",this.EOL_CHAR_CRLF="\u00a4",this.EOL_CHAR=this.EOL_CHAR_LF,this.TAB_CHAR="\u2014",this.SPACE_CHAR="\u00b7",this.$padding=0,this.MAX_LINE_LENGTH=1e4,this.$updateEolChar=function(){var e=this.session.doc,t=e.getNewLineCharacter()=="\n"&&e.getNewLineMode()!="windows",n=t?this.EOL_CHAR_LF:this.EOL_CHAR_CRLF;if(this.EOL_CHAR!=n)return this.EOL_CHAR=n,!0},this.setPadding=function(e){this.$padding=e,this.element.style.margin="0 "+e+"px"},this.getLineHeight=function(){return this.$fontMetrics.$characterSize.height||0},this.getCharacterWidth=function(){return this.$fontMetrics.$characterSize.width||0},this.$setFontMetrics=function(e){this.$fontMetrics=e,this.$fontMetrics.on("changeCharacterSize",function(e){this._signal("changeCharacterSize",e)}.bind(this)),this.$pollSizeChanges()},this.checkForSizeChanges=function(){this.$fontMetrics.checkForSizeChanges()},this.$pollSizeChanges=function(){return this.$pollSizeChangesTimer=this.$fontMetrics.$pollSizeChanges()},this.setSession=function(e){this.session=e,e&&this.$computeTabString()},this.showInvisibles=!1,this.setShowInvisibles=function(e){return this.showInvisibles==e?!1:(this.showInvisibles=e,this.$computeTabString(),!0)},this.displayIndentGuides=!0,this.setDisplayIndentGuides=function(e){return this.displayIndentGuides==e?!1:(this.displayIndentGuides=e,this.$computeTabString(),!0)},this.$tabStrings=[],this.onChangeTabSize=this.$computeTabString=function(){var e=this.session.getTabSize();this.tabSize=e;var t=this.$tabStrings=[0];for(var n=1;nl&&(u=a.end.row+1,a=this.session.getNextFoldLine(u,a),l=a?a.start.row:Infinity);if(u>i)break;var c=s[o++];if(c){this.dom.removeChildren(c),this.$renderLine(c,u,u==l?a:!1);var h=e.lineHeight*this.session.getRowLength(u)+"px";c.style.height!=h&&(f=!0,c.style.height=h)}u++}if(f)while(o0;i--)this.$lines.shift();if(t.lastRow>e.lastRow)for(var i=this.session.getFoldedRowCount(e.lastRow+1,t.lastRow);i>0;i--)this.$lines.pop();e.firstRowt.lastRow&&this.$lines.push(this.$renderLinesFragment(e,t.lastRow+1,e.lastRow))},this.$renderLinesFragment=function(e,t,n){var r=[],s=t,o=this.session.getNextFoldLine(s),u=o?o.start.row:Infinity;for(;;){s>u&&(s=o.end.row+1,o=this.session.getNextFoldLine(s,o),u=o?o.start.row:Infinity);if(s>n)break;var a=this.$lines.createCell(s,e,this.session),f=a.element;this.dom.removeChildren(f),i.setStyle(f.style,"height",this.$lines.computeLineHeight(s,e,this.session)+"px"),i.setStyle(f.style,"top",this.$lines.computeLineTop(s,e,this.session)+"px"),this.$renderLine(f,s,s==u?o:!1),this.$useLineGroups()?f.className="ace_line_group":f.className="ace_line",r.push(a),s++}return r},this.update=function(e){this.$lines.moveContainer(e),this.config=e;var t=e.firstRow,n=e.lastRow,r=this.$lines;while(r.getLength())r.pop();r.push(this.$renderLinesFragment(e,t,n))},this.$textToken={text:!0,rparen:!0,lparen:!0},this.$renderToken=function(e,t,n,r){var i=this,o=/(\t)|( +)|([\x00-\x1f\x80-\xa0\xad\u1680\u180E\u2000-\u200f\u2028\u2029\u202F\u205F\uFEFF\uFFF9-\uFFFC]+)|(\u3000)|([\u1100-\u115F\u11A3-\u11A7\u11FA-\u11FF\u2329-\u232A\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3001-\u303E\u3041-\u3096\u3099-\u30FF\u3105-\u312D\u3131-\u318E\u3190-\u31BA\u31C0-\u31E3\u31F0-\u321E\u3220-\u3247\u3250-\u32FE\u3300-\u4DBF\u4E00-\uA48C\uA490-\uA4C6\uA960-\uA97C\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFAFF\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE66\uFE68-\uFE6B\uFF01-\uFF60\uFFE0-\uFFE6]|[\uD800-\uDBFF][\uDC00-\uDFFF])/g,u=this.dom.createFragment(this.element),a,f=0;while(a=o.exec(r)){var l=a[1],c=a[2],h=a[3],p=a[4],d=a[5];if(!i.showInvisibles&&c)continue;var v=f!=a.index?r.slice(f,a.index):"";f=a.index+a[0].length,v&&u.appendChild(this.dom.createTextNode(v,this.element));if(l){var m=i.session.getScreenTabSize(t+a.index);u.appendChild(i.$tabStrings[m].cloneNode(!0)),t+=m-1}else if(c)if(i.showInvisibles){var g=this.dom.createElement("span");g.className="ace_invisible ace_invisible_space",g.textContent=s.stringRepeat(i.SPACE_CHAR,c.length),u.appendChild(g)}else u.appendChild(this.com.createTextNode(c,this.element));else if(h){var g=this.dom.createElement("span");g.className="ace_invisible ace_invisible_space ace_invalid",g.textContent=s.stringRepeat(i.SPACE_CHAR,h.length),u.appendChild(g)}else if(p){var y=i.showInvisibles?i.SPACE_CHAR:"";t+=1;var g=this.dom.createElement("span");g.style.width=i.config.characterWidth*2+"px",g.className=i.showInvisibles?"ace_cjk ace_invisible ace_invisible_space":"ace_cjk",g.textContent=i.showInvisibles?i.SPACE_CHAR:"",u.appendChild(g)}else if(d){t+=1;var g=this.dom.createElement("span");g.style.width=i.config.characterWidth*2+"px",g.className="ace_cjk",g.textContent=d,u.appendChild(g)}}u.appendChild(this.dom.createTextNode(f?r.slice(f):r,this.element));if(!this.$textToken[n.type]){var b="ace_"+n.type.replace(/\./g," ace_"),g=this.dom.createElement("span");n.type=="fold"&&(g.style.width=n.value.length*this.config.characterWidth+"px"),g.className=b,g.appendChild(u),e.appendChild(g)}else e.appendChild(u);return t+r.length},this.renderIndentGuide=function(e,t,n){var r=t.search(this.$indentGuideRe);if(r<=0||r>=n)return t;if(t[0]==" "){r-=r%this.tabSize;var i=r/this.tabSize;for(var s=0;s=o)u=this.$renderToken(a,u,l,c.substring(0,o-r)),c=c.substring(o-r),r=o,a=this.$createLineElement(),e.appendChild(a),a.appendChild(this.dom.createTextNode(s.stringRepeat("\u00a0",n.indent),this.element)),i++,u=0,o=n[i]||Number.MAX_VALUE;c.length!=0&&(r+=c.length,u=this.$renderToken(a,u,l,c))}}n[n.length-1]>this.MAX_LINE_LENGTH&&this.$renderOverflowMessage(a,u,null,"",!0)},this.$renderSimpleLine=function(e,t){var n=0,r=t[0],i=r.value;this.displayIndentGuides&&(i=this.renderIndentGuide(e,i)),i&&(n=this.$renderToken(e,n,r,i));for(var s=1;sthis.MAX_LINE_LENGTH)return this.$renderOverflowMessage(e,n,r,i);n=this.$renderToken(e,n,r,i)}},this.$renderOverflowMessage=function(e,t,n,r,i){n&&this.$renderToken(e,t,n,r.slice(0,this.MAX_LINE_LENGTH-t));var s=this.dom.createElement("span");s.className="ace_inline_button ace_keyword ace_toggle_wrap",s.textContent=i?"":"",e.appendChild(s)},this.$renderLine=function(e,t,n){!n&&n!=0&&(n=this.session.getFoldLine(t));if(n)var r=this.$getFoldLineTokens(t,n);else var r=this.session.getTokens(t);var i=e;if(r.length){var s=this.session.getRowSplitData(t);if(s&&s.length){this.$renderWrappedLine(e,r,s);var i=e.lastChild}else{var i=e;this.$useLineGroups()&&(i=this.$createLineElement(),e.appendChild(i)),this.$renderSimpleLine(i,r)}}else this.$useLineGroups()&&(i=this.$createLineElement(),e.appendChild(i));if(this.showInvisibles&&i){n&&(t=n.end.row);var o=this.dom.createElement("span");o.className="ace_invisible ace_invisible_eol",o.textContent=t==this.session.getLength()-1?this.EOF_CHAR:this.EOL_CHAR,i.appendChild(o)}},this.$getFoldLineTokens=function(e,t){function i(e,t,n){var i=0,s=0;while(s+e[i].value.lengthn-t&&(o=o.substring(0,n-t)),r.push({type:e[i].type,value:o}),s=t+o.length,i+=1}while(sn?r.push({type:e[i].type,value:o.substring(0,n-s)}):r.push(e[i]),s+=o.length,i+=1}}var n=this.session,r=[],s=n.getTokens(e);return t.walk(function(e,t,o,u,a){e!=null?r.push({type:"fold",value:e}):(a&&(s=n.getTokens(t)),s.length&&i(s,u,o))},t.end.row,this.session.getLine(t.end.row).length),r},this.$useLineGroups=function(){return this.session.getUseWrapMode()},this.destroy=function(){}}).call(a.prototype),t.Text=a}),ace.define("ace/layer/cursor",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";var r=e("../lib/dom"),i=function(e){this.element=r.createElement("div"),this.element.className="ace_layer ace_cursor-layer",e.appendChild(this.element),this.isVisible=!1,this.isBlinking=!0,this.blinkInterval=1e3,this.smoothBlinking=!1,this.cursors=[],this.cursor=this.addCursor(),r.addCssClass(this.element,"ace_hidden-cursors"),this.$updateCursors=this.$updateOpacity.bind(this)};(function(){this.$updateOpacity=function(e){var t=this.cursors;for(var n=t.length;n--;)r.setStyle(t[n].style,"opacity",e?"":"0")},this.$startCssAnimation=function(){var e=this.cursors;for(var t=e.length;t--;)e[t].style.animationDuration=this.blinkInterval+"ms";setTimeout(function(){r.addCssClass(this.element,"ace_animate-blinking")}.bind(this))},this.$stopCssAnimation=function(){r.removeCssClass(this.element,"ace_animate-blinking")},this.$padding=0,this.setPadding=function(e){this.$padding=e},this.setSession=function(e){this.session=e},this.setBlinking=function(e){e!=this.isBlinking&&(this.isBlinking=e,this.restartTimer())},this.setBlinkInterval=function(e){e!=this.blinkInterval&&(this.blinkInterval=e,this.restartTimer())},this.setSmoothBlinking=function(e){e!=this.smoothBlinking&&(this.smoothBlinking=e,r.setCssClass(this.element,"ace_smooth-blinking",e),this.$updateCursors(!0),this.restartTimer())},this.addCursor=function(){var e=r.createElement("div");return e.className="ace_cursor",this.element.appendChild(e),this.cursors.push(e),e},this.removeCursor=function(){if(this.cursors.length>1){var e=this.cursors.pop();return e.parentNode.removeChild(e),e}},this.hideCursor=function(){this.isVisible=!1,r.addCssClass(this.element,"ace_hidden-cursors"),this.restartTimer()},this.showCursor=function(){this.isVisible=!0,r.removeCssClass(this.element,"ace_hidden-cursors"),this.restartTimer()},this.restartTimer=function(){var e=this.$updateCursors;clearInterval(this.intervalId),clearTimeout(this.timeoutId),this.$stopCssAnimation(),this.smoothBlinking&&r.removeCssClass(this.element,"ace_smooth-blinking"),e(!0);if(!this.isBlinking||!this.blinkInterval||!this.isVisible){this.$stopCssAnimation();return}this.smoothBlinking&&setTimeout(function(){r.addCssClass(this.element,"ace_smooth-blinking")}.bind(this));if(r.HAS_CSS_ANIMATION)this.$startCssAnimation();else{var t=function(){this.timeoutId=setTimeout(function(){e(!1)},.6*this.blinkInterval)}.bind(this);this.intervalId=setInterval(function(){e(!0),t()},this.blinkInterval),t()}},this.getPixelPosition=function(e,t){if(!this.config||!this.session)return{left:0,top:0};e||(e=this.session.selection.getCursor());var n=this.session.documentToScreenPosition(e),r=this.$padding+(this.session.$bidiHandler.isBidiRow(n.row,e.row)?this.session.$bidiHandler.getPosLeft(n.column):n.column*this.config.characterWidth),i=(n.row-(t?this.config.firstRowScreen:0))*this.config.lineHeight;return{left:r,top:i}},this.isCursorInView=function(e,t){return e.top>=0&&e.tope.height+e.offset||o.top<0)&&n>1)continue;var u=this.cursors[i++]||this.addCursor(),a=u.style;this.drawCursor?this.drawCursor(u,o,e,t[n],this.session):this.isCursorInView(o,e)?(r.setStyle(a,"display","block"),r.translate(u,o.left,o.top),r.setStyle(a,"width",Math.round(e.characterWidth)+"px"),r.setStyle(a,"height",e.lineHeight+"px")):r.setStyle(a,"display","none")}while(this.cursors.length>i)this.removeCursor();var f=this.session.getOverwrite();this.$setOverwrite(f),this.$pixelPos=o,this.restartTimer()},this.drawCursor=null,this.$setOverwrite=function(e){e!=this.overwrite&&(this.overwrite=e,e?r.addCssClass(this.element,"ace_overwrite-cursors"):r.removeCssClass(this.element,"ace_overwrite-cursors"))},this.destroy=function(){clearInterval(this.intervalId),clearTimeout(this.timeoutId)}}).call(i.prototype),t.Cursor=i}),ace.define("ace/scrollbar",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/event","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./lib/event"),o=e("./lib/event_emitter").EventEmitter,u=32768,a=function(e){this.element=i.createElement("div"),this.element.className="ace_scrollbar ace_scrollbar"+this.classSuffix,this.inner=i.createElement("div"),this.inner.className="ace_scrollbar-inner",this.inner.textContent="\u00a0",this.element.appendChild(this.inner),e.appendChild(this.element),this.setVisible(!1),this.skipEvent=!1,s.addListener(this.element,"scroll",this.onScroll.bind(this)),s.addListener(this.element,"mousedown",s.preventDefault)};(function(){r.implement(this,o),this.setVisible=function(e){this.element.style.display=e?"":"none",this.isVisible=e,this.coeff=1}}).call(a.prototype);var f=function(e,t){a.call(this,e),this.scrollTop=0,this.scrollHeight=0,t.$scrollbarWidth=this.width=i.scrollbarWidth(e.ownerDocument),this.inner.style.width=this.element.style.width=(this.width||15)+5+"px",this.$minWidth=0};r.inherits(f,a),function(){this.classSuffix="-v",this.onScroll=function(){if(!this.skipEvent){this.scrollTop=this.element.scrollTop;if(this.coeff!=1){var e=this.element.clientHeight/this.scrollHeight;this.scrollTop=this.scrollTop*(1-e)/(this.coeff-e)}this._emit("scroll",{data:this.scrollTop})}this.skipEvent=!1},this.getWidth=function(){return Math.max(this.isVisible?this.width:0,this.$minWidth||0)},this.setHeight=function(e){this.element.style.height=e+"px"},this.setInnerHeight=this.setScrollHeight=function(e){this.scrollHeight=e,e>u?(this.coeff=u/e,e=u):this.coeff!=1&&(this.coeff=1),this.inner.style.height=e+"px"},this.setScrollTop=function(e){this.scrollTop!=e&&(this.skipEvent=!0,this.scrollTop=e,this.element.scrollTop=e*this.coeff)}}.call(f.prototype);var l=function(e,t){a.call(this,e),this.scrollLeft=0,this.height=t.$scrollbarWidth,this.inner.style.height=this.element.style.height=(this.height||15)+5+"px"};r.inherits(l,a),function(){this.classSuffix="-h",this.onScroll=function(){this.skipEvent||(this.scrollLeft=this.element.scrollLeft,this._emit("scroll",{data:this.scrollLeft})),this.skipEvent=!1},this.getHeight=function(){return this.isVisible?this.height:0},this.setWidth=function(e){this.element.style.width=e+"px"},this.setInnerWidth=function(e){this.inner.style.width=e+"px"},this.setScrollWidth=function(e){this.inner.style.width=e+"px"},this.setScrollLeft=function(e){this.scrollLeft!=e&&(this.skipEvent=!0,this.scrollLeft=this.element.scrollLeft=e)}}.call(l.prototype),t.ScrollBar=f,t.ScrollBarV=f,t.ScrollBarH=l,t.VScrollBar=f,t.HScrollBar=l}),ace.define("ace/renderloop",["require","exports","module","ace/lib/event"],function(e,t,n){"use strict";var r=e("./lib/event"),i=function(e,t){this.onRender=e,this.pending=!1,this.changes=0,this.$recursionLimit=2,this.window=t||window;var n=this;this._flush=function(e){n.pending=!1;var t=n.changes;t&&(r.blockIdle(100),n.changes=0,n.onRender(t));if(n.changes){if(n.$recursionLimit--<0)return;n.schedule()}else n.$recursionLimit=2}};(function(){this.schedule=function(e){this.changes=this.changes|e,this.changes&&!this.pending&&(r.nextFrame(this._flush),this.pending=!0)},this.clear=function(e){var t=this.changes;return this.changes=0,t}}).call(i.prototype),t.RenderLoop=i}),ace.define("ace/layer/font_metrics",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/event","ace/lib/useragent","ace/lib/event_emitter"],function(e,t,n){var r=e("../lib/oop"),i=e("../lib/dom"),s=e("../lib/lang"),o=e("../lib/event"),u=e("../lib/useragent"),a=e("../lib/event_emitter").EventEmitter,f=256,l=typeof ResizeObserver=="function",c=200,h=t.FontMetrics=function(e){this.el=i.createElement("div"),this.$setMeasureNodeStyles(this.el.style,!0),this.$main=i.createElement("div"),this.$setMeasureNodeStyles(this.$main.style),this.$measureNode=i.createElement("div"),this.$setMeasureNodeStyles(this.$measureNode.style),this.el.appendChild(this.$main),this.el.appendChild(this.$measureNode),e.appendChild(this.el),this.$measureNode.innerHTML=s.stringRepeat("X",f),this.$characterSize={width:0,height:0},l?this.$addObserver():this.checkForSizeChanges()};(function(){r.implement(this,a),this.$characterSize={width:0,height:0},this.$setMeasureNodeStyles=function(e,t){e.width=e.height="auto",e.left=e.top="0px",e.visibility="hidden",e.position="absolute",e.whiteSpace="pre",u.isIE<8?e["font-family"]="inherit":e.font="inherit",e.overflow=t?"hidden":"visible"},this.checkForSizeChanges=function(e){e===undefined&&(e=this.$measureSizes());if(e&&(this.$characterSize.width!==e.width||this.$characterSize.height!==e.height)){this.$measureNode.style.fontWeight="bold";var t=this.$measureSizes();this.$measureNode.style.fontWeight="",this.$characterSize=e,this.charSizes=Object.create(null),this.allowBoldFonts=t&&t.width===e.width&&t.height===e.height,this._emit("changeCharacterSize",{data:e})}},this.$addObserver=function(){var e=this;this.$observer=new window.ResizeObserver(function(t){var n=t[0].contentRect;e.checkForSizeChanges({height:n.height,width:n.width/f})}),this.$observer.observe(this.$measureNode)},this.$pollSizeChanges=function(){if(this.$pollSizeChangesTimer||this.$observer)return this.$pollSizeChangesTimer;var e=this;return this.$pollSizeChangesTimer=o.onIdle(function t(){e.checkForSizeChanges(),o.onIdle(t,500)},500)},this.setPolling=function(e){e?this.$pollSizeChanges():this.$pollSizeChangesTimer&&(clearInterval(this.$pollSizeChangesTimer),this.$pollSizeChangesTimer=0)},this.$measureSizes=function(e){var t={height:(e||this.$measureNode).clientHeight,width:(e||this.$measureNode).clientWidth/f};return t.width===0||t.height===0?null:t},this.$measureCharWidth=function(e){this.$main.innerHTML=s.stringRepeat(e,f);var t=this.$main.getBoundingClientRect();return t.width/f},this.getCharacterWidth=function(e){var t=this.charSizes[e];return t===undefined&&(t=this.charSizes[e]=this.$measureCharWidth(e)/this.$characterSize.width),t},this.destroy=function(){clearInterval(this.$pollSizeChangesTimer),this.$observer&&this.$observer.disconnect(),this.el&&this.el.parentNode&&this.el.parentNode.removeChild(this.el)},this.$getZoom=function e(t){return t?(window.getComputedStyle(t).zoom||1)*e(t.parentElement):1},this.$initTransformMeasureNodes=function(){var e=function(e,t){return["div",{style:"position: absolute;top:"+e+"px;left:"+t+"px;"}]};this.els=i.buildDom([e(0,0),e(c,0),e(0,c),e(c,c)],this.el)},this.transformCoordinates=function(e,t){function r(e,t,n){var r=e[1]*t[0]-e[0]*t[1];return[(-t[1]*n[0]+t[0]*n[1])/r,(+e[1]*n[0]-e[0]*n[1])/r]}function i(e,t){return[e[0]-t[0],e[1]-t[1]]}function s(e,t){return[e[0]+t[0],e[1]+t[1]]}function o(e,t){return[e*t[0],e*t[1]]}function u(e){var t=e.getBoundingClientRect();return[t.left,t.top]}if(e){var n=this.$getZoom(this.el);e=o(1/n,e)}this.els||this.$initTransformMeasureNodes();var a=u(this.els[0]),f=u(this.els[1]),l=u(this.els[2]),h=u(this.els[3]),p=r(i(h,f),i(h,l),i(s(f,l),s(h,a))),d=o(1+p[0],i(f,a)),v=o(1+p[1],i(l,a));if(t){var m=t,g=p[0]*m[0]/c+p[1]*m[1]/c+1,y=s(o(m[0],d),o(m[1],v));return s(o(1/g/c,y),a)}var b=i(e,a),w=r(i(d,o(p[0],b)),i(v,o(p[1],b)),b);return o(c,w)}}).call(h.prototype)}),ace.define("ace/virtual_renderer",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/config","ace/layer/gutter","ace/layer/marker","ace/layer/text","ace/layer/cursor","ace/scrollbar","ace/scrollbar","ace/renderloop","ace/layer/font_metrics","ace/lib/event_emitter","ace/lib/useragent"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./config"),o=e("./layer/gutter").Gutter,u=e("./layer/marker").Marker,a=e("./layer/text").Text,f=e("./layer/cursor").Cursor,l=e("./scrollbar").HScrollBar,c=e("./scrollbar").VScrollBar,h=e("./renderloop").RenderLoop,p=e("./layer/font_metrics").FontMetrics,d=e("./lib/event_emitter").EventEmitter,v='.ace_br1 {border-top-left-radius : 3px;}.ace_br2 {border-top-right-radius : 3px;}.ace_br3 {border-top-left-radius : 3px; border-top-right-radius: 3px;}.ace_br4 {border-bottom-right-radius: 3px;}.ace_br5 {border-top-left-radius : 3px; border-bottom-right-radius: 3px;}.ace_br6 {border-top-right-radius : 3px; border-bottom-right-radius: 3px;}.ace_br7 {border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px;}.ace_br8 {border-bottom-left-radius : 3px;}.ace_br9 {border-top-left-radius : 3px; border-bottom-left-radius: 3px;}.ace_br10{border-top-right-radius : 3px; border-bottom-left-radius: 3px;}.ace_br11{border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_br12{border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_br13{border-top-left-radius : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_br14{border-top-right-radius : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_br15{border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_editor {position: relative;overflow: hidden;font: 12px/normal \'Monaco\', \'Menlo\', \'Ubuntu Mono\', \'Consolas\', \'source-code-pro\', monospace;direction: ltr;text-align: left;-webkit-tap-highlight-color: rgba(0, 0, 0, 0);}.ace_scroller {position: absolute;overflow: hidden;top: 0;bottom: 0;background-color: inherit;-ms-user-select: none;-moz-user-select: none;-webkit-user-select: none;user-select: none;cursor: text;}.ace_content {position: absolute;box-sizing: border-box;min-width: 100%;contain: style size layout;}.ace_dragging .ace_scroller:before{position: absolute;top: 0;left: 0;right: 0;bottom: 0;content: \'\';background: rgba(250, 250, 250, 0.01);z-index: 1000;}.ace_dragging.ace_dark .ace_scroller:before{background: rgba(0, 0, 0, 0.01);}.ace_selecting, .ace_selecting * {cursor: text !important;}.ace_gutter {position: absolute;overflow : hidden;width: auto;top: 0;bottom: 0;left: 0;cursor: default;z-index: 4;-ms-user-select: none;-moz-user-select: none;-webkit-user-select: none;user-select: none;contain: style size layout;}.ace_gutter-active-line {position: absolute;left: 0;right: 0;}.ace_scroller.ace_scroll-left {box-shadow: 17px 0 16px -16px rgba(0, 0, 0, 0.4) inset;}.ace_gutter-cell {position: absolute;top: 0;left: 0;right: 0;padding-left: 19px;padding-right: 6px;background-repeat: no-repeat;}.ace_gutter-cell.ace_error {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABOFBMVEX/////////QRswFAb/Ui4wFAYwFAYwFAaWGAfDRymzOSH/PxswFAb/SiUwFAYwFAbUPRvjQiDllog5HhHdRybsTi3/Tyv9Tir+Syj/UC3////XurebMBIwFAb/RSHbPx/gUzfdwL3kzMivKBAwFAbbvbnhPx66NhowFAYwFAaZJg8wFAaxKBDZurf/RB6mMxb/SCMwFAYwFAbxQB3+RB4wFAb/Qhy4Oh+4QifbNRcwFAYwFAYwFAb/QRzdNhgwFAYwFAbav7v/Uy7oaE68MBK5LxLewr/r2NXewLswFAaxJw4wFAbkPRy2PyYwFAaxKhLm1tMwFAazPiQwFAaUGAb/QBrfOx3bvrv/VC/maE4wFAbRPBq6MRO8Qynew8Dp2tjfwb0wFAbx6eju5+by6uns4uH9/f36+vr/GkHjAAAAYnRSTlMAGt+64rnWu/bo8eAA4InH3+DwoN7j4eLi4xP99Nfg4+b+/u9B/eDs1MD1mO7+4PHg2MXa347g7vDizMLN4eG+Pv7i5evs/v79yu7S3/DV7/498Yv24eH+4ufQ3Ozu/v7+y13sRqwAAADLSURBVHjaZc/XDsFgGIBhtDrshlitmk2IrbHFqL2pvXf/+78DPokj7+Fz9qpU/9UXJIlhmPaTaQ6QPaz0mm+5gwkgovcV6GZzd5JtCQwgsxoHOvJO15kleRLAnMgHFIESUEPmawB9ngmelTtipwwfASilxOLyiV5UVUyVAfbG0cCPHig+GBkzAENHS0AstVF6bacZIOzgLmxsHbt2OecNgJC83JERmePUYq8ARGkJx6XtFsdddBQgZE2nPR6CICZhawjA4Fb/chv+399kfR+MMMDGOQAAAABJRU5ErkJggg==");background-repeat: no-repeat;background-position: 2px center;}.ace_gutter-cell.ace_warning {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAmVBMVEX///8AAAD///8AAAAAAABPSzb/5sAAAAB/blH/73z/ulkAAAAAAAD85pkAAAAAAAACAgP/vGz/rkDerGbGrV7/pkQICAf////e0IsAAAD/oED/qTvhrnUAAAD/yHD/njcAAADuv2r/nz//oTj/p064oGf/zHAAAAA9Nir/tFIAAAD/tlTiuWf/tkIAAACynXEAAAAAAAAtIRW7zBpBAAAAM3RSTlMAABR1m7RXO8Ln31Z36zT+neXe5OzooRDfn+TZ4p3h2hTf4t3k3ucyrN1K5+Xaks52Sfs9CXgrAAAAjklEQVR42o3PbQ+CIBQFYEwboPhSYgoYunIqqLn6/z8uYdH8Vmdnu9vz4WwXgN/xTPRD2+sgOcZjsge/whXZgUaYYvT8QnuJaUrjrHUQreGczuEafQCO/SJTufTbroWsPgsllVhq3wJEk2jUSzX3CUEDJC84707djRc5MTAQxoLgupWRwW6UB5fS++NV8AbOZgnsC7BpEAAAAABJRU5ErkJggg==");background-position: 2px center;}.ace_gutter-cell.ace_info {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAJ0Uk5TAAB2k804AAAAPklEQVQY02NgIB68QuO3tiLznjAwpKTgNyDbMegwisCHZUETUZV0ZqOquBpXj2rtnpSJT1AEnnRmL2OgGgAAIKkRQap2htgAAAAASUVORK5CYII=");background-position: 2px center;}.ace_dark .ace_gutter-cell.ace_info {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAJFBMVEUAAAChoaGAgIAqKiq+vr6tra1ZWVmUlJSbm5s8PDxubm56enrdgzg3AAAAAXRSTlMAQObYZgAAAClJREFUeNpjYMAPdsMYHegyJZFQBlsUlMFVCWUYKkAZMxZAGdxlDMQBAG+TBP4B6RyJAAAAAElFTkSuQmCC");}.ace_scrollbar {contain: strict;position: absolute;right: 0;bottom: 0;z-index: 6;}.ace_scrollbar-inner {position: absolute;cursor: text;left: 0;top: 0;}.ace_scrollbar-v{overflow-x: hidden;overflow-y: scroll;top: 0;}.ace_scrollbar-h {overflow-x: scroll;overflow-y: hidden;left: 0;}.ace_print-margin {position: absolute;height: 100%;}.ace_text-input {position: absolute;z-index: 0;width: 0.5em;height: 1em;opacity: 0;background: transparent;-moz-appearance: none;appearance: none;border: none;resize: none;outline: none;overflow: hidden;font: inherit;padding: 0 1px;margin: 0 -1px;contain: strict;-ms-user-select: text;-moz-user-select: text;-webkit-user-select: text;user-select: text;white-space: pre!important;}.ace_text-input.ace_composition {background: transparent;color: inherit;z-index: 1000;opacity: 1;}.ace_composition_placeholder { color: transparent }.ace_composition_marker { border-bottom: 1px solid;position: absolute;border-radius: 0;margin-top: 1px;}[ace_nocontext=true] {transform: none!important;filter: none!important;perspective: none!important;clip-path: none!important;mask : none!important;contain: none!important;perspective: none!important;mix-blend-mode: initial!important;z-index: auto;}.ace_layer {z-index: 1;position: absolute;overflow: hidden;word-wrap: normal;white-space: pre;height: 100%;width: 100%;box-sizing: border-box;pointer-events: none;}.ace_gutter-layer {position: relative;width: auto;text-align: right;pointer-events: auto;height: 1000000px;contain: style size layout;}.ace_text-layer {font: inherit !important;position: absolute;height: 1000000px;width: 1000000px;contain: style size layout;}.ace_text-layer > .ace_line, .ace_text-layer > .ace_line_group {contain: style size layout;position: absolute;top: 0;left: 0;right: 0;}.ace_hidpi .ace_text-layer,.ace_hidpi .ace_gutter-layer,.ace_hidpi .ace_content,.ace_hidpi .ace_gutter {contain: strict;will-change: transform;}.ace_hidpi .ace_text-layer > .ace_line, .ace_hidpi .ace_text-layer > .ace_line_group {contain: strict;}.ace_cjk {display: inline-block;text-align: center;}.ace_cursor-layer {z-index: 4;}.ace_cursor {z-index: 4;position: absolute;box-sizing: border-box;border-left: 2px solid;transform: translatez(0);}.ace_multiselect .ace_cursor {border-left-width: 1px;}.ace_slim-cursors .ace_cursor {border-left-width: 1px;}.ace_overwrite-cursors .ace_cursor {border-left-width: 0;border-bottom: 1px solid;}.ace_hidden-cursors .ace_cursor {opacity: 0.2;}.ace_smooth-blinking .ace_cursor {transition: opacity 0.18s;}.ace_animate-blinking .ace_cursor {animation-duration: 1000ms;animation-timing-function: step-end;animation-name: blink-ace-animate;animation-iteration-count: infinite;}.ace_animate-blinking.ace_smooth-blinking .ace_cursor {animation-duration: 1000ms;animation-timing-function: ease-in-out;animation-name: blink-ace-animate-smooth;}@keyframes blink-ace-animate {from, to { opacity: 1; }60% { opacity: 0; }}@keyframes blink-ace-animate-smooth {from, to { opacity: 1; }45% { opacity: 1; }60% { opacity: 0; }85% { opacity: 0; }}.ace_marker-layer .ace_step, .ace_marker-layer .ace_stack {position: absolute;z-index: 3;}.ace_marker-layer .ace_selection {position: absolute;z-index: 5;}.ace_marker-layer .ace_bracket {position: absolute;z-index: 6;}.ace_marker-layer .ace_active-line {position: absolute;z-index: 2;}.ace_marker-layer .ace_selected-word {position: absolute;z-index: 4;box-sizing: border-box;}.ace_line .ace_fold {box-sizing: border-box;display: inline-block;height: 11px;margin-top: -2px;vertical-align: middle;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="),url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACJJREFUeNpi+P//fxgTAwPDBxDxD078RSX+YeEyDFMCIMAAI3INmXiwf2YAAAAASUVORK5CYII=");background-repeat: no-repeat, repeat-x;background-position: center center, top left;color: transparent;border: 1px solid black;border-radius: 2px;cursor: pointer;pointer-events: auto;}.ace_dark .ace_fold {}.ace_fold:hover{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="),url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACBJREFUeNpi+P//fz4TAwPDZxDxD5X4i5fLMEwJgAADAEPVDbjNw87ZAAAAAElFTkSuQmCC");}.ace_tooltip {background-color: #FFF;background-image: linear-gradient(to bottom, transparent, rgba(0, 0, 0, 0.1));border: 1px solid gray;border-radius: 1px;box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);color: black;max-width: 100%;padding: 3px 4px;position: fixed;z-index: 999999;box-sizing: border-box;cursor: default;white-space: pre;word-wrap: break-word;line-height: normal;font-style: normal;font-weight: normal;letter-spacing: normal;pointer-events: none;}.ace_folding-enabled > .ace_gutter-cell {padding-right: 13px;}.ace_fold-widget {box-sizing: border-box;margin: 0 -12px 0 1px;display: none;width: 11px;vertical-align: top;background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42mWKsQ0AMAzC8ixLlrzQjzmBiEjp0A6WwBCSPgKAXoLkqSot7nN3yMwR7pZ32NzpKkVoDBUxKAAAAABJRU5ErkJggg==");background-repeat: no-repeat;background-position: center;border-radius: 3px;border: 1px solid transparent;cursor: pointer;}.ace_folding-enabled .ace_fold-widget {display: inline-block; }.ace_fold-widget.ace_end {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42m3HwQkAMAhD0YzsRchFKI7sAikeWkrxwScEB0nh5e7KTPWimZki4tYfVbX+MNl4pyZXejUO1QAAAABJRU5ErkJggg==");}.ace_fold-widget.ace_closed {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAGCAYAAAAG5SQMAAAAOUlEQVR42jXKwQkAMAgDwKwqKD4EwQ26sSOkVWjgIIHAzPiCgaqiqnJHZnKICBERHN194O5b9vbLuAVRL+l0YWnZAAAAAElFTkSuQmCCXA==");}.ace_fold-widget:hover {border: 1px solid rgba(0, 0, 0, 0.3);background-color: rgba(255, 255, 255, 0.2);box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);}.ace_fold-widget:active {border: 1px solid rgba(0, 0, 0, 0.4);background-color: rgba(0, 0, 0, 0.05);box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);}.ace_dark .ace_fold-widget {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHklEQVQIW2P4//8/AzoGEQ7oGCaLLAhWiSwB146BAQCSTPYocqT0AAAAAElFTkSuQmCC");}.ace_dark .ace_fold-widget.ace_end {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAH0lEQVQIW2P4//8/AxQ7wNjIAjDMgC4AxjCVKBirIAAF0kz2rlhxpAAAAABJRU5ErkJggg==");}.ace_dark .ace_fold-widget.ace_closed {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAHElEQVQIW2P4//+/AxAzgDADlOOAznHAKgPWAwARji8UIDTfQQAAAABJRU5ErkJggg==");}.ace_dark .ace_fold-widget:hover {box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);background-color: rgba(255, 255, 255, 0.1);}.ace_dark .ace_fold-widget:active {box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);}.ace_inline_button {border: 1px solid lightgray;display: inline-block;margin: -1px 8px;padding: 0 5px;pointer-events: auto;cursor: pointer;}.ace_inline_button:hover {border-color: gray;background: rgba(200,200,200,0.2);display: inline-block;pointer-events: auto;}.ace_fold-widget.ace_invalid {background-color: #FFB4B4;border-color: #DE5555;}.ace_fade-fold-widgets .ace_fold-widget {transition: opacity 0.4s ease 0.05s;opacity: 0;}.ace_fade-fold-widgets:hover .ace_fold-widget {transition: opacity 0.05s ease 0.05s;opacity:1;}.ace_underline {text-decoration: underline;}.ace_bold {font-weight: bold;}.ace_nobold .ace_bold {font-weight: normal;}.ace_italic {font-style: italic;}.ace_error-marker {background-color: rgba(255, 0, 0,0.2);position: absolute;z-index: 9;}.ace_highlight-marker {background-color: rgba(255, 255, 0,0.2);position: absolute;z-index: 8;}',m=e("./lib/useragent"),g=m.isIE;i.importCssString(v,"ace_editor.css");var y=function(e,t){var n=this;this.container=e||i.createElement("div"),i.addCssClass(this.container,"ace_editor"),i.HI_DPI&&i.addCssClass(this.container,"ace_hidpi"),this.setTheme(t),this.$gutter=i.createElement("div"),this.$gutter.className="ace_gutter",this.container.appendChild(this.$gutter),this.$gutter.setAttribute("aria-hidden",!0),this.scroller=i.createElement("div"),this.scroller.className="ace_scroller",this.container.appendChild(this.scroller),this.content=i.createElement("div"),this.content.className="ace_content",this.scroller.appendChild(this.content),this.$gutterLayer=new o(this.$gutter),this.$gutterLayer.on("changeGutterWidth",this.onGutterResize.bind(this)),this.$markerBack=new u(this.content);var r=this.$textLayer=new a(this.content);this.canvas=r.element,this.$markerFront=new u(this.content),this.$cursorLayer=new f(this.content),this.$horizScroll=!1,this.$vScroll=!1,this.scrollBar=this.scrollBarV=new c(this.container,this),this.scrollBarH=new l(this.container,this),this.scrollBarV.addEventListener("scroll",function(e){n.$scrollAnimation||n.session.setScrollTop(e.data-n.scrollMargin.top)}),this.scrollBarH.addEventListener("scroll",function(e){n.$scrollAnimation||n.session.setScrollLeft(e.data-n.scrollMargin.left)}),this.scrollTop=0,this.scrollLeft=0,this.cursorPos={row:0,column:0},this.$fontMetrics=new p(this.container),this.$textLayer.$setFontMetrics(this.$fontMetrics),this.$textLayer.addEventListener("changeCharacterSize",function(e){n.updateCharacterSize(),n.onResize(!0,n.gutterWidth,n.$size.width,n.$size.height),n._signal("changeCharacterSize",e)}),this.$size={width:0,height:0,scrollerHeight:0,scrollerWidth:0,$dirty:!0},this.layerConfig={width:1,padding:0,firstRow:0,firstRowScreen:0,lastRow:0,lineHeight:0,characterWidth:0,minHeight:1,maxHeight:1,offset:0,height:1,gutterOffset:1},this.scrollMargin={left:0,right:0,top:0,bottom:0,v:0,h:0},this.margin={left:0,right:0,top:0,bottom:0,v:0,h:0},this.$keepTextAreaAtCursor=!m.isIOS,this.$loop=new h(this.$renderChanges.bind(this),this.container.ownerDocument.defaultView),this.$loop.schedule(this.CHANGE_FULL),this.updateCharacterSize(),this.setPadding(4),s.resetOptions(this),s._signal("renderer",this)};(function(){this.CHANGE_CURSOR=1,this.CHANGE_MARKER=2,this.CHANGE_GUTTER=4,this.CHANGE_SCROLL=8,this.CHANGE_LINES=16,this.CHANGE_TEXT=32,this.CHANGE_SIZE=64,this.CHANGE_MARKER_BACK=128,this.CHANGE_MARKER_FRONT=256,this.CHANGE_FULL=512,this.CHANGE_H_SCROLL=1024,r.implement(this,d),this.updateCharacterSize=function(){this.$textLayer.allowBoldFonts!=this.$allowBoldFonts&&(this.$allowBoldFonts=this.$textLayer.allowBoldFonts,this.setStyle("ace_nobold",!this.$allowBoldFonts)),this.layerConfig.characterWidth=this.characterWidth=this.$textLayer.getCharacterWidth(),this.layerConfig.lineHeight=this.lineHeight=this.$textLayer.getLineHeight(),this.$updatePrintMargin()},this.setSession=function(e){this.session&&this.session.doc.off("changeNewLineMode",this.onChangeNewLineMode),this.session=e,e&&this.scrollMargin.top&&e.getScrollTop()<=0&&e.setScrollTop(-this.scrollMargin.top),this.$cursorLayer.setSession(e),this.$markerBack.setSession(e),this.$markerFront.setSession(e),this.$gutterLayer.setSession(e),this.$textLayer.setSession(e);if(!e)return;this.$loop.schedule(this.CHANGE_FULL),this.session.$setFontMetrics(this.$fontMetrics),this.scrollBarH.scrollLeft=this.scrollBarV.scrollTop=null,this.onChangeNewLineMode=this.onChangeNewLineMode.bind(this),this.onChangeNewLineMode(),this.session.doc.on("changeNewLineMode",this.onChangeNewLineMode)},this.updateLines=function(e,t,n){t===undefined&&(t=Infinity),this.$changedLines?(this.$changedLines.firstRow>e&&(this.$changedLines.firstRow=e),this.$changedLines.lastRowthis.layerConfig.lastRow)return;this.$loop.schedule(this.CHANGE_LINES)},this.onChangeNewLineMode=function(){this.$loop.schedule(this.CHANGE_TEXT),this.$textLayer.$updateEolChar(),this.session.$bidiHandler.setEolChar(this.$textLayer.EOL_CHAR)},this.onChangeTabSize=function(){this.$loop.schedule(this.CHANGE_TEXT|this.CHANGE_MARKER),this.$textLayer.onChangeTabSize()},this.updateText=function(){this.$loop.schedule(this.CHANGE_TEXT)},this.updateFull=function(e){e?this.$renderChanges(this.CHANGE_FULL,!0):this.$loop.schedule(this.CHANGE_FULL)},this.updateFontSize=function(){this.$textLayer.checkForSizeChanges()},this.$changes=0,this.$updateSizeAsync=function(){this.$loop.pending?this.$size.$dirty=!0:this.onResize()},this.onResize=function(e,t,n,r){if(this.resizing>2)return;this.resizing>0?this.resizing++:this.resizing=e?1:0;var i=this.container;r||(r=i.clientHeight||i.scrollHeight),n||(n=i.clientWidth||i.scrollWidth);var s=this.$updateCachedSize(e,t,n,r);if(!this.$size.scrollerHeight||!n&&!r)return this.resizing=0;e&&(this.$gutterLayer.$padding=null),e?this.$renderChanges(s|this.$changes,!0):this.$loop.schedule(s|this.$changes),this.resizing&&(this.resizing=0),this.scrollBarV.scrollLeft=this.scrollBarV.scrollTop=null},this.$updateCachedSize=function(e,t,n,r){r-=this.$extraHeight||0;var s=0,o=this.$size,u={width:o.width,height:o.height,scrollerHeight:o.scrollerHeight,scrollerWidth:o.scrollerWidth};r&&(e||o.height!=r)&&(o.height=r,s|=this.CHANGE_SIZE,o.scrollerHeight=o.height,this.$horizScroll&&(o.scrollerHeight-=this.scrollBarH.getHeight()),this.scrollBarV.element.style.bottom=this.scrollBarH.getHeight()+"px",s|=this.CHANGE_SCROLL);if(n&&(e||o.width!=n)){s|=this.CHANGE_SIZE,o.width=n,t==null&&(t=this.$showGutter?this.$gutter.offsetWidth:0),this.gutterWidth=t,i.setStyle(this.scrollBarH.element.style,"left",t+"px"),i.setStyle(this.scroller.style,"left",t+this.margin.left+"px"),o.scrollerWidth=Math.max(0,n-t-this.scrollBarV.getWidth()-this.margin.h),i.setStyle(this.$gutter.style,"left",this.margin.left+"px");var a=this.scrollBarV.getWidth()+"px";i.setStyle(this.scrollBarH.element.style,"right",a),i.setStyle(this.scroller.style,"right",a),i.setStyle(this.scroller.style,"bottom",this.scrollBarH.getHeight());if(this.session&&this.session.getUseWrapMode()&&this.adjustWrapLimit()||e)s|=this.CHANGE_FULL}return o.$dirty=!n||!r,s&&this._signal("resize",u),s},this.onGutterResize=function(e){var t=this.$showGutter?e:0;t!=this.gutterWidth&&(this.$changes|=this.$updateCachedSize(!0,t,this.$size.width,this.$size.height)),this.session.getUseWrapMode()&&this.adjustWrapLimit()?this.$loop.schedule(this.CHANGE_FULL):this.$size.$dirty?this.$loop.schedule(this.CHANGE_FULL):this.$computeLayerConfig()},this.adjustWrapLimit=function(){var e=this.$size.scrollerWidth-this.$padding*2,t=Math.floor(e/this.characterWidth);return this.session.adjustWrapLimit(t,this.$showPrintMargin&&this.$printMarginColumn)},this.setAnimatedScroll=function(e){this.setOption("animatedScroll",e)},this.getAnimatedScroll=function(){return this.$animatedScroll},this.setShowInvisibles=function(e){this.setOption("showInvisibles",e),this.session.$bidiHandler.setShowInvisibles(e)},this.getShowInvisibles=function(){return this.getOption("showInvisibles")},this.getDisplayIndentGuides=function(){return this.getOption("displayIndentGuides")},this.setDisplayIndentGuides=function(e){this.setOption("displayIndentGuides",e)},this.setShowPrintMargin=function(e){this.setOption("showPrintMargin",e)},this.getShowPrintMargin=function(){return this.getOption("showPrintMargin")},this.setPrintMarginColumn=function(e){this.setOption("printMarginColumn",e)},this.getPrintMarginColumn=function(){return this.getOption("printMarginColumn")},this.getShowGutter=function(){return this.getOption("showGutter")},this.setShowGutter=function(e){return this.setOption("showGutter",e)},this.getFadeFoldWidgets=function(){return this.getOption("fadeFoldWidgets")},this.setFadeFoldWidgets=function(e){this.setOption("fadeFoldWidgets",e)},this.setHighlightGutterLine=function(e){this.setOption("highlightGutterLine",e)},this.getHighlightGutterLine=function(){return this.getOption("highlightGutterLine")},this.$updatePrintMargin=function(){if(!this.$showPrintMargin&&!this.$printMarginEl)return;if(!this.$printMarginEl){var e=i.createElement("div");e.className="ace_layer ace_print-margin-layer",this.$printMarginEl=i.createElement("div"),this.$printMarginEl.className="ace_print-margin",e.appendChild(this.$printMarginEl),this.content.insertBefore(e,this.content.firstChild)}var t=this.$printMarginEl.style;t.left=Math.round(this.characterWidth*this.$printMarginColumn+this.$padding)+"px",t.visibility=this.$showPrintMargin?"visible":"hidden",this.session&&this.session.$wrap==-1&&this.adjustWrapLimit()},this.getContainerElement=function(){return this.container},this.getMouseEventTarget=function(){return this.scroller},this.getTextAreaContainer=function(){return this.container},this.$moveTextAreaToCursor=function(){if(this.$isMousePressed)return;var e=this.textarea.style,t=this.$composition;if(!this.$keepTextAreaAtCursor&&!t){i.translate(this.textarea,-100,0);return}var n=this.$cursorLayer.$pixelPos;if(!n)return;t&&t.markerRange&&(n=this.$cursorLayer.getPixelPosition(t.markerRange.start,!0));var r=this.layerConfig,s=n.top,o=n.left;s-=r.offset;var u=t&&t.useTextareaForIME?this.lineHeight:g?0:1;if(s<0||s>r.height-u){i.translate(this.textarea,0,0);return}var a=1;if(!t)s+=this.lineHeight;else if(t.useTextareaForIME){var f=this.textarea.value;a=this.characterWidth*this.session.$getStringScreenWidth(f)[0],u+=2}else s+=this.lineHeight+2;o-=this.scrollLeft,o>this.$size.scrollerWidth-a&&(o=this.$size.scrollerWidth-a),o+=this.gutterWidth+this.margin.left,i.setStyle(e,"height",u+"px"),i.setStyle(e,"width",a+"px"),i.translate(this.textarea,Math.min(o,this.$size.scrollerWidth-a),Math.min(s,this.$size.height-u))},this.getFirstVisibleRow=function(){return this.layerConfig.firstRow},this.getFirstFullyVisibleRow=function(){return this.layerConfig.firstRow+(this.layerConfig.offset===0?0:1)},this.getLastFullyVisibleRow=function(){var e=this.layerConfig,t=e.lastRow,n=this.session.documentToScreenRow(t,0)*e.lineHeight;return n-this.session.getScrollTop()>e.height-e.lineHeight?t-1:t},this.getLastVisibleRow=function(){return this.layerConfig.lastRow},this.$padding=null,this.setPadding=function(e){this.$padding=e,this.$textLayer.setPadding(e),this.$cursorLayer.setPadding(e),this.$markerFront.setPadding(e),this.$markerBack.setPadding(e),this.$loop.schedule(this.CHANGE_FULL),this.$updatePrintMargin()},this.setScrollMargin=function(e,t,n,r){var i=this.scrollMargin;i.top=e|0,i.bottom=t|0,i.right=r|0,i.left=n|0,i.v=i.top+i.bottom,i.h=i.left+i.right,i.top&&this.scrollTop<=0&&this.session&&this.session.setScrollTop(-i.top),this.updateFull()},this.setMargin=function(e,t,n,r){var i=this.margin;i.top=e|0,i.bottom=t|0,i.right=r|0,i.left=n|0,i.v=i.top+i.bottom,i.h=i.left+i.right,this.$updateCachedSize(!0,this.gutterWidth,this.$size.width,this.$size.height),this.updateFull()},this.getHScrollBarAlwaysVisible=function(){return this.$hScrollBarAlwaysVisible},this.setHScrollBarAlwaysVisible=function(e){this.setOption("hScrollBarAlwaysVisible",e)},this.getVScrollBarAlwaysVisible=function(){return this.$vScrollBarAlwaysVisible},this.setVScrollBarAlwaysVisible=function(e){this.setOption("vScrollBarAlwaysVisible",e)},this.$updateScrollBarV=function(){var e=this.layerConfig.maxHeight,t=this.$size.scrollerHeight;!this.$maxLines&&this.$scrollPastEnd&&(e-=(t-this.lineHeight)*this.$scrollPastEnd,this.scrollTop>e-t&&(e=this.scrollTop+t,this.scrollBarV.scrollTop=null)),this.scrollBarV.setScrollHeight(e+this.scrollMargin.v),this.scrollBarV.setScrollTop(this.scrollTop+this.scrollMargin.top)},this.$updateScrollBarH=function(){this.scrollBarH.setScrollWidth(this.layerConfig.width+2*this.$padding+this.scrollMargin.h),this.scrollBarH.setScrollLeft(this.scrollLeft+this.scrollMargin.left)},this.$frozen=!1,this.freeze=function(){this.$frozen=!0},this.unfreeze=function(){this.$frozen=!1},this.$renderChanges=function(e,t){this.$changes&&(e|=this.$changes,this.$changes=0);if(!this.session||!this.container.offsetWidth||this.$frozen||!e&&!t){this.$changes|=e;return}if(this.$size.$dirty)return this.$changes|=e,this.onResize(!0);this.lineHeight||this.$textLayer.checkForSizeChanges(),this._signal("beforeRender"),this.session&&this.session.$bidiHandler&&this.session.$bidiHandler.updateCharacterWidths(this.$fontMetrics);var n=this.layerConfig;if(e&this.CHANGE_FULL||e&this.CHANGE_SIZE||e&this.CHANGE_TEXT||e&this.CHANGE_LINES||e&this.CHANGE_SCROLL||e&this.CHANGE_H_SCROLL){e|=this.$computeLayerConfig()|this.$loop.clear();if(n.firstRow!=this.layerConfig.firstRow&&n.firstRowScreen==this.layerConfig.firstRowScreen){var r=this.scrollTop+(n.firstRow-this.layerConfig.firstRow)*this.lineHeight;r>0&&(this.scrollTop=r,e|=this.CHANGE_SCROLL,e|=this.$computeLayerConfig()|this.$loop.clear())}n=this.layerConfig,this.$updateScrollBarV(),e&this.CHANGE_H_SCROLL&&this.$updateScrollBarH(),i.translate(this.content,-this.scrollLeft,-n.offset);var s=n.width+2*this.$padding+"px",o=n.minHeight+"px";i.setStyle(this.content.style,"width",s),i.setStyle(this.content.style,"height",o)}e&this.CHANGE_H_SCROLL&&(i.translate(this.content,-this.scrollLeft,-n.offset),this.scroller.className=this.scrollLeft<=0?"ace_scroller":"ace_scroller ace_scroll-left");if(e&this.CHANGE_FULL){this.$textLayer.update(n),this.$showGutter&&this.$gutterLayer.update(n),this.$markerBack.update(n),this.$markerFront.update(n),this.$cursorLayer.update(n),this.$moveTextAreaToCursor(),this._signal("afterRender");return}if(e&this.CHANGE_SCROLL){e&this.CHANGE_TEXT||e&this.CHANGE_LINES?this.$textLayer.update(n):this.$textLayer.scrollLines(n),this.$showGutter&&(e&this.CHANGE_GUTTER||e&this.CHANGE_LINES?this.$gutterLayer.update(n):this.$gutterLayer.scrollLines(n)),this.$markerBack.update(n),this.$markerFront.update(n),this.$cursorLayer.update(n),this.$moveTextAreaToCursor(),this._signal("afterRender");return}e&this.CHANGE_TEXT?(this.$textLayer.update(n),this.$showGutter&&this.$gutterLayer.update(n)):e&this.CHANGE_LINES?(this.$updateLines()||e&this.CHANGE_GUTTER&&this.$showGutter)&&this.$gutterLayer.update(n):e&this.CHANGE_TEXT||e&this.CHANGE_GUTTER?this.$showGutter&&this.$gutterLayer.update(n):e&this.CHANGE_CURSOR&&this.$highlightGutterLine&&this.$gutterLayer.updateLineHighlight(n),e&this.CHANGE_CURSOR&&(this.$cursorLayer.update(n),this.$moveTextAreaToCursor()),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_FRONT)&&this.$markerFront.update(n),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_BACK)&&this.$markerBack.update(n),this._signal("afterRender")},this.$autosize=function(){var e=this.session.getScreenLength()*this.lineHeight,t=this.$maxLines*this.lineHeight,n=Math.min(t,Math.max((this.$minLines||1)*this.lineHeight,e))+this.scrollMargin.v+(this.$extraHeight||0);this.$horizScroll&&(n+=this.scrollBarH.getHeight()),this.$maxPixelHeight&&n>this.$maxPixelHeight&&(n=this.$maxPixelHeight);var r=n<=2*this.lineHeight,i=!r&&e>t;if(n!=this.desiredHeight||this.$size.height!=this.desiredHeight||i!=this.$vScroll){i!=this.$vScroll&&(this.$vScroll=i,this.scrollBarV.setVisible(i));var s=this.container.clientWidth;this.container.style.height=n+"px",this.$updateCachedSize(!0,this.$gutterWidth,s,n),this.desiredHeight=n,this._signal("autosize")}},this.$computeLayerConfig=function(){var e=this.session,t=this.$size,n=t.height<=2*this.lineHeight,r=this.session.getScreenLength(),i=r*this.lineHeight,s=this.$getLongestLine(),o=!n&&(this.$hScrollBarAlwaysVisible||t.scrollerWidth-s-2*this.$padding<0),u=this.$horizScroll!==o;u&&(this.$horizScroll=o,this.scrollBarH.setVisible(o));var a=this.$vScroll;this.$maxLines&&this.lineHeight>1&&this.$autosize();var f=t.scrollerHeight+this.lineHeight,l=!this.$maxLines&&this.$scrollPastEnd?(t.scrollerHeight-this.lineHeight)*this.$scrollPastEnd:0;i+=l;var c=this.scrollMargin;this.session.setScrollTop(Math.max(-c.top,Math.min(this.scrollTop,i-t.scrollerHeight+c.bottom))),this.session.setScrollLeft(Math.max(-c.left,Math.min(this.scrollLeft,s+2*this.$padding-t.scrollerWidth+c.right)));var h=!n&&(this.$vScrollBarAlwaysVisible||t.scrollerHeight-i+l<0||this.scrollTop>c.top),p=a!==h;p&&(this.$vScroll=h,this.scrollBarV.setVisible(h));var d=this.scrollTop%this.lineHeight,v=Math.ceil(f/this.lineHeight)-1,m=Math.max(0,Math.round((this.scrollTop-d)/this.lineHeight)),g=m+v,y,b,w=this.lineHeight;m=e.screenToDocumentRow(m,0);var E=e.getFoldLine(m);E&&(m=E.start.row),y=e.documentToScreenRow(m,0),b=e.getRowLength(m)*w,g=Math.min(e.screenToDocumentRow(g,0),e.getLength()-1),f=t.scrollerHeight+e.getRowLength(g)*w+b,d=this.scrollTop-y*w;var S=0;if(this.layerConfig.width!=s||u)S=this.CHANGE_H_SCROLL;if(u||p)S|=this.$updateCachedSize(!0,this.gutterWidth,t.width,t.height),this._signal("scrollbarVisibilityChanged"),p&&(s=this.$getLongestLine());return this.layerConfig={width:s,padding:this.$padding,firstRow:m,firstRowScreen:y,lastRow:g,lineHeight:w,characterWidth:this.characterWidth,minHeight:f,maxHeight:i,offset:d,gutterOffset:w?Math.max(0,Math.ceil((d+t.height-t.scrollerHeight)/w)):0,height:this.$size.scrollerHeight},this.session.$bidiHandler&&this.session.$bidiHandler.setContentWidth(s-this.$padding),S},this.$updateLines=function(){if(!this.$changedLines)return;var e=this.$changedLines.firstRow,t=this.$changedLines.lastRow;this.$changedLines=null;var n=this.layerConfig;if(e>n.lastRow+1)return;if(tthis.$textLayer.MAX_LINE_LENGTH&&(e=this.$textLayer.MAX_LINE_LENGTH+30),Math.max(this.$size.scrollerWidth-2*this.$padding,Math.round(e*this.characterWidth))},this.updateFrontMarkers=function(){this.$markerFront.setMarkers(this.session.getMarkers(!0)),this.$loop.schedule(this.CHANGE_MARKER_FRONT)},this.updateBackMarkers=function(){this.$markerBack.setMarkers(this.session.getMarkers()),this.$loop.schedule(this.CHANGE_MARKER_BACK)},this.addGutterDecoration=function(e,t){this.$gutterLayer.addGutterDecoration(e,t)},this.removeGutterDecoration=function(e,t){this.$gutterLayer.removeGutterDecoration(e,t)},this.updateBreakpoints=function(e){this.$loop.schedule(this.CHANGE_GUTTER)},this.setAnnotations=function(e){this.$gutterLayer.setAnnotations(e),this.$loop.schedule(this.CHANGE_GUTTER)},this.updateCursor=function(){this.$loop.schedule(this.CHANGE_CURSOR)},this.hideCursor=function(){this.$cursorLayer.hideCursor()},this.showCursor=function(){this.$cursorLayer.showCursor()},this.scrollSelectionIntoView=function(e,t,n){this.scrollCursorIntoView(e,n),this.scrollCursorIntoView(t,n)},this.scrollCursorIntoView=function(e,t,n){if(this.$size.scrollerHeight===0)return;var r=this.$cursorLayer.getPixelPosition(e),i=r.left,s=r.top,o=n&&n.top||0,u=n&&n.bottom||0,a=this.$scrollAnimation?this.session.getScrollTop():this.scrollTop;a+o>s?(t&&a+o>s+this.lineHeight&&(s-=t*this.$size.scrollerHeight),s===0&&(s=-this.scrollMargin.top),this.session.setScrollTop(s)):a+this.$size.scrollerHeight-ui?(i=1-this.scrollMargin.top)return!0;if(t>0&&this.session.getScrollTop()+this.$size.scrollerHeight-this.layerConfig.maxHeight<-1+this.scrollMargin.bottom)return!0;if(e<0&&this.session.getScrollLeft()>=1-this.scrollMargin.left)return!0;if(e>0&&this.session.getScrollLeft()+this.$size.scrollerWidth-this.layerConfig.width<-1+this.scrollMargin.right)return!0},this.pixelToScreenCoordinates=function(e,t){var n;if(this.$hasCssTransforms){n={top:0,left:0};var r=this.$fontMetrics.transformCoordinates([e,t]);e=r[1]-this.gutterWidth-this.margin.left,t=r[0]}else n=this.scroller.getBoundingClientRect();var i=e+this.scrollLeft-n.left-this.$padding,s=i/this.characterWidth,o=Math.floor((t+this.scrollTop-n.top)/this.lineHeight),u=this.$blockCursor?Math.floor(s):Math.round(s);return{row:o,column:u,side:s-u>0?1:-1,offsetX:i}},this.screenToTextCoordinates=function(e,t){var n;if(this.$hasCssTransforms){n={top:0,left:0};var r=this.$fontMetrics.transformCoordinates([e,t]);e=r[1]-this.gutterWidth-this.margin.left,t=r[0]}else n=this.scroller.getBoundingClientRect();var i=e+this.scrollLeft-n.left-this.$padding,s=i/this.characterWidth,o=this.$blockCursor?Math.floor(s):Math.round(s),u=Math.floor((t+this.scrollTop-n.top)/this.lineHeight);return this.session.screenToDocumentPosition(u,Math.max(o,0),i)},this.textToScreenCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=this.session.documentToScreenPosition(e,t),i=this.$padding+(this.session.$bidiHandler.isBidiRow(r.row,e)?this.session.$bidiHandler.getPosLeft(r.column):Math.round(r.column*this.characterWidth)),s=r.row*this.lineHeight;return{pageX:n.left+i-this.scrollLeft,pageY:n.top+s-this.scrollTop}},this.visualizeFocus=function(){i.addCssClass(this.container,"ace_focus")},this.visualizeBlur=function(){i.removeCssClass(this.container,"ace_focus")},this.showComposition=function(e){this.$composition=e,e.cssText||(e.cssText=this.textarea.style.cssText),e.useTextareaForIME=this.$useTextareaForIME,this.$useTextareaForIME?(i.addCssClass(this.textarea,"ace_composition"),this.textarea.style.cssText="",this.$moveTextAreaToCursor(),this.$cursorLayer.element.style.display="none"):e.markerId=this.session.addMarker(e.markerRange,"ace_composition_marker","text")},this.setCompositionText=function(e){var t=this.session.selection.cursor;this.addToken(e,"composition_placeholder",t.row,t.column),this.$moveTextAreaToCursor()},this.hideComposition=function(){if(!this.$composition)return;this.$composition.markerId&&this.session.removeMarker(this.$composition.markerId),i.removeCssClass(this.textarea,"ace_composition"),this.textarea.style.cssText=this.$composition.cssText,this.$composition=null,this.$cursorLayer.element.style.display=""},this.addToken=function(e,t,n,r){var i=this.session;i.bgTokenizer.lines[n]=null;var s={type:t,value:e},o=i.getTokens(n);if(r==null)o.push(s);else{var u=0;for(var a=0;a50&&e.length>this.$doc.getLength()>>1?this.call("setValue",[this.$doc.getValue()]):this.emit("change",{data:e})}}).call(f.prototype);var l=function(e,t,n){var r=null,i=!1,u=Object.create(s),a=[],l=new f({messageBuffer:a,terminate:function(){},postMessage:function(e){a.push(e);if(!r)return;i?setTimeout(c):c()}});l.setEmitSync=function(e){i=e};var c=function(){var e=a.shift();e.command?r[e.command].apply(r,e.args):e.event&&u._signal(e.event,e.data)};return u.postMessage=function(e){l.onMessage({data:e})},u.callback=function(e,t){this.postMessage({type:"call",id:t,data:e})},u.emit=function(e,t){this.postMessage({type:"event",name:e,data:t})},o.loadModule(["worker",t],function(e){r=new e[n](u);while(a.length)c()}),l};t.UIWorkerClient=l,t.WorkerClient=f,t.createWorker=a}),ace.define("ace/placeholder",["require","exports","module","ace/range","ace/lib/event_emitter","ace/lib/oop"],function(e,t,n){"use strict";var r=e("./range").Range,i=e("./lib/event_emitter").EventEmitter,s=e("./lib/oop"),o=function(e,t,n,r,i,s){var o=this;this.length=t,this.session=e,this.doc=e.getDocument(),this.mainClass=i,this.othersClass=s,this.$onUpdate=this.onUpdate.bind(this),this.doc.on("change",this.$onUpdate),this.$others=r,this.$onCursorChange=function(){setTimeout(function(){o.onCursorChange()})},this.$pos=n;var u=e.getUndoManager().$undoStack||e.getUndoManager().$undostack||{length:-1};this.$undoStackDepth=u.length,this.setup(),e.selection.on("changeCursor",this.$onCursorChange)};(function(){s.implement(this,i),this.setup=function(){var e=this,t=this.doc,n=this.session;this.selectionBefore=n.selection.toJSON(),n.selection.inMultiSelectMode&&n.selection.toSingleRange(),this.pos=t.createAnchor(this.$pos.row,this.$pos.column);var i=this.pos;i.$insertRight=!0,i.detach(),i.markerId=n.addMarker(new r(i.row,i.column,i.row,i.column+this.length),this.mainClass,null,!1),this.others=[],this.$others.forEach(function(n){var r=t.createAnchor(n.row,n.column);r.$insertRight=!0,r.detach(),e.others.push(r)}),n.setUndoSelect(!1)},this.showOtherMarkers=function(){if(this.othersActive)return;var e=this.session,t=this;this.othersActive=!0,this.others.forEach(function(n){n.markerId=e.addMarker(new r(n.row,n.column,n.row,n.column+t.length),t.othersClass,null,!1)})},this.hideOtherMarkers=function(){if(!this.othersActive)return;this.othersActive=!1;for(var e=0;e=this.pos.column&&t.start.column<=this.pos.column+this.length+1,s=t.start.column-this.pos.column;this.updateAnchors(e),i&&(this.length+=n);if(i&&!this.session.$fromUndo)if(e.action==="insert")for(var o=this.others.length-1;o>=0;o--){var u=this.others[o],a={row:u.row,column:u.column+s};this.doc.insertMergedLines(a,e.lines)}else if(e.action==="remove")for(var o=this.others.length-1;o>=0;o--){var u=this.others[o],a={row:u.row,column:u.column+s};this.doc.remove(new r(a.row,a.column,a.row,a.column-n))}this.$updating=!1,this.updateMarkers()},this.updateAnchors=function(e){this.pos.onChange(e);for(var t=this.others.length;t--;)this.others[t].onChange(e);this.updateMarkers()},this.updateMarkers=function(){if(this.$updating)return;var e=this,t=this.session,n=function(n,i){t.removeMarker(n.markerId),n.markerId=t.addMarker(new r(n.row,n.column,n.row,n.column+e.length),i,null,!1)};n(this.pos,this.mainClass);for(var i=this.others.length;i--;)n(this.others[i],this.othersClass)},this.onCursorChange=function(e){if(this.$updating||!this.session)return;var t=this.session.selection.getCursor();t.row===this.pos.row&&t.column>=this.pos.column&&t.column<=this.pos.column+this.length?(this.showOtherMarkers(),this._emit("cursorEnter",e)):(this.hideOtherMarkers(),this._emit("cursorLeave",e))},this.detach=function(){this.session.removeMarker(this.pos&&this.pos.markerId),this.hideOtherMarkers(),this.doc.removeEventListener("change",this.$onUpdate),this.session.selection.removeEventListener("changeCursor",this.$onCursorChange),this.session.setUndoSelect(!0),this.session=null},this.cancel=function(){if(this.$undoStackDepth===-1)return;var e=this.session.getUndoManager(),t=(e.$undoStack||e.$undostack).length-this.$undoStackDepth;for(var n=0;n1&&!this.inMultiSelectMode&&(this._signal("multiSelect"),this.inMultiSelectMode=!0,this.session.$undoSelect=!1,this.rangeList.attach(this.session)),t||this.fromOrientedRange(e)},this.toSingleRange=function(e){e=e||this.ranges[0];var t=this.rangeList.removeAll();t.length&&this.$onRemoveRange(t),e&&this.fromOrientedRange(e)},this.substractPoint=function(e){var t=this.rangeList.substractPoint(e);if(t)return this.$onRemoveRange(t),t[0]},this.mergeOverlappingRanges=function(){var e=this.rangeList.merge();e.length&&this.$onRemoveRange(e)},this.$onAddRange=function(e){this.rangeCount=this.rangeList.ranges.length,this.ranges.unshift(e),this._signal("addRange",{range:e})},this.$onRemoveRange=function(e){this.rangeCount=this.rangeList.ranges.length;if(this.rangeCount==1&&this.inMultiSelectMode){var t=this.rangeList.ranges.pop();e.push(t),this.rangeCount=0}for(var n=e.length;n--;){var r=this.ranges.indexOf(e[n]);this.ranges.splice(r,1)}this._signal("removeRange",{ranges:e}),this.rangeCount===0&&this.inMultiSelectMode&&(this.inMultiSelectMode=!1,this._signal("singleSelect"),this.session.$undoSelect=!0,this.rangeList.detach(this.session)),t=t||this.ranges[0],t&&!t.isEqual(this.getRange())&&this.fromOrientedRange(t)},this.$initRangeList=function(){if(this.rangeList)return;this.rangeList=new r,this.ranges=[],this.rangeCount=0},this.getAllRanges=function(){return this.rangeCount?this.rangeList.ranges.concat():[this.getRange()]},this.splitIntoLines=function(){if(this.rangeCount>1){var e=this.rangeList.ranges,t=e[e.length-1],n=i.fromPoints(e[0].start,t.end);this.toSingleRange(),this.setSelectionRange(n,t.cursor==t.start)}else{var n=this.getRange(),r=this.isBackwards(),s=n.start.row,o=n.end.row;if(s==o){if(r)var u=n.end,a=n.start;else var u=n.start,a=n.end;this.addRange(i.fromPoints(a,a)),this.addRange(i.fromPoints(u,u));return}var f=[],l=this.getLineRange(s,!0);l.start.column=n.start.column,f.push(l);for(var c=s+1;c1){var e=this.rangeList.ranges,t=e[e.length-1],n=i.fromPoints(e[0].start,t.end);this.toSingleRange(),this.setSelectionRange(n,t.cursor==t.start)}else{var r=this.session.documentToScreenPosition(this.cursor),s=this.session.documentToScreenPosition(this.anchor),o=this.rectangularRangeBlock(r,s);o.forEach(this.addRange,this)}},this.rectangularRangeBlock=function(e,t,n){var r=[],s=e.column0)g--;if(g>0){var y=0;while(r[y].isEmpty())y++}for(var b=g;b>=y;b--)r[b].isEmpty()&&r.splice(b,1)}return r}}.call(s.prototype);var d=e("./editor").Editor;(function(){this.updateSelectionMarkers=function(){this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.addSelectionMarker=function(e){e.cursor||(e.cursor=e.end);var t=this.getSelectionStyle();return e.marker=this.session.addMarker(e,"ace_selection",t),this.session.$selectionMarkers.push(e),this.session.selectionMarkerCount=this.session.$selectionMarkers.length,e},this.removeSelectionMarker=function(e){if(!e.marker)return;this.session.removeMarker(e.marker);var t=this.session.$selectionMarkers.indexOf(e);t!=-1&&this.session.$selectionMarkers.splice(t,1),this.session.selectionMarkerCount=this.session.$selectionMarkers.length},this.removeSelectionMarkers=function(e){var t=this.session.$selectionMarkers;for(var n=e.length;n--;){var r=e[n];if(!r.marker)continue;this.session.removeMarker(r.marker);var i=t.indexOf(r);i!=-1&&t.splice(i,1)}this.session.selectionMarkerCount=t.length},this.$onAddRange=function(e){this.addSelectionMarker(e.range),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onRemoveRange=function(e){this.removeSelectionMarkers(e.ranges),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onMultiSelect=function(e){if(this.inMultiSelectMode)return;this.inMultiSelectMode=!0,this.setStyle("ace_multiselect"),this.keyBinding.addKeyboardHandler(f.keyboardHandler),this.commands.setDefaultHandler("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onSingleSelect=function(e){if(this.session.multiSelect.inVirtualMode)return;this.inMultiSelectMode=!1,this.unsetStyle("ace_multiselect"),this.keyBinding.removeKeyboardHandler(f.keyboardHandler),this.commands.removeDefaultHandler("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers(),this._emit("changeSelection")},this.$onMultiSelectExec=function(e){var t=e.command,n=e.editor;if(!n.multiSelect)return;if(!t.multiSelectAction){var r=t.exec(n,e.args||{});n.multiSelect.addRange(n.multiSelect.toOrientedRange()),n.multiSelect.mergeOverlappingRanges()}else t.multiSelectAction=="forEach"?r=n.forEachSelection(t,e.args):t.multiSelectAction=="forEachLine"?r=n.forEachSelection(t,e.args,!0):t.multiSelectAction=="single"?(n.exitMultiSelectMode(),r=t.exec(n,e.args||{})):r=t.multiSelectAction(n,e.args||{});return r},this.forEachSelection=function(e,t,n){if(this.inVirtualSelectionMode)return;var r=n&&n.keepOrder,i=n==1||n&&n.$byLines,o=this.session,u=this.selection,a=u.rangeList,f=(r?u:a).ranges,l;if(!f.length)return e.exec?e.exec(this,t||{}):e(this,t||{});var c=u._eventRegistry;u._eventRegistry={};var h=new s(o);this.inVirtualSelectionMode=!0;for(var p=f.length;p--;){if(i)while(p>0&&f[p].start.row==f[p-1].end.row)p--;h.fromOrientedRange(f[p]),h.index=p,this.selection=o.selection=h;var d=e.exec?e.exec(this,t||{}):e(this,t||{});!l&&d!==undefined&&(l=d),h.toOrientedRange(f[p])}h.detach(),this.selection=o.selection=u,this.inVirtualSelectionMode=!1,u._eventRegistry=c,u.mergeOverlappingRanges(),u.ranges[0]&&u.fromOrientedRange(u.ranges[0]);var v=this.renderer.$scrollAnimation;return this.onCursorChange(),this.onSelectionChange(),v&&v.from==v.to&&this.renderer.animateScrolling(v.from),l},this.exitMultiSelectMode=function(){if(!this.inMultiSelectMode||this.inVirtualSelectionMode)return;this.multiSelect.toSingleRange()},this.getSelectedText=function(){var e="";if(this.inMultiSelectMode&&!this.inVirtualSelectionMode){var t=this.multiSelect.rangeList.ranges,n=[];for(var r=0;r0);u<0&&(u=0),f>=c&&(f=c-1)}var p=this.session.removeFullLines(u,f);p=this.$reAlignText(p,l),this.session.insert({row:u,column:0},p.join("\n")+"\n"),l||(o.start.column=0,o.end.column=p[p.length-1].length),this.selection.setRange(o)}else{s.forEach(function(e){t.substractPoint(e.cursor)});var d=0,v=Infinity,m=n.map(function(t){var n=t.cursor,r=e.getLine(n.row),i=r.substr(n.column).search(/\S/g);return i==-1&&(i=0),n.column>d&&(d=n.column),io?e.insert(r,a.stringRepeat(" ",s-o)):e.remove(new i(r.row,r.column,r.row,r.column-s+o)),t.start.column=t.end.column=d,t.start.row=t.end.row=r.row,t.cursor=t.end}),t.fromOrientedRange(n[0]),this.renderer.updateCursor(),this.renderer.updateBackMarkers()}},this.$reAlignText=function(e,t){function u(e){return a.stringRepeat(" ",e)}function f(e){return e[2]?u(i)+e[2]+u(s-e[2].length+o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}function l(e){return e[2]?u(i+s-e[2].length)+e[2]+u(o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}function c(e){return e[2]?u(i)+e[2]+u(o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}var n=!0,r=!0,i,s,o;return e.map(function(e){var t=e.match(/(\s*)(.*?)(\s*)([=:].*)/);return t?i==null?(i=t[1].length,s=t[2].length,o=t[3].length,t):(i+s+o!=t[1].length+t[2].length+t[3].length&&(r=!1),i!=t[1].length&&(n=!1),i>t[1].length&&(i=t[1].length),st[3].length&&(o=t[3].length),t):[e]}).map(t?f:n?r?l:f:c)}}).call(d.prototype),t.onSessionChange=function(e){var t=e.session;t&&!t.multiSelect&&(t.$selectionMarkers=[],t.selection.$initRangeList(),t.multiSelect=t.selection),this.multiSelect=t&&t.multiSelect;var n=e.oldSession;n&&(n.multiSelect.off("addRange",this.$onAddRange),n.multiSelect.off("removeRange",this.$onRemoveRange),n.multiSelect.off("multiSelect",this.$onMultiSelect),n.multiSelect.off("singleSelect",this.$onSingleSelect),n.multiSelect.lead.off("change",this.$checkMultiselectChange),n.multiSelect.anchor.off("change",this.$checkMultiselectChange)),t&&(t.multiSelect.on("addRange",this.$onAddRange),t.multiSelect.on("removeRange",this.$onRemoveRange),t.multiSelect.on("multiSelect",this.$onMultiSelect),t.multiSelect.on("singleSelect",this.$onSingleSelect),t.multiSelect.lead.on("change",this.$checkMultiselectChange),t.multiSelect.anchor.on("change",this.$checkMultiselectChange)),t&&this.inMultiSelectMode!=t.selection.inMultiSelectMode&&(t.selection.inMultiSelectMode?this.$onMultiSelect():this.$onSingleSelect())},t.MultiSelect=m,e("./config").defineOptions(d.prototype,"editor",{enableMultiselect:{set:function(e){m(this),e?(this.on("changeSession",this.$multiselectOnSessionChange),this.on("mousedown",o)):(this.off("changeSession",this.$multiselectOnSessionChange),this.off("mousedown",o))},value:!0},enableBlockSelect:{set:function(e){this.$blockSelectEnabled=e},value:!0}})}),ace.define("ace/mode/folding/fold_mode",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../../range").Range,i=t.FoldMode=function(){};(function(){this.foldingStartMarker=null,this.foldingStopMarker=null,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);return this.foldingStartMarker.test(r)?"start":t=="markbeginend"&&this.foldingStopMarker&&this.foldingStopMarker.test(r)?"end":""},this.getFoldWidgetRange=function(e,t,n){return null},this.indentationBlock=function(e,t,n){var i=/\S/,s=e.getLine(t),o=s.search(i);if(o==-1)return;var u=n||s.length,a=e.getLength(),f=t,l=t;while(++tf){var h=e.getLine(l).length;return new r(f,u,l,h)}},this.openingBracketBlock=function(e,t,n,i,s){var o={row:n,column:i+1},u=e.$findClosingBracket(t,o,s);if(!u)return;var a=e.foldWidgets[u.row];return a==null&&(a=e.getFoldWidget(u.row)),a=="start"&&u.row>o.row&&(u.row--,u.column=e.getLine(u.row).length),r.fromPoints(o,u)},this.closingBracketBlock=function(e,t,n,i,s){var o={row:n,column:i},u=e.$findOpeningBracket(t,o);if(!u)return;return u.column++,o.column--,r.fromPoints(u,o)}}).call(i.prototype)}),ace.define("ace/theme/textmate",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";t.isDark=!1,t.cssClass="ace-tm",t.cssText='.ace-tm .ace_gutter {background: #f0f0f0;color: #333;}.ace-tm .ace_print-margin {width: 1px;background: #e8e8e8;}.ace-tm .ace_fold {background-color: #6B72E6;}.ace-tm {background-color: #FFFFFF;color: black;}.ace-tm .ace_cursor {color: black;}.ace-tm .ace_invisible {color: rgb(191, 191, 191);}.ace-tm .ace_storage,.ace-tm .ace_keyword {color: blue;}.ace-tm .ace_constant {color: rgb(197, 6, 11);}.ace-tm .ace_constant.ace_buildin {color: rgb(88, 72, 246);}.ace-tm .ace_constant.ace_language {color: rgb(88, 92, 246);}.ace-tm .ace_constant.ace_library {color: rgb(6, 150, 14);}.ace-tm .ace_invalid {background-color: rgba(255, 0, 0, 0.1);color: red;}.ace-tm .ace_support.ace_function {color: rgb(60, 76, 114);}.ace-tm .ace_support.ace_constant {color: rgb(6, 150, 14);}.ace-tm .ace_support.ace_type,.ace-tm .ace_support.ace_class {color: rgb(109, 121, 222);}.ace-tm .ace_keyword.ace_operator {color: rgb(104, 118, 135);}.ace-tm .ace_string {color: rgb(3, 106, 7);}.ace-tm .ace_comment {color: rgb(76, 136, 107);}.ace-tm .ace_comment.ace_doc {color: rgb(0, 102, 255);}.ace-tm .ace_comment.ace_doc.ace_tag {color: rgb(128, 159, 191);}.ace-tm .ace_constant.ace_numeric {color: rgb(0, 0, 205);}.ace-tm .ace_variable {color: rgb(49, 132, 149);}.ace-tm .ace_xml-pe {color: rgb(104, 104, 91);}.ace-tm .ace_entity.ace_name.ace_function {color: #0000A2;}.ace-tm .ace_heading {color: rgb(12, 7, 255);}.ace-tm .ace_list {color:rgb(185, 6, 144);}.ace-tm .ace_meta.ace_tag {color:rgb(0, 22, 142);}.ace-tm .ace_string.ace_regex {color: rgb(255, 0, 0)}.ace-tm .ace_marker-layer .ace_selection {background: rgb(181, 213, 255);}.ace-tm.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px white;}.ace-tm .ace_marker-layer .ace_step {background: rgb(252, 255, 0);}.ace-tm .ace_marker-layer .ace_stack {background: rgb(164, 229, 101);}.ace-tm .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgb(192, 192, 192);}.ace-tm .ace_marker-layer .ace_active-line {background: rgba(0, 0, 0, 0.07);}.ace-tm .ace_gutter-active-line {background-color : #dcdcdc;}.ace-tm .ace_marker-layer .ace_selected-word {background: rgb(250, 250, 255);border: 1px solid rgb(200, 200, 250);}.ace-tm .ace_indent-guide {background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y;}',t.$id="ace/theme/textmate";var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)}),ace.define("ace/line_widgets",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/range"],function(e,t,n){"use strict";function o(e){this.session=e,this.session.widgetManager=this,this.session.getRowLength=this.getRowLength,this.session.$getWidgetScreenLength=this.$getWidgetScreenLength,this.updateOnChange=this.updateOnChange.bind(this),this.renderWidgets=this.renderWidgets.bind(this),this.measureWidgets=this.measureWidgets.bind(this),this.session._changedWidgets=[],this.$onChangeEditor=this.$onChangeEditor.bind(this),this.session.on("change",this.updateOnChange),this.session.on("changeFold",this.updateOnFold),this.session.on("changeEditor",this.$onChangeEditor)}var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./range").Range;(function(){this.getRowLength=function(e){var t;return this.lineWidgets?t=this.lineWidgets[e]&&this.lineWidgets[e].rowCount||0:t=0,!this.$useWrapMode||!this.$wrapData[e]?1+t:this.$wrapData[e].length+1+t},this.$getWidgetScreenLength=function(){var e=0;return this.lineWidgets.forEach(function(t){t&&t.rowCount&&!t.hidden&&(e+=t.rowCount)}),e},this.$onChangeEditor=function(e){this.attach(e.editor)},this.attach=function(e){e&&e.widgetManager&&e.widgetManager!=this&&e.widgetManager.detach();if(this.editor==e)return;this.detach(),this.editor=e,e&&(e.widgetManager=this,e.renderer.on("beforeRender",this.measureWidgets),e.renderer.on("afterRender",this.renderWidgets))},this.detach=function(e){var t=this.editor;if(!t)return;this.editor=null,t.widgetManager=null,t.renderer.off("beforeRender",this.measureWidgets),t.renderer.off("afterRender",this.renderWidgets);var n=this.session.lineWidgets;n&&n.forEach(function(e){e&&e.el&&e.el.parentNode&&(e._inDocument=!1,e.el.parentNode.removeChild(e.el))})},this.updateOnFold=function(e,t){var n=t.lineWidgets;if(!n||!e.action)return;var r=e.data,i=r.start.row,s=r.end.row,o=e.action=="add";for(var u=i+1;u0&&!r[i])i--;this.firstRow=n.firstRow,this.lastRow=n.lastRow,t.$cursorLayer.config=n;for(var o=i;o<=s;o++){var u=r[o];if(!u||!u.el)continue;if(u.hidden){u.el.style.top=-100-(u.pixelHeight||0)+"px";continue}u._inDocument||(u._inDocument=!0,t.container.appendChild(u.el));var a=t.$cursorLayer.getPixelPosition({row:o,column:0},!0).top;u.coverLine||(a+=n.lineHeight*this.session.getRowLineCount(u.row)),u.el.style.top=a-n.offset+"px";var f=u.coverGutter?0:t.gutterWidth;u.fixedWidth||(f-=t.scrollLeft),u.el.style.left=f+"px",u.fullWidth&&u.screenWidth&&(u.el.style.minWidth=n.width+2*n.padding+"px"),u.fixedWidth?u.el.style.right=t.scrollBar.getWidth()+"px":u.el.style.right=""}}}).call(o.prototype),t.LineWidgets=o}),ace.define("ace/ext/error_marker",["require","exports","module","ace/line_widgets","ace/lib/dom","ace/range"],function(e,t,n){"use strict";function o(e,t,n){var r=0,i=e.length-1;while(r<=i){var s=r+i>>1,o=n(t,e[s]);if(o>0)r=s+1;else{if(!(o<0))return s;i=s-1}}return-(r+1)}function u(e,t,n){var r=e.getAnnotations().sort(s.comparePoints);if(!r.length)return;var i=o(r,{row:t,column:-1},s.comparePoints);i<0&&(i=-i-1),i>=r.length?i=n>0?0:r.length-1:i===0&&n<0&&(i=r.length-1);var u=r[i];if(!u||!n)return;if(u.row===t){do u=r[i+=n];while(u&&u.row===t);if(!u)return r.slice()}var a=[];t=u.row;do a[n<0?"unshift":"push"](u),u=r[i+=n];while(u&&u.row==t);return a.length&&a}var r=e("../line_widgets").LineWidgets,i=e("../lib/dom"),s=e("../range").Range;t.showErrorMarker=function(e,t){var n=e.session;n.widgetManager||(n.widgetManager=new r(n),n.widgetManager.attach(e));var s=e.getCursorPosition(),o=s.row,a=n.widgetManager.getWidgetsAtRow(o).filter(function(e){return e.type=="errorMarker"})[0];a?a.destroy():o-=t;var f=u(n,o,t),l;if(f){var c=f[0];s.column=(c.pos&&typeof c.column!="number"?c.pos.sc:c.column)||0,s.row=c.row,l=e.renderer.$gutterLayer.$annotations[s.row]}else{if(a)return;l={text:["Looks good!"],className:"ace_ok"}}e.session.unfold(s.row),e.selection.moveToPosition(s);var h={row:s.row,fixedWidth:!0,coverGutter:!0,el:i.createElement("div"),type:"errorMarker"},p=h.el.appendChild(i.createElement("div")),d=h.el.appendChild(i.createElement("div"));d.className="error_widget_arrow "+l.className;var v=e.renderer.$cursorLayer.getPixelPosition(s).left;d.style.left=v+e.renderer.gutterWidth-5+"px",h.el.className="error_widget_wrapper",p.className="error_widget "+l.className,p.innerHTML=l.text.join("
"),p.appendChild(i.createElement("div"));var m=function(e,t,n){if(t===0&&(n==="esc"||n==="return"))return h.destroy(),{command:"null"}};h.destroy=function(){if(e.$mouseHandler.isMousePressed)return;e.keyBinding.removeKeyboardHandler(m),n.widgetManager.removeLineWidget(h),e.off("changeSelection",h.destroy),e.off("changeSession",h.destroy),e.off("mouseup",h.destroy),e.off("change",h.destroy)},e.keyBinding.addKeyboardHandler(m),e.on("changeSelection",h.destroy),e.on("changeSession",h.destroy),e.on("mouseup",h.destroy),e.on("change",h.destroy),e.session.widgetManager.addLineWidget(h),h.el.onmousedown=e.focus.bind(e),e.renderer.scrollCursorIntoView(null,.5,{bottom:h.el.offsetHeight})},i.importCssString(" .error_widget_wrapper { background: inherit; color: inherit; border:none } .error_widget { border-top: solid 2px; border-bottom: solid 2px; margin: 5px 0; padding: 10px 40px; white-space: pre-wrap; } .error_widget.ace_error, .error_widget_arrow.ace_error{ border-color: #ff5a5a } .error_widget.ace_warning, .error_widget_arrow.ace_warning{ border-color: #F1D817 } .error_widget.ace_info, .error_widget_arrow.ace_info{ border-color: #5a5a5a } .error_widget.ace_ok, .error_widget_arrow.ace_ok{ border-color: #5aaa5a } .error_widget_arrow { position: absolute; border: solid 5px; border-top-color: transparent!important; border-right-color: transparent!important; border-left-color: transparent!important; top: -5px; }","")}),ace.define("ace/ace",["require","exports","module","ace/lib/fixoldbrowsers","ace/lib/dom","ace/lib/event","ace/range","ace/editor","ace/edit_session","ace/undomanager","ace/virtual_renderer","ace/worker/worker_client","ace/keyboard/hash_handler","ace/placeholder","ace/multi_select","ace/mode/folding/fold_mode","ace/theme/textmate","ace/ext/error_marker","ace/config"],function(e,t,n){"use strict";e("./lib/fixoldbrowsers");var r=e("./lib/dom"),i=e("./lib/event"),s=e("./range").Range,o=e("./editor").Editor,u=e("./edit_session").EditSession,a=e("./undomanager").UndoManager,f=e("./virtual_renderer").VirtualRenderer;e("./worker/worker_client"),e("./keyboard/hash_handler"),e("./placeholder"),e("./multi_select"),e("./mode/folding/fold_mode"),e("./theme/textmate"),e("./ext/error_marker"),t.config=e("./config"),t.require=e,typeof define=="function"&&(t.define=define),t.edit=function(e,n){if(typeof e=="string"){var s=e;e=document.getElementById(s);if(!e)throw new Error("ace.edit can't find div #"+s)}if(e&&e.env&&e.env.editor instanceof o)return e.env.editor;var u="";if(e&&/input|textarea/i.test(e.tagName)){var a=e;u=a.value,e=r.createElement("pre"),a.parentNode.replaceChild(e,a)}else e&&(u=e.textContent,e.innerHTML="");var l=t.createEditSession(u),c=new o(new f(e),l,n),h={document:l,editor:c,onResize:c.resize.bind(c,null)};return a&&(h.textarea=a),i.addListener(window,"resize",h.onResize),c.on("destroy",function(){i.removeListener(window,"resize",h.onResize),h.editor.container.env=null}),c.container.env=c.env=h,c},t.createEditSession=function(e,t){var n=new u(e,t);return n.setUndoManager(new a),n},t.Range=s,t.Editor=o,t.EditSession=u,t.UndoManager=a,t.VirtualRenderer=f,t.version=t.config.version}); (function() { +/* ***** BEGIN LICENSE BLOCK ***** + * Distributed under the BSD license: + * + * Copyright (c) 2010, Ajax.org B.V. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Ajax.org B.V. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ***** END LICENSE BLOCK ***** */ + +/** + * Define a module along with a payload + * @param module a name for the payload + * @param payload a function to call with (require, exports, module) params + */ + +(function() { + +var ACE_NAMESPACE = "ace"; + +var global = (function() { return this; })(); +if (!global && typeof window != "undefined") global = window; // strict mode + + +if (!ACE_NAMESPACE && typeof requirejs !== "undefined") + return; + + +var define = function(module, deps, payload) { + if (typeof module !== "string") { + if (define.original) + define.original.apply(this, arguments); + else { + console.error("dropping module because define wasn\'t a string."); + console.trace(); + } + return; + } + if (arguments.length == 2) + payload = deps; + if (!define.modules[module]) { + define.payloads[module] = payload; + define.modules[module] = null; + } +}; + +define.modules = {}; +define.payloads = {}; + +/** + * Get at functionality define()ed using the function above + */ +var _require = function(parentId, module, callback) { + if (typeof module === "string") { + var payload = lookup(parentId, module); + if (payload != undefined) { + callback && callback(); + return payload; + } + } else if (Object.prototype.toString.call(module) === "[object Array]") { + var params = []; + for (var i = 0, l = module.length; i < l; ++i) { + var dep = lookup(parentId, module[i]); + if (dep == undefined && require.original) + return; + params.push(dep); + } + return callback && callback.apply(null, params) || true; + } +}; + +var require = function(module, callback) { + var packagedModule = _require("", module, callback); + if (packagedModule == undefined && require.original) + return require.original.apply(this, arguments); + return packagedModule; +}; + +var normalizeModule = function(parentId, moduleName) { + // normalize plugin requires + if (moduleName.indexOf("!") !== -1) { + var chunks = moduleName.split("!"); + return normalizeModule(parentId, chunks[0]) + "!" + normalizeModule(parentId, chunks[1]); + } + // normalize relative requires + if (moduleName.charAt(0) == ".") { + var base = parentId.split("/").slice(0, -1).join("/"); + moduleName = base + "/" + moduleName; + + while(moduleName.indexOf(".") !== -1 && previous != moduleName) { + var previous = moduleName; + moduleName = moduleName.replace(/\/\.\//, "/").replace(/[^\/]+\/\.\.\//, ""); + } + } + return moduleName; +}; + +/** + * Internal function to lookup moduleNames and resolve them by calling the + * definition function if needed. + */ +var lookup = function(parentId, moduleName) { + moduleName = normalizeModule(parentId, moduleName); + + var module = define.modules[moduleName]; + if (!module) { + module = define.payloads[moduleName]; + if (typeof module === 'function') { + var exports = {}; + var mod = { + id: moduleName, + uri: '', + exports: exports, + packaged: true + }; + + var req = function(module, callback) { + return _require(moduleName, module, callback); + }; + + var returnValue = module(req, exports, mod); + exports = returnValue || mod.exports; + define.modules[moduleName] = exports; + delete define.payloads[moduleName]; + } + module = define.modules[moduleName] = exports || module; + } + return module; +}; + +function exportAce(ns) { + var root = global; + if (ns) { + if (!global[ns]) + global[ns] = {}; + root = global[ns]; + } + + if (!root.define || !root.define.packaged) { + define.original = root.define; + root.define = define; + root.define.packaged = true; + } + + if (!root.require || !root.require.packaged) { + require.original = root.require; + root.require = require; + root.require.packaged = true; + } +} + +exportAce(ACE_NAMESPACE); + +})(); + +ace.define("ace/lib/fixoldbrowsers",["require","exports","module"], function(require, exports, module) { +"use strict"; +if (typeof Element != "undefined" && !Element.prototype.remove) { + Object.defineProperty(Element.prototype, "remove", { + enumerable: false, + writable: true, + configurable: true, + value: function() { this.parentNode && this.parentNode.removeChild(this); } + }); +} + + +}); + +ace.define("ace/lib/useragent",["require","exports","module"], function(require, exports, module) { +"use strict"; +exports.OS = { + LINUX: "LINUX", + MAC: "MAC", + WINDOWS: "WINDOWS" +}; +exports.getOS = function() { + if (exports.isMac) { + return exports.OS.MAC; + } else if (exports.isLinux) { + return exports.OS.LINUX; + } else { + return exports.OS.WINDOWS; + } +}; +var _navigator = typeof navigator == "object" ? navigator : {}; + +var os = (/mac|win|linux/i.exec(_navigator.platform) || ["other"])[0].toLowerCase(); +var ua = _navigator.userAgent || ""; +var appName = _navigator.appName || ""; +exports.isWin = (os == "win"); +exports.isMac = (os == "mac"); +exports.isLinux = (os == "linux"); +exports.isIE = + (appName == "Microsoft Internet Explorer" || appName.indexOf("MSAppHost") >= 0) + ? parseFloat((ua.match(/(?:MSIE |Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]) + : parseFloat((ua.match(/(?:Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]); // for ie + +exports.isOldIE = exports.isIE && exports.isIE < 9; +exports.isGecko = exports.isMozilla = ua.match(/ Gecko\/\d+/); +exports.isOpera = typeof opera == "object" && Object.prototype.toString.call(window.opera) == "[object Opera]"; +exports.isWebKit = parseFloat(ua.split("WebKit/")[1]) || undefined; + +exports.isChrome = parseFloat(ua.split(" Chrome/")[1]) || undefined; + +exports.isEdge = parseFloat(ua.split(" Edge/")[1]) || undefined; + +exports.isAIR = ua.indexOf("AdobeAIR") >= 0; + +exports.isAndroid = ua.indexOf("Android") >= 0; + +exports.isChromeOS = ua.indexOf(" CrOS ") >= 0; + +exports.isIOS = /iPad|iPhone|iPod/.test(ua) && !window.MSStream; + +if (exports.isIOS) exports.isMac = true; + +exports.isMobile = exports.isIOS || exports.isAndroid; + +}); + +ace.define("ace/lib/dom",["require","exports","module","ace/lib/useragent"], function(require, exports, module) { +"use strict"; + +var useragent = require("./useragent"); +var XHTML_NS = "http://www.w3.org/1999/xhtml"; + +exports.buildDom = function buildDom(arr, parent, refs) { + if (typeof arr == "string" && arr) { + var txt = document.createTextNode(arr); + if (parent) + parent.appendChild(txt); + return txt; + } + + if (!Array.isArray(arr)) { + if (arr && arr.appendChild && parent) + parent.appendChild(arr); + return arr; + } + if (typeof arr[0] != "string" || !arr[0]) { + var els = []; + for (var i = 0; i < arr.length; i++) { + var ch = buildDom(arr[i], parent, refs); + ch && els.push(ch); + } + return els; + } + + var el = document.createElement(arr[0]); + var options = arr[1]; + var childIndex = 1; + if (options && typeof options == "object" && !Array.isArray(options)) + childIndex = 2; + for (var i = childIndex; i < arr.length; i++) + buildDom(arr[i], el, refs); + if (childIndex == 2) { + Object.keys(options).forEach(function(n) { + var val = options[n]; + if (n === "class") { + el.className = Array.isArray(val) ? val.join(" ") : val; + } else if (typeof val == "function" || n == "value" || n[0] == "$") { + el[n] = val; + } else if (n === "ref") { + if (refs) refs[val] = el; + } else if (n === "style") { + if (typeof val == "string") el.style.cssText = val; + } else if (val != null) { + el.setAttribute(n, val); + } + }); + } + if (parent) + parent.appendChild(el); + return el; +}; + +exports.getDocumentHead = function(doc) { + if (!doc) + doc = document; + return doc.head || doc.getElementsByTagName("head")[0] || doc.documentElement; +}; + +exports.createElement = function(tag, ns) { + return document.createElementNS ? + document.createElementNS(ns || XHTML_NS, tag) : + document.createElement(tag); +}; + +exports.removeChildren = function(element) { + element.innerHTML = ""; +}; + +exports.createTextNode = function(textContent, element) { + var doc = element ? element.ownerDocument : document; + return doc.createTextNode(textContent); +}; + +exports.createFragment = function(element) { + var doc = element ? element.ownerDocument : document; + return doc.createDocumentFragment(); +}; + +exports.hasCssClass = function(el, name) { + var classes = (el.className + "").split(/\s+/g); + return classes.indexOf(name) !== -1; +}; +exports.addCssClass = function(el, name) { + if (!exports.hasCssClass(el, name)) { + el.className += " " + name; + } +}; +exports.removeCssClass = function(el, name) { + var classes = el.className.split(/\s+/g); + while (true) { + var index = classes.indexOf(name); + if (index == -1) { + break; + } + classes.splice(index, 1); + } + el.className = classes.join(" "); +}; + +exports.toggleCssClass = function(el, name) { + var classes = el.className.split(/\s+/g), add = true; + while (true) { + var index = classes.indexOf(name); + if (index == -1) { + break; + } + add = false; + classes.splice(index, 1); + } + if (add) + classes.push(name); + + el.className = classes.join(" "); + return add; +}; +exports.setCssClass = function(node, className, include) { + if (include) { + exports.addCssClass(node, className); + } else { + exports.removeCssClass(node, className); + } +}; + +exports.hasCssString = function(id, doc) { + var index = 0, sheets; + doc = doc || document; + if ((sheets = doc.querySelectorAll("style"))) { + while (index < sheets.length) + if (sheets[index++].id === id) + return true; + } +}; + +var strictCSP; +var cssCache = []; +exports.useStrictCSP = function(value) { + strictCSP = value; + if (value == false) insertPendingStyles(); + else if (!cssCache) cssCache = []; +}; + +function insertPendingStyles() { + var cache = cssCache; + cssCache = null; + cache && cache.forEach(function(item) { + importCssString(item[0], item[1]); + }); +} + +function importCssString(cssText, id, target) { + if (typeof document == "undefined") + return; + if (cssCache) { + if (target) { + insertPendingStyles(); + } else if (target === false) { + return cssCache.push([cssText, id]); + } + } + if (strictCSP) return; + + var container = target; + if (!target || !target.getRootNode) { + container = document; + } else { + container = target.getRootNode(); + if (!container || container == target) + container = document; + } + + var doc = container.ownerDocument || container; + if (id && exports.hasCssString(id, container)) + return null; + + if (id) + cssText += "\n/*# sourceURL=ace/css/" + id + " */"; + + var style = exports.createElement("style"); + style.appendChild(doc.createTextNode(cssText)); + if (id) + style.id = id; + + if (container == doc) + container = exports.getDocumentHead(doc); + container.insertBefore(style, container.firstChild); +} +exports.importCssString = importCssString; + +exports.importCssStylsheet = function(uri, doc) { + exports.buildDom(["link", {rel: "stylesheet", href: uri}], exports.getDocumentHead(doc)); +}; +exports.scrollbarWidth = function(document) { + var inner = exports.createElement("ace_inner"); + inner.style.width = "100%"; + inner.style.minWidth = "0px"; + inner.style.height = "200px"; + inner.style.display = "block"; + + var outer = exports.createElement("ace_outer"); + var style = outer.style; + + style.position = "absolute"; + style.left = "-10000px"; + style.overflow = "hidden"; + style.width = "200px"; + style.minWidth = "0px"; + style.height = "150px"; + style.display = "block"; + + outer.appendChild(inner); + + var body = document.documentElement; + body.appendChild(outer); + + var noScrollbar = inner.offsetWidth; + + style.overflow = "scroll"; + var withScrollbar = inner.offsetWidth; + + if (noScrollbar == withScrollbar) { + withScrollbar = outer.clientWidth; + } + + body.removeChild(outer); + + return noScrollbar-withScrollbar; +}; + +exports.computedStyle = function(element, style) { + return window.getComputedStyle(element, "") || {}; +}; + +exports.setStyle = function(styles, property, value) { + if (styles[property] !== value) { + styles[property] = value; + } +}; + +exports.HAS_CSS_ANIMATION = false; +exports.HAS_CSS_TRANSFORMS = false; +exports.HI_DPI = useragent.isWin + ? typeof window !== "undefined" && window.devicePixelRatio >= 1.5 + : true; + +if (useragent.isChromeOS) exports.HI_DPI = false; + +if (typeof document !== "undefined") { + var div = document.createElement("div"); + if (exports.HI_DPI && div.style.transform !== undefined) + exports.HAS_CSS_TRANSFORMS = true; + if (!useragent.isEdge && typeof div.style.animationName !== "undefined") + exports.HAS_CSS_ANIMATION = true; + div = null; +} + +if (exports.HAS_CSS_TRANSFORMS) { + exports.translate = function(element, tx, ty) { + element.style.transform = "translate(" + Math.round(tx) + "px, " + Math.round(ty) +"px)"; + }; +} else { + exports.translate = function(element, tx, ty) { + element.style.top = Math.round(ty) + "px"; + element.style.left = Math.round(tx) + "px"; + }; +} + +}); + +ace.define("ace/lib/oop",["require","exports","module"], function(require, exports, module) { +"use strict"; + +exports.inherits = function(ctor, superCtor) { + ctor.super_ = superCtor; + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); +}; + +exports.mixin = function(obj, mixin) { + for (var key in mixin) { + obj[key] = mixin[key]; + } + return obj; +}; + +exports.implement = function(proto, mixin) { + exports.mixin(proto, mixin); +}; + +}); + +ace.define("ace/lib/keys",["require","exports","module","ace/lib/oop"], function(require, exports, module) { +"use strict"; + +var oop = require("./oop"); +var Keys = (function() { + var ret = { + MODIFIER_KEYS: { + 16: 'Shift', 17: 'Ctrl', 18: 'Alt', 224: 'Meta', + 91: 'MetaLeft', 92: 'MetaRight', 93: 'ContextMenu' + }, + + KEY_MODS: { + "ctrl": 1, "alt": 2, "option" : 2, "shift": 4, + "super": 8, "meta": 8, "command": 8, "cmd": 8, + "control": 1 + }, + + FUNCTION_KEYS : { + 8 : "Backspace", + 9 : "Tab", + 13 : "Return", + 19 : "Pause", + 27 : "Esc", + 32 : "Space", + 33 : "PageUp", + 34 : "PageDown", + 35 : "End", + 36 : "Home", + 37 : "Left", + 38 : "Up", + 39 : "Right", + 40 : "Down", + 44 : "Print", + 45 : "Insert", + 46 : "Delete", + 96 : "Numpad0", + 97 : "Numpad1", + 98 : "Numpad2", + 99 : "Numpad3", + 100: "Numpad4", + 101: "Numpad5", + 102: "Numpad6", + 103: "Numpad7", + 104: "Numpad8", + 105: "Numpad9", + '-13': "NumpadEnter", + 112: "F1", + 113: "F2", + 114: "F3", + 115: "F4", + 116: "F5", + 117: "F6", + 118: "F7", + 119: "F8", + 120: "F9", + 121: "F10", + 122: "F11", + 123: "F12", + 144: "Numlock", + 145: "Scrolllock" + }, + + PRINTABLE_KEYS: { + 32: ' ', 48: '0', 49: '1', 50: '2', 51: '3', 52: '4', 53: '5', + 54: '6', 55: '7', 56: '8', 57: '9', 59: ';', 61: '=', 65: 'a', + 66: 'b', 67: 'c', 68: 'd', 69: 'e', 70: 'f', 71: 'g', 72: 'h', + 73: 'i', 74: 'j', 75: 'k', 76: 'l', 77: 'm', 78: 'n', 79: 'o', + 80: 'p', 81: 'q', 82: 'r', 83: 's', 84: 't', 85: 'u', 86: 'v', + 87: 'w', 88: 'x', 89: 'y', 90: 'z', 107: '+', 109: '-', 110: '.', + 186: ';', 187: '=', 188: ',', 189: '-', 190: '.', 191: '/', 192: '`', + 219: '[', 220: '\\',221: ']', 222: "'", 111: '/', 106: '*' + } + }; + var name, i; + for (i in ret.FUNCTION_KEYS) { + name = ret.FUNCTION_KEYS[i].toLowerCase(); + ret[name] = parseInt(i, 10); + } + for (i in ret.PRINTABLE_KEYS) { + name = ret.PRINTABLE_KEYS[i].toLowerCase(); + ret[name] = parseInt(i, 10); + } + oop.mixin(ret, ret.MODIFIER_KEYS); + oop.mixin(ret, ret.PRINTABLE_KEYS); + oop.mixin(ret, ret.FUNCTION_KEYS); + ret.enter = ret["return"]; + ret.escape = ret.esc; + ret.del = ret["delete"]; + ret[173] = '-'; + + (function() { + var mods = ["cmd", "ctrl", "alt", "shift"]; + for (var i = Math.pow(2, mods.length); i--;) { + ret.KEY_MODS[i] = mods.filter(function(x) { + return i & ret.KEY_MODS[x]; + }).join("-") + "-"; + } + })(); + + ret.KEY_MODS[0] = ""; + ret.KEY_MODS[-1] = "input-"; + + return ret; +})(); +oop.mixin(exports, Keys); + +exports.keyCodeToString = function(keyCode) { + var keyString = Keys[keyCode]; + if (typeof keyString != "string") + keyString = String.fromCharCode(keyCode); + return keyString.toLowerCase(); +}; + +}); + +ace.define("ace/lib/event",["require","exports","module","ace/lib/keys","ace/lib/useragent"], function(require, exports, module) { +"use strict"; + +var keys = require("./keys"); +var useragent = require("./useragent"); + +var pressedKeys = null; +var ts = 0; + +var activeListenerOptions; +function detectListenerOptionsSupport() { + activeListenerOptions = false; + try { + document.createComment("").addEventListener("test", function() {}, { + get passive() { + activeListenerOptions = {passive: false}; + } + }); + } catch(e) {} +} + +function getListenerOptions() { + if (activeListenerOptions == undefined) + detectListenerOptionsSupport(); + return activeListenerOptions; +} + +function EventListener(elem, type, callback) { + this.elem = elem; + this.type = type; + this.callback = callback; +} +EventListener.prototype.destroy = function() { + removeListener(this.elem, this.type, this.callback); + this.elem = this.type = this.callback = undefined; +}; + +var addListener = exports.addListener = function(elem, type, callback, destroyer) { + elem.addEventListener(type, callback, getListenerOptions()); + if (destroyer) + destroyer.$toDestroy.push(new EventListener(elem, type, callback)); +}; + +var removeListener = exports.removeListener = function(elem, type, callback) { + elem.removeEventListener(type, callback, getListenerOptions()); +}; +exports.stopEvent = function(e) { + exports.stopPropagation(e); + exports.preventDefault(e); + return false; +}; + +exports.stopPropagation = function(e) { + if (e.stopPropagation) + e.stopPropagation(); +}; + +exports.preventDefault = function(e) { + if (e.preventDefault) + e.preventDefault(); +}; +exports.getButton = function(e) { + if (e.type == "dblclick") + return 0; + if (e.type == "contextmenu" || (useragent.isMac && (e.ctrlKey && !e.altKey && !e.shiftKey))) + return 2; + return e.button; +}; + +exports.capture = function(el, eventHandler, releaseCaptureHandler) { + var ownerDocument = el && el.ownerDocument || document; + function onMouseUp(e) { + eventHandler && eventHandler(e); + releaseCaptureHandler && releaseCaptureHandler(e); + + removeListener(ownerDocument, "mousemove", eventHandler); + removeListener(ownerDocument, "mouseup", onMouseUp); + removeListener(ownerDocument, "dragstart", onMouseUp); + } + + addListener(ownerDocument, "mousemove", eventHandler); + addListener(ownerDocument, "mouseup", onMouseUp); + addListener(ownerDocument, "dragstart", onMouseUp); + + return onMouseUp; +}; + +exports.addMouseWheelListener = function(el, callback, destroyer) { + addListener(el, "wheel", function(e) { + var factor = 0.15; + var deltaX = e.deltaX || 0; + var deltaY = e.deltaY || 0; + switch (e.deltaMode) { + case e.DOM_DELTA_PIXEL: + e.wheelX = deltaX * factor; + e.wheelY = deltaY * factor; + break; + case e.DOM_DELTA_LINE: + var linePixels = 15; + e.wheelX = deltaX * linePixels; + e.wheelY = deltaY * linePixels; + break; + case e.DOM_DELTA_PAGE: + var pagePixels = 150; + e.wheelX = deltaX * pagePixels; + e.wheelY = deltaY * pagePixels; + break; + } + callback(e); + }, destroyer); +}; + +exports.addMultiMouseDownListener = function(elements, timeouts, eventHandler, callbackName, destroyer) { + var clicks = 0; + var startX, startY, timer; + var eventNames = { + 2: "dblclick", + 3: "tripleclick", + 4: "quadclick" + }; + + function onMousedown(e) { + if (exports.getButton(e) !== 0) { + clicks = 0; + } else if (e.detail > 1) { + clicks++; + if (clicks > 4) + clicks = 1; + } else { + clicks = 1; + } + if (useragent.isIE) { + var isNewClick = Math.abs(e.clientX - startX) > 5 || Math.abs(e.clientY - startY) > 5; + if (!timer || isNewClick) + clicks = 1; + if (timer) + clearTimeout(timer); + timer = setTimeout(function() {timer = null;}, timeouts[clicks - 1] || 600); + + if (clicks == 1) { + startX = e.clientX; + startY = e.clientY; + } + } + + e._clicks = clicks; + + eventHandler[callbackName]("mousedown", e); + + if (clicks > 4) + clicks = 0; + else if (clicks > 1) + return eventHandler[callbackName](eventNames[clicks], e); + } + if (!Array.isArray(elements)) + elements = [elements]; + elements.forEach(function(el) { + addListener(el, "mousedown", onMousedown, destroyer); + }); +}; + +var getModifierHash = function(e) { + return 0 | (e.ctrlKey ? 1 : 0) | (e.altKey ? 2 : 0) | (e.shiftKey ? 4 : 0) | (e.metaKey ? 8 : 0); +}; + +exports.getModifierString = function(e) { + return keys.KEY_MODS[getModifierHash(e)]; +}; + +function normalizeCommandKeys(callback, e, keyCode) { + var hashId = getModifierHash(e); + + if (!useragent.isMac && pressedKeys) { + if (e.getModifierState && (e.getModifierState("OS") || e.getModifierState("Win"))) + hashId |= 8; + if (pressedKeys.altGr) { + if ((3 & hashId) != 3) + pressedKeys.altGr = 0; + else + return; + } + if (keyCode === 18 || keyCode === 17) { + var location = "location" in e ? e.location : e.keyLocation; + if (keyCode === 17 && location === 1) { + if (pressedKeys[keyCode] == 1) + ts = e.timeStamp; + } else if (keyCode === 18 && hashId === 3 && location === 2) { + var dt = e.timeStamp - ts; + if (dt < 50) + pressedKeys.altGr = true; + } + } + } + + if (keyCode in keys.MODIFIER_KEYS) { + keyCode = -1; + } + + if (!hashId && keyCode === 13) { + var location = "location" in e ? e.location : e.keyLocation; + if (location === 3) { + callback(e, hashId, -keyCode); + if (e.defaultPrevented) + return; + } + } + + if (useragent.isChromeOS && hashId & 8) { + callback(e, hashId, keyCode); + if (e.defaultPrevented) + return; + else + hashId &= ~8; + } + if (!hashId && !(keyCode in keys.FUNCTION_KEYS) && !(keyCode in keys.PRINTABLE_KEYS)) { + return false; + } + + return callback(e, hashId, keyCode); +} + + +exports.addCommandKeyListener = function(el, callback, destroyer) { + if (useragent.isOldGecko || (useragent.isOpera && !("KeyboardEvent" in window))) { + var lastKeyDownKeyCode = null; + addListener(el, "keydown", function(e) { + lastKeyDownKeyCode = e.keyCode; + }, destroyer); + addListener(el, "keypress", function(e) { + return normalizeCommandKeys(callback, e, lastKeyDownKeyCode); + }, destroyer); + } else { + var lastDefaultPrevented = null; + + addListener(el, "keydown", function(e) { + pressedKeys[e.keyCode] = (pressedKeys[e.keyCode] || 0) + 1; + var result = normalizeCommandKeys(callback, e, e.keyCode); + lastDefaultPrevented = e.defaultPrevented; + return result; + }, destroyer); + + addListener(el, "keypress", function(e) { + if (lastDefaultPrevented && (e.ctrlKey || e.altKey || e.shiftKey || e.metaKey)) { + exports.stopEvent(e); + lastDefaultPrevented = null; + } + }, destroyer); + + addListener(el, "keyup", function(e) { + pressedKeys[e.keyCode] = null; + }, destroyer); + + if (!pressedKeys) { + resetPressedKeys(); + addListener(window, "focus", resetPressedKeys); + } + } +}; +function resetPressedKeys() { + pressedKeys = Object.create(null); +} + +if (typeof window == "object" && window.postMessage && !useragent.isOldIE) { + var postMessageId = 1; + exports.nextTick = function(callback, win) { + win = win || window; + var messageName = "zero-timeout-message-" + (postMessageId++); + + var listener = function(e) { + if (e.data == messageName) { + exports.stopPropagation(e); + removeListener(win, "message", listener); + callback(); + } + }; + + addListener(win, "message", listener); + win.postMessage(messageName, "*"); + }; +} + +exports.$idleBlocked = false; +exports.onIdle = function(cb, timeout) { + return setTimeout(function handler() { + if (!exports.$idleBlocked) { + cb(); + } else { + setTimeout(handler, 100); + } + }, timeout); +}; + +exports.$idleBlockId = null; +exports.blockIdle = function(delay) { + if (exports.$idleBlockId) + clearTimeout(exports.$idleBlockId); + + exports.$idleBlocked = true; + exports.$idleBlockId = setTimeout(function() { + exports.$idleBlocked = false; + }, delay || 100); +}; + +exports.nextFrame = typeof window == "object" && (window.requestAnimationFrame + || window.mozRequestAnimationFrame + || window.webkitRequestAnimationFrame + || window.msRequestAnimationFrame + || window.oRequestAnimationFrame); + +if (exports.nextFrame) + exports.nextFrame = exports.nextFrame.bind(window); +else + exports.nextFrame = function(callback) { + setTimeout(callback, 17); + }; +}); + +ace.define("ace/range",["require","exports","module"], function(require, exports, module) { +"use strict"; +var comparePoints = function(p1, p2) { + return p1.row - p2.row || p1.column - p2.column; +}; +var Range = function(startRow, startColumn, endRow, endColumn) { + this.start = { + row: startRow, + column: startColumn + }; + + this.end = { + row: endRow, + column: endColumn + }; +}; + +(function() { + this.isEqual = function(range) { + return this.start.row === range.start.row && + this.end.row === range.end.row && + this.start.column === range.start.column && + this.end.column === range.end.column; + }; + this.toString = function() { + return ("Range: [" + this.start.row + "/" + this.start.column + + "] -> [" + this.end.row + "/" + this.end.column + "]"); + }; + + this.contains = function(row, column) { + return this.compare(row, column) == 0; + }; + this.compareRange = function(range) { + var cmp, + end = range.end, + start = range.start; + + cmp = this.compare(end.row, end.column); + if (cmp == 1) { + cmp = this.compare(start.row, start.column); + if (cmp == 1) { + return 2; + } else if (cmp == 0) { + return 1; + } else { + return 0; + } + } else if (cmp == -1) { + return -2; + } else { + cmp = this.compare(start.row, start.column); + if (cmp == -1) { + return -1; + } else if (cmp == 1) { + return 42; + } else { + return 0; + } + } + }; + this.comparePoint = function(p) { + return this.compare(p.row, p.column); + }; + this.containsRange = function(range) { + return this.comparePoint(range.start) == 0 && this.comparePoint(range.end) == 0; + }; + this.intersects = function(range) { + var cmp = this.compareRange(range); + return (cmp == -1 || cmp == 0 || cmp == 1); + }; + this.isEnd = function(row, column) { + return this.end.row == row && this.end.column == column; + }; + this.isStart = function(row, column) { + return this.start.row == row && this.start.column == column; + }; + this.setStart = function(row, column) { + if (typeof row == "object") { + this.start.column = row.column; + this.start.row = row.row; + } else { + this.start.row = row; + this.start.column = column; + } + }; + this.setEnd = function(row, column) { + if (typeof row == "object") { + this.end.column = row.column; + this.end.row = row.row; + } else { + this.end.row = row; + this.end.column = column; + } + }; + this.inside = function(row, column) { + if (this.compare(row, column) == 0) { + if (this.isEnd(row, column) || this.isStart(row, column)) { + return false; + } else { + return true; + } + } + return false; + }; + this.insideStart = function(row, column) { + if (this.compare(row, column) == 0) { + if (this.isEnd(row, column)) { + return false; + } else { + return true; + } + } + return false; + }; + this.insideEnd = function(row, column) { + if (this.compare(row, column) == 0) { + if (this.isStart(row, column)) { + return false; + } else { + return true; + } + } + return false; + }; + this.compare = function(row, column) { + if (!this.isMultiLine()) { + if (row === this.start.row) { + return column < this.start.column ? -1 : (column > this.end.column ? 1 : 0); + } + } + + if (row < this.start.row) + return -1; + + if (row > this.end.row) + return 1; + + if (this.start.row === row) + return column >= this.start.column ? 0 : -1; + + if (this.end.row === row) + return column <= this.end.column ? 0 : 1; + + return 0; + }; + this.compareStart = function(row, column) { + if (this.start.row == row && this.start.column == column) { + return -1; + } else { + return this.compare(row, column); + } + }; + this.compareEnd = function(row, column) { + if (this.end.row == row && this.end.column == column) { + return 1; + } else { + return this.compare(row, column); + } + }; + this.compareInside = function(row, column) { + if (this.end.row == row && this.end.column == column) { + return 1; + } else if (this.start.row == row && this.start.column == column) { + return -1; + } else { + return this.compare(row, column); + } + }; + this.clipRows = function(firstRow, lastRow) { + if (this.end.row > lastRow) + var end = {row: lastRow + 1, column: 0}; + else if (this.end.row < firstRow) + var end = {row: firstRow, column: 0}; + + if (this.start.row > lastRow) + var start = {row: lastRow + 1, column: 0}; + else if (this.start.row < firstRow) + var start = {row: firstRow, column: 0}; + + return Range.fromPoints(start || this.start, end || this.end); + }; + this.extend = function(row, column) { + var cmp = this.compare(row, column); + + if (cmp == 0) + return this; + else if (cmp == -1) + var start = {row: row, column: column}; + else + var end = {row: row, column: column}; + + return Range.fromPoints(start || this.start, end || this.end); + }; + + this.isEmpty = function() { + return (this.start.row === this.end.row && this.start.column === this.end.column); + }; + this.isMultiLine = function() { + return (this.start.row !== this.end.row); + }; + this.clone = function() { + return Range.fromPoints(this.start, this.end); + }; + this.collapseRows = function() { + if (this.end.column == 0) + return new Range(this.start.row, 0, Math.max(this.start.row, this.end.row-1), 0); + else + return new Range(this.start.row, 0, this.end.row, 0); + }; + this.toScreenRange = function(session) { + var screenPosStart = session.documentToScreenPosition(this.start); + var screenPosEnd = session.documentToScreenPosition(this.end); + + return new Range( + screenPosStart.row, screenPosStart.column, + screenPosEnd.row, screenPosEnd.column + ); + }; + this.moveBy = function(row, column) { + this.start.row += row; + this.start.column += column; + this.end.row += row; + this.end.column += column; + }; + +}).call(Range.prototype); +Range.fromPoints = function(start, end) { + return new Range(start.row, start.column, end.row, end.column); +}; +Range.comparePoints = comparePoints; + +Range.comparePoints = function(p1, p2) { + return p1.row - p2.row || p1.column - p2.column; +}; + + +exports.Range = Range; +}); + +ace.define("ace/lib/lang",["require","exports","module"], function(require, exports, module) { +"use strict"; + +exports.last = function(a) { + return a[a.length - 1]; +}; + +exports.stringReverse = function(string) { + return string.split("").reverse().join(""); +}; + +exports.stringRepeat = function (string, count) { + var result = ''; + while (count > 0) { + if (count & 1) + result += string; + + if (count >>= 1) + string += string; + } + return result; +}; + +var trimBeginRegexp = /^\s\s*/; +var trimEndRegexp = /\s\s*$/; + +exports.stringTrimLeft = function (string) { + return string.replace(trimBeginRegexp, ''); +}; + +exports.stringTrimRight = function (string) { + return string.replace(trimEndRegexp, ''); +}; + +exports.copyObject = function(obj) { + var copy = {}; + for (var key in obj) { + copy[key] = obj[key]; + } + return copy; +}; + +exports.copyArray = function(array){ + var copy = []; + for (var i=0, l=array.length; i Date.now() - 50) + return true; + return $cancelT = false; + }, + cancel: function() { + $cancelT = Date.now(); + } +}; + +}); + +ace.define("ace/keyboard/textinput",["require","exports","module","ace/lib/event","ace/lib/useragent","ace/lib/dom","ace/lib/lang","ace/clipboard","ace/lib/keys"], function(require, exports, module) { +"use strict"; + +var event = require("../lib/event"); +var useragent = require("../lib/useragent"); +var dom = require("../lib/dom"); +var lang = require("../lib/lang"); +var clipboard = require("../clipboard"); +var BROKEN_SETDATA = useragent.isChrome < 18; +var USE_IE_MIME_TYPE = useragent.isIE; +var HAS_FOCUS_ARGS = useragent.isChrome > 63; +var MAX_LINE_LENGTH = 400; + +var KEYS = require("../lib/keys"); +var MODS = KEYS.KEY_MODS; +var isIOS = useragent.isIOS; +var valueResetRegex = isIOS ? /\s/ : /\n/; +var isMobile = useragent.isMobile; + +var TextInput = function(parentNode, host) { + var text = dom.createElement("textarea"); + text.className = "ace_text-input"; + + text.setAttribute("wrap", "off"); + text.setAttribute("autocorrect", "off"); + text.setAttribute("autocapitalize", "off"); + text.setAttribute("spellcheck", false); + + text.style.opacity = "0"; + parentNode.insertBefore(text, parentNode.firstChild); + + var copied = false; + var pasted = false; + var inComposition = false; + var sendingText = false; + var tempStyle = ''; + + if (!isMobile) + text.style.fontSize = "1px"; + + var commandMode = false; + var ignoreFocusEvents = false; + + var lastValue = ""; + var lastSelectionStart = 0; + var lastSelectionEnd = 0; + var lastRestoreEnd = 0; + try { var isFocused = document.activeElement === text; } catch(e) {} + + event.addListener(text, "blur", function(e) { + if (ignoreFocusEvents) return; + host.onBlur(e); + isFocused = false; + }, host); + event.addListener(text, "focus", function(e) { + if (ignoreFocusEvents) return; + isFocused = true; + if (useragent.isEdge) { + try { + if (!document.hasFocus()) + return; + } catch(e) {} + } + host.onFocus(e); + if (useragent.isEdge) + setTimeout(resetSelection); + else + resetSelection(); + }, host); + this.$focusScroll = false; + this.focus = function() { + if (tempStyle || HAS_FOCUS_ARGS || this.$focusScroll == "browser") + return text.focus({ preventScroll: true }); + + var top = text.style.top; + text.style.position = "fixed"; + text.style.top = "0px"; + try { + var isTransformed = text.getBoundingClientRect().top != 0; + } catch(e) { + return; + } + var ancestors = []; + if (isTransformed) { + var t = text.parentElement; + while (t && t.nodeType == 1) { + ancestors.push(t); + t.setAttribute("ace_nocontext", true); + if (!t.parentElement && t.getRootNode) + t = t.getRootNode().host; + else + t = t.parentElement; + } + } + text.focus({ preventScroll: true }); + if (isTransformed) { + ancestors.forEach(function(p) { + p.removeAttribute("ace_nocontext"); + }); + } + setTimeout(function() { + text.style.position = ""; + if (text.style.top == "0px") + text.style.top = top; + }, 0); + }; + this.blur = function() { + text.blur(); + }; + this.isFocused = function() { + return isFocused; + }; + + host.on("beforeEndOperation", function() { + var curOp = host.curOp; + var commandName = curOp && curOp.command && curOp.command.name; + if (commandName == "insertstring") + return; + var isUserAction = commandName && (curOp.docChanged || curOp.selectionChanged); + if (inComposition && isUserAction) { + lastValue = text.value = ""; + onCompositionEnd(); + } + resetSelection(); + }); + + var resetSelection = isIOS + ? function(value) { + if (!isFocused || (copied && !value) || sendingText) return; + if (!value) + value = ""; + var newValue = "\n ab" + value + "cde fg\n"; + if (newValue != text.value) + text.value = lastValue = newValue; + + var selectionStart = 4; + var selectionEnd = 4 + (value.length || (host.selection.isEmpty() ? 0 : 1)); + + if (lastSelectionStart != selectionStart || lastSelectionEnd != selectionEnd) { + text.setSelectionRange(selectionStart, selectionEnd); + } + lastSelectionStart = selectionStart; + lastSelectionEnd = selectionEnd; + } + : function() { + if (inComposition || sendingText) + return; + if (!isFocused && !afterContextMenu) + return; + inComposition = true; + + var selectionStart = 0; + var selectionEnd = 0; + var line = ""; + + if (host.session) { + var selection = host.selection; + var range = selection.getRange(); + var row = selection.cursor.row; + selectionStart = range.start.column; + selectionEnd = range.end.column; + line = host.session.getLine(row); + + if (range.start.row != row) { + var prevLine = host.session.getLine(row - 1); + selectionStart = range.start.row < row - 1 ? 0 : selectionStart; + selectionEnd += prevLine.length + 1; + line = prevLine + "\n" + line; + } + else if (range.end.row != row) { + var nextLine = host.session.getLine(row + 1); + selectionEnd = range.end.row > row + 1 ? nextLine.length : selectionEnd; + selectionEnd += line.length + 1; + line = line + "\n" + nextLine; + } + else if (isMobile && row > 0) { + line = "\n" + line; + selectionEnd += 1; + selectionStart += 1; + } + + if (line.length > MAX_LINE_LENGTH) { + if (selectionStart < MAX_LINE_LENGTH && selectionEnd < MAX_LINE_LENGTH) { + line = line.slice(0, MAX_LINE_LENGTH); + } else { + line = "\n"; + if (selectionStart == selectionEnd) { + selectionStart = selectionEnd = 0; + } + else { + selectionStart = 0; + selectionEnd = 1; + } + } + } + } + + var newValue = line + "\n\n"; + if (newValue != lastValue) { + text.value = lastValue = newValue; + lastSelectionStart = lastSelectionEnd = newValue.length; + } + if (afterContextMenu) { + lastSelectionStart = text.selectionStart; + lastSelectionEnd = text.selectionEnd; + } + if ( + lastSelectionEnd != selectionEnd + || lastSelectionStart != selectionStart + || text.selectionEnd != lastSelectionEnd // on ie edge selectionEnd changes silently after the initialization + ) { + try { + text.setSelectionRange(selectionStart, selectionEnd); + lastSelectionStart = selectionStart; + lastSelectionEnd = selectionEnd; + } catch(e){} + } + inComposition = false; + }; + this.resetSelection = resetSelection; + + if (isFocused) + host.onFocus(); + + + var isAllSelected = function(text) { + return text.selectionStart === 0 && text.selectionEnd >= lastValue.length + && text.value === lastValue && lastValue + && text.selectionEnd !== lastSelectionEnd; + }; + + var onSelect = function(e) { + if (inComposition) + return; + if (copied) { + copied = false; + } else if (isAllSelected(text)) { + host.selectAll(); + resetSelection(); + } else if (isMobile && text.selectionStart != lastSelectionStart) { + resetSelection(); + } + }; + + var inputHandler = null; + this.setInputHandler = function(cb) {inputHandler = cb;}; + this.getInputHandler = function() {return inputHandler;}; + var afterContextMenu = false; + + var sendText = function(value, fromInput) { + if (afterContextMenu) + afterContextMenu = false; + if (pasted) { + resetSelection(); + if (value) + host.onPaste(value); + pasted = false; + return ""; + } else { + var selectionStart = text.selectionStart; + var selectionEnd = text.selectionEnd; + + var extendLeft = lastSelectionStart; + var extendRight = lastValue.length - lastSelectionEnd; + + var inserted = value; + var restoreStart = value.length - selectionStart; + var restoreEnd = value.length - selectionEnd; + + var i = 0; + while (extendLeft > 0 && lastValue[i] == value[i]) { + i++; + extendLeft--; + } + inserted = inserted.slice(i); + i = 1; + while (extendRight > 0 && lastValue.length - i > lastSelectionStart - 1 && lastValue[lastValue.length - i] == value[value.length - i]) { + i++; + extendRight--; + } + restoreStart -= i-1; + restoreEnd -= i-1; + var endIndex = inserted.length - i + 1; + if (endIndex < 0) { + extendLeft = -endIndex; + endIndex = 0; + } + inserted = inserted.slice(0, endIndex); + if (!fromInput && !inserted && !restoreStart && !extendLeft && !extendRight && !restoreEnd) + return ""; + sendingText = true; + var shouldReset = false; + if (useragent.isAndroid && inserted == ". ") { + inserted = " "; + shouldReset = true; + } + + if (inserted && !extendLeft && !extendRight && !restoreStart && !restoreEnd || commandMode) { + host.onTextInput(inserted); + } else { + host.onTextInput(inserted, { + extendLeft: extendLeft, + extendRight: extendRight, + restoreStart: restoreStart, + restoreEnd: restoreEnd + }); + } + sendingText = false; + + lastValue = value; + lastSelectionStart = selectionStart; + lastSelectionEnd = selectionEnd; + lastRestoreEnd = restoreEnd; + return shouldReset ? "\n" : inserted; + } + }; + var onInput = function(e) { + if (inComposition) + return onCompositionUpdate(); + if (e && e.inputType) { + if (e.inputType == "historyUndo") return host.execCommand("undo"); + if (e.inputType == "historyRedo") return host.execCommand("redo"); + } + var data = text.value; + var inserted = sendText(data, true); + if ( + data.length > MAX_LINE_LENGTH + 100 + || valueResetRegex.test(inserted) + || isMobile && lastSelectionStart < 1 && lastSelectionStart == lastSelectionEnd + ) { + resetSelection(); + } + }; + + var handleClipboardData = function(e, data, forceIEMime) { + var clipboardData = e.clipboardData || window.clipboardData; + if (!clipboardData || BROKEN_SETDATA) + return; + var mime = USE_IE_MIME_TYPE || forceIEMime ? "Text" : "text/plain"; + try { + if (data) { + return clipboardData.setData(mime, data) !== false; + } else { + return clipboardData.getData(mime); + } + } catch(e) { + if (!forceIEMime) + return handleClipboardData(e, data, true); + } + }; + + var doCopy = function(e, isCut) { + var data = host.getCopyText(); + if (!data) + return event.preventDefault(e); + + if (handleClipboardData(e, data)) { + if (isIOS) { + resetSelection(data); + copied = data; + setTimeout(function () { + copied = false; + }, 10); + } + isCut ? host.onCut() : host.onCopy(); + event.preventDefault(e); + } else { + copied = true; + text.value = data; + text.select(); + setTimeout(function(){ + copied = false; + resetSelection(); + isCut ? host.onCut() : host.onCopy(); + }); + } + }; + + var onCut = function(e) { + doCopy(e, true); + }; + + var onCopy = function(e) { + doCopy(e, false); + }; + + var onPaste = function(e) { + var data = handleClipboardData(e); + if (clipboard.pasteCancelled()) + return; + if (typeof data == "string") { + if (data) + host.onPaste(data, e); + if (useragent.isIE) + setTimeout(resetSelection); + event.preventDefault(e); + } + else { + text.value = ""; + pasted = true; + } + }; + + event.addCommandKeyListener(text, host.onCommandKey.bind(host), host); + + event.addListener(text, "select", onSelect, host); + event.addListener(text, "input", onInput, host); + + event.addListener(text, "cut", onCut, host); + event.addListener(text, "copy", onCopy, host); + event.addListener(text, "paste", onPaste, host); + if (!('oncut' in text) || !('oncopy' in text) || !('onpaste' in text)) { + event.addListener(parentNode, "keydown", function(e) { + if ((useragent.isMac && !e.metaKey) || !e.ctrlKey) + return; + + switch (e.keyCode) { + case 67: + onCopy(e); + break; + case 86: + onPaste(e); + break; + case 88: + onCut(e); + break; + } + }, host); + } + var onCompositionStart = function(e) { + if (inComposition || !host.onCompositionStart || host.$readOnly) + return; + + inComposition = {}; + + if (commandMode) + return; + + if (e.data) + inComposition.useTextareaForIME = false; + + setTimeout(onCompositionUpdate, 0); + host._signal("compositionStart"); + host.on("mousedown", cancelComposition); + + var range = host.getSelectionRange(); + range.end.row = range.start.row; + range.end.column = range.start.column; + inComposition.markerRange = range; + inComposition.selectionStart = lastSelectionStart; + host.onCompositionStart(inComposition); + + if (inComposition.useTextareaForIME) { + lastValue = text.value = ""; + lastSelectionStart = 0; + lastSelectionEnd = 0; + } + else { + if (text.msGetInputContext) + inComposition.context = text.msGetInputContext(); + if (text.getInputContext) + inComposition.context = text.getInputContext(); + } + }; + + var onCompositionUpdate = function() { + if (!inComposition || !host.onCompositionUpdate || host.$readOnly) + return; + if (commandMode) + return cancelComposition(); + + if (inComposition.useTextareaForIME) { + host.onCompositionUpdate(text.value); + } + else { + var data = text.value; + sendText(data); + if (inComposition.markerRange) { + if (inComposition.context) { + inComposition.markerRange.start.column = inComposition.selectionStart + = inComposition.context.compositionStartOffset; + } + inComposition.markerRange.end.column = inComposition.markerRange.start.column + + lastSelectionEnd - inComposition.selectionStart + lastRestoreEnd; + } + } + }; + + var onCompositionEnd = function(e) { + if (!host.onCompositionEnd || host.$readOnly) return; + inComposition = false; + host.onCompositionEnd(); + host.off("mousedown", cancelComposition); + if (e) onInput(); + }; + + + function cancelComposition() { + ignoreFocusEvents = true; + text.blur(); + text.focus(); + ignoreFocusEvents = false; + } + + var syncComposition = lang.delayedCall(onCompositionUpdate, 50).schedule.bind(null, null); + + function onKeyup(e) { + if (e.keyCode == 27 && text.value.length < text.selectionStart) { + if (!inComposition) + lastValue = text.value; + lastSelectionStart = lastSelectionEnd = -1; + resetSelection(); + } + syncComposition(); + } + + event.addListener(text, "compositionstart", onCompositionStart, host); + event.addListener(text, "compositionupdate", onCompositionUpdate, host); + event.addListener(text, "keyup", onKeyup, host); + event.addListener(text, "keydown", syncComposition, host); + event.addListener(text, "compositionend", onCompositionEnd, host); + + this.getElement = function() { + return text; + }; + this.setCommandMode = function(value) { + commandMode = value; + text.readOnly = false; + }; + + this.setReadOnly = function(readOnly) { + if (!commandMode) + text.readOnly = readOnly; + }; + + this.setCopyWithEmptySelection = function(value) { + }; + + this.onContextMenu = function(e) { + afterContextMenu = true; + resetSelection(); + host._emit("nativecontextmenu", {target: host, domEvent: e}); + this.moveToMouse(e, true); + }; + + this.moveToMouse = function(e, bringToFront) { + if (!tempStyle) + tempStyle = text.style.cssText; + text.style.cssText = (bringToFront ? "z-index:100000;" : "") + + (useragent.isIE ? "opacity:0.1;" : "") + + "text-indent: -" + (lastSelectionStart + lastSelectionEnd) * host.renderer.characterWidth * 0.5 + "px;"; + + var rect = host.container.getBoundingClientRect(); + var style = dom.computedStyle(host.container); + var top = rect.top + (parseInt(style.borderTopWidth) || 0); + var left = rect.left + (parseInt(rect.borderLeftWidth) || 0); + var maxTop = rect.bottom - top - text.clientHeight -2; + var move = function(e) { + dom.translate(text, e.clientX - left - 2, Math.min(e.clientY - top - 2, maxTop)); + }; + move(e); + + if (e.type != "mousedown") + return; + + host.renderer.$isMousePressed = true; + + clearTimeout(closeTimeout); + if (useragent.isWin) + event.capture(host.container, move, onContextMenuClose); + }; + + this.onContextMenuClose = onContextMenuClose; + var closeTimeout; + function onContextMenuClose() { + clearTimeout(closeTimeout); + closeTimeout = setTimeout(function () { + if (tempStyle) { + text.style.cssText = tempStyle; + tempStyle = ''; + } + host.renderer.$isMousePressed = false; + if (host.renderer.$keepTextAreaAtCursor) + host.renderer.$moveTextAreaToCursor(); + }, 0); + } + + var onContextMenu = function(e) { + host.textInput.onContextMenu(e); + onContextMenuClose(); + }; + event.addListener(text, "mouseup", onContextMenu, host); + event.addListener(text, "mousedown", function(e) { + e.preventDefault(); + onContextMenuClose(); + }, host); + event.addListener(host.renderer.scroller, "contextmenu", onContextMenu, host); + event.addListener(text, "contextmenu", onContextMenu, host); + + if (isIOS) + addIosSelectionHandler(parentNode, host, text); + + function addIosSelectionHandler(parentNode, host, text) { + var typingResetTimeout = null; + var typing = false; + + text.addEventListener("keydown", function (e) { + if (typingResetTimeout) clearTimeout(typingResetTimeout); + typing = true; + }, true); + + text.addEventListener("keyup", function (e) { + typingResetTimeout = setTimeout(function () { + typing = false; + }, 100); + }, true); + var detectArrowKeys = function(e) { + if (document.activeElement !== text) return; + if (typing || inComposition || host.$mouseHandler.isMousePressed) return; + + if (copied) { + return; + } + var selectionStart = text.selectionStart; + var selectionEnd = text.selectionEnd; + + var key = null; + var modifier = 0; + if (selectionStart == 0) { + key = KEYS.up; + } else if (selectionStart == 1) { + key = KEYS.home; + } else if (selectionEnd > lastSelectionEnd && lastValue[selectionEnd] == "\n") { + key = KEYS.end; + } else if (selectionStart < lastSelectionStart && lastValue[selectionStart - 1] == " ") { + key = KEYS.left; + modifier = MODS.option; + } else if ( + selectionStart < lastSelectionStart + || ( + selectionStart == lastSelectionStart + && lastSelectionEnd != lastSelectionStart + && selectionStart == selectionEnd + ) + ) { + key = KEYS.left; + } else if (selectionEnd > lastSelectionEnd && lastValue.slice(0, selectionEnd).split("\n").length > 2) { + key = KEYS.down; + } else if (selectionEnd > lastSelectionEnd && lastValue[selectionEnd - 1] == " ") { + key = KEYS.right; + modifier = MODS.option; + } else if ( + selectionEnd > lastSelectionEnd + || ( + selectionEnd == lastSelectionEnd + && lastSelectionEnd != lastSelectionStart + && selectionStart == selectionEnd + ) + ) { + key = KEYS.right; + } + + if (selectionStart !== selectionEnd) + modifier |= MODS.shift; + + if (key) { + var result = host.onCommandKey({}, modifier, key); + if (!result && host.commands) { + key = KEYS.keyCodeToString(key); + var command = host.commands.findKeyCommand(modifier, key); + if (command) + host.execCommand(command); + } + lastSelectionStart = selectionStart; + lastSelectionEnd = selectionEnd; + resetSelection(""); + } + }; + document.addEventListener("selectionchange", detectArrowKeys); + host.on("destroy", function() { + document.removeEventListener("selectionchange", detectArrowKeys); + }); + } +}; + +exports.TextInput = TextInput; +exports.$setUserAgentForTests = function(_isMobile, _isIOS) { + isMobile = _isMobile; + isIOS = _isIOS; +}; +}); + +ace.define("ace/mouse/default_handlers",["require","exports","module","ace/lib/useragent"], function(require, exports, module) { +"use strict"; + +var useragent = require("../lib/useragent"); + +var DRAG_OFFSET = 0; // pixels +var SCROLL_COOLDOWN_T = 550; // milliseconds + +function DefaultHandlers(mouseHandler) { + mouseHandler.$clickSelection = null; + + var editor = mouseHandler.editor; + editor.setDefaultHandler("mousedown", this.onMouseDown.bind(mouseHandler)); + editor.setDefaultHandler("dblclick", this.onDoubleClick.bind(mouseHandler)); + editor.setDefaultHandler("tripleclick", this.onTripleClick.bind(mouseHandler)); + editor.setDefaultHandler("quadclick", this.onQuadClick.bind(mouseHandler)); + editor.setDefaultHandler("mousewheel", this.onMouseWheel.bind(mouseHandler)); + + var exports = ["select", "startSelect", "selectEnd", "selectAllEnd", "selectByWordsEnd", + "selectByLinesEnd", "dragWait", "dragWaitEnd", "focusWait"]; + + exports.forEach(function(x) { + mouseHandler[x] = this[x]; + }, this); + + mouseHandler.selectByLines = this.extendSelectionBy.bind(mouseHandler, "getLineRange"); + mouseHandler.selectByWords = this.extendSelectionBy.bind(mouseHandler, "getWordRange"); +} + +(function() { + + this.onMouseDown = function(ev) { + var inSelection = ev.inSelection(); + var pos = ev.getDocumentPosition(); + this.mousedownEvent = ev; + var editor = this.editor; + + var button = ev.getButton(); + if (button !== 0) { + var selectionRange = editor.getSelectionRange(); + var selectionEmpty = selectionRange.isEmpty(); + if (selectionEmpty || button == 1) + editor.selection.moveToPosition(pos); + if (button == 2) { + editor.textInput.onContextMenu(ev.domEvent); + if (!useragent.isMozilla) + ev.preventDefault(); + } + return; + } + + this.mousedownEvent.time = Date.now(); + if (inSelection && !editor.isFocused()) { + editor.focus(); + if (this.$focusTimeout && !this.$clickSelection && !editor.inMultiSelectMode) { + this.setState("focusWait"); + this.captureMouse(ev); + return; + } + } + + this.captureMouse(ev); + this.startSelect(pos, ev.domEvent._clicks > 1); + return ev.preventDefault(); + }; + + this.startSelect = function(pos, waitForClickSelection) { + pos = pos || this.editor.renderer.screenToTextCoordinates(this.x, this.y); + var editor = this.editor; + if (!this.mousedownEvent) return; + if (this.mousedownEvent.getShiftKey()) + editor.selection.selectToPosition(pos); + else if (!waitForClickSelection) + editor.selection.moveToPosition(pos); + if (!waitForClickSelection) + this.select(); + if (editor.renderer.scroller.setCapture) { + editor.renderer.scroller.setCapture(); + } + editor.setStyle("ace_selecting"); + this.setState("select"); + }; + + this.select = function() { + var anchor, editor = this.editor; + var cursor = editor.renderer.screenToTextCoordinates(this.x, this.y); + if (this.$clickSelection) { + var cmp = this.$clickSelection.comparePoint(cursor); + + if (cmp == -1) { + anchor = this.$clickSelection.end; + } else if (cmp == 1) { + anchor = this.$clickSelection.start; + } else { + var orientedRange = calcRangeOrientation(this.$clickSelection, cursor); + cursor = orientedRange.cursor; + anchor = orientedRange.anchor; + } + editor.selection.setSelectionAnchor(anchor.row, anchor.column); + } + editor.selection.selectToPosition(cursor); + editor.renderer.scrollCursorIntoView(); + }; + + this.extendSelectionBy = function(unitName) { + var anchor, editor = this.editor; + var cursor = editor.renderer.screenToTextCoordinates(this.x, this.y); + var range = editor.selection[unitName](cursor.row, cursor.column); + if (this.$clickSelection) { + var cmpStart = this.$clickSelection.comparePoint(range.start); + var cmpEnd = this.$clickSelection.comparePoint(range.end); + + if (cmpStart == -1 && cmpEnd <= 0) { + anchor = this.$clickSelection.end; + if (range.end.row != cursor.row || range.end.column != cursor.column) + cursor = range.start; + } else if (cmpEnd == 1 && cmpStart >= 0) { + anchor = this.$clickSelection.start; + if (range.start.row != cursor.row || range.start.column != cursor.column) + cursor = range.end; + } else if (cmpStart == -1 && cmpEnd == 1) { + cursor = range.end; + anchor = range.start; + } else { + var orientedRange = calcRangeOrientation(this.$clickSelection, cursor); + cursor = orientedRange.cursor; + anchor = orientedRange.anchor; + } + editor.selection.setSelectionAnchor(anchor.row, anchor.column); + } + editor.selection.selectToPosition(cursor); + editor.renderer.scrollCursorIntoView(); + }; + + this.selectEnd = + this.selectAllEnd = + this.selectByWordsEnd = + this.selectByLinesEnd = function() { + this.$clickSelection = null; + this.editor.unsetStyle("ace_selecting"); + if (this.editor.renderer.scroller.releaseCapture) { + this.editor.renderer.scroller.releaseCapture(); + } + }; + + this.focusWait = function() { + var distance = calcDistance(this.mousedownEvent.x, this.mousedownEvent.y, this.x, this.y); + var time = Date.now(); + + if (distance > DRAG_OFFSET || time - this.mousedownEvent.time > this.$focusTimeout) + this.startSelect(this.mousedownEvent.getDocumentPosition()); + }; + + this.onDoubleClick = function(ev) { + var pos = ev.getDocumentPosition(); + var editor = this.editor; + var session = editor.session; + + var range = session.getBracketRange(pos); + if (range) { + if (range.isEmpty()) { + range.start.column--; + range.end.column++; + } + this.setState("select"); + } else { + range = editor.selection.getWordRange(pos.row, pos.column); + this.setState("selectByWords"); + } + this.$clickSelection = range; + this.select(); + }; + + this.onTripleClick = function(ev) { + var pos = ev.getDocumentPosition(); + var editor = this.editor; + + this.setState("selectByLines"); + var range = editor.getSelectionRange(); + if (range.isMultiLine() && range.contains(pos.row, pos.column)) { + this.$clickSelection = editor.selection.getLineRange(range.start.row); + this.$clickSelection.end = editor.selection.getLineRange(range.end.row).end; + } else { + this.$clickSelection = editor.selection.getLineRange(pos.row); + } + this.select(); + }; + + this.onQuadClick = function(ev) { + var editor = this.editor; + + editor.selectAll(); + this.$clickSelection = editor.getSelectionRange(); + this.setState("selectAll"); + }; + + this.onMouseWheel = function(ev) { + if (ev.getAccelKey()) + return; + if (ev.getShiftKey() && ev.wheelY && !ev.wheelX) { + ev.wheelX = ev.wheelY; + ev.wheelY = 0; + } + + var editor = this.editor; + + if (!this.$lastScroll) + this.$lastScroll = { t: 0, vx: 0, vy: 0, allowed: 0 }; + + var prevScroll = this.$lastScroll; + var t = ev.domEvent.timeStamp; + var dt = t - prevScroll.t; + var vx = dt ? ev.wheelX / dt : prevScroll.vx; + var vy = dt ? ev.wheelY / dt : prevScroll.vy; + if (dt < SCROLL_COOLDOWN_T) { + vx = (vx + prevScroll.vx) / 2; + vy = (vy + prevScroll.vy) / 2; + } + + var direction = Math.abs(vx / vy); + + var canScroll = false; + if (direction >= 1 && editor.renderer.isScrollableBy(ev.wheelX * ev.speed, 0)) + canScroll = true; + if (direction <= 1 && editor.renderer.isScrollableBy(0, ev.wheelY * ev.speed)) + canScroll = true; + + if (canScroll) { + prevScroll.allowed = t; + } else if (t - prevScroll.allowed < SCROLL_COOLDOWN_T) { + var isSlower = Math.abs(vx) <= 1.5 * Math.abs(prevScroll.vx) + && Math.abs(vy) <= 1.5 * Math.abs(prevScroll.vy); + if (isSlower) { + canScroll = true; + prevScroll.allowed = t; + } + else { + prevScroll.allowed = 0; + } + } + + prevScroll.t = t; + prevScroll.vx = vx; + prevScroll.vy = vy; + + if (canScroll) { + editor.renderer.scrollBy(ev.wheelX * ev.speed, ev.wheelY * ev.speed); + return ev.stop(); + } + }; + +}).call(DefaultHandlers.prototype); + +exports.DefaultHandlers = DefaultHandlers; + +function calcDistance(ax, ay, bx, by) { + return Math.sqrt(Math.pow(bx - ax, 2) + Math.pow(by - ay, 2)); +} + +function calcRangeOrientation(range, cursor) { + if (range.start.row == range.end.row) + var cmp = 2 * cursor.column - range.start.column - range.end.column; + else if (range.start.row == range.end.row - 1 && !range.start.column && !range.end.column) + var cmp = cursor.column - 4; + else + var cmp = 2 * cursor.row - range.start.row - range.end.row; + + if (cmp < 0) + return {cursor: range.start, anchor: range.end}; + else + return {cursor: range.end, anchor: range.start}; +} + +}); + +ace.define("ace/tooltip",["require","exports","module","ace/lib/oop","ace/lib/dom"], function(require, exports, module) { +"use strict"; + +var oop = require("./lib/oop"); +var dom = require("./lib/dom"); +function Tooltip (parentNode) { + this.isOpen = false; + this.$element = null; + this.$parentNode = parentNode; +} + +(function() { + this.$init = function() { + this.$element = dom.createElement("div"); + this.$element.className = "ace_tooltip"; + this.$element.style.display = "none"; + this.$parentNode.appendChild(this.$element); + return this.$element; + }; + this.getElement = function() { + return this.$element || this.$init(); + }; + this.setText = function(text) { + this.getElement().textContent = text; + }; + this.setHtml = function(html) { + this.getElement().innerHTML = html; + }; + this.setPosition = function(x, y) { + this.getElement().style.left = x + "px"; + this.getElement().style.top = y + "px"; + }; + this.setClassName = function(className) { + dom.addCssClass(this.getElement(), className); + }; + this.show = function(text, x, y) { + if (text != null) + this.setText(text); + if (x != null && y != null) + this.setPosition(x, y); + if (!this.isOpen) { + this.getElement().style.display = "block"; + this.isOpen = true; + } + }; + + this.hide = function() { + if (this.isOpen) { + this.getElement().style.display = "none"; + this.isOpen = false; + } + }; + this.getHeight = function() { + return this.getElement().offsetHeight; + }; + this.getWidth = function() { + return this.getElement().offsetWidth; + }; + + this.destroy = function() { + this.isOpen = false; + if (this.$element && this.$element.parentNode) { + this.$element.parentNode.removeChild(this.$element); + } + }; + +}).call(Tooltip.prototype); + +exports.Tooltip = Tooltip; +}); + +ace.define("ace/mouse/default_gutter_handler",["require","exports","module","ace/lib/dom","ace/lib/oop","ace/lib/event","ace/tooltip"], function(require, exports, module) { +"use strict"; +var dom = require("../lib/dom"); +var oop = require("../lib/oop"); +var event = require("../lib/event"); +var Tooltip = require("../tooltip").Tooltip; + +function GutterHandler(mouseHandler) { + var editor = mouseHandler.editor; + var gutter = editor.renderer.$gutterLayer; + var tooltip = new GutterTooltip(editor.container); + + mouseHandler.editor.setDefaultHandler("guttermousedown", function(e) { + if (!editor.isFocused() || e.getButton() != 0) + return; + var gutterRegion = gutter.getRegion(e); + + if (gutterRegion == "foldWidgets") + return; + + var row = e.getDocumentPosition().row; + var selection = editor.session.selection; + + if (e.getShiftKey()) + selection.selectTo(row, 0); + else { + if (e.domEvent.detail == 2) { + editor.selectAll(); + return e.preventDefault(); + } + mouseHandler.$clickSelection = editor.selection.getLineRange(row); + } + mouseHandler.setState("selectByLines"); + mouseHandler.captureMouse(e); + return e.preventDefault(); + }); + + + var tooltipTimeout, mouseEvent, tooltipAnnotation; + + function showTooltip() { + var row = mouseEvent.getDocumentPosition().row; + var annotation = gutter.$annotations[row]; + if (!annotation) + return hideTooltip(); + + var maxRow = editor.session.getLength(); + if (row == maxRow) { + var screenRow = editor.renderer.pixelToScreenCoordinates(0, mouseEvent.y).row; + var pos = mouseEvent.$pos; + if (screenRow > editor.session.documentToScreenRow(pos.row, pos.column)) + return hideTooltip(); + } + + if (tooltipAnnotation == annotation) + return; + tooltipAnnotation = annotation.text.join("
"); + + tooltip.setHtml(tooltipAnnotation); + tooltip.show(); + editor._signal("showGutterTooltip", tooltip); + editor.on("mousewheel", hideTooltip); + + if (mouseHandler.$tooltipFollowsMouse) { + moveTooltip(mouseEvent); + } else { + var gutterElement = mouseEvent.domEvent.target; + var rect = gutterElement.getBoundingClientRect(); + var style = tooltip.getElement().style; + style.left = rect.right + "px"; + style.top = rect.bottom + "px"; + } + } + + function hideTooltip() { + if (tooltipTimeout) + tooltipTimeout = clearTimeout(tooltipTimeout); + if (tooltipAnnotation) { + tooltip.hide(); + tooltipAnnotation = null; + editor._signal("hideGutterTooltip", tooltip); + editor.off("mousewheel", hideTooltip); + } + } + + function moveTooltip(e) { + tooltip.setPosition(e.x, e.y); + } + + mouseHandler.editor.setDefaultHandler("guttermousemove", function(e) { + var target = e.domEvent.target || e.domEvent.srcElement; + if (dom.hasCssClass(target, "ace_fold-widget")) + return hideTooltip(); + + if (tooltipAnnotation && mouseHandler.$tooltipFollowsMouse) + moveTooltip(e); + + mouseEvent = e; + if (tooltipTimeout) + return; + tooltipTimeout = setTimeout(function() { + tooltipTimeout = null; + if (mouseEvent && !mouseHandler.isMousePressed) + showTooltip(); + else + hideTooltip(); + }, 50); + }); + + event.addListener(editor.renderer.$gutter, "mouseout", function(e) { + mouseEvent = null; + if (!tooltipAnnotation || tooltipTimeout) + return; + + tooltipTimeout = setTimeout(function() { + tooltipTimeout = null; + hideTooltip(); + }, 50); + }, editor); + + editor.on("changeSession", hideTooltip); +} + +function GutterTooltip(parentNode) { + Tooltip.call(this, parentNode); +} + +oop.inherits(GutterTooltip, Tooltip); + +(function(){ + this.setPosition = function(x, y) { + var windowWidth = window.innerWidth || document.documentElement.clientWidth; + var windowHeight = window.innerHeight || document.documentElement.clientHeight; + var width = this.getWidth(); + var height = this.getHeight(); + x += 15; + y += 15; + if (x + width > windowWidth) { + x -= (x + width) - windowWidth; + } + if (y + height > windowHeight) { + y -= 20 + height; + } + Tooltip.prototype.setPosition.call(this, x, y); + }; + +}).call(GutterTooltip.prototype); + + + +exports.GutterHandler = GutterHandler; + +}); + +ace.define("ace/mouse/mouse_event",["require","exports","module","ace/lib/event","ace/lib/useragent"], function(require, exports, module) { +"use strict"; + +var event = require("../lib/event"); +var useragent = require("../lib/useragent"); +var MouseEvent = exports.MouseEvent = function(domEvent, editor) { + this.domEvent = domEvent; + this.editor = editor; + + this.x = this.clientX = domEvent.clientX; + this.y = this.clientY = domEvent.clientY; + + this.$pos = null; + this.$inSelection = null; + + this.propagationStopped = false; + this.defaultPrevented = false; +}; + +(function() { + + this.stopPropagation = function() { + event.stopPropagation(this.domEvent); + this.propagationStopped = true; + }; + + this.preventDefault = function() { + event.preventDefault(this.domEvent); + this.defaultPrevented = true; + }; + + this.stop = function() { + this.stopPropagation(); + this.preventDefault(); + }; + this.getDocumentPosition = function() { + if (this.$pos) + return this.$pos; + + this.$pos = this.editor.renderer.screenToTextCoordinates(this.clientX, this.clientY); + return this.$pos; + }; + this.inSelection = function() { + if (this.$inSelection !== null) + return this.$inSelection; + + var editor = this.editor; + + + var selectionRange = editor.getSelectionRange(); + if (selectionRange.isEmpty()) + this.$inSelection = false; + else { + var pos = this.getDocumentPosition(); + this.$inSelection = selectionRange.contains(pos.row, pos.column); + } + + return this.$inSelection; + }; + this.getButton = function() { + return event.getButton(this.domEvent); + }; + this.getShiftKey = function() { + return this.domEvent.shiftKey; + }; + + this.getAccelKey = useragent.isMac + ? function() { return this.domEvent.metaKey; } + : function() { return this.domEvent.ctrlKey; }; + +}).call(MouseEvent.prototype); + +}); + +ace.define("ace/mouse/dragdrop_handler",["require","exports","module","ace/lib/dom","ace/lib/event","ace/lib/useragent"], function(require, exports, module) { +"use strict"; + +var dom = require("../lib/dom"); +var event = require("../lib/event"); +var useragent = require("../lib/useragent"); + +var AUTOSCROLL_DELAY = 200; +var SCROLL_CURSOR_DELAY = 200; +var SCROLL_CURSOR_HYSTERESIS = 5; + +function DragdropHandler(mouseHandler) { + + var editor = mouseHandler.editor; + + var dragImage = dom.createElement("div"); + dragImage.style.cssText = "top:-100px;position:absolute;z-index:2147483647;opacity:0.5"; + dragImage.textContent = "\xa0"; + + var exports = ["dragWait", "dragWaitEnd", "startDrag", "dragReadyEnd", "onMouseDrag"]; + + exports.forEach(function(x) { + mouseHandler[x] = this[x]; + }, this); + editor.on("mousedown", this.onMouseDown.bind(mouseHandler)); + + var mouseTarget = editor.container; + var dragSelectionMarker, x, y; + var timerId, range; + var dragCursor, counter = 0; + var dragOperation; + var isInternal; + var autoScrollStartTime; + var cursorMovedTime; + var cursorPointOnCaretMoved; + + this.onDragStart = function(e) { + if (this.cancelDrag || !mouseTarget.draggable) { + var self = this; + setTimeout(function(){ + self.startSelect(); + self.captureMouse(e); + }, 0); + return e.preventDefault(); + } + range = editor.getSelectionRange(); + + var dataTransfer = e.dataTransfer; + dataTransfer.effectAllowed = editor.getReadOnly() ? "copy" : "copyMove"; + editor.container.appendChild(dragImage); + + dataTransfer.setDragImage && dataTransfer.setDragImage(dragImage, 0, 0); + setTimeout(function() { + editor.container.removeChild(dragImage); + }); + dataTransfer.clearData(); + dataTransfer.setData("Text", editor.session.getTextRange()); + + isInternal = true; + this.setState("drag"); + }; + + this.onDragEnd = function(e) { + mouseTarget.draggable = false; + isInternal = false; + this.setState(null); + if (!editor.getReadOnly()) { + var dropEffect = e.dataTransfer.dropEffect; + if (!dragOperation && dropEffect == "move") + editor.session.remove(editor.getSelectionRange()); + editor.$resetCursorStyle(); + } + this.editor.unsetStyle("ace_dragging"); + this.editor.renderer.setCursorStyle(""); + }; + + this.onDragEnter = function(e) { + if (editor.getReadOnly() || !canAccept(e.dataTransfer)) + return; + x = e.clientX; + y = e.clientY; + if (!dragSelectionMarker) + addDragMarker(); + counter++; + e.dataTransfer.dropEffect = dragOperation = getDropEffect(e); + return event.preventDefault(e); + }; + + this.onDragOver = function(e) { + if (editor.getReadOnly() || !canAccept(e.dataTransfer)) + return; + x = e.clientX; + y = e.clientY; + if (!dragSelectionMarker) { + addDragMarker(); + counter++; + } + if (onMouseMoveTimer !== null) + onMouseMoveTimer = null; + + e.dataTransfer.dropEffect = dragOperation = getDropEffect(e); + return event.preventDefault(e); + }; + + this.onDragLeave = function(e) { + counter--; + if (counter <= 0 && dragSelectionMarker) { + clearDragMarker(); + dragOperation = null; + return event.preventDefault(e); + } + }; + + this.onDrop = function(e) { + if (!dragCursor) + return; + var dataTransfer = e.dataTransfer; + if (isInternal) { + switch (dragOperation) { + case "move": + if (range.contains(dragCursor.row, dragCursor.column)) { + range = { + start: dragCursor, + end: dragCursor + }; + } else { + range = editor.moveText(range, dragCursor); + } + break; + case "copy": + range = editor.moveText(range, dragCursor, true); + break; + } + } else { + var dropData = dataTransfer.getData('Text'); + range = { + start: dragCursor, + end: editor.session.insert(dragCursor, dropData) + }; + editor.focus(); + dragOperation = null; + } + clearDragMarker(); + return event.preventDefault(e); + }; + + event.addListener(mouseTarget, "dragstart", this.onDragStart.bind(mouseHandler), editor); + event.addListener(mouseTarget, "dragend", this.onDragEnd.bind(mouseHandler), editor); + event.addListener(mouseTarget, "dragenter", this.onDragEnter.bind(mouseHandler), editor); + event.addListener(mouseTarget, "dragover", this.onDragOver.bind(mouseHandler), editor); + event.addListener(mouseTarget, "dragleave", this.onDragLeave.bind(mouseHandler), editor); + event.addListener(mouseTarget, "drop", this.onDrop.bind(mouseHandler), editor); + + function scrollCursorIntoView(cursor, prevCursor) { + var now = Date.now(); + var vMovement = !prevCursor || cursor.row != prevCursor.row; + var hMovement = !prevCursor || cursor.column != prevCursor.column; + if (!cursorMovedTime || vMovement || hMovement) { + editor.moveCursorToPosition(cursor); + cursorMovedTime = now; + cursorPointOnCaretMoved = {x: x, y: y}; + } else { + var distance = calcDistance(cursorPointOnCaretMoved.x, cursorPointOnCaretMoved.y, x, y); + if (distance > SCROLL_CURSOR_HYSTERESIS) { + cursorMovedTime = null; + } else if (now - cursorMovedTime >= SCROLL_CURSOR_DELAY) { + editor.renderer.scrollCursorIntoView(); + cursorMovedTime = null; + } + } + } + + function autoScroll(cursor, prevCursor) { + var now = Date.now(); + var lineHeight = editor.renderer.layerConfig.lineHeight; + var characterWidth = editor.renderer.layerConfig.characterWidth; + var editorRect = editor.renderer.scroller.getBoundingClientRect(); + var offsets = { + x: { + left: x - editorRect.left, + right: editorRect.right - x + }, + y: { + top: y - editorRect.top, + bottom: editorRect.bottom - y + } + }; + var nearestXOffset = Math.min(offsets.x.left, offsets.x.right); + var nearestYOffset = Math.min(offsets.y.top, offsets.y.bottom); + var scrollCursor = {row: cursor.row, column: cursor.column}; + if (nearestXOffset / characterWidth <= 2) { + scrollCursor.column += (offsets.x.left < offsets.x.right ? -3 : +2); + } + if (nearestYOffset / lineHeight <= 1) { + scrollCursor.row += (offsets.y.top < offsets.y.bottom ? -1 : +1); + } + var vScroll = cursor.row != scrollCursor.row; + var hScroll = cursor.column != scrollCursor.column; + var vMovement = !prevCursor || cursor.row != prevCursor.row; + if (vScroll || (hScroll && !vMovement)) { + if (!autoScrollStartTime) + autoScrollStartTime = now; + else if (now - autoScrollStartTime >= AUTOSCROLL_DELAY) + editor.renderer.scrollCursorIntoView(scrollCursor); + } else { + autoScrollStartTime = null; + } + } + + function onDragInterval() { + var prevCursor = dragCursor; + dragCursor = editor.renderer.screenToTextCoordinates(x, y); + scrollCursorIntoView(dragCursor, prevCursor); + autoScroll(dragCursor, prevCursor); + } + + function addDragMarker() { + range = editor.selection.toOrientedRange(); + dragSelectionMarker = editor.session.addMarker(range, "ace_selection", editor.getSelectionStyle()); + editor.clearSelection(); + if (editor.isFocused()) + editor.renderer.$cursorLayer.setBlinking(false); + clearInterval(timerId); + onDragInterval(); + timerId = setInterval(onDragInterval, 20); + counter = 0; + event.addListener(document, "mousemove", onMouseMove); + } + + function clearDragMarker() { + clearInterval(timerId); + editor.session.removeMarker(dragSelectionMarker); + dragSelectionMarker = null; + editor.selection.fromOrientedRange(range); + if (editor.isFocused() && !isInternal) + editor.$resetCursorStyle(); + range = null; + dragCursor = null; + counter = 0; + autoScrollStartTime = null; + cursorMovedTime = null; + event.removeListener(document, "mousemove", onMouseMove); + } + var onMouseMoveTimer = null; + function onMouseMove() { + if (onMouseMoveTimer == null) { + onMouseMoveTimer = setTimeout(function() { + if (onMouseMoveTimer != null && dragSelectionMarker) + clearDragMarker(); + }, 20); + } + } + + function canAccept(dataTransfer) { + var types = dataTransfer.types; + return !types || Array.prototype.some.call(types, function(type) { + return type == 'text/plain' || type == 'Text'; + }); + } + + function getDropEffect(e) { + var copyAllowed = ['copy', 'copymove', 'all', 'uninitialized']; + var moveAllowed = ['move', 'copymove', 'linkmove', 'all', 'uninitialized']; + + var copyModifierState = useragent.isMac ? e.altKey : e.ctrlKey; + var effectAllowed = "uninitialized"; + try { + effectAllowed = e.dataTransfer.effectAllowed.toLowerCase(); + } catch (e) {} + var dropEffect = "none"; + + if (copyModifierState && copyAllowed.indexOf(effectAllowed) >= 0) + dropEffect = "copy"; + else if (moveAllowed.indexOf(effectAllowed) >= 0) + dropEffect = "move"; + else if (copyAllowed.indexOf(effectAllowed) >= 0) + dropEffect = "copy"; + + return dropEffect; + } +} + +(function() { + + this.dragWait = function() { + var interval = Date.now() - this.mousedownEvent.time; + if (interval > this.editor.getDragDelay()) + this.startDrag(); + }; + + this.dragWaitEnd = function() { + var target = this.editor.container; + target.draggable = false; + this.startSelect(this.mousedownEvent.getDocumentPosition()); + this.selectEnd(); + }; + + this.dragReadyEnd = function(e) { + this.editor.$resetCursorStyle(); + this.editor.unsetStyle("ace_dragging"); + this.editor.renderer.setCursorStyle(""); + this.dragWaitEnd(); + }; + + this.startDrag = function(){ + this.cancelDrag = false; + var editor = this.editor; + var target = editor.container; + target.draggable = true; + editor.renderer.$cursorLayer.setBlinking(false); + editor.setStyle("ace_dragging"); + var cursorStyle = useragent.isWin ? "default" : "move"; + editor.renderer.setCursorStyle(cursorStyle); + this.setState("dragReady"); + }; + + this.onMouseDrag = function(e) { + var target = this.editor.container; + if (useragent.isIE && this.state == "dragReady") { + var distance = calcDistance(this.mousedownEvent.x, this.mousedownEvent.y, this.x, this.y); + if (distance > 3) + target.dragDrop(); + } + if (this.state === "dragWait") { + var distance = calcDistance(this.mousedownEvent.x, this.mousedownEvent.y, this.x, this.y); + if (distance > 0) { + target.draggable = false; + this.startSelect(this.mousedownEvent.getDocumentPosition()); + } + } + }; + + this.onMouseDown = function(e) { + if (!this.$dragEnabled) + return; + this.mousedownEvent = e; + var editor = this.editor; + + var inSelection = e.inSelection(); + var button = e.getButton(); + var clickCount = e.domEvent.detail || 1; + if (clickCount === 1 && button === 0 && inSelection) { + if (e.editor.inMultiSelectMode && (e.getAccelKey() || e.getShiftKey())) + return; + this.mousedownEvent.time = Date.now(); + var eventTarget = e.domEvent.target || e.domEvent.srcElement; + if ("unselectable" in eventTarget) + eventTarget.unselectable = "on"; + if (editor.getDragDelay()) { + if (useragent.isWebKit) { + this.cancelDrag = true; + var mouseTarget = editor.container; + mouseTarget.draggable = true; + } + this.setState("dragWait"); + } else { + this.startDrag(); + } + this.captureMouse(e, this.onMouseDrag.bind(this)); + e.defaultPrevented = true; + } + }; + +}).call(DragdropHandler.prototype); + + +function calcDistance(ax, ay, bx, by) { + return Math.sqrt(Math.pow(bx - ax, 2) + Math.pow(by - ay, 2)); +} + +exports.DragdropHandler = DragdropHandler; + +}); + +ace.define("ace/mouse/touch_handler",["require","exports","module","ace/mouse/mouse_event","ace/lib/event","ace/lib/dom"], function(require, exports, module) { +"use strict"; + +var MouseEvent = require("./mouse_event").MouseEvent; +var event = require("../lib/event"); +var dom = require("../lib/dom"); + +exports.addTouchListeners = function(el, editor) { + var mode = "scroll"; + var startX; + var startY; + var touchStartT; + var lastT; + var longTouchTimer; + var animationTimer; + var animationSteps = 0; + var pos; + var clickCount = 0; + var vX = 0; + var vY = 0; + var pressed; + var contextMenu; + + function createContextMenu() { + var clipboard = window.navigator && window.navigator.clipboard; + var isOpen = false; + var updateMenu = function() { + var selected = editor.getCopyText(); + var hasUndo = editor.session.getUndoManager().hasUndo(); + contextMenu.replaceChild( + dom.buildDom(isOpen ? ["span", + !selected && ["span", { class: "ace_mobile-button", action: "selectall" }, "Select All"], + selected && ["span", { class: "ace_mobile-button", action: "copy" }, "Copy"], + selected && ["span", { class: "ace_mobile-button", action: "cut" }, "Cut"], + clipboard && ["span", { class: "ace_mobile-button", action: "paste" }, "Paste"], + hasUndo && ["span", { class: "ace_mobile-button", action: "undo" }, "Undo"], + ["span", { class: "ace_mobile-button", action: "find" }, "Find"], + ["span", { class: "ace_mobile-button", action: "openCommandPallete" }, "Pallete"] + ] : ["span"]), + contextMenu.firstChild + ); + }; + var handleClick = function(e) { + var action = e.target.getAttribute("action"); + + if (action == "more" || !isOpen) { + isOpen = !isOpen; + return updateMenu(); + } + if (action == "paste") { + clipboard.readText().then(function (text) { + editor.execCommand(action, text); + }); + } + else if (action) { + if (action == "cut" || action == "copy") { + if (clipboard) + clipboard.writeText(editor.getCopyText()); + else + document.execCommand("copy"); + } + editor.execCommand(action); + } + contextMenu.firstChild.style.display = "none"; + isOpen = false; + if (action != "openCommandPallete") + editor.focus(); + }; + contextMenu = dom.buildDom(["div", + { + class: "ace_mobile-menu", + ontouchstart: function(e) { + mode = "menu"; + e.stopPropagation(); + e.preventDefault(); + editor.textInput.focus(); + }, + ontouchend: function(e) { + e.stopPropagation(); + e.preventDefault(); + handleClick(e); + }, + onclick: handleClick + }, + ["span"], + ["span", { class: "ace_mobile-button", action: "more" }, "..."] + ], editor.container); + } + function showContextMenu() { + if (!contextMenu) createContextMenu(); + var cursor = editor.selection.cursor; + var pagePos = editor.renderer.textToScreenCoordinates(cursor.row, cursor.column); + var leftOffset = editor.renderer.textToScreenCoordinates(0, 0).pageX; + var scrollLeft = editor.renderer.scrollLeft; + var rect = editor.container.getBoundingClientRect(); + contextMenu.style.top = pagePos.pageY - rect.top - 3 + "px"; + if (pagePos.pageX - rect.left < rect.width - 70) { + contextMenu.style.left = ""; + contextMenu.style.right = "10px"; + } else { + contextMenu.style.right = ""; + contextMenu.style.left = leftOffset + scrollLeft - rect.left + "px"; + } + contextMenu.style.display = ""; + contextMenu.firstChild.style.display = "none"; + editor.on("input", hideContextMenu); + } + function hideContextMenu(e) { + if (contextMenu) + contextMenu.style.display = "none"; + editor.off("input", hideContextMenu); + } + + function handleLongTap() { + longTouchTimer = null; + clearTimeout(longTouchTimer); + var range = editor.selection.getRange(); + var inSelection = range.contains(pos.row, pos.column); + if (range.isEmpty() || !inSelection) { + editor.selection.moveToPosition(pos); + editor.selection.selectWord(); + } + mode = "wait"; + showContextMenu(); + } + function switchToSelectionMode() { + longTouchTimer = null; + clearTimeout(longTouchTimer); + editor.selection.moveToPosition(pos); + var range = clickCount >= 2 + ? editor.selection.getLineRange(pos.row) + : editor.session.getBracketRange(pos); + if (range && !range.isEmpty()) { + editor.selection.setRange(range); + } else { + editor.selection.selectWord(); + } + mode = "wait"; + } + event.addListener(el, "contextmenu", function(e) { + if (!pressed) return; + var textarea = editor.textInput.getElement(); + textarea.focus(); + }, editor); + event.addListener(el, "touchstart", function (e) { + var touches = e.touches; + if (longTouchTimer || touches.length > 1) { + clearTimeout(longTouchTimer); + longTouchTimer = null; + touchStartT = -1; + mode = "zoom"; + return; + } + + pressed = editor.$mouseHandler.isMousePressed = true; + var h = editor.renderer.layerConfig.lineHeight; + var w = editor.renderer.layerConfig.lineHeight; + var t = e.timeStamp; + lastT = t; + var touchObj = touches[0]; + var x = touchObj.clientX; + var y = touchObj.clientY; + if (Math.abs(startX - x) + Math.abs(startY - y) > h) + touchStartT = -1; + + startX = e.clientX = x; + startY = e.clientY = y; + vX = vY = 0; + + var ev = new MouseEvent(e, editor); + pos = ev.getDocumentPosition(); + + if (t - touchStartT < 500 && touches.length == 1 && !animationSteps) { + clickCount++; + e.preventDefault(); + e.button = 0; + switchToSelectionMode(); + } else { + clickCount = 0; + var cursor = editor.selection.cursor; + var anchor = editor.selection.isEmpty() ? cursor : editor.selection.anchor; + + var cursorPos = editor.renderer.$cursorLayer.getPixelPosition(cursor, true); + var anchorPos = editor.renderer.$cursorLayer.getPixelPosition(anchor, true); + var rect = editor.renderer.scroller.getBoundingClientRect(); + var offsetTop = editor.renderer.layerConfig.offset; + var offsetLeft = editor.renderer.scrollLeft; + var weightedDistance = function(x, y) { + x = x / w; + y = y / h - 0.75; + return x * x + y * y; + }; + + if (e.clientX < rect.left) { + mode = "zoom"; + return; + } + + var diff1 = weightedDistance( + e.clientX - rect.left - cursorPos.left + offsetLeft, + e.clientY - rect.top - cursorPos.top + offsetTop + ); + var diff2 = weightedDistance( + e.clientX - rect.left - anchorPos.left + offsetLeft, + e.clientY - rect.top - anchorPos.top + offsetTop + ); + if (diff1 < 3.5 && diff2 < 3.5) + mode = diff1 > diff2 ? "cursor" : "anchor"; + + if (diff2 < 3.5) + mode = "anchor"; + else if (diff1 < 3.5) + mode = "cursor"; + else + mode = "scroll"; + longTouchTimer = setTimeout(handleLongTap, 450); + } + touchStartT = t; + }, editor); + + event.addListener(el, "touchend", function (e) { + pressed = editor.$mouseHandler.isMousePressed = false; + if (animationTimer) clearInterval(animationTimer); + if (mode == "zoom") { + mode = ""; + animationSteps = 0; + } else if (longTouchTimer) { + editor.selection.moveToPosition(pos); + animationSteps = 0; + showContextMenu(); + } else if (mode == "scroll") { + animate(); + hideContextMenu(); + } else { + showContextMenu(); + } + clearTimeout(longTouchTimer); + longTouchTimer = null; + }, editor); + event.addListener(el, "touchmove", function (e) { + if (longTouchTimer) { + clearTimeout(longTouchTimer); + longTouchTimer = null; + } + var touches = e.touches; + if (touches.length > 1 || mode == "zoom") return; + + var touchObj = touches[0]; + + var wheelX = startX - touchObj.clientX; + var wheelY = startY - touchObj.clientY; + + if (mode == "wait") { + if (wheelX * wheelX + wheelY * wheelY > 4) + mode = "cursor"; + else + return e.preventDefault(); + } + + startX = touchObj.clientX; + startY = touchObj.clientY; + + e.clientX = touchObj.clientX; + e.clientY = touchObj.clientY; + + var t = e.timeStamp; + var dt = t - lastT; + lastT = t; + if (mode == "scroll") { + var mouseEvent = new MouseEvent(e, editor); + mouseEvent.speed = 1; + mouseEvent.wheelX = wheelX; + mouseEvent.wheelY = wheelY; + if (10 * Math.abs(wheelX) < Math.abs(wheelY)) wheelX = 0; + if (10 * Math.abs(wheelY) < Math.abs(wheelX)) wheelY = 0; + if (dt != 0) { + vX = wheelX / dt; + vY = wheelY / dt; + } + editor._emit("mousewheel", mouseEvent); + if (!mouseEvent.propagationStopped) { + vX = vY = 0; + } + } + else { + var ev = new MouseEvent(e, editor); + var pos = ev.getDocumentPosition(); + if (mode == "cursor") + editor.selection.moveCursorToPosition(pos); + else if (mode == "anchor") + editor.selection.setSelectionAnchor(pos.row, pos.column); + editor.renderer.scrollCursorIntoView(pos); + e.preventDefault(); + } + }, editor); + + function animate() { + animationSteps += 60; + animationTimer = setInterval(function() { + if (animationSteps-- <= 0) { + clearInterval(animationTimer); + animationTimer = null; + } + if (Math.abs(vX) < 0.01) vX = 0; + if (Math.abs(vY) < 0.01) vY = 0; + if (animationSteps < 20) vX = 0.9 * vX; + if (animationSteps < 20) vY = 0.9 * vY; + var oldScrollTop = editor.session.getScrollTop(); + editor.renderer.scrollBy(10 * vX, 10 * vY); + if (oldScrollTop == editor.session.getScrollTop()) + animationSteps = 0; + }, 10); + } +}; + +}); + +ace.define("ace/lib/net",["require","exports","module","ace/lib/dom"], function(require, exports, module) { +"use strict"; +var dom = require("./dom"); + +exports.get = function (url, callback) { + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, true); + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + callback(xhr.responseText); + } + }; + xhr.send(null); +}; + +exports.loadScript = function(path, callback) { + var head = dom.getDocumentHead(); + var s = document.createElement('script'); + + s.src = path; + head.appendChild(s); + + s.onload = s.onreadystatechange = function(_, isAbort) { + if (isAbort || !s.readyState || s.readyState == "loaded" || s.readyState == "complete") { + s = s.onload = s.onreadystatechange = null; + if (!isAbort) + callback(); + } + }; +}; +exports.qualifyURL = function(url) { + var a = document.createElement('a'); + a.href = url; + return a.href; +}; + +}); + +ace.define("ace/lib/event_emitter",["require","exports","module"], function(require, exports, module) { +"use strict"; + +var EventEmitter = {}; +var stopPropagation = function() { this.propagationStopped = true; }; +var preventDefault = function() { this.defaultPrevented = true; }; + +EventEmitter._emit = +EventEmitter._dispatchEvent = function(eventName, e) { + this._eventRegistry || (this._eventRegistry = {}); + this._defaultHandlers || (this._defaultHandlers = {}); + + var listeners = this._eventRegistry[eventName] || []; + var defaultHandler = this._defaultHandlers[eventName]; + if (!listeners.length && !defaultHandler) + return; + + if (typeof e != "object" || !e) + e = {}; + + if (!e.type) + e.type = eventName; + if (!e.stopPropagation) + e.stopPropagation = stopPropagation; + if (!e.preventDefault) + e.preventDefault = preventDefault; + + listeners = listeners.slice(); + for (var i=0; i 1) + base = parts[parts.length - 2]; + var path = options[component + "Path"]; + if (path == null) { + path = options.basePath; + } else if (sep == "/") { + component = sep = ""; + } + if (path && path.slice(-1) != "/") + path += "/"; + return path + component + sep + base + this.get("suffix"); +}; + +exports.setModuleUrl = function(name, subst) { + return options.$moduleUrls[name] = subst; +}; + +exports.$loading = {}; +exports.loadModule = function(moduleName, onLoad) { + var module, moduleType; + if (Array.isArray(moduleName)) { + moduleType = moduleName[0]; + moduleName = moduleName[1]; + } + + try { + module = require(moduleName); + } catch (e) {} + if (module && !exports.$loading[moduleName]) + return onLoad && onLoad(module); + + if (!exports.$loading[moduleName]) + exports.$loading[moduleName] = []; + + exports.$loading[moduleName].push(onLoad); + + if (exports.$loading[moduleName].length > 1) + return; + + var afterLoad = function() { + require([moduleName], function(module) { + exports._emit("load.module", {name: moduleName, module: module}); + var listeners = exports.$loading[moduleName]; + exports.$loading[moduleName] = null; + listeners.forEach(function(onLoad) { + onLoad && onLoad(module); + }); + }); + }; + + if (!exports.get("packaged")) + return afterLoad(); + + net.loadScript(exports.moduleUrl(moduleName, moduleType), afterLoad); + reportErrorIfPathIsNotConfigured(); +}; + +var reportErrorIfPathIsNotConfigured = function() { + if ( + !options.basePath && !options.workerPath + && !options.modePath && !options.themePath + && !Object.keys(options.$moduleUrls).length + ) { + console.error( + "Unable to infer path to ace from script src,", + "use ace.config.set('basePath', 'path') to enable dynamic loading of modes and themes", + "or with webpack use ace/webpack-resolver" + ); + reportErrorIfPathIsNotConfigured = function() {}; + } +}; +init(true);function init(packaged) { + + if (!global || !global.document) + return; + + options.packaged = packaged || require.packaged || module.packaged || (global.define && define.packaged); + + var scriptOptions = {}; + var scriptUrl = ""; + var currentScript = (document.currentScript || document._currentScript ); // native or polyfill + var currentDocument = currentScript && currentScript.ownerDocument || document; + + var scripts = currentDocument.getElementsByTagName("script"); + for (var i=0; i 0){ + if (action == 0x10){ + for(i = condPos; i < ix; i++){ + levels[i] = 1; + } + condPos = -1; + } else { + condPos = -1; + } + } + cond = impTab[newState][6]; + if (cond){ + if(condPos == -1){ + condPos = ix; + } + }else{ + if (condPos > -1){ + for(i = condPos; i < ix; i++){ + levels[i] = newLevel; + } + condPos = -1; + } + } + if (charTypes[ix] == B){ + levels[ix] = 0; + } + hiLevel |= newLevel; + } + if (hasUBAT_S){ + for(i = 0; i < len; i++){ + if(charTypes[i] == S){ + levels[i] = dir; + for(var j = i - 1; j >= 0; j--){ + if(charTypes[j] == WS){ + levels[j] = dir; + }else{ + break; + } + } + } + } + } +} + +function _invertLevel(lev, levels, _array) { + if (hiLevel < lev){ + return; + } + if (lev == 1 && dir == RTL && !hasUBAT_B){ + _array.reverse(); + return; + } + var len = _array.length, start = 0, end, lo, hi, tmp; + while(start < len){ + if (levels[start] >= lev){ + end = start + 1; + while(end < len && levels[end] >= lev){ + end++; + } + for(lo = start, hi = end - 1 ; lo < hi; lo++, hi--){ + tmp = _array[lo]; + _array[lo] = _array[hi]; + _array[hi] = tmp; + } + start = end; + } + start++; + } +} + +function _getCharClass(chars, types, classes, ix) { + var cType = types[ix], wType, nType, len, i; + switch(cType){ + case L: + case R: + lastArabic = false; + case ON: + case AN: + return cType; + case EN: + return lastArabic ? AN : EN; + case AL: + lastArabic = true; + hasUBAT_AL = true; + return R; + case WS: + return ON; + case CS: + if (ix < 1 || (ix + 1) >= types.length || + ((wType = classes[ix - 1]) != EN && wType != AN) || + ((nType = types[ix + 1]) != EN && nType != AN)){ + return ON; + } + if (lastArabic){nType = AN;} + return nType == wType ? nType : ON; + case ES: + wType = ix > 0 ? classes[ix - 1] : B; + if (wType == EN && (ix + 1) < types.length && types[ix + 1] == EN){ + return EN; + } + return ON; + case ET: + if (ix > 0 && classes[ix - 1] == EN){ + return EN; + } + if (lastArabic){ + return ON; + } + i = ix + 1; + len = types.length; + while (i < len && types[i] == ET){ + i++; + } + if (i < len && types[i] == EN){ + return EN; + } + return ON; + case NSM: + len = types.length; + i = ix + 1; + while (i < len && types[i] == NSM){ + i++; + } + if (i < len){ + var c = chars[ix], rtlCandidate = (c >= 0x0591 && c <= 0x08FF) || c == 0xFB1E; + + wType = types[i]; + if (rtlCandidate && (wType == R || wType == AL)){ + return R; + } + } + + if (ix < 1 || (wType = types[ix - 1]) == B){ + return ON; + } + return classes[ix - 1]; + case B: + lastArabic = false; + hasUBAT_B = true; + return dir; + case S: + hasUBAT_S = true; + return ON; + case LRE: + case RLE: + case LRO: + case RLO: + case PDF: + lastArabic = false; + case BN: + return ON; + } +} + +function _getCharacterType( ch ) { + var uc = ch.charCodeAt(0), hi = uc >> 8; + + if (hi == 0) { + return ((uc > 0x00BF) ? L : UnicodeTBL00[uc]); + } else if (hi == 5) { + return (/[\u0591-\u05f4]/.test(ch) ? R : L); + } else if (hi == 6) { + if (/[\u0610-\u061a\u064b-\u065f\u06d6-\u06e4\u06e7-\u06ed]/.test(ch)) + return NSM; + else if (/[\u0660-\u0669\u066b-\u066c]/.test(ch)) + return AN; + else if (uc == 0x066A) + return ET; + else if (/[\u06f0-\u06f9]/.test(ch)) + return EN; + else + return AL; + } else if (hi == 0x20 && uc <= 0x205F) { + return UnicodeTBL20[uc & 0xFF]; + } else if (hi == 0xFE) { + return (uc >= 0xFE70 ? AL : ON); + } + return ON; +} + +function _isArabicDiacritics( ch ) { + return (ch >= '\u064b' && ch <= '\u0655'); +} +exports.L = L; +exports.R = R; +exports.EN = EN; +exports.ON_R = 3; +exports.AN = 4; +exports.R_H = 5; +exports.B = 6; +exports.RLE = 7; + +exports.DOT = "\xB7"; +exports.doBidiReorder = function(text, textCharTypes, isRtl) { + if (text.length < 2) + return {}; + + var chars = text.split(""), logicalFromVisual = new Array(chars.length), + bidiLevels = new Array(chars.length), levels = []; + + dir = isRtl ? RTL : LTR; + + _computeLevels(chars, levels, chars.length, textCharTypes); + + for (var i = 0; i < logicalFromVisual.length; logicalFromVisual[i] = i, i++); + + _invertLevel(2, levels, logicalFromVisual); + _invertLevel(1, levels, logicalFromVisual); + + for (var i = 0; i < logicalFromVisual.length - 1; i++) { //fix levels to reflect character width + if (textCharTypes[i] === AN) { + levels[i] = exports.AN; + } else if (levels[i] === R && ((textCharTypes[i] > AL && textCharTypes[i] < LRE) + || textCharTypes[i] === ON || textCharTypes[i] === BN)) { + levels[i] = exports.ON_R; + } else if ((i > 0 && chars[i - 1] === '\u0644') && /\u0622|\u0623|\u0625|\u0627/.test(chars[i])) { + levels[i - 1] = levels[i] = exports.R_H; + i++; + } + } + if (chars[chars.length - 1] === exports.DOT) + levels[chars.length - 1] = exports.B; + + if (chars[0] === '\u202B') + levels[0] = exports.RLE; + + for (var i = 0; i < logicalFromVisual.length; i++) { + bidiLevels[i] = levels[logicalFromVisual[i]]; + } + + return {'logicalFromVisual': logicalFromVisual, 'bidiLevels': bidiLevels}; +}; +exports.hasBidiCharacters = function(text, textCharTypes){ + var ret = false; + for (var i = 0; i < text.length; i++){ + textCharTypes[i] = _getCharacterType(text.charAt(i)); + if (!ret && (textCharTypes[i] == R || textCharTypes[i] == AL || textCharTypes[i] == AN)) + ret = true; + } + return ret; +}; +exports.getVisualFromLogicalIdx = function(logIdx, rowMap) { + for (var i = 0; i < rowMap.logicalFromVisual.length; i++) { + if (rowMap.logicalFromVisual[i] == logIdx) + return i; + } + return 0; +}; + +}); + +ace.define("ace/bidihandler",["require","exports","module","ace/lib/bidiutil","ace/lib/lang"], function(require, exports, module) { +"use strict"; + +var bidiUtil = require("./lib/bidiutil"); +var lang = require("./lib/lang"); +var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac\u202B]/; +var BidiHandler = function(session) { + this.session = session; + this.bidiMap = {}; + this.currentRow = null; + this.bidiUtil = bidiUtil; + this.charWidths = []; + this.EOL = "\xAC"; + this.showInvisibles = true; + this.isRtlDir = false; + this.$isRtl = false; + this.line = ""; + this.wrapIndent = 0; + this.EOF = "\xB6"; + this.RLE = "\u202B"; + this.contentWidth = 0; + this.fontMetrics = null; + this.rtlLineOffset = 0; + this.wrapOffset = 0; + this.isMoveLeftOperation = false; + this.seenBidi = bidiRE.test(session.getValue()); +}; + +(function() { + this.isBidiRow = function(screenRow, docRow, splitIndex) { + if (!this.seenBidi) + return false; + if (screenRow !== this.currentRow) { + this.currentRow = screenRow; + this.updateRowLine(docRow, splitIndex); + this.updateBidiMap(); + } + return this.bidiMap.bidiLevels; + }; + + this.onChange = function(delta) { + if (!this.seenBidi) { + if (delta.action == "insert" && bidiRE.test(delta.lines.join("\n"))) { + this.seenBidi = true; + this.currentRow = null; + } + } + else { + this.currentRow = null; + } + }; + + this.getDocumentRow = function() { + var docRow = 0; + var rowCache = this.session.$screenRowCache; + if (rowCache.length) { + var index = this.session.$getRowCacheIndex(rowCache, this.currentRow); + if (index >= 0) + docRow = this.session.$docRowCache[index]; + } + + return docRow; + }; + + this.getSplitIndex = function() { + var splitIndex = 0; + var rowCache = this.session.$screenRowCache; + if (rowCache.length) { + var currentIndex, prevIndex = this.session.$getRowCacheIndex(rowCache, this.currentRow); + while (this.currentRow - splitIndex > 0) { + currentIndex = this.session.$getRowCacheIndex(rowCache, this.currentRow - splitIndex - 1); + if (currentIndex !== prevIndex) + break; + + prevIndex = currentIndex; + splitIndex++; + } + } else { + splitIndex = this.currentRow; + } + + return splitIndex; + }; + + this.updateRowLine = function(docRow, splitIndex) { + if (docRow === undefined) + docRow = this.getDocumentRow(); + + var isLastRow = (docRow === this.session.getLength() - 1), + endOfLine = isLastRow ? this.EOF : this.EOL; + + this.wrapIndent = 0; + this.line = this.session.getLine(docRow); + this.isRtlDir = this.$isRtl || this.line.charAt(0) === this.RLE; + if (this.session.$useWrapMode) { + var splits = this.session.$wrapData[docRow]; + if (splits) { + if (splitIndex === undefined) + splitIndex = this.getSplitIndex(); + + if(splitIndex > 0 && splits.length) { + this.wrapIndent = splits.indent; + this.wrapOffset = this.wrapIndent * this.charWidths[bidiUtil.L]; + this.line = (splitIndex < splits.length) ? + this.line.substring(splits[splitIndex - 1], splits[splitIndex]) : + this.line.substring(splits[splits.length - 1]); + } else { + this.line = this.line.substring(0, splits[splitIndex]); + } + } + if (splitIndex == splits.length) + this.line += (this.showInvisibles) ? endOfLine : bidiUtil.DOT; + } else { + this.line += this.showInvisibles ? endOfLine : bidiUtil.DOT; + } + var session = this.session, shift = 0, size; + this.line = this.line.replace(/\t|[\u1100-\u2029, \u202F-\uFFE6]/g, function(ch, i){ + if (ch === '\t' || session.isFullWidth(ch.charCodeAt(0))) { + size = (ch === '\t') ? session.getScreenTabSize(i + shift) : 2; + shift += size - 1; + return lang.stringRepeat(bidiUtil.DOT, size); + } + return ch; + }); + + if (this.isRtlDir) { + this.fontMetrics.$main.textContent = (this.line.charAt(this.line.length - 1) == bidiUtil.DOT) ? this.line.substr(0, this.line.length - 1) : this.line; + this.rtlLineOffset = this.contentWidth - this.fontMetrics.$main.getBoundingClientRect().width; + } + }; + + this.updateBidiMap = function() { + var textCharTypes = []; + if (bidiUtil.hasBidiCharacters(this.line, textCharTypes) || this.isRtlDir) { + this.bidiMap = bidiUtil.doBidiReorder(this.line, textCharTypes, this.isRtlDir); + } else { + this.bidiMap = {}; + } + }; + this.markAsDirty = function() { + this.currentRow = null; + }; + this.updateCharacterWidths = function(fontMetrics) { + if (this.characterWidth === fontMetrics.$characterSize.width) + return; + + this.fontMetrics = fontMetrics; + var characterWidth = this.characterWidth = fontMetrics.$characterSize.width; + var bidiCharWidth = fontMetrics.$measureCharWidth("\u05d4"); + + this.charWidths[bidiUtil.L] = this.charWidths[bidiUtil.EN] = this.charWidths[bidiUtil.ON_R] = characterWidth; + this.charWidths[bidiUtil.R] = this.charWidths[bidiUtil.AN] = bidiCharWidth; + this.charWidths[bidiUtil.R_H] = bidiCharWidth * 0.45; + this.charWidths[bidiUtil.B] = this.charWidths[bidiUtil.RLE] = 0; + + this.currentRow = null; + }; + + this.setShowInvisibles = function(showInvisibles) { + this.showInvisibles = showInvisibles; + this.currentRow = null; + }; + + this.setEolChar = function(eolChar) { + this.EOL = eolChar; + }; + + this.setContentWidth = function(width) { + this.contentWidth = width; + }; + + this.isRtlLine = function(row) { + if (this.$isRtl) return true; + if (row != undefined) + return (this.session.getLine(row).charAt(0) == this.RLE); + else + return this.isRtlDir; + }; + + this.setRtlDirection = function(editor, isRtlDir) { + var cursor = editor.getCursorPosition(); + for (var row = editor.selection.getSelectionAnchor().row; row <= cursor.row; row++) { + if (!isRtlDir && editor.session.getLine(row).charAt(0) === editor.session.$bidiHandler.RLE) + editor.session.doc.removeInLine(row, 0, 1); + else if (isRtlDir && editor.session.getLine(row).charAt(0) !== editor.session.$bidiHandler.RLE) + editor.session.doc.insert({column: 0, row: row}, editor.session.$bidiHandler.RLE); + } + }; + this.getPosLeft = function(col) { + col -= this.wrapIndent; + var leftBoundary = (this.line.charAt(0) === this.RLE) ? 1 : 0; + var logicalIdx = (col > leftBoundary) ? (this.session.getOverwrite() ? col : col - 1) : leftBoundary; + var visualIdx = bidiUtil.getVisualFromLogicalIdx(logicalIdx, this.bidiMap), + levels = this.bidiMap.bidiLevels, left = 0; + + if (!this.session.getOverwrite() && col <= leftBoundary && levels[visualIdx] % 2 !== 0) + visualIdx++; + + for (var i = 0; i < visualIdx; i++) { + left += this.charWidths[levels[i]]; + } + + if (!this.session.getOverwrite() && (col > leftBoundary) && (levels[visualIdx] % 2 === 0)) + left += this.charWidths[levels[visualIdx]]; + + if (this.wrapIndent) + left += this.isRtlDir ? (-1 * this.wrapOffset) : this.wrapOffset; + + if (this.isRtlDir) + left += this.rtlLineOffset; + + return left; + }; + this.getSelections = function(startCol, endCol) { + var map = this.bidiMap, levels = map.bidiLevels, level, selections = [], offset = 0, + selColMin = Math.min(startCol, endCol) - this.wrapIndent, selColMax = Math.max(startCol, endCol) - this.wrapIndent, + isSelected = false, isSelectedPrev = false, selectionStart = 0; + + if (this.wrapIndent) + offset += this.isRtlDir ? (-1 * this.wrapOffset) : this.wrapOffset; + + for (var logIdx, visIdx = 0; visIdx < levels.length; visIdx++) { + logIdx = map.logicalFromVisual[visIdx]; + level = levels[visIdx]; + isSelected = (logIdx >= selColMin) && (logIdx < selColMax); + if (isSelected && !isSelectedPrev) { + selectionStart = offset; + } else if (!isSelected && isSelectedPrev) { + selections.push({left: selectionStart, width: offset - selectionStart}); + } + offset += this.charWidths[level]; + isSelectedPrev = isSelected; + } + + if (isSelected && (visIdx === levels.length)) { + selections.push({left: selectionStart, width: offset - selectionStart}); + } + + if(this.isRtlDir) { + for (var i = 0; i < selections.length; i++) { + selections[i].left += this.rtlLineOffset; + } + } + return selections; + }; + this.offsetToCol = function(posX) { + if(this.isRtlDir) + posX -= this.rtlLineOffset; + + var logicalIdx = 0, posX = Math.max(posX, 0), + offset = 0, visualIdx = 0, levels = this.bidiMap.bidiLevels, + charWidth = this.charWidths[levels[visualIdx]]; + + if (this.wrapIndent) + posX -= this.isRtlDir ? (-1 * this.wrapOffset) : this.wrapOffset; + + while(posX > offset + charWidth/2) { + offset += charWidth; + if(visualIdx === levels.length - 1) { + charWidth = 0; + break; + } + charWidth = this.charWidths[levels[++visualIdx]]; + } + + if (visualIdx > 0 && (levels[visualIdx - 1] % 2 !== 0) && (levels[visualIdx] % 2 === 0)){ + if(posX < offset) + visualIdx--; + logicalIdx = this.bidiMap.logicalFromVisual[visualIdx]; + + } else if (visualIdx > 0 && (levels[visualIdx - 1] % 2 === 0) && (levels[visualIdx] % 2 !== 0)){ + logicalIdx = 1 + ((posX > offset) ? this.bidiMap.logicalFromVisual[visualIdx] + : this.bidiMap.logicalFromVisual[visualIdx - 1]); + + } else if ((this.isRtlDir && visualIdx === levels.length - 1 && charWidth === 0 && (levels[visualIdx - 1] % 2 === 0)) + || (!this.isRtlDir && visualIdx === 0 && (levels[visualIdx] % 2 !== 0))){ + logicalIdx = 1 + this.bidiMap.logicalFromVisual[visualIdx]; + } else { + if (visualIdx > 0 && (levels[visualIdx - 1] % 2 !== 0) && charWidth !== 0) + visualIdx--; + logicalIdx = this.bidiMap.logicalFromVisual[visualIdx]; + } + + if (logicalIdx === 0 && this.isRtlDir) + logicalIdx++; + + return (logicalIdx + this.wrapIndent); + }; + +}).call(BidiHandler.prototype); + +exports.BidiHandler = BidiHandler; +}); + +ace.define("ace/selection",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/lib/event_emitter","ace/range"], function(require, exports, module) { +"use strict"; + +var oop = require("./lib/oop"); +var lang = require("./lib/lang"); +var EventEmitter = require("./lib/event_emitter").EventEmitter; +var Range = require("./range").Range; +var Selection = function(session) { + this.session = session; + this.doc = session.getDocument(); + + this.clearSelection(); + this.cursor = this.lead = this.doc.createAnchor(0, 0); + this.anchor = this.doc.createAnchor(0, 0); + this.$silent = false; + + var self = this; + this.cursor.on("change", function(e) { + self.$cursorChanged = true; + if (!self.$silent) + self._emit("changeCursor"); + if (!self.$isEmpty && !self.$silent) + self._emit("changeSelection"); + if (!self.$keepDesiredColumnOnChange && e.old.column != e.value.column) + self.$desiredColumn = null; + }); + + this.anchor.on("change", function() { + self.$anchorChanged = true; + if (!self.$isEmpty && !self.$silent) + self._emit("changeSelection"); + }); +}; + +(function() { + + oop.implement(this, EventEmitter); + this.isEmpty = function() { + return this.$isEmpty || ( + this.anchor.row == this.lead.row && + this.anchor.column == this.lead.column + ); + }; + this.isMultiLine = function() { + return !this.$isEmpty && this.anchor.row != this.cursor.row; + }; + this.getCursor = function() { + return this.lead.getPosition(); + }; + this.setSelectionAnchor = function(row, column) { + this.$isEmpty = false; + this.anchor.setPosition(row, column); + }; + this.getAnchor = + this.getSelectionAnchor = function() { + if (this.$isEmpty) + return this.getSelectionLead(); + + return this.anchor.getPosition(); + }; + this.getSelectionLead = function() { + return this.lead.getPosition(); + }; + this.isBackwards = function() { + var anchor = this.anchor; + var lead = this.lead; + return (anchor.row > lead.row || (anchor.row == lead.row && anchor.column > lead.column)); + }; + this.getRange = function() { + var anchor = this.anchor; + var lead = this.lead; + + if (this.$isEmpty) + return Range.fromPoints(lead, lead); + + return this.isBackwards() + ? Range.fromPoints(lead, anchor) + : Range.fromPoints(anchor, lead); + }; + this.clearSelection = function() { + if (!this.$isEmpty) { + this.$isEmpty = true; + this._emit("changeSelection"); + } + }; + this.selectAll = function() { + this.$setSelection(0, 0, Number.MAX_VALUE, Number.MAX_VALUE); + }; + this.setRange = + this.setSelectionRange = function(range, reverse) { + var start = reverse ? range.end : range.start; + var end = reverse ? range.start : range.end; + this.$setSelection(start.row, start.column, end.row, end.column); + }; + + this.$setSelection = function(anchorRow, anchorColumn, cursorRow, cursorColumn) { + if (this.$silent) + return; + var wasEmpty = this.$isEmpty; + var wasMultiselect = this.inMultiSelectMode; + this.$silent = true; + this.$cursorChanged = this.$anchorChanged = false; + this.anchor.setPosition(anchorRow, anchorColumn); + this.cursor.setPosition(cursorRow, cursorColumn); + this.$isEmpty = !Range.comparePoints(this.anchor, this.cursor); + this.$silent = false; + if (this.$cursorChanged) + this._emit("changeCursor"); + if (this.$cursorChanged || this.$anchorChanged || wasEmpty != this.$isEmpty || wasMultiselect) + this._emit("changeSelection"); + }; + + this.$moveSelection = function(mover) { + var lead = this.lead; + if (this.$isEmpty) + this.setSelectionAnchor(lead.row, lead.column); + + mover.call(this); + }; + this.selectTo = function(row, column) { + this.$moveSelection(function() { + this.moveCursorTo(row, column); + }); + }; + this.selectToPosition = function(pos) { + this.$moveSelection(function() { + this.moveCursorToPosition(pos); + }); + }; + this.moveTo = function(row, column) { + this.clearSelection(); + this.moveCursorTo(row, column); + }; + this.moveToPosition = function(pos) { + this.clearSelection(); + this.moveCursorToPosition(pos); + }; + this.selectUp = function() { + this.$moveSelection(this.moveCursorUp); + }; + this.selectDown = function() { + this.$moveSelection(this.moveCursorDown); + }; + this.selectRight = function() { + this.$moveSelection(this.moveCursorRight); + }; + this.selectLeft = function() { + this.$moveSelection(this.moveCursorLeft); + }; + this.selectLineStart = function() { + this.$moveSelection(this.moveCursorLineStart); + }; + this.selectLineEnd = function() { + this.$moveSelection(this.moveCursorLineEnd); + }; + this.selectFileEnd = function() { + this.$moveSelection(this.moveCursorFileEnd); + }; + this.selectFileStart = function() { + this.$moveSelection(this.moveCursorFileStart); + }; + this.selectWordRight = function() { + this.$moveSelection(this.moveCursorWordRight); + }; + this.selectWordLeft = function() { + this.$moveSelection(this.moveCursorWordLeft); + }; + this.getWordRange = function(row, column) { + if (typeof column == "undefined") { + var cursor = row || this.lead; + row = cursor.row; + column = cursor.column; + } + return this.session.getWordRange(row, column); + }; + this.selectWord = function() { + this.setSelectionRange(this.getWordRange()); + }; + this.selectAWord = function() { + var cursor = this.getCursor(); + var range = this.session.getAWordRange(cursor.row, cursor.column); + this.setSelectionRange(range); + }; + + this.getLineRange = function(row, excludeLastChar) { + var rowStart = typeof row == "number" ? row : this.lead.row; + var rowEnd; + + var foldLine = this.session.getFoldLine(rowStart); + if (foldLine) { + rowStart = foldLine.start.row; + rowEnd = foldLine.end.row; + } else { + rowEnd = rowStart; + } + if (excludeLastChar === true) + return new Range(rowStart, 0, rowEnd, this.session.getLine(rowEnd).length); + else + return new Range(rowStart, 0, rowEnd + 1, 0); + }; + this.selectLine = function() { + this.setSelectionRange(this.getLineRange()); + }; + this.moveCursorUp = function() { + this.moveCursorBy(-1, 0); + }; + this.moveCursorDown = function() { + this.moveCursorBy(1, 0); + }; + this.wouldMoveIntoSoftTab = function(cursor, tabSize, direction) { + var start = cursor.column; + var end = cursor.column + tabSize; + + if (direction < 0) { + start = cursor.column - tabSize; + end = cursor.column; + } + return this.session.isTabStop(cursor) && this.doc.getLine(cursor.row).slice(start, end).split(" ").length-1 == tabSize; + }; + this.moveCursorLeft = function() { + var cursor = this.lead.getPosition(), + fold; + + if (fold = this.session.getFoldAt(cursor.row, cursor.column, -1)) { + this.moveCursorTo(fold.start.row, fold.start.column); + } else if (cursor.column === 0) { + if (cursor.row > 0) { + this.moveCursorTo(cursor.row - 1, this.doc.getLine(cursor.row - 1).length); + } + } + else { + var tabSize = this.session.getTabSize(); + if (this.wouldMoveIntoSoftTab(cursor, tabSize, -1) && !this.session.getNavigateWithinSoftTabs()) { + this.moveCursorBy(0, -tabSize); + } else { + this.moveCursorBy(0, -1); + } + } + }; + this.moveCursorRight = function() { + var cursor = this.lead.getPosition(), + fold; + if (fold = this.session.getFoldAt(cursor.row, cursor.column, 1)) { + this.moveCursorTo(fold.end.row, fold.end.column); + } + else if (this.lead.column == this.doc.getLine(this.lead.row).length) { + if (this.lead.row < this.doc.getLength() - 1) { + this.moveCursorTo(this.lead.row + 1, 0); + } + } + else { + var tabSize = this.session.getTabSize(); + var cursor = this.lead; + if (this.wouldMoveIntoSoftTab(cursor, tabSize, 1) && !this.session.getNavigateWithinSoftTabs()) { + this.moveCursorBy(0, tabSize); + } else { + this.moveCursorBy(0, 1); + } + } + }; + this.moveCursorLineStart = function() { + var row = this.lead.row; + var column = this.lead.column; + var screenRow = this.session.documentToScreenRow(row, column); + var firstColumnPosition = this.session.screenToDocumentPosition(screenRow, 0); + var beforeCursor = this.session.getDisplayLine( + row, null, firstColumnPosition.row, + firstColumnPosition.column + ); + + var leadingSpace = beforeCursor.match(/^\s*/); + if (leadingSpace[0].length != column && !this.session.$useEmacsStyleLineStart) + firstColumnPosition.column += leadingSpace[0].length; + this.moveCursorToPosition(firstColumnPosition); + }; + this.moveCursorLineEnd = function() { + var lead = this.lead; + var lineEnd = this.session.getDocumentLastRowColumnPosition(lead.row, lead.column); + if (this.lead.column == lineEnd.column) { + var line = this.session.getLine(lineEnd.row); + if (lineEnd.column == line.length) { + var textEnd = line.search(/\s+$/); + if (textEnd > 0) + lineEnd.column = textEnd; + } + } + + this.moveCursorTo(lineEnd.row, lineEnd.column); + }; + this.moveCursorFileEnd = function() { + var row = this.doc.getLength() - 1; + var column = this.doc.getLine(row).length; + this.moveCursorTo(row, column); + }; + this.moveCursorFileStart = function() { + this.moveCursorTo(0, 0); + }; + this.moveCursorLongWordRight = function() { + var row = this.lead.row; + var column = this.lead.column; + var line = this.doc.getLine(row); + var rightOfCursor = line.substring(column); + + this.session.nonTokenRe.lastIndex = 0; + this.session.tokenRe.lastIndex = 0; + var fold = this.session.getFoldAt(row, column, 1); + if (fold) { + this.moveCursorTo(fold.end.row, fold.end.column); + return; + } + if (this.session.nonTokenRe.exec(rightOfCursor)) { + column += this.session.nonTokenRe.lastIndex; + this.session.nonTokenRe.lastIndex = 0; + rightOfCursor = line.substring(column); + } + if (column >= line.length) { + this.moveCursorTo(row, line.length); + this.moveCursorRight(); + if (row < this.doc.getLength() - 1) + this.moveCursorWordRight(); + return; + } + if (this.session.tokenRe.exec(rightOfCursor)) { + column += this.session.tokenRe.lastIndex; + this.session.tokenRe.lastIndex = 0; + } + + this.moveCursorTo(row, column); + }; + this.moveCursorLongWordLeft = function() { + var row = this.lead.row; + var column = this.lead.column; + var fold; + if (fold = this.session.getFoldAt(row, column, -1)) { + this.moveCursorTo(fold.start.row, fold.start.column); + return; + } + + var str = this.session.getFoldStringAt(row, column, -1); + if (str == null) { + str = this.doc.getLine(row).substring(0, column); + } + + var leftOfCursor = lang.stringReverse(str); + this.session.nonTokenRe.lastIndex = 0; + this.session.tokenRe.lastIndex = 0; + if (this.session.nonTokenRe.exec(leftOfCursor)) { + column -= this.session.nonTokenRe.lastIndex; + leftOfCursor = leftOfCursor.slice(this.session.nonTokenRe.lastIndex); + this.session.nonTokenRe.lastIndex = 0; + } + if (column <= 0) { + this.moveCursorTo(row, 0); + this.moveCursorLeft(); + if (row > 0) + this.moveCursorWordLeft(); + return; + } + if (this.session.tokenRe.exec(leftOfCursor)) { + column -= this.session.tokenRe.lastIndex; + this.session.tokenRe.lastIndex = 0; + } + + this.moveCursorTo(row, column); + }; + + this.$shortWordEndIndex = function(rightOfCursor) { + var index = 0, ch; + var whitespaceRe = /\s/; + var tokenRe = this.session.tokenRe; + + tokenRe.lastIndex = 0; + if (this.session.tokenRe.exec(rightOfCursor)) { + index = this.session.tokenRe.lastIndex; + } else { + while ((ch = rightOfCursor[index]) && whitespaceRe.test(ch)) + index ++; + + if (index < 1) { + tokenRe.lastIndex = 0; + while ((ch = rightOfCursor[index]) && !tokenRe.test(ch)) { + tokenRe.lastIndex = 0; + index ++; + if (whitespaceRe.test(ch)) { + if (index > 2) { + index--; + break; + } else { + while ((ch = rightOfCursor[index]) && whitespaceRe.test(ch)) + index ++; + if (index > 2) + break; + } + } + } + } + } + tokenRe.lastIndex = 0; + + return index; + }; + + this.moveCursorShortWordRight = function() { + var row = this.lead.row; + var column = this.lead.column; + var line = this.doc.getLine(row); + var rightOfCursor = line.substring(column); + + var fold = this.session.getFoldAt(row, column, 1); + if (fold) + return this.moveCursorTo(fold.end.row, fold.end.column); + + if (column == line.length) { + var l = this.doc.getLength(); + do { + row++; + rightOfCursor = this.doc.getLine(row); + } while (row < l && /^\s*$/.test(rightOfCursor)); + + if (!/^\s+/.test(rightOfCursor)) + rightOfCursor = ""; + column = 0; + } + + var index = this.$shortWordEndIndex(rightOfCursor); + + this.moveCursorTo(row, column + index); + }; + + this.moveCursorShortWordLeft = function() { + var row = this.lead.row; + var column = this.lead.column; + + var fold; + if (fold = this.session.getFoldAt(row, column, -1)) + return this.moveCursorTo(fold.start.row, fold.start.column); + + var line = this.session.getLine(row).substring(0, column); + if (column === 0) { + do { + row--; + line = this.doc.getLine(row); + } while (row > 0 && /^\s*$/.test(line)); + + column = line.length; + if (!/\s+$/.test(line)) + line = ""; + } + + var leftOfCursor = lang.stringReverse(line); + var index = this.$shortWordEndIndex(leftOfCursor); + + return this.moveCursorTo(row, column - index); + }; + + this.moveCursorWordRight = function() { + if (this.session.$selectLongWords) + this.moveCursorLongWordRight(); + else + this.moveCursorShortWordRight(); + }; + + this.moveCursorWordLeft = function() { + if (this.session.$selectLongWords) + this.moveCursorLongWordLeft(); + else + this.moveCursorShortWordLeft(); + }; + this.moveCursorBy = function(rows, chars) { + var screenPos = this.session.documentToScreenPosition( + this.lead.row, + this.lead.column + ); + + var offsetX; + + if (chars === 0) { + if (rows !== 0) { + if (this.session.$bidiHandler.isBidiRow(screenPos.row, this.lead.row)) { + offsetX = this.session.$bidiHandler.getPosLeft(screenPos.column); + screenPos.column = Math.round(offsetX / this.session.$bidiHandler.charWidths[0]); + } else { + offsetX = screenPos.column * this.session.$bidiHandler.charWidths[0]; + } + } + + if (this.$desiredColumn) + screenPos.column = this.$desiredColumn; + else + this.$desiredColumn = screenPos.column; + } + + if (rows != 0 && this.session.lineWidgets && this.session.lineWidgets[this.lead.row]) { + var widget = this.session.lineWidgets[this.lead.row]; + if (rows < 0) + rows -= widget.rowsAbove || 0; + else if (rows > 0) + rows += widget.rowCount - (widget.rowsAbove || 0); + } + + var docPos = this.session.screenToDocumentPosition(screenPos.row + rows, screenPos.column, offsetX); + + if (rows !== 0 && chars === 0 && docPos.row === this.lead.row && docPos.column === this.lead.column) { + + } + this.moveCursorTo(docPos.row, docPos.column + chars, chars === 0); + }; + this.moveCursorToPosition = function(position) { + this.moveCursorTo(position.row, position.column); + }; + this.moveCursorTo = function(row, column, keepDesiredColumn) { + var fold = this.session.getFoldAt(row, column, 1); + if (fold) { + row = fold.start.row; + column = fold.start.column; + } + + this.$keepDesiredColumnOnChange = true; + var line = this.session.getLine(row); + if (/[\uDC00-\uDFFF]/.test(line.charAt(column)) && line.charAt(column - 1)) { + if (this.lead.row == row && this.lead.column == column + 1) + column = column - 1; + else + column = column + 1; + } + this.lead.setPosition(row, column); + this.$keepDesiredColumnOnChange = false; + + if (!keepDesiredColumn) + this.$desiredColumn = null; + }; + this.moveCursorToScreen = function(row, column, keepDesiredColumn) { + var pos = this.session.screenToDocumentPosition(row, column); + this.moveCursorTo(pos.row, pos.column, keepDesiredColumn); + }; + this.detach = function() { + this.lead.detach(); + this.anchor.detach(); + }; + + this.fromOrientedRange = function(range) { + this.setSelectionRange(range, range.cursor == range.start); + this.$desiredColumn = range.desiredColumn || this.$desiredColumn; + }; + + this.toOrientedRange = function(range) { + var r = this.getRange(); + if (range) { + range.start.column = r.start.column; + range.start.row = r.start.row; + range.end.column = r.end.column; + range.end.row = r.end.row; + } else { + range = r; + } + + range.cursor = this.isBackwards() ? range.start : range.end; + range.desiredColumn = this.$desiredColumn; + return range; + }; + this.getRangeOfMovements = function(func) { + var start = this.getCursor(); + try { + func(this); + var end = this.getCursor(); + return Range.fromPoints(start, end); + } catch(e) { + return Range.fromPoints(start, start); + } finally { + this.moveCursorToPosition(start); + } + }; + + this.toJSON = function() { + if (this.rangeCount) { + var data = this.ranges.map(function(r) { + var r1 = r.clone(); + r1.isBackwards = r.cursor == r.start; + return r1; + }); + } else { + var data = this.getRange(); + data.isBackwards = this.isBackwards(); + } + return data; + }; + + this.fromJSON = function(data) { + if (data.start == undefined) { + if (this.rangeList && data.length > 1) { + this.toSingleRange(data[0]); + for (var i = data.length; i--; ) { + var r = Range.fromPoints(data[i].start, data[i].end); + if (data[i].isBackwards) + r.cursor = r.start; + this.addRange(r, true); + } + return; + } else { + data = data[0]; + } + } + if (this.rangeList) + this.toSingleRange(data); + this.setSelectionRange(data, data.isBackwards); + }; + + this.isEqual = function(data) { + if ((data.length || this.rangeCount) && data.length != this.rangeCount) + return false; + if (!data.length || !this.ranges) + return this.getRange().isEqual(data); + + for (var i = this.ranges.length; i--; ) { + if (!this.ranges[i].isEqual(data[i])) + return false; + } + return true; + }; + +}).call(Selection.prototype); + +exports.Selection = Selection; +}); + +ace.define("ace/tokenizer",["require","exports","module","ace/config"], function(require, exports, module) { +"use strict"; + +var config = require("./config"); +var MAX_TOKEN_COUNT = 2000; +var Tokenizer = function(rules) { + this.states = rules; + + this.regExps = {}; + this.matchMappings = {}; + for (var key in this.states) { + var state = this.states[key]; + var ruleRegExps = []; + var matchTotal = 0; + var mapping = this.matchMappings[key] = {defaultToken: "text"}; + var flag = "g"; + + var splitterRurles = []; + for (var i = 0; i < state.length; i++) { + var rule = state[i]; + if (rule.defaultToken) + mapping.defaultToken = rule.defaultToken; + if (rule.caseInsensitive && flag.indexOf("i") === -1) + flag += "i"; + if (rule.unicode && flag.indexOf("u") === -1) + flag += "u"; + if (rule.regex == null) + continue; + + if (rule.regex instanceof RegExp) + rule.regex = rule.regex.toString().slice(1, -1); + var adjustedregex = rule.regex; + var matchcount = new RegExp("(?:(" + adjustedregex + ")|(.))").exec("a").length - 2; + if (Array.isArray(rule.token)) { + if (rule.token.length == 1 || matchcount == 1) { + rule.token = rule.token[0]; + } else if (matchcount - 1 != rule.token.length) { + this.reportError("number of classes and regexp groups doesn't match", { + rule: rule, + groupCount: matchcount - 1 + }); + rule.token = rule.token[0]; + } else { + rule.tokenArray = rule.token; + rule.token = null; + rule.onMatch = this.$arrayTokens; + } + } else if (typeof rule.token == "function" && !rule.onMatch) { + if (matchcount > 1) + rule.onMatch = this.$applyToken; + else + rule.onMatch = rule.token; + } + + if (matchcount > 1) { + if (/\\\d/.test(rule.regex)) { + adjustedregex = rule.regex.replace(/\\([0-9]+)/g, function(match, digit) { + return "\\" + (parseInt(digit, 10) + matchTotal + 1); + }); + } else { + matchcount = 1; + adjustedregex = this.removeCapturingGroups(rule.regex); + } + if (!rule.splitRegex && typeof rule.token != "string") + splitterRurles.push(rule); // flag will be known only at the very end + } + + mapping[matchTotal] = i; + matchTotal += matchcount; + + ruleRegExps.push(adjustedregex); + if (!rule.onMatch) + rule.onMatch = null; + } + + if (!ruleRegExps.length) { + mapping[0] = 0; + ruleRegExps.push("$"); + } + + splitterRurles.forEach(function(rule) { + rule.splitRegex = this.createSplitterRegexp(rule.regex, flag); + }, this); + + this.regExps[key] = new RegExp("(" + ruleRegExps.join(")|(") + ")|($)", flag); + } +}; + +(function() { + this.$setMaxTokenCount = function(m) { + MAX_TOKEN_COUNT = m | 0; + }; + + this.$applyToken = function(str) { + var values = this.splitRegex.exec(str).slice(1); + var types = this.token.apply(this, values); + if (typeof types === "string") + return [{type: types, value: str}]; + + var tokens = []; + for (var i = 0, l = types.length; i < l; i++) { + if (values[i]) + tokens[tokens.length] = { + type: types[i], + value: values[i] + }; + } + return tokens; + }; + + this.$arrayTokens = function(str) { + if (!str) + return []; + var values = this.splitRegex.exec(str); + if (!values) + return "text"; + var tokens = []; + var types = this.tokenArray; + for (var i = 0, l = types.length; i < l; i++) { + if (values[i + 1]) + tokens[tokens.length] = { + type: types[i], + value: values[i + 1] + }; + } + return tokens; + }; + + this.removeCapturingGroups = function(src) { + var r = src.replace( + /\\.|\[(?:\\.|[^\\\]])*|\(\?[:=!<]|(\()/g, + function(x, y) {return y ? "(?:" : x;} + ); + return r; + }; + + this.createSplitterRegexp = function(src, flag) { + if (src.indexOf("(?=") != -1) { + var stack = 0; + var inChClass = false; + var lastCapture = {}; + src.replace(/(\\.)|(\((?:\?[=!])?)|(\))|([\[\]])/g, function( + m, esc, parenOpen, parenClose, square, index + ) { + if (inChClass) { + inChClass = square != "]"; + } else if (square) { + inChClass = true; + } else if (parenClose) { + if (stack == lastCapture.stack) { + lastCapture.end = index+1; + lastCapture.stack = -1; + } + stack--; + } else if (parenOpen) { + stack++; + if (parenOpen.length != 1) { + lastCapture.stack = stack; + lastCapture.start = index; + } + } + return m; + }); + + if (lastCapture.end != null && /^\)*$/.test(src.substr(lastCapture.end))) + src = src.substring(0, lastCapture.start) + src.substr(lastCapture.end); + } + if (src.charAt(0) != "^") src = "^" + src; + if (src.charAt(src.length - 1) != "$") src += "$"; + + return new RegExp(src, (flag||"").replace("g", "")); + }; + this.getLineTokens = function(line, startState) { + if (startState && typeof startState != "string") { + var stack = startState.slice(0); + startState = stack[0]; + if (startState === "#tmp") { + stack.shift(); + startState = stack.shift(); + } + } else + var stack = []; + + var currentState = startState || "start"; + var state = this.states[currentState]; + if (!state) { + currentState = "start"; + state = this.states[currentState]; + } + var mapping = this.matchMappings[currentState]; + var re = this.regExps[currentState]; + re.lastIndex = 0; + + var match, tokens = []; + var lastIndex = 0; + var matchAttempts = 0; + + var token = {type: null, value: ""}; + + while (match = re.exec(line)) { + var type = mapping.defaultToken; + var rule = null; + var value = match[0]; + var index = re.lastIndex; + + if (index - value.length > lastIndex) { + var skipped = line.substring(lastIndex, index - value.length); + if (token.type == type) { + token.value += skipped; + } else { + if (token.type) + tokens.push(token); + token = {type: type, value: skipped}; + } + } + + for (var i = 0; i < match.length-2; i++) { + if (match[i + 1] === undefined) + continue; + + rule = state[mapping[i]]; + + if (rule.onMatch) + type = rule.onMatch(value, currentState, stack, line); + else + type = rule.token; + + if (rule.next) { + if (typeof rule.next == "string") { + currentState = rule.next; + } else { + currentState = rule.next(currentState, stack); + } + + state = this.states[currentState]; + if (!state) { + this.reportError("state doesn't exist", currentState); + currentState = "start"; + state = this.states[currentState]; + } + mapping = this.matchMappings[currentState]; + lastIndex = index; + re = this.regExps[currentState]; + re.lastIndex = index; + } + if (rule.consumeLineEnd) + lastIndex = index; + break; + } + + if (value) { + if (typeof type === "string") { + if ((!rule || rule.merge !== false) && token.type === type) { + token.value += value; + } else { + if (token.type) + tokens.push(token); + token = {type: type, value: value}; + } + } else if (type) { + if (token.type) + tokens.push(token); + token = {type: null, value: ""}; + for (var i = 0; i < type.length; i++) + tokens.push(type[i]); + } + } + + if (lastIndex == line.length) + break; + + lastIndex = index; + + if (matchAttempts++ > MAX_TOKEN_COUNT) { + if (matchAttempts > 2 * line.length) { + this.reportError("infinite loop with in ace tokenizer", { + startState: startState, + line: line + }); + } + while (lastIndex < line.length) { + if (token.type) + tokens.push(token); + token = { + value: line.substring(lastIndex, lastIndex += 500), + type: "overflow" + }; + } + currentState = "start"; + stack = []; + break; + } + } + + if (token.type) + tokens.push(token); + + if (stack.length > 1) { + if (stack[0] !== currentState) + stack.unshift("#tmp", currentState); + } + return { + tokens : tokens, + state : stack.length ? stack : currentState + }; + }; + + this.reportError = config.reportError; + +}).call(Tokenizer.prototype); + +exports.Tokenizer = Tokenizer; +}); + +ace.define("ace/mode/text_highlight_rules",["require","exports","module","ace/lib/lang"], function(require, exports, module) { +"use strict"; + +var lang = require("../lib/lang"); + +var TextHighlightRules = function() { + + this.$rules = { + "start" : [{ + token : "empty_line", + regex : '^$' + }, { + defaultToken : "text" + }] + }; +}; + +(function() { + + this.addRules = function(rules, prefix) { + if (!prefix) { + for (var key in rules) + this.$rules[key] = rules[key]; + return; + } + for (var key in rules) { + var state = rules[key]; + for (var i = 0; i < state.length; i++) { + var rule = state[i]; + if (rule.next || rule.onMatch) { + if (typeof rule.next == "string") { + if (rule.next.indexOf(prefix) !== 0) + rule.next = prefix + rule.next; + } + if (rule.nextState && rule.nextState.indexOf(prefix) !== 0) + rule.nextState = prefix + rule.nextState; + } + } + this.$rules[prefix + key] = state; + } + }; + + this.getRules = function() { + return this.$rules; + }; + + this.embedRules = function (HighlightRules, prefix, escapeRules, states, append) { + var embedRules = typeof HighlightRules == "function" + ? new HighlightRules().getRules() + : HighlightRules; + if (states) { + for (var i = 0; i < states.length; i++) + states[i] = prefix + states[i]; + } else { + states = []; + for (var key in embedRules) + states.push(prefix + key); + } + + this.addRules(embedRules, prefix); + + if (escapeRules) { + var addRules = Array.prototype[append ? "push" : "unshift"]; + for (var i = 0; i < states.length; i++) + addRules.apply(this.$rules[states[i]], lang.deepCopy(escapeRules)); + } + + if (!this.$embeds) + this.$embeds = []; + this.$embeds.push(prefix); + }; + + this.getEmbeds = function() { + return this.$embeds; + }; + + var pushState = function(currentState, stack) { + if (currentState != "start" || stack.length) + stack.unshift(this.nextState, currentState); + return this.nextState; + }; + var popState = function(currentState, stack) { + stack.shift(); + return stack.shift() || "start"; + }; + + this.normalizeRules = function() { + var id = 0; + var rules = this.$rules; + function processState(key) { + var state = rules[key]; + state.processed = true; + for (var i = 0; i < state.length; i++) { + var rule = state[i]; + var toInsert = null; + if (Array.isArray(rule)) { + toInsert = rule; + rule = {}; + } + if (!rule.regex && rule.start) { + rule.regex = rule.start; + if (!rule.next) + rule.next = []; + rule.next.push({ + defaultToken: rule.token + }, { + token: rule.token + ".end", + regex: rule.end || rule.start, + next: "pop" + }); + rule.token = rule.token + ".start"; + rule.push = true; + } + var next = rule.next || rule.push; + if (next && Array.isArray(next)) { + var stateName = rule.stateName; + if (!stateName) { + stateName = rule.token; + if (typeof stateName != "string") + stateName = stateName[0] || ""; + if (rules[stateName]) + stateName += id++; + } + rules[stateName] = next; + rule.next = stateName; + processState(stateName); + } else if (next == "pop") { + rule.next = popState; + } + + if (rule.push) { + rule.nextState = rule.next || rule.push; + rule.next = pushState; + delete rule.push; + } + + if (rule.rules) { + for (var r in rule.rules) { + if (rules[r]) { + if (rules[r].push) + rules[r].push.apply(rules[r], rule.rules[r]); + } else { + rules[r] = rule.rules[r]; + } + } + } + var includeName = typeof rule == "string" ? rule : rule.include; + if (includeName) { + if (Array.isArray(includeName)) + toInsert = includeName.map(function(x) { return rules[x]; }); + else + toInsert = rules[includeName]; + } + + if (toInsert) { + var args = [i, 1].concat(toInsert); + if (rule.noEscape) + args = args.filter(function(x) {return !x.next;}); + state.splice.apply(state, args); + i--; + } + + if (rule.keywordMap) { + rule.token = this.createKeywordMapper( + rule.keywordMap, rule.defaultToken || "text", rule.caseInsensitive + ); + delete rule.defaultToken; + } + } + } + Object.keys(rules).forEach(processState, this); + }; + + this.createKeywordMapper = function(map, defaultToken, ignoreCase, splitChar) { + var keywords = Object.create(null); + this.$keywordList = []; + Object.keys(map).forEach(function(className) { + var a = map[className]; + var list = a.split(splitChar || "|"); + for (var i = list.length; i--; ) { + var word = list[i]; + this.$keywordList.push(word); + if (ignoreCase) + word = word.toLowerCase(); + keywords[word] = className; + } + }, this); + map = null; + return ignoreCase + ? function(value) {return keywords[value.toLowerCase()] || defaultToken; } + : function(value) {return keywords[value] || defaultToken; }; + }; + + this.getKeywords = function() { + return this.$keywords; + }; + +}).call(TextHighlightRules.prototype); + +exports.TextHighlightRules = TextHighlightRules; +}); + +ace.define("ace/mode/behaviour",["require","exports","module"], function(require, exports, module) { +"use strict"; + +var Behaviour = function() { + this.$behaviours = {}; +}; + +(function () { + + this.add = function (name, action, callback) { + switch (undefined) { + case this.$behaviours: + this.$behaviours = {}; + case this.$behaviours[name]: + this.$behaviours[name] = {}; + } + this.$behaviours[name][action] = callback; + }; + + this.addBehaviours = function (behaviours) { + for (var key in behaviours) { + for (var action in behaviours[key]) { + this.add(key, action, behaviours[key][action]); + } + } + }; + + this.remove = function (name) { + if (this.$behaviours && this.$behaviours[name]) { + delete this.$behaviours[name]; + } + }; + + this.inherit = function (mode, filter) { + if (typeof mode === "function") { + var behaviours = new mode().getBehaviours(filter); + } else { + var behaviours = mode.getBehaviours(filter); + } + this.addBehaviours(behaviours); + }; + + this.getBehaviours = function (filter) { + if (!filter) { + return this.$behaviours; + } else { + var ret = {}; + for (var i = 0; i < filter.length; i++) { + if (this.$behaviours[filter[i]]) { + ret[filter[i]] = this.$behaviours[filter[i]]; + } + } + return ret; + } + }; + +}).call(Behaviour.prototype); + +exports.Behaviour = Behaviour; +}); + +ace.define("ace/token_iterator",["require","exports","module","ace/range"], function(require, exports, module) { +"use strict"; + +var Range = require("./range").Range; +var TokenIterator = function(session, initialRow, initialColumn) { + this.$session = session; + this.$row = initialRow; + this.$rowTokens = session.getTokens(initialRow); + + var token = session.getTokenAt(initialRow, initialColumn); + this.$tokenIndex = token ? token.index : -1; +}; + +(function() { + this.stepBackward = function() { + this.$tokenIndex -= 1; + + while (this.$tokenIndex < 0) { + this.$row -= 1; + if (this.$row < 0) { + this.$row = 0; + return null; + } + + this.$rowTokens = this.$session.getTokens(this.$row); + this.$tokenIndex = this.$rowTokens.length - 1; + } + + return this.$rowTokens[this.$tokenIndex]; + }; + this.stepForward = function() { + this.$tokenIndex += 1; + var rowCount; + while (this.$tokenIndex >= this.$rowTokens.length) { + this.$row += 1; + if (!rowCount) + rowCount = this.$session.getLength(); + if (this.$row >= rowCount) { + this.$row = rowCount - 1; + return null; + } + + this.$rowTokens = this.$session.getTokens(this.$row); + this.$tokenIndex = 0; + } + + return this.$rowTokens[this.$tokenIndex]; + }; + this.getCurrentToken = function () { + return this.$rowTokens[this.$tokenIndex]; + }; + this.getCurrentTokenRow = function () { + return this.$row; + }; + this.getCurrentTokenColumn = function() { + var rowTokens = this.$rowTokens; + var tokenIndex = this.$tokenIndex; + var column = rowTokens[tokenIndex].start; + if (column !== undefined) + return column; + + column = 0; + while (tokenIndex > 0) { + tokenIndex -= 1; + column += rowTokens[tokenIndex].value.length; + } + + return column; + }; + this.getCurrentTokenPosition = function() { + return {row: this.$row, column: this.getCurrentTokenColumn()}; + }; + this.getCurrentTokenRange = function() { + var token = this.$rowTokens[this.$tokenIndex]; + var column = this.getCurrentTokenColumn(); + return new Range(this.$row, column, this.$row, column + token.value.length); + }; + +}).call(TokenIterator.prototype); + +exports.TokenIterator = TokenIterator; +}); + +ace.define("ace/mode/behaviour/cstyle",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"], function(require, exports, module) { +"use strict"; + +var oop = require("../../lib/oop"); +var Behaviour = require("../behaviour").Behaviour; +var TokenIterator = require("../../token_iterator").TokenIterator; +var lang = require("../../lib/lang"); + +var SAFE_INSERT_IN_TOKENS = + ["text", "paren.rparen", "rparen", "paren", "punctuation.operator"]; +var SAFE_INSERT_BEFORE_TOKENS = + ["text", "paren.rparen", "rparen", "paren", "punctuation.operator", "comment"]; + +var context; +var contextCache = {}; +var defaultQuotes = {'"' : '"', "'" : "'"}; + +var initContext = function(editor) { + var id = -1; + if (editor.multiSelect) { + id = editor.selection.index; + if (contextCache.rangeCount != editor.multiSelect.rangeCount) + contextCache = {rangeCount: editor.multiSelect.rangeCount}; + } + if (contextCache[id]) + return context = contextCache[id]; + context = contextCache[id] = { + autoInsertedBrackets: 0, + autoInsertedRow: -1, + autoInsertedLineEnd: "", + maybeInsertedBrackets: 0, + maybeInsertedRow: -1, + maybeInsertedLineStart: "", + maybeInsertedLineEnd: "" + }; +}; + +var getWrapped = function(selection, selected, opening, closing) { + var rowDiff = selection.end.row - selection.start.row; + return { + text: opening + selected + closing, + selection: [ + 0, + selection.start.column + 1, + rowDiff, + selection.end.column + (rowDiff ? 0 : 1) + ] + }; +}; + +var CstyleBehaviour = function(options) { + this.add("braces", "insertion", function(state, action, editor, session, text) { + var cursor = editor.getCursorPosition(); + var line = session.doc.getLine(cursor.row); + if (text == '{') { + initContext(editor); + var selection = editor.getSelectionRange(); + var selected = session.doc.getTextRange(selection); + if (selected !== "" && selected !== "{" && editor.getWrapBehavioursEnabled()) { + return getWrapped(selection, selected, '{', '}'); + } else if (CstyleBehaviour.isSaneInsertion(editor, session)) { + if (/[\]\}\)]/.test(line[cursor.column]) || editor.inMultiSelectMode || options && options.braces) { + CstyleBehaviour.recordAutoInsert(editor, session, "}"); + return { + text: '{}', + selection: [1, 1] + }; + } else { + CstyleBehaviour.recordMaybeInsert(editor, session, "{"); + return { + text: '{', + selection: [1, 1] + }; + } + } + } else if (text == '}') { + initContext(editor); + var rightChar = line.substring(cursor.column, cursor.column + 1); + if (rightChar == '}') { + var matching = session.$findOpeningBracket('}', {column: cursor.column + 1, row: cursor.row}); + if (matching !== null && CstyleBehaviour.isAutoInsertedClosing(cursor, line, text)) { + CstyleBehaviour.popAutoInsertedClosing(); + return { + text: '', + selection: [1, 1] + }; + } + } + } else if (text == "\n" || text == "\r\n") { + initContext(editor); + var closing = ""; + if (CstyleBehaviour.isMaybeInsertedClosing(cursor, line)) { + closing = lang.stringRepeat("}", context.maybeInsertedBrackets); + CstyleBehaviour.clearMaybeInsertedClosing(); + } + var rightChar = line.substring(cursor.column, cursor.column + 1); + if (rightChar === '}') { + var openBracePos = session.findMatchingBracket({row: cursor.row, column: cursor.column+1}, '}'); + if (!openBracePos) + return null; + var next_indent = this.$getIndent(session.getLine(openBracePos.row)); + } else if (closing) { + var next_indent = this.$getIndent(line); + } else { + CstyleBehaviour.clearMaybeInsertedClosing(); + return; + } + var indent = next_indent + session.getTabString(); + + return { + text: '\n' + indent + '\n' + next_indent + closing, + selection: [1, indent.length, 1, indent.length] + }; + } else { + CstyleBehaviour.clearMaybeInsertedClosing(); + } + }); + + this.add("braces", "deletion", function(state, action, editor, session, range) { + var selected = session.doc.getTextRange(range); + if (!range.isMultiLine() && selected == '{') { + initContext(editor); + var line = session.doc.getLine(range.start.row); + var rightChar = line.substring(range.end.column, range.end.column + 1); + if (rightChar == '}') { + range.end.column++; + return range; + } else { + context.maybeInsertedBrackets--; + } + } + }); + + this.add("parens", "insertion", function(state, action, editor, session, text) { + if (text == '(') { + initContext(editor); + var selection = editor.getSelectionRange(); + var selected = session.doc.getTextRange(selection); + if (selected !== "" && editor.getWrapBehavioursEnabled()) { + return getWrapped(selection, selected, '(', ')'); + } else if (CstyleBehaviour.isSaneInsertion(editor, session)) { + CstyleBehaviour.recordAutoInsert(editor, session, ")"); + return { + text: '()', + selection: [1, 1] + }; + } + } else if (text == ')') { + initContext(editor); + var cursor = editor.getCursorPosition(); + var line = session.doc.getLine(cursor.row); + var rightChar = line.substring(cursor.column, cursor.column + 1); + if (rightChar == ')') { + var matching = session.$findOpeningBracket(')', {column: cursor.column + 1, row: cursor.row}); + if (matching !== null && CstyleBehaviour.isAutoInsertedClosing(cursor, line, text)) { + CstyleBehaviour.popAutoInsertedClosing(); + return { + text: '', + selection: [1, 1] + }; + } + } + } + }); + + this.add("parens", "deletion", function(state, action, editor, session, range) { + var selected = session.doc.getTextRange(range); + if (!range.isMultiLine() && selected == '(') { + initContext(editor); + var line = session.doc.getLine(range.start.row); + var rightChar = line.substring(range.start.column + 1, range.start.column + 2); + if (rightChar == ')') { + range.end.column++; + return range; + } + } + }); + + this.add("brackets", "insertion", function(state, action, editor, session, text) { + if (text == '[') { + initContext(editor); + var selection = editor.getSelectionRange(); + var selected = session.doc.getTextRange(selection); + if (selected !== "" && editor.getWrapBehavioursEnabled()) { + return getWrapped(selection, selected, '[', ']'); + } else if (CstyleBehaviour.isSaneInsertion(editor, session)) { + CstyleBehaviour.recordAutoInsert(editor, session, "]"); + return { + text: '[]', + selection: [1, 1] + }; + } + } else if (text == ']') { + initContext(editor); + var cursor = editor.getCursorPosition(); + var line = session.doc.getLine(cursor.row); + var rightChar = line.substring(cursor.column, cursor.column + 1); + if (rightChar == ']') { + var matching = session.$findOpeningBracket(']', {column: cursor.column + 1, row: cursor.row}); + if (matching !== null && CstyleBehaviour.isAutoInsertedClosing(cursor, line, text)) { + CstyleBehaviour.popAutoInsertedClosing(); + return { + text: '', + selection: [1, 1] + }; + } + } + } + }); + + this.add("brackets", "deletion", function(state, action, editor, session, range) { + var selected = session.doc.getTextRange(range); + if (!range.isMultiLine() && selected == '[') { + initContext(editor); + var line = session.doc.getLine(range.start.row); + var rightChar = line.substring(range.start.column + 1, range.start.column + 2); + if (rightChar == ']') { + range.end.column++; + return range; + } + } + }); + + this.add("string_dquotes", "insertion", function(state, action, editor, session, text) { + var quotes = session.$mode.$quotes || defaultQuotes; + if (text.length == 1 && quotes[text]) { + if (this.lineCommentStart && this.lineCommentStart.indexOf(text) != -1) + return; + initContext(editor); + var quote = text; + var selection = editor.getSelectionRange(); + var selected = session.doc.getTextRange(selection); + if (selected !== "" && (selected.length != 1 || !quotes[selected]) && editor.getWrapBehavioursEnabled()) { + return getWrapped(selection, selected, quote, quote); + } else if (!selected) { + var cursor = editor.getCursorPosition(); + var line = session.doc.getLine(cursor.row); + var leftChar = line.substring(cursor.column-1, cursor.column); + var rightChar = line.substring(cursor.column, cursor.column + 1); + + var token = session.getTokenAt(cursor.row, cursor.column); + var rightToken = session.getTokenAt(cursor.row, cursor.column + 1); + if (leftChar == "\\" && token && /escape/.test(token.type)) + return null; + + var stringBefore = token && /string|escape/.test(token.type); + var stringAfter = !rightToken || /string|escape/.test(rightToken.type); + + var pair; + if (rightChar == quote) { + pair = stringBefore !== stringAfter; + if (pair && /string\.end/.test(rightToken.type)) + pair = false; + } else { + if (stringBefore && !stringAfter) + return null; // wrap string with different quote + if (stringBefore && stringAfter) + return null; // do not pair quotes inside strings + var wordRe = session.$mode.tokenRe; + wordRe.lastIndex = 0; + var isWordBefore = wordRe.test(leftChar); + wordRe.lastIndex = 0; + var isWordAfter = wordRe.test(leftChar); + if (isWordBefore || isWordAfter) + return null; // before or after alphanumeric + if (rightChar && !/[\s;,.})\]\\]/.test(rightChar)) + return null; // there is rightChar and it isn't closing + var charBefore = line[cursor.column - 2]; + if (leftChar == quote && (charBefore == quote || wordRe.test(charBefore))) + return null; + pair = true; + } + return { + text: pair ? quote + quote : "", + selection: [1,1] + }; + } + } + }); + + this.add("string_dquotes", "deletion", function(state, action, editor, session, range) { + var quotes = session.$mode.$quotes || defaultQuotes; + + var selected = session.doc.getTextRange(range); + if (!range.isMultiLine() && quotes.hasOwnProperty(selected)) { + initContext(editor); + var line = session.doc.getLine(range.start.row); + var rightChar = line.substring(range.start.column + 1, range.start.column + 2); + if (rightChar == selected) { + range.end.column++; + return range; + } + } + }); + +}; + + +CstyleBehaviour.isSaneInsertion = function(editor, session) { + var cursor = editor.getCursorPosition(); + var iterator = new TokenIterator(session, cursor.row, cursor.column); + if (!this.$matchTokenType(iterator.getCurrentToken() || "text", SAFE_INSERT_IN_TOKENS)) { + if (/[)}\]]/.test(editor.session.getLine(cursor.row)[cursor.column])) + return true; + var iterator2 = new TokenIterator(session, cursor.row, cursor.column + 1); + if (!this.$matchTokenType(iterator2.getCurrentToken() || "text", SAFE_INSERT_IN_TOKENS)) + return false; + } + iterator.stepForward(); + return iterator.getCurrentTokenRow() !== cursor.row || + this.$matchTokenType(iterator.getCurrentToken() || "text", SAFE_INSERT_BEFORE_TOKENS); +}; + +CstyleBehaviour.$matchTokenType = function(token, types) { + return types.indexOf(token.type || token) > -1; +}; + +CstyleBehaviour.recordAutoInsert = function(editor, session, bracket) { + var cursor = editor.getCursorPosition(); + var line = session.doc.getLine(cursor.row); + if (!this.isAutoInsertedClosing(cursor, line, context.autoInsertedLineEnd[0])) + context.autoInsertedBrackets = 0; + context.autoInsertedRow = cursor.row; + context.autoInsertedLineEnd = bracket + line.substr(cursor.column); + context.autoInsertedBrackets++; +}; + +CstyleBehaviour.recordMaybeInsert = function(editor, session, bracket) { + var cursor = editor.getCursorPosition(); + var line = session.doc.getLine(cursor.row); + if (!this.isMaybeInsertedClosing(cursor, line)) + context.maybeInsertedBrackets = 0; + context.maybeInsertedRow = cursor.row; + context.maybeInsertedLineStart = line.substr(0, cursor.column) + bracket; + context.maybeInsertedLineEnd = line.substr(cursor.column); + context.maybeInsertedBrackets++; +}; + +CstyleBehaviour.isAutoInsertedClosing = function(cursor, line, bracket) { + return context.autoInsertedBrackets > 0 && + cursor.row === context.autoInsertedRow && + bracket === context.autoInsertedLineEnd[0] && + line.substr(cursor.column) === context.autoInsertedLineEnd; +}; + +CstyleBehaviour.isMaybeInsertedClosing = function(cursor, line) { + return context.maybeInsertedBrackets > 0 && + cursor.row === context.maybeInsertedRow && + line.substr(cursor.column) === context.maybeInsertedLineEnd && + line.substr(0, cursor.column) == context.maybeInsertedLineStart; +}; + +CstyleBehaviour.popAutoInsertedClosing = function() { + context.autoInsertedLineEnd = context.autoInsertedLineEnd.substr(1); + context.autoInsertedBrackets--; +}; + +CstyleBehaviour.clearMaybeInsertedClosing = function() { + if (context) { + context.maybeInsertedBrackets = 0; + context.maybeInsertedRow = -1; + } +}; + + + +oop.inherits(CstyleBehaviour, Behaviour); + +exports.CstyleBehaviour = CstyleBehaviour; +}); + +ace.define("ace/unicode",["require","exports","module"], function(require, exports, module) { +"use strict"; +var wordChars = [48,9,8,25,5,0,2,25,48,0,11,0,5,0,6,22,2,30,2,457,5,11,15,4,8,0,2,0,18,116,2,1,3,3,9,0,2,2,2,0,2,19,2,82,2,138,2,4,3,155,12,37,3,0,8,38,10,44,2,0,2,1,2,1,2,0,9,26,6,2,30,10,7,61,2,9,5,101,2,7,3,9,2,18,3,0,17,58,3,100,15,53,5,0,6,45,211,57,3,18,2,5,3,11,3,9,2,1,7,6,2,2,2,7,3,1,3,21,2,6,2,0,4,3,3,8,3,1,3,3,9,0,5,1,2,4,3,11,16,2,2,5,5,1,3,21,2,6,2,1,2,1,2,1,3,0,2,4,5,1,3,2,4,0,8,3,2,0,8,15,12,2,2,8,2,2,2,21,2,6,2,1,2,4,3,9,2,2,2,2,3,0,16,3,3,9,18,2,2,7,3,1,3,21,2,6,2,1,2,4,3,8,3,1,3,2,9,1,5,1,2,4,3,9,2,0,17,1,2,5,4,2,2,3,4,1,2,0,2,1,4,1,4,2,4,11,5,4,4,2,2,3,3,0,7,0,15,9,18,2,2,7,2,2,2,22,2,9,2,4,4,7,2,2,2,3,8,1,2,1,7,3,3,9,19,1,2,7,2,2,2,22,2,9,2,4,3,8,2,2,2,3,8,1,8,0,2,3,3,9,19,1,2,7,2,2,2,22,2,15,4,7,2,2,2,3,10,0,9,3,3,9,11,5,3,1,2,17,4,23,2,8,2,0,3,6,4,0,5,5,2,0,2,7,19,1,14,57,6,14,2,9,40,1,2,0,3,1,2,0,3,0,7,3,2,6,2,2,2,0,2,0,3,1,2,12,2,2,3,4,2,0,2,5,3,9,3,1,35,0,24,1,7,9,12,0,2,0,2,0,5,9,2,35,5,19,2,5,5,7,2,35,10,0,58,73,7,77,3,37,11,42,2,0,4,328,2,3,3,6,2,0,2,3,3,40,2,3,3,32,2,3,3,6,2,0,2,3,3,14,2,56,2,3,3,66,5,0,33,15,17,84,13,619,3,16,2,25,6,74,22,12,2,6,12,20,12,19,13,12,2,2,2,1,13,51,3,29,4,0,5,1,3,9,34,2,3,9,7,87,9,42,6,69,11,28,4,11,5,11,11,39,3,4,12,43,5,25,7,10,38,27,5,62,2,28,3,10,7,9,14,0,89,75,5,9,18,8,13,42,4,11,71,55,9,9,4,48,83,2,2,30,14,230,23,280,3,5,3,37,3,5,3,7,2,0,2,0,2,0,2,30,3,52,2,6,2,0,4,2,2,6,4,3,3,5,5,12,6,2,2,6,67,1,20,0,29,0,14,0,17,4,60,12,5,0,4,11,18,0,5,0,3,9,2,0,4,4,7,0,2,0,2,0,2,3,2,10,3,3,6,4,5,0,53,1,2684,46,2,46,2,132,7,6,15,37,11,53,10,0,17,22,10,6,2,6,2,6,2,6,2,6,2,6,2,6,2,6,2,31,48,0,470,1,36,5,2,4,6,1,5,85,3,1,3,2,2,89,2,3,6,40,4,93,18,23,57,15,513,6581,75,20939,53,1164,68,45,3,268,4,27,21,31,3,13,13,1,2,24,9,69,11,1,38,8,3,102,3,1,111,44,25,51,13,68,12,9,7,23,4,0,5,45,3,35,13,28,4,64,15,10,39,54,10,13,3,9,7,22,4,1,5,66,25,2,227,42,2,1,3,9,7,11171,13,22,5,48,8453,301,3,61,3,105,39,6,13,4,6,11,2,12,2,4,2,0,2,1,2,1,2,107,34,362,19,63,3,53,41,11,5,15,17,6,13,1,25,2,33,4,2,134,20,9,8,25,5,0,2,25,12,88,4,5,3,5,3,5,3,2]; + +var code = 0; +var str = []; +for (var i = 0; i < wordChars.length; i += 2) { + str.push(code += wordChars[i]); + if (wordChars[i + 1]) + str.push(45, code += wordChars[i + 1]); +} + +exports.wordChars = String.fromCharCode.apply(null, str); + +}); + +ace.define("ace/mode/text",["require","exports","module","ace/config","ace/tokenizer","ace/mode/text_highlight_rules","ace/mode/behaviour/cstyle","ace/unicode","ace/lib/lang","ace/token_iterator","ace/range"], function(require, exports, module) { +"use strict"; +var config = require("../config"); + +var Tokenizer = require("../tokenizer").Tokenizer; +var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules; +var CstyleBehaviour = require("./behaviour/cstyle").CstyleBehaviour; +var unicode = require("../unicode"); +var lang = require("../lib/lang"); +var TokenIterator = require("../token_iterator").TokenIterator; +var Range = require("../range").Range; + +var Mode = function() { + this.HighlightRules = TextHighlightRules; +}; + +(function() { + this.$defaultBehaviour = new CstyleBehaviour(); + + this.tokenRe = new RegExp("^[" + unicode.wordChars + "\\$_]+", "g"); + + this.nonTokenRe = new RegExp("^(?:[^" + unicode.wordChars + "\\$_]|\\s])+", "g"); + + this.getTokenizer = function() { + if (!this.$tokenizer) { + this.$highlightRules = this.$highlightRules || new this.HighlightRules(this.$highlightRuleConfig); + this.$tokenizer = new Tokenizer(this.$highlightRules.getRules()); + } + return this.$tokenizer; + }; + + this.lineCommentStart = ""; + this.blockComment = ""; + + this.toggleCommentLines = function(state, session, startRow, endRow) { + var doc = session.doc; + + var ignoreBlankLines = true; + var shouldRemove = true; + var minIndent = Infinity; + var tabSize = session.getTabSize(); + var insertAtTabStop = false; + + if (!this.lineCommentStart) { + if (!this.blockComment) + return false; + var lineCommentStart = this.blockComment.start; + var lineCommentEnd = this.blockComment.end; + var regexpStart = new RegExp("^(\\s*)(?:" + lang.escapeRegExp(lineCommentStart) + ")"); + var regexpEnd = new RegExp("(?:" + lang.escapeRegExp(lineCommentEnd) + ")\\s*$"); + + var comment = function(line, i) { + if (testRemove(line, i)) + return; + if (!ignoreBlankLines || /\S/.test(line)) { + doc.insertInLine({row: i, column: line.length}, lineCommentEnd); + doc.insertInLine({row: i, column: minIndent}, lineCommentStart); + } + }; + + var uncomment = function(line, i) { + var m; + if (m = line.match(regexpEnd)) + doc.removeInLine(i, line.length - m[0].length, line.length); + if (m = line.match(regexpStart)) + doc.removeInLine(i, m[1].length, m[0].length); + }; + + var testRemove = function(line, row) { + if (regexpStart.test(line)) + return true; + var tokens = session.getTokens(row); + for (var i = 0; i < tokens.length; i++) { + if (tokens[i].type === "comment") + return true; + } + }; + } else { + if (Array.isArray(this.lineCommentStart)) { + var regexpStart = this.lineCommentStart.map(lang.escapeRegExp).join("|"); + var lineCommentStart = this.lineCommentStart[0]; + } else { + var regexpStart = lang.escapeRegExp(this.lineCommentStart); + var lineCommentStart = this.lineCommentStart; + } + regexpStart = new RegExp("^(\\s*)(?:" + regexpStart + ") ?"); + + insertAtTabStop = session.getUseSoftTabs(); + + var uncomment = function(line, i) { + var m = line.match(regexpStart); + if (!m) return; + var start = m[1].length, end = m[0].length; + if (!shouldInsertSpace(line, start, end) && m[0][end - 1] == " ") + end--; + doc.removeInLine(i, start, end); + }; + var commentWithSpace = lineCommentStart + " "; + var comment = function(line, i) { + if (!ignoreBlankLines || /\S/.test(line)) { + if (shouldInsertSpace(line, minIndent, minIndent)) + doc.insertInLine({row: i, column: minIndent}, commentWithSpace); + else + doc.insertInLine({row: i, column: minIndent}, lineCommentStart); + } + }; + var testRemove = function(line, i) { + return regexpStart.test(line); + }; + + var shouldInsertSpace = function(line, before, after) { + var spaces = 0; + while (before-- && line.charAt(before) == " ") + spaces++; + if (spaces % tabSize != 0) + return false; + var spaces = 0; + while (line.charAt(after++) == " ") + spaces++; + if (tabSize > 2) + return spaces % tabSize != tabSize - 1; + else + return spaces % tabSize == 0; + }; + } + + function iter(fun) { + for (var i = startRow; i <= endRow; i++) + fun(doc.getLine(i), i); + } + + + var minEmptyLength = Infinity; + iter(function(line, i) { + var indent = line.search(/\S/); + if (indent !== -1) { + if (indent < minIndent) + minIndent = indent; + if (shouldRemove && !testRemove(line, i)) + shouldRemove = false; + } else if (minEmptyLength > line.length) { + minEmptyLength = line.length; + } + }); + + if (minIndent == Infinity) { + minIndent = minEmptyLength; + ignoreBlankLines = false; + shouldRemove = false; + } + + if (insertAtTabStop && minIndent % tabSize != 0) + minIndent = Math.floor(minIndent / tabSize) * tabSize; + + iter(shouldRemove ? uncomment : comment); + }; + + this.toggleBlockComment = function(state, session, range, cursor) { + var comment = this.blockComment; + if (!comment) + return; + if (!comment.start && comment[0]) + comment = comment[0]; + + var iterator = new TokenIterator(session, cursor.row, cursor.column); + var token = iterator.getCurrentToken(); + + var sel = session.selection; + var initialRange = session.selection.toOrientedRange(); + var startRow, colDiff; + + if (token && /comment/.test(token.type)) { + var startRange, endRange; + while (token && /comment/.test(token.type)) { + var i = token.value.indexOf(comment.start); + if (i != -1) { + var row = iterator.getCurrentTokenRow(); + var column = iterator.getCurrentTokenColumn() + i; + startRange = new Range(row, column, row, column + comment.start.length); + break; + } + token = iterator.stepBackward(); + } + + var iterator = new TokenIterator(session, cursor.row, cursor.column); + var token = iterator.getCurrentToken(); + while (token && /comment/.test(token.type)) { + var i = token.value.indexOf(comment.end); + if (i != -1) { + var row = iterator.getCurrentTokenRow(); + var column = iterator.getCurrentTokenColumn() + i; + endRange = new Range(row, column, row, column + comment.end.length); + break; + } + token = iterator.stepForward(); + } + if (endRange) + session.remove(endRange); + if (startRange) { + session.remove(startRange); + startRow = startRange.start.row; + colDiff = -comment.start.length; + } + } else { + colDiff = comment.start.length; + startRow = range.start.row; + session.insert(range.end, comment.end); + session.insert(range.start, comment.start); + } + if (initialRange.start.row == startRow) + initialRange.start.column += colDiff; + if (initialRange.end.row == startRow) + initialRange.end.column += colDiff; + session.selection.fromOrientedRange(initialRange); + }; + + this.getNextLineIndent = function(state, line, tab) { + return this.$getIndent(line); + }; + + this.checkOutdent = function(state, line, input) { + return false; + }; + + this.autoOutdent = function(state, doc, row) { + }; + + this.$getIndent = function(line) { + return line.match(/^\s*/)[0]; + }; + + this.createWorker = function(session) { + return null; + }; + + this.createModeDelegates = function (mapping) { + this.$embeds = []; + this.$modes = {}; + for (var i in mapping) { + if (mapping[i]) { + var Mode = mapping[i]; + var id = Mode.prototype.$id; + var mode = config.$modes[id]; + if (!mode) + config.$modes[id] = mode = new Mode(); + if (!config.$modes[i]) + config.$modes[i] = mode; + this.$embeds.push(i); + this.$modes[i] = mode; + } + } + + var delegations = ["toggleBlockComment", "toggleCommentLines", "getNextLineIndent", + "checkOutdent", "autoOutdent", "transformAction", "getCompletions"]; + + for (var i = 0; i < delegations.length; i++) { + (function(scope) { + var functionName = delegations[i]; + var defaultHandler = scope[functionName]; + scope[delegations[i]] = function() { + return this.$delegator(functionName, arguments, defaultHandler); + }; + }(this)); + } + }; + + this.$delegator = function(method, args, defaultHandler) { + var state = args[0] || "start"; + if (typeof state != "string") { + if (Array.isArray(state[2])) { + var language = state[2][state[2].length - 1]; + var mode = this.$modes[language]; + if (mode) + return mode[method].apply(mode, [state[1]].concat([].slice.call(args, 1))); + } + state = state[0] || "start"; + } + + for (var i = 0; i < this.$embeds.length; i++) { + if (!this.$modes[this.$embeds[i]]) continue; + + var split = state.split(this.$embeds[i]); + if (!split[0] && split[1]) { + args[0] = split[1]; + var mode = this.$modes[this.$embeds[i]]; + return mode[method].apply(mode, args); + } + } + var ret = defaultHandler.apply(this, args); + return defaultHandler ? ret : undefined; + }; + + this.transformAction = function(state, action, editor, session, param) { + if (this.$behaviour) { + var behaviours = this.$behaviour.getBehaviours(); + for (var key in behaviours) { + if (behaviours[key][action]) { + var ret = behaviours[key][action].apply(this, arguments); + if (ret) { + return ret; + } + } + } + } + }; + + this.getKeywords = function(append) { + if (!this.completionKeywords) { + var rules = this.$tokenizer.rules; + var completionKeywords = []; + for (var rule in rules) { + var ruleItr = rules[rule]; + for (var r = 0, l = ruleItr.length; r < l; r++) { + if (typeof ruleItr[r].token === "string") { + if (/keyword|support|storage/.test(ruleItr[r].token)) + completionKeywords.push(ruleItr[r].regex); + } + else if (typeof ruleItr[r].token === "object") { + for (var a = 0, aLength = ruleItr[r].token.length; a < aLength; a++) { + if (/keyword|support|storage/.test(ruleItr[r].token[a])) { + var rule = ruleItr[r].regex.match(/\(.+?\)/g)[a]; + completionKeywords.push(rule.substr(1, rule.length - 2)); + } + } + } + } + } + this.completionKeywords = completionKeywords; + } + if (!append) + return this.$keywordList; + return completionKeywords.concat(this.$keywordList || []); + }; + + this.$createKeywordList = function() { + if (!this.$highlightRules) + this.getTokenizer(); + return this.$keywordList = this.$highlightRules.$keywordList || []; + }; + + this.getCompletions = function(state, session, pos, prefix) { + var keywords = this.$keywordList || this.$createKeywordList(); + return keywords.map(function(word) { + return { + name: word, + value: word, + score: 0, + meta: "keyword" + }; + }); + }; + + this.$id = "ace/mode/text"; +}).call(Mode.prototype); + +exports.Mode = Mode; +}); + +ace.define("ace/apply_delta",["require","exports","module"], function(require, exports, module) { +"use strict"; + +function throwDeltaError(delta, errorText){ + console.log("Invalid Delta:", delta); + throw "Invalid Delta: " + errorText; +} + +function positionInDocument(docLines, position) { + return position.row >= 0 && position.row < docLines.length && + position.column >= 0 && position.column <= docLines[position.row].length; +} + +function validateDelta(docLines, delta) { + if (delta.action != "insert" && delta.action != "remove") + throwDeltaError(delta, "delta.action must be 'insert' or 'remove'"); + if (!(delta.lines instanceof Array)) + throwDeltaError(delta, "delta.lines must be an Array"); + if (!delta.start || !delta.end) + throwDeltaError(delta, "delta.start/end must be an present"); + var start = delta.start; + if (!positionInDocument(docLines, delta.start)) + throwDeltaError(delta, "delta.start must be contained in document"); + var end = delta.end; + if (delta.action == "remove" && !positionInDocument(docLines, end)) + throwDeltaError(delta, "delta.end must contained in document for 'remove' actions"); + var numRangeRows = end.row - start.row; + var numRangeLastLineChars = (end.column - (numRangeRows == 0 ? start.column : 0)); + if (numRangeRows != delta.lines.length - 1 || delta.lines[numRangeRows].length != numRangeLastLineChars) + throwDeltaError(delta, "delta.range must match delta lines"); +} + +exports.applyDelta = function(docLines, delta, doNotValidate) { + + var row = delta.start.row; + var startColumn = delta.start.column; + var line = docLines[row] || ""; + switch (delta.action) { + case "insert": + var lines = delta.lines; + if (lines.length === 1) { + docLines[row] = line.substring(0, startColumn) + delta.lines[0] + line.substring(startColumn); + } else { + var args = [row, 1].concat(delta.lines); + docLines.splice.apply(docLines, args); + docLines[row] = line.substring(0, startColumn) + docLines[row]; + docLines[row + delta.lines.length - 1] += line.substring(startColumn); + } + break; + case "remove": + var endColumn = delta.end.column; + var endRow = delta.end.row; + if (row === endRow) { + docLines[row] = line.substring(0, startColumn) + line.substring(endColumn); + } else { + docLines.splice( + row, endRow - row + 1, + line.substring(0, startColumn) + docLines[endRow].substring(endColumn) + ); + } + break; + } +}; +}); + +ace.define("ace/anchor",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"], function(require, exports, module) { +"use strict"; + +var oop = require("./lib/oop"); +var EventEmitter = require("./lib/event_emitter").EventEmitter; + +var Anchor = exports.Anchor = function(doc, row, column) { + this.$onChange = this.onChange.bind(this); + this.attach(doc); + + if (typeof column == "undefined") + this.setPosition(row.row, row.column); + else + this.setPosition(row, column); +}; + +(function() { + + oop.implement(this, EventEmitter); + this.getPosition = function() { + return this.$clipPositionToDocument(this.row, this.column); + }; + this.getDocument = function() { + return this.document; + }; + this.$insertRight = false; + this.onChange = function(delta) { + if (delta.start.row == delta.end.row && delta.start.row != this.row) + return; + + if (delta.start.row > this.row) + return; + + var point = $getTransformedPoint(delta, {row: this.row, column: this.column}, this.$insertRight); + this.setPosition(point.row, point.column, true); + }; + + function $pointsInOrder(point1, point2, equalPointsInOrder) { + var bColIsAfter = equalPointsInOrder ? point1.column <= point2.column : point1.column < point2.column; + return (point1.row < point2.row) || (point1.row == point2.row && bColIsAfter); + } + + function $getTransformedPoint(delta, point, moveIfEqual) { + var deltaIsInsert = delta.action == "insert"; + var deltaRowShift = (deltaIsInsert ? 1 : -1) * (delta.end.row - delta.start.row); + var deltaColShift = (deltaIsInsert ? 1 : -1) * (delta.end.column - delta.start.column); + var deltaStart = delta.start; + var deltaEnd = deltaIsInsert ? deltaStart : delta.end; // Collapse insert range. + if ($pointsInOrder(point, deltaStart, moveIfEqual)) { + return { + row: point.row, + column: point.column + }; + } + if ($pointsInOrder(deltaEnd, point, !moveIfEqual)) { + return { + row: point.row + deltaRowShift, + column: point.column + (point.row == deltaEnd.row ? deltaColShift : 0) + }; + } + + return { + row: deltaStart.row, + column: deltaStart.column + }; + } + this.setPosition = function(row, column, noClip) { + var pos; + if (noClip) { + pos = { + row: row, + column: column + }; + } else { + pos = this.$clipPositionToDocument(row, column); + } + + if (this.row == pos.row && this.column == pos.column) + return; + + var old = { + row: this.row, + column: this.column + }; + + this.row = pos.row; + this.column = pos.column; + this._signal("change", { + old: old, + value: pos + }); + }; + this.detach = function() { + this.document.off("change", this.$onChange); + }; + this.attach = function(doc) { + this.document = doc || this.document; + this.document.on("change", this.$onChange); + }; + this.$clipPositionToDocument = function(row, column) { + var pos = {}; + + if (row >= this.document.getLength()) { + pos.row = Math.max(0, this.document.getLength() - 1); + pos.column = this.document.getLine(pos.row).length; + } + else if (row < 0) { + pos.row = 0; + pos.column = 0; + } + else { + pos.row = row; + pos.column = Math.min(this.document.getLine(pos.row).length, Math.max(0, column)); + } + + if (column < 0) + pos.column = 0; + + return pos; + }; + +}).call(Anchor.prototype); + +}); + +ace.define("ace/document",["require","exports","module","ace/lib/oop","ace/apply_delta","ace/lib/event_emitter","ace/range","ace/anchor"], function(require, exports, module) { +"use strict"; + +var oop = require("./lib/oop"); +var applyDelta = require("./apply_delta").applyDelta; +var EventEmitter = require("./lib/event_emitter").EventEmitter; +var Range = require("./range").Range; +var Anchor = require("./anchor").Anchor; + +var Document = function(textOrLines) { + this.$lines = [""]; + if (textOrLines.length === 0) { + this.$lines = [""]; + } else if (Array.isArray(textOrLines)) { + this.insertMergedLines({row: 0, column: 0}, textOrLines); + } else { + this.insert({row: 0, column:0}, textOrLines); + } +}; + +(function() { + + oop.implement(this, EventEmitter); + this.setValue = function(text) { + var len = this.getLength() - 1; + this.remove(new Range(0, 0, len, this.getLine(len).length)); + this.insert({row: 0, column: 0}, text); + }; + this.getValue = function() { + return this.getAllLines().join(this.getNewLineCharacter()); + }; + this.createAnchor = function(row, column) { + return new Anchor(this, row, column); + }; + if ("aaa".split(/a/).length === 0) { + this.$split = function(text) { + return text.replace(/\r\n|\r/g, "\n").split("\n"); + }; + } else { + this.$split = function(text) { + return text.split(/\r\n|\r|\n/); + }; + } + + + this.$detectNewLine = function(text) { + var match = text.match(/^.*?(\r\n|\r|\n)/m); + this.$autoNewLine = match ? match[1] : "\n"; + this._signal("changeNewLineMode"); + }; + this.getNewLineCharacter = function() { + switch (this.$newLineMode) { + case "windows": + return "\r\n"; + case "unix": + return "\n"; + default: + return this.$autoNewLine || "\n"; + } + }; + + this.$autoNewLine = ""; + this.$newLineMode = "auto"; + this.setNewLineMode = function(newLineMode) { + if (this.$newLineMode === newLineMode) + return; + + this.$newLineMode = newLineMode; + this._signal("changeNewLineMode"); + }; + this.getNewLineMode = function() { + return this.$newLineMode; + }; + this.isNewLine = function(text) { + return (text == "\r\n" || text == "\r" || text == "\n"); + }; + this.getLine = function(row) { + return this.$lines[row] || ""; + }; + this.getLines = function(firstRow, lastRow) { + return this.$lines.slice(firstRow, lastRow + 1); + }; + this.getAllLines = function() { + return this.getLines(0, this.getLength()); + }; + this.getLength = function() { + return this.$lines.length; + }; + this.getTextRange = function(range) { + return this.getLinesForRange(range).join(this.getNewLineCharacter()); + }; + this.getLinesForRange = function(range) { + var lines; + if (range.start.row === range.end.row) { + lines = [this.getLine(range.start.row).substring(range.start.column, range.end.column)]; + } else { + lines = this.getLines(range.start.row, range.end.row); + lines[0] = (lines[0] || "").substring(range.start.column); + var l = lines.length - 1; + if (range.end.row - range.start.row == l) + lines[l] = lines[l].substring(0, range.end.column); + } + return lines; + }; + this.insertLines = function(row, lines) { + console.warn("Use of document.insertLines is deprecated. Use the insertFullLines method instead."); + return this.insertFullLines(row, lines); + }; + this.removeLines = function(firstRow, lastRow) { + console.warn("Use of document.removeLines is deprecated. Use the removeFullLines method instead."); + return this.removeFullLines(firstRow, lastRow); + }; + this.insertNewLine = function(position) { + console.warn("Use of document.insertNewLine is deprecated. Use insertMergedLines(position, ['', '']) instead."); + return this.insertMergedLines(position, ["", ""]); + }; + this.insert = function(position, text) { + if (this.getLength() <= 1) + this.$detectNewLine(text); + + return this.insertMergedLines(position, this.$split(text)); + }; + this.insertInLine = function(position, text) { + var start = this.clippedPos(position.row, position.column); + var end = this.pos(position.row, position.column + text.length); + + this.applyDelta({ + start: start, + end: end, + action: "insert", + lines: [text] + }, true); + + return this.clonePos(end); + }; + + this.clippedPos = function(row, column) { + var length = this.getLength(); + if (row === undefined) { + row = length; + } else if (row < 0) { + row = 0; + } else if (row >= length) { + row = length - 1; + column = undefined; + } + var line = this.getLine(row); + if (column == undefined) + column = line.length; + column = Math.min(Math.max(column, 0), line.length); + return {row: row, column: column}; + }; + + this.clonePos = function(pos) { + return {row: pos.row, column: pos.column}; + }; + + this.pos = function(row, column) { + return {row: row, column: column}; + }; + + this.$clipPosition = function(position) { + var length = this.getLength(); + if (position.row >= length) { + position.row = Math.max(0, length - 1); + position.column = this.getLine(length - 1).length; + } else { + position.row = Math.max(0, position.row); + position.column = Math.min(Math.max(position.column, 0), this.getLine(position.row).length); + } + return position; + }; + this.insertFullLines = function(row, lines) { + row = Math.min(Math.max(row, 0), this.getLength()); + var column = 0; + if (row < this.getLength()) { + lines = lines.concat([""]); + column = 0; + } else { + lines = [""].concat(lines); + row--; + column = this.$lines[row].length; + } + this.insertMergedLines({row: row, column: column}, lines); + }; + this.insertMergedLines = function(position, lines) { + var start = this.clippedPos(position.row, position.column); + var end = { + row: start.row + lines.length - 1, + column: (lines.length == 1 ? start.column : 0) + lines[lines.length - 1].length + }; + + this.applyDelta({ + start: start, + end: end, + action: "insert", + lines: lines + }); + + return this.clonePos(end); + }; + this.remove = function(range) { + var start = this.clippedPos(range.start.row, range.start.column); + var end = this.clippedPos(range.end.row, range.end.column); + this.applyDelta({ + start: start, + end: end, + action: "remove", + lines: this.getLinesForRange({start: start, end: end}) + }); + return this.clonePos(start); + }; + this.removeInLine = function(row, startColumn, endColumn) { + var start = this.clippedPos(row, startColumn); + var end = this.clippedPos(row, endColumn); + + this.applyDelta({ + start: start, + end: end, + action: "remove", + lines: this.getLinesForRange({start: start, end: end}) + }, true); + + return this.clonePos(start); + }; + this.removeFullLines = function(firstRow, lastRow) { + firstRow = Math.min(Math.max(0, firstRow), this.getLength() - 1); + lastRow = Math.min(Math.max(0, lastRow ), this.getLength() - 1); + var deleteFirstNewLine = lastRow == this.getLength() - 1 && firstRow > 0; + var deleteLastNewLine = lastRow < this.getLength() - 1; + var startRow = ( deleteFirstNewLine ? firstRow - 1 : firstRow ); + var startCol = ( deleteFirstNewLine ? this.getLine(startRow).length : 0 ); + var endRow = ( deleteLastNewLine ? lastRow + 1 : lastRow ); + var endCol = ( deleteLastNewLine ? 0 : this.getLine(endRow).length ); + var range = new Range(startRow, startCol, endRow, endCol); + var deletedLines = this.$lines.slice(firstRow, lastRow + 1); + + this.applyDelta({ + start: range.start, + end: range.end, + action: "remove", + lines: this.getLinesForRange(range) + }); + return deletedLines; + }; + this.removeNewLine = function(row) { + if (row < this.getLength() - 1 && row >= 0) { + this.applyDelta({ + start: this.pos(row, this.getLine(row).length), + end: this.pos(row + 1, 0), + action: "remove", + lines: ["", ""] + }); + } + }; + this.replace = function(range, text) { + if (!(range instanceof Range)) + range = Range.fromPoints(range.start, range.end); + if (text.length === 0 && range.isEmpty()) + return range.start; + if (text == this.getTextRange(range)) + return range.end; + + this.remove(range); + var end; + if (text) { + end = this.insert(range.start, text); + } + else { + end = range.start; + } + + return end; + }; + this.applyDeltas = function(deltas) { + for (var i=0; i=0; i--) { + this.revertDelta(deltas[i]); + } + }; + this.applyDelta = function(delta, doNotValidate) { + var isInsert = delta.action == "insert"; + if (isInsert ? delta.lines.length <= 1 && !delta.lines[0] + : !Range.comparePoints(delta.start, delta.end)) { + return; + } + + if (isInsert && delta.lines.length > 20000) { + this.$splitAndapplyLargeDelta(delta, 20000); + } + else { + applyDelta(this.$lines, delta, doNotValidate); + this._signal("change", delta); + } + }; + + this.$safeApplyDelta = function(delta) { + var docLength = this.$lines.length; + if ( + delta.action == "remove" && delta.start.row < docLength && delta.end.row < docLength + || delta.action == "insert" && delta.start.row <= docLength + ) { + this.applyDelta(delta); + } + }; + + this.$splitAndapplyLargeDelta = function(delta, MAX) { + var lines = delta.lines; + var l = lines.length - MAX + 1; + var row = delta.start.row; + var column = delta.start.column; + for (var from = 0, to = 0; from < l; from = to) { + to += MAX - 1; + var chunk = lines.slice(from, to); + chunk.push(""); + this.applyDelta({ + start: this.pos(row + from, column), + end: this.pos(row + to, column = 0), + action: delta.action, + lines: chunk + }, true); + } + delta.lines = lines.slice(from); + delta.start.row = row + from; + delta.start.column = column; + this.applyDelta(delta, true); + }; + this.revertDelta = function(delta) { + this.$safeApplyDelta({ + start: this.clonePos(delta.start), + end: this.clonePos(delta.end), + action: (delta.action == "insert" ? "remove" : "insert"), + lines: delta.lines.slice() + }); + }; + this.indexToPosition = function(index, startRow) { + var lines = this.$lines || this.getAllLines(); + var newlineLength = this.getNewLineCharacter().length; + for (var i = startRow || 0, l = lines.length; i < l; i++) { + index -= lines[i].length + newlineLength; + if (index < 0) + return {row: i, column: index + lines[i].length + newlineLength}; + } + return {row: l-1, column: index + lines[l-1].length + newlineLength}; + }; + this.positionToIndex = function(pos, startRow) { + var lines = this.$lines || this.getAllLines(); + var newlineLength = this.getNewLineCharacter().length; + var index = 0; + var row = Math.min(pos.row, lines.length); + for (var i = startRow || 0; i < row; ++i) + index += lines[i].length + newlineLength; + + return index + pos.column; + }; + +}).call(Document.prototype); + +exports.Document = Document; +}); + +ace.define("ace/background_tokenizer",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"], function(require, exports, module) { +"use strict"; + +var oop = require("./lib/oop"); +var EventEmitter = require("./lib/event_emitter").EventEmitter; + +var BackgroundTokenizer = function(tokenizer, editor) { + this.running = false; + this.lines = []; + this.states = []; + this.currentLine = 0; + this.tokenizer = tokenizer; + + var self = this; + + this.$worker = function() { + if (!self.running) { return; } + + var workerStart = new Date(); + var currentLine = self.currentLine; + var endLine = -1; + var doc = self.doc; + + var startLine = currentLine; + while (self.lines[currentLine]) + currentLine++; + + var len = doc.getLength(); + var processedLines = 0; + self.running = false; + while (currentLine < len) { + self.$tokenizeRow(currentLine); + endLine = currentLine; + do { + currentLine++; + } while (self.lines[currentLine]); + processedLines ++; + if ((processedLines % 5 === 0) && (new Date() - workerStart) > 20) { + self.running = setTimeout(self.$worker, 20); + break; + } + } + self.currentLine = currentLine; + + if (endLine == -1) + endLine = currentLine; + + if (startLine <= endLine) + self.fireUpdateEvent(startLine, endLine); + }; +}; + +(function(){ + + oop.implement(this, EventEmitter); + this.setTokenizer = function(tokenizer) { + this.tokenizer = tokenizer; + this.lines = []; + this.states = []; + + this.start(0); + }; + this.setDocument = function(doc) { + this.doc = doc; + this.lines = []; + this.states = []; + + this.stop(); + }; + this.fireUpdateEvent = function(firstRow, lastRow) { + var data = { + first: firstRow, + last: lastRow + }; + this._signal("update", {data: data}); + }; + this.start = function(startRow) { + this.currentLine = Math.min(startRow || 0, this.currentLine, this.doc.getLength()); + this.lines.splice(this.currentLine, this.lines.length); + this.states.splice(this.currentLine, this.states.length); + + this.stop(); + this.running = setTimeout(this.$worker, 700); + }; + + this.scheduleStart = function() { + if (!this.running) + this.running = setTimeout(this.$worker, 700); + }; + + this.$updateOnChange = function(delta) { + var startRow = delta.start.row; + var len = delta.end.row - startRow; + + if (len === 0) { + this.lines[startRow] = null; + } else if (delta.action == "remove") { + this.lines.splice(startRow, len + 1, null); + this.states.splice(startRow, len + 1, null); + } else { + var args = Array(len + 1); + args.unshift(startRow, 1); + this.lines.splice.apply(this.lines, args); + this.states.splice.apply(this.states, args); + } + + this.currentLine = Math.min(startRow, this.currentLine, this.doc.getLength()); + + this.stop(); + }; + this.stop = function() { + if (this.running) + clearTimeout(this.running); + this.running = false; + }; + this.getTokens = function(row) { + return this.lines[row] || this.$tokenizeRow(row); + }; + this.getState = function(row) { + if (this.currentLine == row) + this.$tokenizeRow(row); + return this.states[row] || "start"; + }; + + this.$tokenizeRow = function(row) { + var line = this.doc.getLine(row); + var state = this.states[row - 1]; + + var data = this.tokenizer.getLineTokens(line, state, row); + + if (this.states[row] + "" !== data.state + "") { + this.states[row] = data.state; + this.lines[row + 1] = null; + if (this.currentLine > row + 1) + this.currentLine = row + 1; + } else if (this.currentLine == row) { + this.currentLine = row + 1; + } + + return this.lines[row] = data.tokens; + }; + +}).call(BackgroundTokenizer.prototype); + +exports.BackgroundTokenizer = BackgroundTokenizer; +}); + +ace.define("ace/search_highlight",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"], function(require, exports, module) { +"use strict"; + +var lang = require("./lib/lang"); +var oop = require("./lib/oop"); +var Range = require("./range").Range; + +var SearchHighlight = function(regExp, clazz, type) { + this.setRegexp(regExp); + this.clazz = clazz; + this.type = type || "text"; +}; + +(function() { + this.MAX_RANGES = 500; + + this.setRegexp = function(regExp) { + if (this.regExp+"" == regExp+"") + return; + this.regExp = regExp; + this.cache = []; + }; + + this.update = function(html, markerLayer, session, config) { + if (!this.regExp) + return; + var start = config.firstRow, end = config.lastRow; + + for (var i = start; i <= end; i++) { + var ranges = this.cache[i]; + if (ranges == null) { + ranges = lang.getMatchOffsets(session.getLine(i), this.regExp); + if (ranges.length > this.MAX_RANGES) + ranges = ranges.slice(0, this.MAX_RANGES); + ranges = ranges.map(function(match) { + return new Range(i, match.offset, i, match.offset + match.length); + }); + this.cache[i] = ranges.length ? ranges : ""; + } + + for (var j = ranges.length; j --; ) { + markerLayer.drawSingleLineMarker( + html, ranges[j].toScreenRange(session), this.clazz, config); + } + } + }; + +}).call(SearchHighlight.prototype); + +exports.SearchHighlight = SearchHighlight; +}); + +ace.define("ace/edit_session/fold_line",["require","exports","module","ace/range"], function(require, exports, module) { +"use strict"; + +var Range = require("../range").Range; +function FoldLine(foldData, folds) { + this.foldData = foldData; + if (Array.isArray(folds)) { + this.folds = folds; + } else { + folds = this.folds = [ folds ]; + } + + var last = folds[folds.length - 1]; + this.range = new Range(folds[0].start.row, folds[0].start.column, + last.end.row, last.end.column); + this.start = this.range.start; + this.end = this.range.end; + + this.folds.forEach(function(fold) { + fold.setFoldLine(this); + }, this); +} + +(function() { + this.shiftRow = function(shift) { + this.start.row += shift; + this.end.row += shift; + this.folds.forEach(function(fold) { + fold.start.row += shift; + fold.end.row += shift; + }); + }; + + this.addFold = function(fold) { + if (fold.sameRow) { + if (fold.start.row < this.startRow || fold.endRow > this.endRow) { + throw new Error("Can't add a fold to this FoldLine as it has no connection"); + } + this.folds.push(fold); + this.folds.sort(function(a, b) { + return -a.range.compareEnd(b.start.row, b.start.column); + }); + if (this.range.compareEnd(fold.start.row, fold.start.column) > 0) { + this.end.row = fold.end.row; + this.end.column = fold.end.column; + } else if (this.range.compareStart(fold.end.row, fold.end.column) < 0) { + this.start.row = fold.start.row; + this.start.column = fold.start.column; + } + } else if (fold.start.row == this.end.row) { + this.folds.push(fold); + this.end.row = fold.end.row; + this.end.column = fold.end.column; + } else if (fold.end.row == this.start.row) { + this.folds.unshift(fold); + this.start.row = fold.start.row; + this.start.column = fold.start.column; + } else { + throw new Error("Trying to add fold to FoldRow that doesn't have a matching row"); + } + fold.foldLine = this; + }; + + this.containsRow = function(row) { + return row >= this.start.row && row <= this.end.row; + }; + + this.walk = function(callback, endRow, endColumn) { + var lastEnd = 0, + folds = this.folds, + fold, + cmp, stop, isNewRow = true; + + if (endRow == null) { + endRow = this.end.row; + endColumn = this.end.column; + } + + for (var i = 0; i < folds.length; i++) { + fold = folds[i]; + + cmp = fold.range.compareStart(endRow, endColumn); + if (cmp == -1) { + callback(null, endRow, endColumn, lastEnd, isNewRow); + return; + } + + stop = callback(null, fold.start.row, fold.start.column, lastEnd, isNewRow); + stop = !stop && callback(fold.placeholder, fold.start.row, fold.start.column, lastEnd); + if (stop || cmp === 0) { + return; + } + isNewRow = !fold.sameRow; + lastEnd = fold.end.column; + } + callback(null, endRow, endColumn, lastEnd, isNewRow); + }; + + this.getNextFoldTo = function(row, column) { + var fold, cmp; + for (var i = 0; i < this.folds.length; i++) { + fold = this.folds[i]; + cmp = fold.range.compareEnd(row, column); + if (cmp == -1) { + return { + fold: fold, + kind: "after" + }; + } else if (cmp === 0) { + return { + fold: fold, + kind: "inside" + }; + } + } + return null; + }; + + this.addRemoveChars = function(row, column, len) { + var ret = this.getNextFoldTo(row, column), + fold, folds; + if (ret) { + fold = ret.fold; + if (ret.kind == "inside" + && fold.start.column != column + && fold.start.row != row) + { + window.console && window.console.log(row, column, fold); + } else if (fold.start.row == row) { + folds = this.folds; + var i = folds.indexOf(fold); + if (i === 0) { + this.start.column += len; + } + for (i; i < folds.length; i++) { + fold = folds[i]; + fold.start.column += len; + if (!fold.sameRow) { + return; + } + fold.end.column += len; + } + this.end.column += len; + } + } + }; + + this.split = function(row, column) { + var pos = this.getNextFoldTo(row, column); + + if (!pos || pos.kind == "inside") + return null; + + var fold = pos.fold; + var folds = this.folds; + var foldData = this.foldData; + + var i = folds.indexOf(fold); + var foldBefore = folds[i - 1]; + this.end.row = foldBefore.end.row; + this.end.column = foldBefore.end.column; + folds = folds.splice(i, folds.length - i); + + var newFoldLine = new FoldLine(foldData, folds); + foldData.splice(foldData.indexOf(this) + 1, 0, newFoldLine); + return newFoldLine; + }; + + this.merge = function(foldLineNext) { + var folds = foldLineNext.folds; + for (var i = 0; i < folds.length; i++) { + this.addFold(folds[i]); + } + var foldData = this.foldData; + foldData.splice(foldData.indexOf(foldLineNext), 1); + }; + + this.toString = function() { + var ret = [this.range.toString() + ": [" ]; + + this.folds.forEach(function(fold) { + ret.push(" " + fold.toString()); + }); + ret.push("]"); + return ret.join("\n"); + }; + + this.idxToPosition = function(idx) { + var lastFoldEndColumn = 0; + + for (var i = 0; i < this.folds.length; i++) { + var fold = this.folds[i]; + + idx -= fold.start.column - lastFoldEndColumn; + if (idx < 0) { + return { + row: fold.start.row, + column: fold.start.column + idx + }; + } + + idx -= fold.placeholder.length; + if (idx < 0) { + return fold.start; + } + + lastFoldEndColumn = fold.end.column; + } + + return { + row: this.end.row, + column: this.end.column + idx + }; + }; +}).call(FoldLine.prototype); + +exports.FoldLine = FoldLine; +}); + +ace.define("ace/range_list",["require","exports","module","ace/range"], function(require, exports, module) { +"use strict"; +var Range = require("./range").Range; +var comparePoints = Range.comparePoints; + +var RangeList = function() { + this.ranges = []; + this.$bias = 1; +}; + +(function() { + this.comparePoints = comparePoints; + + this.pointIndex = function(pos, excludeEdges, startIndex) { + var list = this.ranges; + + for (var i = startIndex || 0; i < list.length; i++) { + var range = list[i]; + var cmpEnd = comparePoints(pos, range.end); + if (cmpEnd > 0) + continue; + var cmpStart = comparePoints(pos, range.start); + if (cmpEnd === 0) + return excludeEdges && cmpStart !== 0 ? -i-2 : i; + if (cmpStart > 0 || (cmpStart === 0 && !excludeEdges)) + return i; + + return -i-1; + } + return -i - 1; + }; + + this.add = function(range) { + var excludeEdges = !range.isEmpty(); + var startIndex = this.pointIndex(range.start, excludeEdges); + if (startIndex < 0) + startIndex = -startIndex - 1; + + var endIndex = this.pointIndex(range.end, excludeEdges, startIndex); + + if (endIndex < 0) + endIndex = -endIndex - 1; + else + endIndex++; + return this.ranges.splice(startIndex, endIndex - startIndex, range); + }; + + this.addList = function(list) { + var removed = []; + for (var i = list.length; i--; ) { + removed.push.apply(removed, this.add(list[i])); + } + return removed; + }; + + this.substractPoint = function(pos) { + var i = this.pointIndex(pos); + + if (i >= 0) + return this.ranges.splice(i, 1); + }; + this.merge = function() { + var removed = []; + var list = this.ranges; + + list = list.sort(function(a, b) { + return comparePoints(a.start, b.start); + }); + + var next = list[0], range; + for (var i = 1; i < list.length; i++) { + range = next; + next = list[i]; + var cmp = comparePoints(range.end, next.start); + if (cmp < 0) + continue; + + if (cmp == 0 && !range.isEmpty() && !next.isEmpty()) + continue; + + if (comparePoints(range.end, next.end) < 0) { + range.end.row = next.end.row; + range.end.column = next.end.column; + } + + list.splice(i, 1); + removed.push(next); + next = range; + i--; + } + + this.ranges = list; + + return removed; + }; + + this.contains = function(row, column) { + return this.pointIndex({row: row, column: column}) >= 0; + }; + + this.containsPoint = function(pos) { + return this.pointIndex(pos) >= 0; + }; + + this.rangeAtPoint = function(pos) { + var i = this.pointIndex(pos); + if (i >= 0) + return this.ranges[i]; + }; + + + this.clipRows = function(startRow, endRow) { + var list = this.ranges; + if (list[0].start.row > endRow || list[list.length - 1].start.row < startRow) + return []; + + var startIndex = this.pointIndex({row: startRow, column: 0}); + if (startIndex < 0) + startIndex = -startIndex - 1; + var endIndex = this.pointIndex({row: endRow, column: 0}, startIndex); + if (endIndex < 0) + endIndex = -endIndex - 1; + + var clipped = []; + for (var i = startIndex; i < endIndex; i++) { + clipped.push(list[i]); + } + return clipped; + }; + + this.removeAll = function() { + return this.ranges.splice(0, this.ranges.length); + }; + + this.attach = function(session) { + if (this.session) + this.detach(); + + this.session = session; + this.onChange = this.$onChange.bind(this); + + this.session.on('change', this.onChange); + }; + + this.detach = function() { + if (!this.session) + return; + this.session.removeListener('change', this.onChange); + this.session = null; + }; + + this.$onChange = function(delta) { + var start = delta.start; + var end = delta.end; + var startRow = start.row; + var endRow = end.row; + var ranges = this.ranges; + for (var i = 0, n = ranges.length; i < n; i++) { + var r = ranges[i]; + if (r.end.row >= startRow) + break; + } + + if (delta.action == "insert") { + var lineDif = endRow - startRow; + var colDiff = -start.column + end.column; + for (; i < n; i++) { + var r = ranges[i]; + if (r.start.row > startRow) + break; + + if (r.start.row == startRow && r.start.column >= start.column) { + if (r.start.column == start.column && this.$bias <= 0) { + } else { + r.start.column += colDiff; + r.start.row += lineDif; + } + } + if (r.end.row == startRow && r.end.column >= start.column) { + if (r.end.column == start.column && this.$bias < 0) { + continue; + } + if (r.end.column == start.column && colDiff > 0 && i < n - 1) { + if (r.end.column > r.start.column && r.end.column == ranges[i+1].start.column) + r.end.column -= colDiff; + } + r.end.column += colDiff; + r.end.row += lineDif; + } + } + } else { + var lineDif = startRow - endRow; + var colDiff = start.column - end.column; + for (; i < n; i++) { + var r = ranges[i]; + + if (r.start.row > endRow) + break; + + if (r.end.row < endRow + && ( + startRow < r.end.row + || startRow == r.end.row && start.column < r.end.column + ) + ) { + r.end.row = startRow; + r.end.column = start.column; + } + else if (r.end.row == endRow) { + if (r.end.column <= end.column) { + if (lineDif || r.end.column > start.column) { + r.end.column = start.column; + r.end.row = start.row; + } + } + else { + r.end.column += colDiff; + r.end.row += lineDif; + } + } + else if (r.end.row > endRow) { + r.end.row += lineDif; + } + + if (r.start.row < endRow + && ( + startRow < r.start.row + || startRow == r.start.row && start.column < r.start.column + ) + ) { + r.start.row = startRow; + r.start.column = start.column; + } + else if (r.start.row == endRow) { + if (r.start.column <= end.column) { + if (lineDif || r.start.column > start.column) { + r.start.column = start.column; + r.start.row = start.row; + } + } + else { + r.start.column += colDiff; + r.start.row += lineDif; + } + } + else if (r.start.row > endRow) { + r.start.row += lineDif; + } + } + } + + if (lineDif != 0 && i < n) { + for (; i < n; i++) { + var r = ranges[i]; + r.start.row += lineDif; + r.end.row += lineDif; + } + } + }; + +}).call(RangeList.prototype); + +exports.RangeList = RangeList; +}); + +ace.define("ace/edit_session/fold",["require","exports","module","ace/range_list","ace/lib/oop"], function(require, exports, module) { +"use strict"; + +var RangeList = require("../range_list").RangeList; +var oop = require("../lib/oop"); +var Fold = exports.Fold = function(range, placeholder) { + this.foldLine = null; + this.placeholder = placeholder; + this.range = range; + this.start = range.start; + this.end = range.end; + + this.sameRow = range.start.row == range.end.row; + this.subFolds = this.ranges = []; +}; + +oop.inherits(Fold, RangeList); + +(function() { + + this.toString = function() { + return '"' + this.placeholder + '" ' + this.range.toString(); + }; + + this.setFoldLine = function(foldLine) { + this.foldLine = foldLine; + this.subFolds.forEach(function(fold) { + fold.setFoldLine(foldLine); + }); + }; + + this.clone = function() { + var range = this.range.clone(); + var fold = new Fold(range, this.placeholder); + this.subFolds.forEach(function(subFold) { + fold.subFolds.push(subFold.clone()); + }); + fold.collapseChildren = this.collapseChildren; + return fold; + }; + + this.addSubFold = function(fold) { + if (this.range.isEqual(fold)) + return; + consumeRange(fold, this.start); + + var row = fold.start.row, column = fold.start.column; + for (var i = 0, cmp = -1; i < this.subFolds.length; i++) { + cmp = this.subFolds[i].range.compare(row, column); + if (cmp != 1) + break; + } + var afterStart = this.subFolds[i]; + var firstConsumed = 0; + + if (cmp == 0) { + if (afterStart.range.containsRange(fold)) + return afterStart.addSubFold(fold); + else + firstConsumed = 1; + } + var row = fold.range.end.row, column = fold.range.end.column; + for (var j = i, cmp = -1; j < this.subFolds.length; j++) { + cmp = this.subFolds[j].range.compare(row, column); + if (cmp != 1) + break; + } + if (cmp == 0) j++; + var consumedFolds = this.subFolds.splice(i, j - i, fold); + var last = cmp == 0 ? consumedFolds.length - 1 : consumedFolds.length; + for (var k = firstConsumed; k < last; k++) { + fold.addSubFold(consumedFolds[k]); + } + fold.setFoldLine(this.foldLine); + + return fold; + }; + + this.restoreRange = function(range) { + return restoreRange(range, this.start); + }; + +}).call(Fold.prototype); + +function consumePoint(point, anchor) { + point.row -= anchor.row; + if (point.row == 0) + point.column -= anchor.column; +} +function consumeRange(range, anchor) { + consumePoint(range.start, anchor); + consumePoint(range.end, anchor); +} +function restorePoint(point, anchor) { + if (point.row == 0) + point.column += anchor.column; + point.row += anchor.row; +} +function restoreRange(range, anchor) { + restorePoint(range.start, anchor); + restorePoint(range.end, anchor); +} + +}); + +ace.define("ace/edit_session/folding",["require","exports","module","ace/range","ace/edit_session/fold_line","ace/edit_session/fold","ace/token_iterator"], function(require, exports, module) { +"use strict"; + +var Range = require("../range").Range; +var FoldLine = require("./fold_line").FoldLine; +var Fold = require("./fold").Fold; +var TokenIterator = require("../token_iterator").TokenIterator; + +function Folding() { + this.getFoldAt = function(row, column, side) { + var foldLine = this.getFoldLine(row); + if (!foldLine) + return null; + + var folds = foldLine.folds; + for (var i = 0; i < folds.length; i++) { + var range = folds[i].range; + if (range.contains(row, column)) { + if (side == 1 && range.isEnd(row, column) && !range.isEmpty()) { + continue; + } else if (side == -1 && range.isStart(row, column) && !range.isEmpty()) { + continue; + } + return folds[i]; + } + } + }; + this.getFoldsInRange = function(range) { + var start = range.start; + var end = range.end; + var foldLines = this.$foldData; + var foundFolds = []; + + start.column += 1; + end.column -= 1; + + for (var i = 0; i < foldLines.length; i++) { + var cmp = foldLines[i].range.compareRange(range); + if (cmp == 2) { + continue; + } + else if (cmp == -2) { + break; + } + + var folds = foldLines[i].folds; + for (var j = 0; j < folds.length; j++) { + var fold = folds[j]; + cmp = fold.range.compareRange(range); + if (cmp == -2) { + break; + } else if (cmp == 2) { + continue; + } else + if (cmp == 42) { + break; + } + foundFolds.push(fold); + } + } + start.column -= 1; + end.column += 1; + + return foundFolds; + }; + + this.getFoldsInRangeList = function(ranges) { + if (Array.isArray(ranges)) { + var folds = []; + ranges.forEach(function(range) { + folds = folds.concat(this.getFoldsInRange(range)); + }, this); + } else { + var folds = this.getFoldsInRange(ranges); + } + return folds; + }; + this.getAllFolds = function() { + var folds = []; + var foldLines = this.$foldData; + + for (var i = 0; i < foldLines.length; i++) + for (var j = 0; j < foldLines[i].folds.length; j++) + folds.push(foldLines[i].folds[j]); + + return folds; + }; + this.getFoldStringAt = function(row, column, trim, foldLine) { + foldLine = foldLine || this.getFoldLine(row); + if (!foldLine) + return null; + + var lastFold = { + end: { column: 0 } + }; + var str, fold; + for (var i = 0; i < foldLine.folds.length; i++) { + fold = foldLine.folds[i]; + var cmp = fold.range.compareEnd(row, column); + if (cmp == -1) { + str = this + .getLine(fold.start.row) + .substring(lastFold.end.column, fold.start.column); + break; + } + else if (cmp === 0) { + return null; + } + lastFold = fold; + } + if (!str) + str = this.getLine(fold.start.row).substring(lastFold.end.column); + + if (trim == -1) + return str.substring(0, column - lastFold.end.column); + else if (trim == 1) + return str.substring(column - lastFold.end.column); + else + return str; + }; + + this.getFoldLine = function(docRow, startFoldLine) { + var foldData = this.$foldData; + var i = 0; + if (startFoldLine) + i = foldData.indexOf(startFoldLine); + if (i == -1) + i = 0; + for (i; i < foldData.length; i++) { + var foldLine = foldData[i]; + if (foldLine.start.row <= docRow && foldLine.end.row >= docRow) { + return foldLine; + } else if (foldLine.end.row > docRow) { + return null; + } + } + return null; + }; + this.getNextFoldLine = function(docRow, startFoldLine) { + var foldData = this.$foldData; + var i = 0; + if (startFoldLine) + i = foldData.indexOf(startFoldLine); + if (i == -1) + i = 0; + for (i; i < foldData.length; i++) { + var foldLine = foldData[i]; + if (foldLine.end.row >= docRow) { + return foldLine; + } + } + return null; + }; + + this.getFoldedRowCount = function(first, last) { + var foldData = this.$foldData, rowCount = last-first+1; + for (var i = 0; i < foldData.length; i++) { + var foldLine = foldData[i], + end = foldLine.end.row, + start = foldLine.start.row; + if (end >= last) { + if (start < last) { + if (start >= first) + rowCount -= last-start; + else + rowCount = 0; // in one fold + } + break; + } else if (end >= first){ + if (start >= first) // fold inside range + rowCount -= end-start; + else + rowCount -= end-first+1; + } + } + return rowCount; + }; + + this.$addFoldLine = function(foldLine) { + this.$foldData.push(foldLine); + this.$foldData.sort(function(a, b) { + return a.start.row - b.start.row; + }); + return foldLine; + }; + this.addFold = function(placeholder, range) { + var foldData = this.$foldData; + var added = false; + var fold; + + if (placeholder instanceof Fold) + fold = placeholder; + else { + fold = new Fold(range, placeholder); + fold.collapseChildren = range.collapseChildren; + } + this.$clipRangeToDocument(fold.range); + + var startRow = fold.start.row; + var startColumn = fold.start.column; + var endRow = fold.end.row; + var endColumn = fold.end.column; + + var startFold = this.getFoldAt(startRow, startColumn, 1); + var endFold = this.getFoldAt(endRow, endColumn, -1); + if (startFold && endFold == startFold) + return startFold.addSubFold(fold); + + if (startFold && !startFold.range.isStart(startRow, startColumn)) + this.removeFold(startFold); + + if (endFold && !endFold.range.isEnd(endRow, endColumn)) + this.removeFold(endFold); + var folds = this.getFoldsInRange(fold.range); + if (folds.length > 0) { + this.removeFolds(folds); + if (!fold.collapseChildren) { + folds.forEach(function(subFold) { + fold.addSubFold(subFold); + }); + } + } + + for (var i = 0; i < foldData.length; i++) { + var foldLine = foldData[i]; + if (endRow == foldLine.start.row) { + foldLine.addFold(fold); + added = true; + break; + } else if (startRow == foldLine.end.row) { + foldLine.addFold(fold); + added = true; + if (!fold.sameRow) { + var foldLineNext = foldData[i + 1]; + if (foldLineNext && foldLineNext.start.row == endRow) { + foldLine.merge(foldLineNext); + break; + } + } + break; + } else if (endRow <= foldLine.start.row) { + break; + } + } + + if (!added) + foldLine = this.$addFoldLine(new FoldLine(this.$foldData, fold)); + + if (this.$useWrapMode) + this.$updateWrapData(foldLine.start.row, foldLine.start.row); + else + this.$updateRowLengthCache(foldLine.start.row, foldLine.start.row); + this.$modified = true; + this._signal("changeFold", { data: fold, action: "add" }); + + return fold; + }; + + this.addFolds = function(folds) { + folds.forEach(function(fold) { + this.addFold(fold); + }, this); + }; + + this.removeFold = function(fold) { + var foldLine = fold.foldLine; + var startRow = foldLine.start.row; + var endRow = foldLine.end.row; + + var foldLines = this.$foldData; + var folds = foldLine.folds; + if (folds.length == 1) { + foldLines.splice(foldLines.indexOf(foldLine), 1); + } else + if (foldLine.range.isEnd(fold.end.row, fold.end.column)) { + folds.pop(); + foldLine.end.row = folds[folds.length - 1].end.row; + foldLine.end.column = folds[folds.length - 1].end.column; + } else + if (foldLine.range.isStart(fold.start.row, fold.start.column)) { + folds.shift(); + foldLine.start.row = folds[0].start.row; + foldLine.start.column = folds[0].start.column; + } else + if (fold.sameRow) { + folds.splice(folds.indexOf(fold), 1); + } else + { + var newFoldLine = foldLine.split(fold.start.row, fold.start.column); + folds = newFoldLine.folds; + folds.shift(); + newFoldLine.start.row = folds[0].start.row; + newFoldLine.start.column = folds[0].start.column; + } + + if (!this.$updating) { + if (this.$useWrapMode) + this.$updateWrapData(startRow, endRow); + else + this.$updateRowLengthCache(startRow, endRow); + } + this.$modified = true; + this._signal("changeFold", { data: fold, action: "remove" }); + }; + + this.removeFolds = function(folds) { + var cloneFolds = []; + for (var i = 0; i < folds.length; i++) { + cloneFolds.push(folds[i]); + } + + cloneFolds.forEach(function(fold) { + this.removeFold(fold); + }, this); + this.$modified = true; + }; + + this.expandFold = function(fold) { + this.removeFold(fold); + fold.subFolds.forEach(function(subFold) { + fold.restoreRange(subFold); + this.addFold(subFold); + }, this); + if (fold.collapseChildren > 0) { + this.foldAll(fold.start.row+1, fold.end.row, fold.collapseChildren-1); + } + fold.subFolds = []; + }; + + this.expandFolds = function(folds) { + folds.forEach(function(fold) { + this.expandFold(fold); + }, this); + }; + + this.unfold = function(location, expandInner) { + var range, folds; + if (location == null) { + range = new Range(0, 0, this.getLength(), 0); + if (expandInner == null) expandInner = true; + } else if (typeof location == "number") { + range = new Range(location, 0, location, this.getLine(location).length); + } else if ("row" in location) { + range = Range.fromPoints(location, location); + } else if (Array.isArray(location)) { + folds = []; + location.forEach(function(range) { + folds = folds.concat(this.unfold(range)); + }, this); + return folds; + } else { + range = location; + } + + folds = this.getFoldsInRangeList(range); + var outermostFolds = folds; + while ( + folds.length == 1 + && Range.comparePoints(folds[0].start, range.start) < 0 + && Range.comparePoints(folds[0].end, range.end) > 0 + ) { + this.expandFolds(folds); + folds = this.getFoldsInRangeList(range); + } + + if (expandInner != false) { + this.removeFolds(folds); + } else { + this.expandFolds(folds); + } + if (outermostFolds.length) + return outermostFolds; + }; + this.isRowFolded = function(docRow, startFoldRow) { + return !!this.getFoldLine(docRow, startFoldRow); + }; + + this.getRowFoldEnd = function(docRow, startFoldRow) { + var foldLine = this.getFoldLine(docRow, startFoldRow); + return foldLine ? foldLine.end.row : docRow; + }; + + this.getRowFoldStart = function(docRow, startFoldRow) { + var foldLine = this.getFoldLine(docRow, startFoldRow); + return foldLine ? foldLine.start.row : docRow; + }; + + this.getFoldDisplayLine = function(foldLine, endRow, endColumn, startRow, startColumn) { + if (startRow == null) + startRow = foldLine.start.row; + if (startColumn == null) + startColumn = 0; + if (endRow == null) + endRow = foldLine.end.row; + if (endColumn == null) + endColumn = this.getLine(endRow).length; + var doc = this.doc; + var textLine = ""; + + foldLine.walk(function(placeholder, row, column, lastColumn) { + if (row < startRow) + return; + if (row == startRow) { + if (column < startColumn) + return; + lastColumn = Math.max(startColumn, lastColumn); + } + + if (placeholder != null) { + textLine += placeholder; + } else { + textLine += doc.getLine(row).substring(lastColumn, column); + } + }, endRow, endColumn); + return textLine; + }; + + this.getDisplayLine = function(row, endColumn, startRow, startColumn) { + var foldLine = this.getFoldLine(row); + + if (!foldLine) { + var line; + line = this.doc.getLine(row); + return line.substring(startColumn || 0, endColumn || line.length); + } else { + return this.getFoldDisplayLine( + foldLine, row, endColumn, startRow, startColumn); + } + }; + + this.$cloneFoldData = function() { + var fd = []; + fd = this.$foldData.map(function(foldLine) { + var folds = foldLine.folds.map(function(fold) { + return fold.clone(); + }); + return new FoldLine(fd, folds); + }); + + return fd; + }; + + this.toggleFold = function(tryToUnfold) { + var selection = this.selection; + var range = selection.getRange(); + var fold; + var bracketPos; + + if (range.isEmpty()) { + var cursor = range.start; + fold = this.getFoldAt(cursor.row, cursor.column); + + if (fold) { + this.expandFold(fold); + return; + } else if (bracketPos = this.findMatchingBracket(cursor)) { + if (range.comparePoint(bracketPos) == 1) { + range.end = bracketPos; + } else { + range.start = bracketPos; + range.start.column++; + range.end.column--; + } + } else if (bracketPos = this.findMatchingBracket({row: cursor.row, column: cursor.column + 1})) { + if (range.comparePoint(bracketPos) == 1) + range.end = bracketPos; + else + range.start = bracketPos; + + range.start.column++; + } else { + range = this.getCommentFoldRange(cursor.row, cursor.column) || range; + } + } else { + var folds = this.getFoldsInRange(range); + if (tryToUnfold && folds.length) { + this.expandFolds(folds); + return; + } else if (folds.length == 1 ) { + fold = folds[0]; + } + } + + if (!fold) + fold = this.getFoldAt(range.start.row, range.start.column); + + if (fold && fold.range.toString() == range.toString()) { + this.expandFold(fold); + return; + } + + var placeholder = "..."; + if (!range.isMultiLine()) { + placeholder = this.getTextRange(range); + if (placeholder.length < 4) + return; + placeholder = placeholder.trim().substring(0, 2) + ".."; + } + + this.addFold(placeholder, range); + }; + + this.getCommentFoldRange = function(row, column, dir) { + var iterator = new TokenIterator(this, row, column); + var token = iterator.getCurrentToken(); + var type = token && token.type; + if (token && /^comment|string/.test(type)) { + type = type.match(/comment|string/)[0]; + if (type == "comment") + type += "|doc-start"; + var re = new RegExp(type); + var range = new Range(); + if (dir != 1) { + do { + token = iterator.stepBackward(); + } while (token && re.test(token.type)); + iterator.stepForward(); + } + + range.start.row = iterator.getCurrentTokenRow(); + range.start.column = iterator.getCurrentTokenColumn() + 2; + + iterator = new TokenIterator(this, row, column); + + if (dir != -1) { + var lastRow = -1; + do { + token = iterator.stepForward(); + if (lastRow == -1) { + var state = this.getState(iterator.$row); + if (!re.test(state)) + lastRow = iterator.$row; + } else if (iterator.$row > lastRow) { + break; + } + } while (token && re.test(token.type)); + token = iterator.stepBackward(); + } else + token = iterator.getCurrentToken(); + + range.end.row = iterator.getCurrentTokenRow(); + range.end.column = iterator.getCurrentTokenColumn() + token.value.length - 2; + return range; + } + }; + + this.foldAll = function(startRow, endRow, depth, test) { + if (depth == undefined) + depth = 100000; // JSON.stringify doesn't hanle Infinity + var foldWidgets = this.foldWidgets; + if (!foldWidgets) + return; // mode doesn't support folding + endRow = endRow || this.getLength(); + startRow = startRow || 0; + for (var row = startRow; row < endRow; row++) { + if (foldWidgets[row] == null) + foldWidgets[row] = this.getFoldWidget(row); + if (foldWidgets[row] != "start") + continue; + + if (test && !test(row)) continue; + + var range = this.getFoldWidgetRange(row); + if (range && range.isMultiLine() + && range.end.row <= endRow + && range.start.row >= startRow + ) { + row = range.end.row; + range.collapseChildren = depth; + this.addFold("...", range); + } + } + }; + + this.foldToLevel = function(level) { + this.foldAll(); + while (level-- > 0) + this.unfold(null, false); + }; + + this.foldAllComments = function() { + var session = this; + this.foldAll(null, null, null, function(row) { + var tokens = session.getTokens(row); + for (var i = 0; i < tokens.length; i++) { + var token = tokens[i]; + if (token.type == "text" && /^\s+$/.test(token.value)) + continue; + if (/comment/.test(token.type)) + return true; + return false; + } + }); + }; + this.$foldStyles = { + "manual": 1, + "markbegin": 1, + "markbeginend": 1 + }; + this.$foldStyle = "markbegin"; + this.setFoldStyle = function(style) { + if (!this.$foldStyles[style]) + throw new Error("invalid fold style: " + style + "[" + Object.keys(this.$foldStyles).join(", ") + "]"); + + if (this.$foldStyle == style) + return; + + this.$foldStyle = style; + + if (style == "manual") + this.unfold(); + var mode = this.$foldMode; + this.$setFolding(null); + this.$setFolding(mode); + }; + + this.$setFolding = function(foldMode) { + if (this.$foldMode == foldMode) + return; + + this.$foldMode = foldMode; + + this.off('change', this.$updateFoldWidgets); + this.off('tokenizerUpdate', this.$tokenizerUpdateFoldWidgets); + this._signal("changeAnnotation"); + + if (!foldMode || this.$foldStyle == "manual") { + this.foldWidgets = null; + return; + } + + this.foldWidgets = []; + this.getFoldWidget = foldMode.getFoldWidget.bind(foldMode, this, this.$foldStyle); + this.getFoldWidgetRange = foldMode.getFoldWidgetRange.bind(foldMode, this, this.$foldStyle); + + this.$updateFoldWidgets = this.updateFoldWidgets.bind(this); + this.$tokenizerUpdateFoldWidgets = this.tokenizerUpdateFoldWidgets.bind(this); + this.on('change', this.$updateFoldWidgets); + this.on('tokenizerUpdate', this.$tokenizerUpdateFoldWidgets); + }; + + this.getParentFoldRangeData = function (row, ignoreCurrent) { + var fw = this.foldWidgets; + if (!fw || (ignoreCurrent && fw[row])) + return {}; + + var i = row - 1, firstRange; + while (i >= 0) { + var c = fw[i]; + if (c == null) + c = fw[i] = this.getFoldWidget(i); + + if (c == "start") { + var range = this.getFoldWidgetRange(i); + if (!firstRange) + firstRange = range; + if (range && range.end.row >= row) + break; + } + i--; + } + + return { + range: i !== -1 && range, + firstRange: firstRange + }; + }; + + this.onFoldWidgetClick = function(row, e) { + e = e.domEvent; + var options = { + children: e.shiftKey, + all: e.ctrlKey || e.metaKey, + siblings: e.altKey + }; + + var range = this.$toggleFoldWidget(row, options); + if (!range) { + var el = (e.target || e.srcElement); + if (el && /ace_fold-widget/.test(el.className)) + el.className += " ace_invalid"; + } + }; + + this.$toggleFoldWidget = function(row, options) { + if (!this.getFoldWidget) + return; + var type = this.getFoldWidget(row); + var line = this.getLine(row); + + var dir = type === "end" ? -1 : 1; + var fold = this.getFoldAt(row, dir === -1 ? 0 : line.length, dir); + + if (fold) { + if (options.children || options.all) + this.removeFold(fold); + else + this.expandFold(fold); + return fold; + } + + var range = this.getFoldWidgetRange(row, true); + if (range && !range.isMultiLine()) { + fold = this.getFoldAt(range.start.row, range.start.column, 1); + if (fold && range.isEqual(fold.range)) { + this.removeFold(fold); + return fold; + } + } + + if (options.siblings) { + var data = this.getParentFoldRangeData(row); + if (data.range) { + var startRow = data.range.start.row + 1; + var endRow = data.range.end.row; + } + this.foldAll(startRow, endRow, options.all ? 10000 : 0); + } else if (options.children) { + endRow = range ? range.end.row : this.getLength(); + this.foldAll(row + 1, endRow, options.all ? 10000 : 0); + } else if (range) { + if (options.all) + range.collapseChildren = 10000; + this.addFold("...", range); + } + + return range; + }; + + + + this.toggleFoldWidget = function(toggleParent) { + var row = this.selection.getCursor().row; + row = this.getRowFoldStart(row); + var range = this.$toggleFoldWidget(row, {}); + + if (range) + return; + var data = this.getParentFoldRangeData(row, true); + range = data.range || data.firstRange; + + if (range) { + row = range.start.row; + var fold = this.getFoldAt(row, this.getLine(row).length, 1); + + if (fold) { + this.removeFold(fold); + } else { + this.addFold("...", range); + } + } + }; + + this.updateFoldWidgets = function(delta) { + var firstRow = delta.start.row; + var len = delta.end.row - firstRow; + + if (len === 0) { + this.foldWidgets[firstRow] = null; + } else if (delta.action == 'remove') { + this.foldWidgets.splice(firstRow, len + 1, null); + } else { + var args = Array(len + 1); + args.unshift(firstRow, 1); + this.foldWidgets.splice.apply(this.foldWidgets, args); + } + }; + this.tokenizerUpdateFoldWidgets = function(e) { + var rows = e.data; + if (rows.first != rows.last) { + if (this.foldWidgets.length > rows.first) + this.foldWidgets.splice(rows.first, this.foldWidgets.length); + } + }; +} + +exports.Folding = Folding; + +}); + +ace.define("ace/edit_session/bracket_match",["require","exports","module","ace/token_iterator","ace/range"], function(require, exports, module) { +"use strict"; + +var TokenIterator = require("../token_iterator").TokenIterator; +var Range = require("../range").Range; + + +function BracketMatch() { + + this.findMatchingBracket = function(position, chr) { + if (position.column == 0) return null; + + var charBeforeCursor = chr || this.getLine(position.row).charAt(position.column-1); + if (charBeforeCursor == "") return null; + + var match = charBeforeCursor.match(/([\(\[\{])|([\)\]\}])/); + if (!match) + return null; + + if (match[1]) + return this.$findClosingBracket(match[1], position); + else + return this.$findOpeningBracket(match[2], position); + }; + + this.getBracketRange = function(pos) { + var line = this.getLine(pos.row); + var before = true, range; + + var chr = line.charAt(pos.column - 1); + var match = chr && chr.match(/([\(\[\{])|([\)\]\}])/); + if (!match) { + chr = line.charAt(pos.column); + pos = {row: pos.row, column: pos.column + 1}; + match = chr && chr.match(/([\(\[\{])|([\)\]\}])/); + before = false; + } + if (!match) + return null; + + if (match[1]) { + var bracketPos = this.$findClosingBracket(match[1], pos); + if (!bracketPos) + return null; + range = Range.fromPoints(pos, bracketPos); + if (!before) { + range.end.column++; + range.start.column--; + } + range.cursor = range.end; + } else { + var bracketPos = this.$findOpeningBracket(match[2], pos); + if (!bracketPos) + return null; + range = Range.fromPoints(bracketPos, pos); + if (!before) { + range.start.column++; + range.end.column--; + } + range.cursor = range.start; + } + + return range; + }; + this.getMatchingBracketRanges = function(pos) { + var line = this.getLine(pos.row); + + var chr = line.charAt(pos.column - 1); + var match = chr && chr.match(/([\(\[\{])|([\)\]\}])/); + if (!match) { + chr = line.charAt(pos.column); + pos = {row: pos.row, column: pos.column + 1}; + match = chr && chr.match(/([\(\[\{])|([\)\]\}])/); + } + + if (!match) + return null; + + var startRange = new Range(pos.row, pos.column - 1, pos.row, pos.column); + var bracketPos = match[1] ? this.$findClosingBracket(match[1], pos) + : this.$findOpeningBracket(match[2], pos); + if (!bracketPos) + return [startRange]; + var endRange = new Range(bracketPos.row, bracketPos.column, bracketPos.row, bracketPos.column + 1); + + return [startRange, endRange]; + }; + + this.$brackets = { + ")": "(", + "(": ")", + "]": "[", + "[": "]", + "{": "}", + "}": "{", + "<": ">", + ">": "<" + }; + + this.$findOpeningBracket = function(bracket, position, typeRe) { + var openBracket = this.$brackets[bracket]; + var depth = 1; + + var iterator = new TokenIterator(this, position.row, position.column); + var token = iterator.getCurrentToken(); + if (!token) + token = iterator.stepForward(); + if (!token) + return; + + if (!typeRe){ + typeRe = new RegExp( + "(\\.?" + + token.type.replace(".", "\\.").replace("rparen", ".paren") + .replace(/\b(?:end)\b/, "(?:start|begin|end)") + + ")+" + ); + } + var valueIndex = position.column - iterator.getCurrentTokenColumn() - 2; + var value = token.value; + + while (true) { + + while (valueIndex >= 0) { + var chr = value.charAt(valueIndex); + if (chr == openBracket) { + depth -= 1; + if (depth == 0) { + return {row: iterator.getCurrentTokenRow(), + column: valueIndex + iterator.getCurrentTokenColumn()}; + } + } + else if (chr == bracket) { + depth += 1; + } + valueIndex -= 1; + } + do { + token = iterator.stepBackward(); + } while (token && !typeRe.test(token.type)); + + if (token == null) + break; + + value = token.value; + valueIndex = value.length - 1; + } + + return null; + }; + + this.$findClosingBracket = function(bracket, position, typeRe) { + var closingBracket = this.$brackets[bracket]; + var depth = 1; + + var iterator = new TokenIterator(this, position.row, position.column); + var token = iterator.getCurrentToken(); + if (!token) + token = iterator.stepForward(); + if (!token) + return; + + if (!typeRe){ + typeRe = new RegExp( + "(\\.?" + + token.type.replace(".", "\\.").replace("lparen", ".paren") + .replace(/\b(?:start|begin)\b/, "(?:start|begin|end)") + + ")+" + ); + } + var valueIndex = position.column - iterator.getCurrentTokenColumn(); + + while (true) { + + var value = token.value; + var valueLength = value.length; + while (valueIndex < valueLength) { + var chr = value.charAt(valueIndex); + if (chr == closingBracket) { + depth -= 1; + if (depth == 0) { + return {row: iterator.getCurrentTokenRow(), + column: valueIndex + iterator.getCurrentTokenColumn()}; + } + } + else if (chr == bracket) { + depth += 1; + } + valueIndex += 1; + } + do { + token = iterator.stepForward(); + } while (token && !typeRe.test(token.type)); + + if (token == null) + break; + + valueIndex = 0; + } + + return null; + }; +} +exports.BracketMatch = BracketMatch; + +}); + +ace.define("ace/edit_session",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/bidihandler","ace/config","ace/lib/event_emitter","ace/selection","ace/mode/text","ace/range","ace/document","ace/background_tokenizer","ace/search_highlight","ace/edit_session/folding","ace/edit_session/bracket_match"], function(require, exports, module) { +"use strict"; + +var oop = require("./lib/oop"); +var lang = require("./lib/lang"); +var BidiHandler = require("./bidihandler").BidiHandler; +var config = require("./config"); +var EventEmitter = require("./lib/event_emitter").EventEmitter; +var Selection = require("./selection").Selection; +var TextMode = require("./mode/text").Mode; +var Range = require("./range").Range; +var Document = require("./document").Document; +var BackgroundTokenizer = require("./background_tokenizer").BackgroundTokenizer; +var SearchHighlight = require("./search_highlight").SearchHighlight; + +var EditSession = function(text, mode) { + this.$breakpoints = []; + this.$decorations = []; + this.$frontMarkers = {}; + this.$backMarkers = {}; + this.$markerId = 1; + this.$undoSelect = true; + + this.$foldData = []; + this.id = "session" + (++EditSession.$uid); + this.$foldData.toString = function() { + return this.join("\n"); + }; + this.on("changeFold", this.onChangeFold.bind(this)); + this.$onChange = this.onChange.bind(this); + + if (typeof text != "object" || !text.getLine) + text = new Document(text); + + this.setDocument(text); + this.selection = new Selection(this); + this.$bidiHandler = new BidiHandler(this); + + config.resetOptions(this); + this.setMode(mode); + config._signal("session", this); +}; + + +EditSession.$uid = 0; + +(function() { + + oop.implement(this, EventEmitter); + this.setDocument = function(doc) { + if (this.doc) + this.doc.off("change", this.$onChange); + + this.doc = doc; + doc.on("change", this.$onChange); + + if (this.bgTokenizer) + this.bgTokenizer.setDocument(this.getDocument()); + + this.resetCaches(); + }; + this.getDocument = function() { + return this.doc; + }; + this.$resetRowCache = function(docRow) { + if (!docRow) { + this.$docRowCache = []; + this.$screenRowCache = []; + return; + } + var l = this.$docRowCache.length; + var i = this.$getRowCacheIndex(this.$docRowCache, docRow) + 1; + if (l > i) { + this.$docRowCache.splice(i, l); + this.$screenRowCache.splice(i, l); + } + }; + + this.$getRowCacheIndex = function(cacheArray, val) { + var low = 0; + var hi = cacheArray.length - 1; + + while (low <= hi) { + var mid = (low + hi) >> 1; + var c = cacheArray[mid]; + + if (val > c) + low = mid + 1; + else if (val < c) + hi = mid - 1; + else + return mid; + } + + return low -1; + }; + + this.resetCaches = function() { + this.$modified = true; + this.$wrapData = []; + this.$rowLengthCache = []; + this.$resetRowCache(0); + if (this.bgTokenizer) + this.bgTokenizer.start(0); + }; + + this.onChangeFold = function(e) { + var fold = e.data; + this.$resetRowCache(fold.start.row); + }; + + this.onChange = function(delta) { + this.$modified = true; + this.$bidiHandler.onChange(delta); + this.$resetRowCache(delta.start.row); + + var removedFolds = this.$updateInternalDataOnChange(delta); + if (!this.$fromUndo && this.$undoManager) { + if (removedFolds && removedFolds.length) { + this.$undoManager.add({ + action: "removeFolds", + folds: removedFolds + }, this.mergeUndoDeltas); + this.mergeUndoDeltas = true; + } + this.$undoManager.add(delta, this.mergeUndoDeltas); + this.mergeUndoDeltas = true; + + this.$informUndoManager.schedule(); + } + + this.bgTokenizer && this.bgTokenizer.$updateOnChange(delta); + this._signal("change", delta); + }; + this.setValue = function(text) { + this.doc.setValue(text); + this.selection.moveTo(0, 0); + + this.$resetRowCache(0); + this.setUndoManager(this.$undoManager); + this.getUndoManager().reset(); + }; + this.getValue = + this.toString = function() { + return this.doc.getValue(); + }; + this.getSelection = function() { + return this.selection; + }; + this.getState = function(row) { + return this.bgTokenizer.getState(row); + }; + this.getTokens = function(row) { + return this.bgTokenizer.getTokens(row); + }; + this.getTokenAt = function(row, column) { + var tokens = this.bgTokenizer.getTokens(row); + var token, c = 0; + if (column == null) { + var i = tokens.length - 1; + c = this.getLine(row).length; + } else { + for (var i = 0; i < tokens.length; i++) { + c += tokens[i].value.length; + if (c >= column) + break; + } + } + token = tokens[i]; + if (!token) + return null; + token.index = i; + token.start = c - token.value.length; + return token; + }; + this.setUndoManager = function(undoManager) { + this.$undoManager = undoManager; + + if (this.$informUndoManager) + this.$informUndoManager.cancel(); + + if (undoManager) { + var self = this; + undoManager.addSession(this); + this.$syncInformUndoManager = function() { + self.$informUndoManager.cancel(); + self.mergeUndoDeltas = false; + }; + this.$informUndoManager = lang.delayedCall(this.$syncInformUndoManager); + } else { + this.$syncInformUndoManager = function() {}; + } + }; + this.markUndoGroup = function() { + if (this.$syncInformUndoManager) + this.$syncInformUndoManager(); + }; + + this.$defaultUndoManager = { + undo: function() {}, + redo: function() {}, + hasUndo: function() {}, + hasRedo: function() {}, + reset: function() {}, + add: function() {}, + addSelection: function() {}, + startNewGroup: function() {}, + addSession: function() {} + }; + this.getUndoManager = function() { + return this.$undoManager || this.$defaultUndoManager; + }; + this.getTabString = function() { + if (this.getUseSoftTabs()) { + return lang.stringRepeat(" ", this.getTabSize()); + } else { + return "\t"; + } + }; + this.setUseSoftTabs = function(val) { + this.setOption("useSoftTabs", val); + }; + this.getUseSoftTabs = function() { + return this.$useSoftTabs && !this.$mode.$indentWithTabs; + }; + this.setTabSize = function(tabSize) { + this.setOption("tabSize", tabSize); + }; + this.getTabSize = function() { + return this.$tabSize; + }; + this.isTabStop = function(position) { + return this.$useSoftTabs && (position.column % this.$tabSize === 0); + }; + this.setNavigateWithinSoftTabs = function (navigateWithinSoftTabs) { + this.setOption("navigateWithinSoftTabs", navigateWithinSoftTabs); + }; + this.getNavigateWithinSoftTabs = function() { + return this.$navigateWithinSoftTabs; + }; + + this.$overwrite = false; + this.setOverwrite = function(overwrite) { + this.setOption("overwrite", overwrite); + }; + this.getOverwrite = function() { + return this.$overwrite; + }; + this.toggleOverwrite = function() { + this.setOverwrite(!this.$overwrite); + }; + this.addGutterDecoration = function(row, className) { + if (!this.$decorations[row]) + this.$decorations[row] = ""; + this.$decorations[row] += " " + className; + this._signal("changeBreakpoint", {}); + }; + this.removeGutterDecoration = function(row, className) { + this.$decorations[row] = (this.$decorations[row] || "").replace(" " + className, ""); + this._signal("changeBreakpoint", {}); + }; + this.getBreakpoints = function() { + return this.$breakpoints; + }; + this.setBreakpoints = function(rows) { + this.$breakpoints = []; + for (var i=0; i 0) + inToken = !!line.charAt(column - 1).match(this.tokenRe); + + if (!inToken) + inToken = !!line.charAt(column).match(this.tokenRe); + + if (inToken) + var re = this.tokenRe; + else if (/^\s+$/.test(line.slice(column-1, column+1))) + var re = /\s/; + else + var re = this.nonTokenRe; + + var start = column; + if (start > 0) { + do { + start--; + } + while (start >= 0 && line.charAt(start).match(re)); + start++; + } + + var end = column; + while (end < line.length && line.charAt(end).match(re)) { + end++; + } + + return new Range(row, start, row, end); + }; + this.getAWordRange = function(row, column) { + var wordRange = this.getWordRange(row, column); + var line = this.getLine(wordRange.end.row); + + while (line.charAt(wordRange.end.column).match(/[ \t]/)) { + wordRange.end.column += 1; + } + return wordRange; + }; + this.setNewLineMode = function(newLineMode) { + this.doc.setNewLineMode(newLineMode); + }; + this.getNewLineMode = function() { + return this.doc.getNewLineMode(); + }; + this.setUseWorker = function(useWorker) { this.setOption("useWorker", useWorker); }; + this.getUseWorker = function() { return this.$useWorker; }; + this.onReloadTokenizer = function(e) { + var rows = e.data; + this.bgTokenizer.start(rows.first); + this._signal("tokenizerUpdate", e); + }; + + this.$modes = config.$modes; + this.$mode = null; + this.$modeId = null; + this.setMode = function(mode, cb) { + if (mode && typeof mode === "object") { + if (mode.getTokenizer) + return this.$onChangeMode(mode); + var options = mode; + var path = options.path; + } else { + path = mode || "ace/mode/text"; + } + if (!this.$modes["ace/mode/text"]) + this.$modes["ace/mode/text"] = new TextMode(); + + if (this.$modes[path] && !options) { + this.$onChangeMode(this.$modes[path]); + cb && cb(); + return; + } + this.$modeId = path; + config.loadModule(["mode", path], function(m) { + if (this.$modeId !== path) + return cb && cb(); + if (this.$modes[path] && !options) { + this.$onChangeMode(this.$modes[path]); + } else if (m && m.Mode) { + m = new m.Mode(options); + if (!options) { + this.$modes[path] = m; + m.$id = path; + } + this.$onChangeMode(m); + } + cb && cb(); + }.bind(this)); + if (!this.$mode) + this.$onChangeMode(this.$modes["ace/mode/text"], true); + }; + + this.$onChangeMode = function(mode, $isPlaceholder) { + if (!$isPlaceholder) + this.$modeId = mode.$id; + if (this.$mode === mode) + return; + + var oldMode = this.$mode; + this.$mode = mode; + + this.$stopWorker(); + + if (this.$useWorker) + this.$startWorker(); + + var tokenizer = mode.getTokenizer(); + + if(tokenizer.on !== undefined) { + var onReloadTokenizer = this.onReloadTokenizer.bind(this); + tokenizer.on("update", onReloadTokenizer); + } + + if (!this.bgTokenizer) { + this.bgTokenizer = new BackgroundTokenizer(tokenizer); + var _self = this; + this.bgTokenizer.on("update", function(e) { + _self._signal("tokenizerUpdate", e); + }); + } else { + this.bgTokenizer.setTokenizer(tokenizer); + } + + this.bgTokenizer.setDocument(this.getDocument()); + + this.tokenRe = mode.tokenRe; + this.nonTokenRe = mode.nonTokenRe; + + + if (!$isPlaceholder) { + if (mode.attachToSession) + mode.attachToSession(this); + this.$options.wrapMethod.set.call(this, this.$wrapMethod); + this.$setFolding(mode.foldingRules); + this.bgTokenizer.start(0); + this._emit("changeMode", {oldMode: oldMode, mode: mode}); + } + }; + + this.$stopWorker = function() { + if (this.$worker) { + this.$worker.terminate(); + this.$worker = null; + } + }; + + this.$startWorker = function() { + try { + this.$worker = this.$mode.createWorker(this); + } catch (e) { + config.warn("Could not load worker", e); + this.$worker = null; + } + }; + this.getMode = function() { + return this.$mode; + }; + + this.$scrollTop = 0; + this.setScrollTop = function(scrollTop) { + if (this.$scrollTop === scrollTop || isNaN(scrollTop)) + return; + + this.$scrollTop = scrollTop; + this._signal("changeScrollTop", scrollTop); + }; + this.getScrollTop = function() { + return this.$scrollTop; + }; + + this.$scrollLeft = 0; + this.setScrollLeft = function(scrollLeft) { + if (this.$scrollLeft === scrollLeft || isNaN(scrollLeft)) + return; + + this.$scrollLeft = scrollLeft; + this._signal("changeScrollLeft", scrollLeft); + }; + this.getScrollLeft = function() { + return this.$scrollLeft; + }; + this.getScreenWidth = function() { + this.$computeWidth(); + if (this.lineWidgets) + return Math.max(this.getLineWidgetMaxWidth(), this.screenWidth); + return this.screenWidth; + }; + + this.getLineWidgetMaxWidth = function() { + if (this.lineWidgetsWidth != null) return this.lineWidgetsWidth; + var width = 0; + this.lineWidgets.forEach(function(w) { + if (w && w.screenWidth > width) + width = w.screenWidth; + }); + return this.lineWidgetWidth = width; + }; + + this.$computeWidth = function(force) { + if (this.$modified || force) { + this.$modified = false; + + if (this.$useWrapMode) + return this.screenWidth = this.$wrapLimit; + + var lines = this.doc.getAllLines(); + var cache = this.$rowLengthCache; + var longestScreenLine = 0; + var foldIndex = 0; + var foldLine = this.$foldData[foldIndex]; + var foldStart = foldLine ? foldLine.start.row : Infinity; + var len = lines.length; + + for (var i = 0; i < len; i++) { + if (i > foldStart) { + i = foldLine.end.row + 1; + if (i >= len) + break; + foldLine = this.$foldData[foldIndex++]; + foldStart = foldLine ? foldLine.start.row : Infinity; + } + + if (cache[i] == null) + cache[i] = this.$getStringScreenWidth(lines[i])[0]; + + if (cache[i] > longestScreenLine) + longestScreenLine = cache[i]; + } + this.screenWidth = longestScreenLine; + } + }; + this.getLine = function(row) { + return this.doc.getLine(row); + }; + this.getLines = function(firstRow, lastRow) { + return this.doc.getLines(firstRow, lastRow); + }; + this.getLength = function() { + return this.doc.getLength(); + }; + this.getTextRange = function(range) { + return this.doc.getTextRange(range || this.selection.getRange()); + }; + this.insert = function(position, text) { + return this.doc.insert(position, text); + }; + this.remove = function(range) { + return this.doc.remove(range); + }; + this.removeFullLines = function(firstRow, lastRow){ + return this.doc.removeFullLines(firstRow, lastRow); + }; + this.undoChanges = function(deltas, dontSelect) { + if (!deltas.length) + return; + + this.$fromUndo = true; + for (var i = deltas.length - 1; i != -1; i--) { + var delta = deltas[i]; + if (delta.action == "insert" || delta.action == "remove") { + this.doc.revertDelta(delta); + } else if (delta.folds) { + this.addFolds(delta.folds); + } + } + if (!dontSelect && this.$undoSelect) { + if (deltas.selectionBefore) + this.selection.fromJSON(deltas.selectionBefore); + else + this.selection.setRange(this.$getUndoSelection(deltas, true)); + } + this.$fromUndo = false; + }; + this.redoChanges = function(deltas, dontSelect) { + if (!deltas.length) + return; + + this.$fromUndo = true; + for (var i = 0; i < deltas.length; i++) { + var delta = deltas[i]; + if (delta.action == "insert" || delta.action == "remove") { + this.doc.$safeApplyDelta(delta); + } + } + + if (!dontSelect && this.$undoSelect) { + if (deltas.selectionAfter) + this.selection.fromJSON(deltas.selectionAfter); + else + this.selection.setRange(this.$getUndoSelection(deltas, false)); + } + this.$fromUndo = false; + }; + this.setUndoSelect = function(enable) { + this.$undoSelect = enable; + }; + + this.$getUndoSelection = function(deltas, isUndo) { + function isInsert(delta) { + return isUndo ? delta.action !== "insert" : delta.action === "insert"; + } + + var range, point; + + for (var i = 0; i < deltas.length; i++) { + var delta = deltas[i]; + if (!delta.start) continue; // skip folds + if (!range) { + if (isInsert(delta)) { + range = Range.fromPoints(delta.start, delta.end); + } else { + range = Range.fromPoints(delta.start, delta.start); + } + continue; + } + + if (isInsert(delta)) { + point = delta.start; + if (range.compare(point.row, point.column) == -1) { + range.setStart(point); + } + point = delta.end; + if (range.compare(point.row, point.column) == 1) { + range.setEnd(point); + } + } else { + point = delta.start; + if (range.compare(point.row, point.column) == -1) { + range = Range.fromPoints(delta.start, delta.start); + } + } + } + return range; + }; + this.replace = function(range, text) { + return this.doc.replace(range, text); + }; + this.moveText = function(fromRange, toPosition, copy) { + var text = this.getTextRange(fromRange); + var folds = this.getFoldsInRange(fromRange); + + var toRange = Range.fromPoints(toPosition, toPosition); + if (!copy) { + this.remove(fromRange); + var rowDiff = fromRange.start.row - fromRange.end.row; + var collDiff = rowDiff ? -fromRange.end.column : fromRange.start.column - fromRange.end.column; + if (collDiff) { + if (toRange.start.row == fromRange.end.row && toRange.start.column > fromRange.end.column) + toRange.start.column += collDiff; + if (toRange.end.row == fromRange.end.row && toRange.end.column > fromRange.end.column) + toRange.end.column += collDiff; + } + if (rowDiff && toRange.start.row >= fromRange.end.row) { + toRange.start.row += rowDiff; + toRange.end.row += rowDiff; + } + } + + toRange.end = this.insert(toRange.start, text); + if (folds.length) { + var oldStart = fromRange.start; + var newStart = toRange.start; + var rowDiff = newStart.row - oldStart.row; + var collDiff = newStart.column - oldStart.column; + this.addFolds(folds.map(function(x) { + x = x.clone(); + if (x.start.row == oldStart.row) + x.start.column += collDiff; + if (x.end.row == oldStart.row) + x.end.column += collDiff; + x.start.row += rowDiff; + x.end.row += rowDiff; + return x; + })); + } + + return toRange; + }; + this.indentRows = function(startRow, endRow, indentString) { + indentString = indentString.replace(/\t/g, this.getTabString()); + for (var row=startRow; row<=endRow; row++) + this.doc.insertInLine({row: row, column: 0}, indentString); + }; + this.outdentRows = function (range) { + var rowRange = range.collapseRows(); + var deleteRange = new Range(0, 0, 0, 0); + var size = this.getTabSize(); + + for (var i = rowRange.start.row; i <= rowRange.end.row; ++i) { + var line = this.getLine(i); + + deleteRange.start.row = i; + deleteRange.end.row = i; + for (var j = 0; j < size; ++j) + if (line.charAt(j) != ' ') + break; + if (j < size && line.charAt(j) == '\t') { + deleteRange.start.column = j; + deleteRange.end.column = j + 1; + } else { + deleteRange.start.column = 0; + deleteRange.end.column = j; + } + this.remove(deleteRange); + } + }; + + this.$moveLines = function(firstRow, lastRow, dir) { + firstRow = this.getRowFoldStart(firstRow); + lastRow = this.getRowFoldEnd(lastRow); + if (dir < 0) { + var row = this.getRowFoldStart(firstRow + dir); + if (row < 0) return 0; + var diff = row-firstRow; + } else if (dir > 0) { + var row = this.getRowFoldEnd(lastRow + dir); + if (row > this.doc.getLength()-1) return 0; + var diff = row-lastRow; + } else { + firstRow = this.$clipRowToDocument(firstRow); + lastRow = this.$clipRowToDocument(lastRow); + var diff = lastRow - firstRow + 1; + } + + var range = new Range(firstRow, 0, lastRow, Number.MAX_VALUE); + var folds = this.getFoldsInRange(range).map(function(x){ + x = x.clone(); + x.start.row += diff; + x.end.row += diff; + return x; + }); + + var lines = dir == 0 + ? this.doc.getLines(firstRow, lastRow) + : this.doc.removeFullLines(firstRow, lastRow); + this.doc.insertFullLines(firstRow+diff, lines); + folds.length && this.addFolds(folds); + return diff; + }; + this.moveLinesUp = function(firstRow, lastRow) { + return this.$moveLines(firstRow, lastRow, -1); + }; + this.moveLinesDown = function(firstRow, lastRow) { + return this.$moveLines(firstRow, lastRow, 1); + }; + this.duplicateLines = function(firstRow, lastRow) { + return this.$moveLines(firstRow, lastRow, 0); + }; + + + this.$clipRowToDocument = function(row) { + return Math.max(0, Math.min(row, this.doc.getLength()-1)); + }; + + this.$clipColumnToRow = function(row, column) { + if (column < 0) + return 0; + return Math.min(this.doc.getLine(row).length, column); + }; + + + this.$clipPositionToDocument = function(row, column) { + column = Math.max(0, column); + + if (row < 0) { + row = 0; + column = 0; + } else { + var len = this.doc.getLength(); + if (row >= len) { + row = len - 1; + column = this.doc.getLine(len-1).length; + } else { + column = Math.min(this.doc.getLine(row).length, column); + } + } + + return { + row: row, + column: column + }; + }; + + this.$clipRangeToDocument = function(range) { + if (range.start.row < 0) { + range.start.row = 0; + range.start.column = 0; + } else { + range.start.column = this.$clipColumnToRow( + range.start.row, + range.start.column + ); + } + + var len = this.doc.getLength() - 1; + if (range.end.row > len) { + range.end.row = len; + range.end.column = this.doc.getLine(len).length; + } else { + range.end.column = this.$clipColumnToRow( + range.end.row, + range.end.column + ); + } + return range; + }; + this.$wrapLimit = 80; + this.$useWrapMode = false; + this.$wrapLimitRange = { + min : null, + max : null + }; + this.setUseWrapMode = function(useWrapMode) { + if (useWrapMode != this.$useWrapMode) { + this.$useWrapMode = useWrapMode; + this.$modified = true; + this.$resetRowCache(0); + if (useWrapMode) { + var len = this.getLength(); + this.$wrapData = Array(len); + this.$updateWrapData(0, len - 1); + } + + this._signal("changeWrapMode"); + } + }; + this.getUseWrapMode = function() { + return this.$useWrapMode; + }; + this.setWrapLimitRange = function(min, max) { + if (this.$wrapLimitRange.min !== min || this.$wrapLimitRange.max !== max) { + this.$wrapLimitRange = { min: min, max: max }; + this.$modified = true; + this.$bidiHandler.markAsDirty(); + if (this.$useWrapMode) + this._signal("changeWrapMode"); + } + }; + this.adjustWrapLimit = function(desiredLimit, $printMargin) { + var limits = this.$wrapLimitRange; + if (limits.max < 0) + limits = {min: $printMargin, max: $printMargin}; + var wrapLimit = this.$constrainWrapLimit(desiredLimit, limits.min, limits.max); + if (wrapLimit != this.$wrapLimit && wrapLimit > 1) { + this.$wrapLimit = wrapLimit; + this.$modified = true; + if (this.$useWrapMode) { + this.$updateWrapData(0, this.getLength() - 1); + this.$resetRowCache(0); + this._signal("changeWrapLimit"); + } + return true; + } + return false; + }; + + this.$constrainWrapLimit = function(wrapLimit, min, max) { + if (min) + wrapLimit = Math.max(min, wrapLimit); + + if (max) + wrapLimit = Math.min(max, wrapLimit); + + return wrapLimit; + }; + this.getWrapLimit = function() { + return this.$wrapLimit; + }; + this.setWrapLimit = function (limit) { + this.setWrapLimitRange(limit, limit); + }; + this.getWrapLimitRange = function() { + return { + min : this.$wrapLimitRange.min, + max : this.$wrapLimitRange.max + }; + }; + + this.$updateInternalDataOnChange = function(delta) { + var useWrapMode = this.$useWrapMode; + var action = delta.action; + var start = delta.start; + var end = delta.end; + var firstRow = start.row; + var lastRow = end.row; + var len = lastRow - firstRow; + var removedFolds = null; + + this.$updating = true; + if (len != 0) { + if (action === "remove") { + this[useWrapMode ? "$wrapData" : "$rowLengthCache"].splice(firstRow, len); + + var foldLines = this.$foldData; + removedFolds = this.getFoldsInRange(delta); + this.removeFolds(removedFolds); + + var foldLine = this.getFoldLine(end.row); + var idx = 0; + if (foldLine) { + foldLine.addRemoveChars(end.row, end.column, start.column - end.column); + foldLine.shiftRow(-len); + + var foldLineBefore = this.getFoldLine(firstRow); + if (foldLineBefore && foldLineBefore !== foldLine) { + foldLineBefore.merge(foldLine); + foldLine = foldLineBefore; + } + idx = foldLines.indexOf(foldLine) + 1; + } + + for (idx; idx < foldLines.length; idx++) { + var foldLine = foldLines[idx]; + if (foldLine.start.row >= end.row) { + foldLine.shiftRow(-len); + } + } + + lastRow = firstRow; + } else { + var args = Array(len); + args.unshift(firstRow, 0); + var arr = useWrapMode ? this.$wrapData : this.$rowLengthCache; + arr.splice.apply(arr, args); + var foldLines = this.$foldData; + var foldLine = this.getFoldLine(firstRow); + var idx = 0; + if (foldLine) { + var cmp = foldLine.range.compareInside(start.row, start.column); + if (cmp == 0) { + foldLine = foldLine.split(start.row, start.column); + if (foldLine) { + foldLine.shiftRow(len); + foldLine.addRemoveChars(lastRow, 0, end.column - start.column); + } + } else + if (cmp == -1) { + foldLine.addRemoveChars(firstRow, 0, end.column - start.column); + foldLine.shiftRow(len); + } + idx = foldLines.indexOf(foldLine) + 1; + } + + for (idx; idx < foldLines.length; idx++) { + var foldLine = foldLines[idx]; + if (foldLine.start.row >= firstRow) { + foldLine.shiftRow(len); + } + } + } + } else { + len = Math.abs(delta.start.column - delta.end.column); + if (action === "remove") { + removedFolds = this.getFoldsInRange(delta); + this.removeFolds(removedFolds); + + len = -len; + } + var foldLine = this.getFoldLine(firstRow); + if (foldLine) { + foldLine.addRemoveChars(firstRow, start.column, len); + } + } + + if (useWrapMode && this.$wrapData.length != this.doc.getLength()) { + console.error("doc.getLength() and $wrapData.length have to be the same!"); + } + this.$updating = false; + + if (useWrapMode) + this.$updateWrapData(firstRow, lastRow); + else + this.$updateRowLengthCache(firstRow, lastRow); + + return removedFolds; + }; + + this.$updateRowLengthCache = function(firstRow, lastRow, b) { + this.$rowLengthCache[firstRow] = null; + this.$rowLengthCache[lastRow] = null; + }; + + this.$updateWrapData = function(firstRow, lastRow) { + var lines = this.doc.getAllLines(); + var tabSize = this.getTabSize(); + var wrapData = this.$wrapData; + var wrapLimit = this.$wrapLimit; + var tokens; + var foldLine; + + var row = firstRow; + lastRow = Math.min(lastRow, lines.length - 1); + while (row <= lastRow) { + foldLine = this.getFoldLine(row, foldLine); + if (!foldLine) { + tokens = this.$getDisplayTokens(lines[row]); + wrapData[row] = this.$computeWrapSplits(tokens, wrapLimit, tabSize); + row ++; + } else { + tokens = []; + foldLine.walk(function(placeholder, row, column, lastColumn) { + var walkTokens; + if (placeholder != null) { + walkTokens = this.$getDisplayTokens( + placeholder, tokens.length); + walkTokens[0] = PLACEHOLDER_START; + for (var i = 1; i < walkTokens.length; i++) { + walkTokens[i] = PLACEHOLDER_BODY; + } + } else { + walkTokens = this.$getDisplayTokens( + lines[row].substring(lastColumn, column), + tokens.length); + } + tokens = tokens.concat(walkTokens); + }.bind(this), + foldLine.end.row, + lines[foldLine.end.row].length + 1 + ); + + wrapData[foldLine.start.row] = this.$computeWrapSplits(tokens, wrapLimit, tabSize); + row = foldLine.end.row + 1; + } + } + }; + var CHAR = 1, + CHAR_EXT = 2, + PLACEHOLDER_START = 3, + PLACEHOLDER_BODY = 4, + PUNCTUATION = 9, + SPACE = 10, + TAB = 11, + TAB_SPACE = 12; + + + this.$computeWrapSplits = function(tokens, wrapLimit, tabSize) { + if (tokens.length == 0) { + return []; + } + + var splits = []; + var displayLength = tokens.length; + var lastSplit = 0, lastDocSplit = 0; + + var isCode = this.$wrapAsCode; + + var indentedSoftWrap = this.$indentedSoftWrap; + var maxIndent = wrapLimit <= Math.max(2 * tabSize, 8) + || indentedSoftWrap === false ? 0 : Math.floor(wrapLimit / 2); + + function getWrapIndent() { + var indentation = 0; + if (maxIndent === 0) + return indentation; + if (indentedSoftWrap) { + for (var i = 0; i < tokens.length; i++) { + var token = tokens[i]; + if (token == SPACE) + indentation += 1; + else if (token == TAB) + indentation += tabSize; + else if (token == TAB_SPACE) + continue; + else + break; + } + } + if (isCode && indentedSoftWrap !== false) + indentation += tabSize; + return Math.min(indentation, maxIndent); + } + function addSplit(screenPos) { + var len = screenPos - lastSplit; + for (var i = lastSplit; i < screenPos; i++) { + var ch = tokens[i]; + if (ch === 12 || ch === 2) len -= 1; + } + + if (!splits.length) { + indent = getWrapIndent(); + splits.indent = indent; + } + lastDocSplit += len; + splits.push(lastDocSplit); + lastSplit = screenPos; + } + var indent = 0; + while (displayLength - lastSplit > wrapLimit - indent) { + var split = lastSplit + wrapLimit - indent; + if (tokens[split - 1] >= SPACE && tokens[split] >= SPACE) { + addSplit(split); + continue; + } + if (tokens[split] == PLACEHOLDER_START || tokens[split] == PLACEHOLDER_BODY) { + for (split; split != lastSplit - 1; split--) { + if (tokens[split] == PLACEHOLDER_START) { + break; + } + } + if (split > lastSplit) { + addSplit(split); + continue; + } + split = lastSplit + wrapLimit; + for (split; split < tokens.length; split++) { + if (tokens[split] != PLACEHOLDER_BODY) { + break; + } + } + if (split == tokens.length) { + break; // Breaks the while-loop. + } + addSplit(split); + continue; + } + var minSplit = Math.max(split - (wrapLimit -(wrapLimit>>2)), lastSplit - 1); + while (split > minSplit && tokens[split] < PLACEHOLDER_START) { + split --; + } + if (isCode) { + while (split > minSplit && tokens[split] < PLACEHOLDER_START) { + split --; + } + while (split > minSplit && tokens[split] == PUNCTUATION) { + split --; + } + } else { + while (split > minSplit && tokens[split] < SPACE) { + split --; + } + } + if (split > minSplit) { + addSplit(++split); + continue; + } + split = lastSplit + wrapLimit; + if (tokens[split] == CHAR_EXT) + split--; + addSplit(split - indent); + } + return splits; + }; + this.$getDisplayTokens = function(str, offset) { + var arr = []; + var tabSize; + offset = offset || 0; + + for (var i = 0; i < str.length; i++) { + var c = str.charCodeAt(i); + if (c == 9) { + tabSize = this.getScreenTabSize(arr.length + offset); + arr.push(TAB); + for (var n = 1; n < tabSize; n++) { + arr.push(TAB_SPACE); + } + } + else if (c == 32) { + arr.push(SPACE); + } else if((c > 39 && c < 48) || (c > 57 && c < 64)) { + arr.push(PUNCTUATION); + } + else if (c >= 0x1100 && isFullWidth(c)) { + arr.push(CHAR, CHAR_EXT); + } else { + arr.push(CHAR); + } + } + return arr; + }; + this.$getStringScreenWidth = function(str, maxScreenColumn, screenColumn) { + if (maxScreenColumn == 0) + return [0, 0]; + if (maxScreenColumn == null) + maxScreenColumn = Infinity; + screenColumn = screenColumn || 0; + + var c, column; + for (column = 0; column < str.length; column++) { + c = str.charCodeAt(column); + if (c == 9) { + screenColumn += this.getScreenTabSize(screenColumn); + } + else if (c >= 0x1100 && isFullWidth(c)) { + screenColumn += 2; + } else { + screenColumn += 1; + } + if (screenColumn > maxScreenColumn) { + break; + } + } + + return [screenColumn, column]; + }; + + this.lineWidgets = null; + this.getRowLength = function(row) { + var h = 1; + if (this.lineWidgets) + h += this.lineWidgets[row] && this.lineWidgets[row].rowCount || 0; + + if (!this.$useWrapMode || !this.$wrapData[row]) + return h; + else + return this.$wrapData[row].length + h; + }; + this.getRowLineCount = function(row) { + if (!this.$useWrapMode || !this.$wrapData[row]) { + return 1; + } else { + return this.$wrapData[row].length + 1; + } + }; + + this.getRowWrapIndent = function(screenRow) { + if (this.$useWrapMode) { + var pos = this.screenToDocumentPosition(screenRow, Number.MAX_VALUE); + var splits = this.$wrapData[pos.row]; + return splits.length && splits[0] < pos.column ? splits.indent : 0; + } else { + return 0; + } + }; + this.getScreenLastRowColumn = function(screenRow) { + var pos = this.screenToDocumentPosition(screenRow, Number.MAX_VALUE); + return this.documentToScreenColumn(pos.row, pos.column); + }; + this.getDocumentLastRowColumn = function(docRow, docColumn) { + var screenRow = this.documentToScreenRow(docRow, docColumn); + return this.getScreenLastRowColumn(screenRow); + }; + this.getDocumentLastRowColumnPosition = function(docRow, docColumn) { + var screenRow = this.documentToScreenRow(docRow, docColumn); + return this.screenToDocumentPosition(screenRow, Number.MAX_VALUE / 10); + }; + this.getRowSplitData = function(row) { + if (!this.$useWrapMode) { + return undefined; + } else { + return this.$wrapData[row]; + } + }; + this.getScreenTabSize = function(screenColumn) { + return this.$tabSize - (screenColumn % this.$tabSize | 0); + }; + + + this.screenToDocumentRow = function(screenRow, screenColumn) { + return this.screenToDocumentPosition(screenRow, screenColumn).row; + }; + + + this.screenToDocumentColumn = function(screenRow, screenColumn) { + return this.screenToDocumentPosition(screenRow, screenColumn).column; + }; + this.screenToDocumentPosition = function(screenRow, screenColumn, offsetX) { + if (screenRow < 0) + return {row: 0, column: 0}; + + var line; + var docRow = 0; + var docColumn = 0; + var column; + var row = 0; + var rowLength = 0; + + var rowCache = this.$screenRowCache; + var i = this.$getRowCacheIndex(rowCache, screenRow); + var l = rowCache.length; + if (l && i >= 0) { + var row = rowCache[i]; + var docRow = this.$docRowCache[i]; + var doCache = screenRow > rowCache[l - 1]; + } else { + var doCache = !l; + } + + var maxRow = this.getLength() - 1; + var foldLine = this.getNextFoldLine(docRow); + var foldStart = foldLine ? foldLine.start.row : Infinity; + + while (row <= screenRow) { + rowLength = this.getRowLength(docRow); + if (row + rowLength > screenRow || docRow >= maxRow) { + break; + } else { + row += rowLength; + docRow++; + if (docRow > foldStart) { + docRow = foldLine.end.row+1; + foldLine = this.getNextFoldLine(docRow, foldLine); + foldStart = foldLine ? foldLine.start.row : Infinity; + } + } + + if (doCache) { + this.$docRowCache.push(docRow); + this.$screenRowCache.push(row); + } + } + + if (foldLine && foldLine.start.row <= docRow) { + line = this.getFoldDisplayLine(foldLine); + docRow = foldLine.start.row; + } else if (row + rowLength <= screenRow || docRow > maxRow) { + return { + row: maxRow, + column: this.getLine(maxRow).length + }; + } else { + line = this.getLine(docRow); + foldLine = null; + } + var wrapIndent = 0, splitIndex = Math.floor(screenRow - row); + if (this.$useWrapMode) { + var splits = this.$wrapData[docRow]; + if (splits) { + column = splits[splitIndex]; + if(splitIndex > 0 && splits.length) { + wrapIndent = splits.indent; + docColumn = splits[splitIndex - 1] || splits[splits.length - 1]; + line = line.substring(docColumn); + } + } + } + + if (offsetX !== undefined && this.$bidiHandler.isBidiRow(row + splitIndex, docRow, splitIndex)) + screenColumn = this.$bidiHandler.offsetToCol(offsetX); + + docColumn += this.$getStringScreenWidth(line, screenColumn - wrapIndent)[1]; + if (this.$useWrapMode && docColumn >= column) + docColumn = column - 1; + + if (foldLine) + return foldLine.idxToPosition(docColumn); + + return {row: docRow, column: docColumn}; + }; + this.documentToScreenPosition = function(docRow, docColumn) { + if (typeof docColumn === "undefined") + var pos = this.$clipPositionToDocument(docRow.row, docRow.column); + else + pos = this.$clipPositionToDocument(docRow, docColumn); + + docRow = pos.row; + docColumn = pos.column; + + var screenRow = 0; + var foldStartRow = null; + var fold = null; + fold = this.getFoldAt(docRow, docColumn, 1); + if (fold) { + docRow = fold.start.row; + docColumn = fold.start.column; + } + + var rowEnd, row = 0; + + + var rowCache = this.$docRowCache; + var i = this.$getRowCacheIndex(rowCache, docRow); + var l = rowCache.length; + if (l && i >= 0) { + var row = rowCache[i]; + var screenRow = this.$screenRowCache[i]; + var doCache = docRow > rowCache[l - 1]; + } else { + var doCache = !l; + } + + var foldLine = this.getNextFoldLine(row); + var foldStart = foldLine ?foldLine.start.row :Infinity; + + while (row < docRow) { + if (row >= foldStart) { + rowEnd = foldLine.end.row + 1; + if (rowEnd > docRow) + break; + foldLine = this.getNextFoldLine(rowEnd, foldLine); + foldStart = foldLine ?foldLine.start.row :Infinity; + } + else { + rowEnd = row + 1; + } + + screenRow += this.getRowLength(row); + row = rowEnd; + + if (doCache) { + this.$docRowCache.push(row); + this.$screenRowCache.push(screenRow); + } + } + var textLine = ""; + if (foldLine && row >= foldStart) { + textLine = this.getFoldDisplayLine(foldLine, docRow, docColumn); + foldStartRow = foldLine.start.row; + } else { + textLine = this.getLine(docRow).substring(0, docColumn); + foldStartRow = docRow; + } + var wrapIndent = 0; + if (this.$useWrapMode) { + var wrapRow = this.$wrapData[foldStartRow]; + if (wrapRow) { + var screenRowOffset = 0; + while (textLine.length >= wrapRow[screenRowOffset]) { + screenRow ++; + screenRowOffset++; + } + textLine = textLine.substring( + wrapRow[screenRowOffset - 1] || 0, textLine.length + ); + wrapIndent = screenRowOffset > 0 ? wrapRow.indent : 0; + } + } + + if (this.lineWidgets && this.lineWidgets[row] && this.lineWidgets[row].rowsAbove) + screenRow += this.lineWidgets[row].rowsAbove; + + return { + row: screenRow, + column: wrapIndent + this.$getStringScreenWidth(textLine)[0] + }; + }; + this.documentToScreenColumn = function(row, docColumn) { + return this.documentToScreenPosition(row, docColumn).column; + }; + this.documentToScreenRow = function(docRow, docColumn) { + return this.documentToScreenPosition(docRow, docColumn).row; + }; + this.getScreenLength = function() { + var screenRows = 0; + var fold = null; + if (!this.$useWrapMode) { + screenRows = this.getLength(); + var foldData = this.$foldData; + for (var i = 0; i < foldData.length; i++) { + fold = foldData[i]; + screenRows -= fold.end.row - fold.start.row; + } + } else { + var lastRow = this.$wrapData.length; + var row = 0, i = 0; + var fold = this.$foldData[i++]; + var foldStart = fold ? fold.start.row :Infinity; + + while (row < lastRow) { + var splits = this.$wrapData[row]; + screenRows += splits ? splits.length + 1 : 1; + row ++; + if (row > foldStart) { + row = fold.end.row+1; + fold = this.$foldData[i++]; + foldStart = fold ?fold.start.row :Infinity; + } + } + } + if (this.lineWidgets) + screenRows += this.$getWidgetScreenLength(); + + return screenRows; + }; + this.$setFontMetrics = function(fm) { + if (!this.$enableVarChar) return; + this.$getStringScreenWidth = function(str, maxScreenColumn, screenColumn) { + if (maxScreenColumn === 0) + return [0, 0]; + if (!maxScreenColumn) + maxScreenColumn = Infinity; + screenColumn = screenColumn || 0; + + var c, column; + for (column = 0; column < str.length; column++) { + c = str.charAt(column); + if (c === "\t") { + screenColumn += this.getScreenTabSize(screenColumn); + } else { + screenColumn += fm.getCharacterWidth(c); + } + if (screenColumn > maxScreenColumn) { + break; + } + } + + return [screenColumn, column]; + }; + }; + + this.destroy = function() { + if (this.bgTokenizer) { + this.bgTokenizer.setDocument(null); + this.bgTokenizer = null; + } + this.$stopWorker(); + this.removeAllListeners(); + if (this.doc) { + this.doc.off("change", this.$onChange); + } + this.selection.detach(); + }; + + this.isFullWidth = isFullWidth; + function isFullWidth(c) { + if (c < 0x1100) + return false; + return c >= 0x1100 && c <= 0x115F || + c >= 0x11A3 && c <= 0x11A7 || + c >= 0x11FA && c <= 0x11FF || + c >= 0x2329 && c <= 0x232A || + c >= 0x2E80 && c <= 0x2E99 || + c >= 0x2E9B && c <= 0x2EF3 || + c >= 0x2F00 && c <= 0x2FD5 || + c >= 0x2FF0 && c <= 0x2FFB || + c >= 0x3000 && c <= 0x303E || + c >= 0x3041 && c <= 0x3096 || + c >= 0x3099 && c <= 0x30FF || + c >= 0x3105 && c <= 0x312D || + c >= 0x3131 && c <= 0x318E || + c >= 0x3190 && c <= 0x31BA || + c >= 0x31C0 && c <= 0x31E3 || + c >= 0x31F0 && c <= 0x321E || + c >= 0x3220 && c <= 0x3247 || + c >= 0x3250 && c <= 0x32FE || + c >= 0x3300 && c <= 0x4DBF || + c >= 0x4E00 && c <= 0xA48C || + c >= 0xA490 && c <= 0xA4C6 || + c >= 0xA960 && c <= 0xA97C || + c >= 0xAC00 && c <= 0xD7A3 || + c >= 0xD7B0 && c <= 0xD7C6 || + c >= 0xD7CB && c <= 0xD7FB || + c >= 0xF900 && c <= 0xFAFF || + c >= 0xFE10 && c <= 0xFE19 || + c >= 0xFE30 && c <= 0xFE52 || + c >= 0xFE54 && c <= 0xFE66 || + c >= 0xFE68 && c <= 0xFE6B || + c >= 0xFF01 && c <= 0xFF60 || + c >= 0xFFE0 && c <= 0xFFE6; + } + +}).call(EditSession.prototype); + +require("./edit_session/folding").Folding.call(EditSession.prototype); +require("./edit_session/bracket_match").BracketMatch.call(EditSession.prototype); + + +config.defineOptions(EditSession.prototype, "session", { + wrap: { + set: function(value) { + if (!value || value == "off") + value = false; + else if (value == "free") + value = true; + else if (value == "printMargin") + value = -1; + else if (typeof value == "string") + value = parseInt(value, 10) || false; + + if (this.$wrap == value) + return; + this.$wrap = value; + if (!value) { + this.setUseWrapMode(false); + } else { + var col = typeof value == "number" ? value : null; + this.setWrapLimitRange(col, col); + this.setUseWrapMode(true); + } + }, + get: function() { + if (this.getUseWrapMode()) { + if (this.$wrap == -1) + return "printMargin"; + if (!this.getWrapLimitRange().min) + return "free"; + return this.$wrap; + } + return "off"; + }, + handlesSet: true + }, + wrapMethod: { + set: function(val) { + val = val == "auto" + ? this.$mode.type != "text" + : val != "text"; + if (val != this.$wrapAsCode) { + this.$wrapAsCode = val; + if (this.$useWrapMode) { + this.$useWrapMode = false; + this.setUseWrapMode(true); + } + } + }, + initialValue: "auto" + }, + indentedSoftWrap: { + set: function() { + if (this.$useWrapMode) { + this.$useWrapMode = false; + this.setUseWrapMode(true); + } + }, + initialValue: true + }, + firstLineNumber: { + set: function() {this._signal("changeBreakpoint");}, + initialValue: 1 + }, + useWorker: { + set: function(useWorker) { + this.$useWorker = useWorker; + + this.$stopWorker(); + if (useWorker) + this.$startWorker(); + }, + initialValue: true + }, + useSoftTabs: {initialValue: true}, + tabSize: { + set: function(tabSize) { + tabSize = parseInt(tabSize); + if (tabSize > 0 && this.$tabSize !== tabSize) { + this.$modified = true; + this.$rowLengthCache = []; + this.$tabSize = tabSize; + this._signal("changeTabSize"); + } + }, + initialValue: 4, + handlesSet: true + }, + navigateWithinSoftTabs: {initialValue: false}, + foldStyle: { + set: function(val) {this.setFoldStyle(val);}, + handlesSet: true + }, + overwrite: { + set: function(val) {this._signal("changeOverwrite");}, + initialValue: false + }, + newLineMode: { + set: function(val) {this.doc.setNewLineMode(val);}, + get: function() {return this.doc.getNewLineMode();}, + handlesSet: true + }, + mode: { + set: function(val) { this.setMode(val); }, + get: function() { return this.$modeId; }, + handlesSet: true + } +}); + +exports.EditSession = EditSession; +}); + +ace.define("ace/search",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"], function(require, exports, module) { +"use strict"; + +var lang = require("./lib/lang"); +var oop = require("./lib/oop"); +var Range = require("./range").Range; + +var Search = function() { + this.$options = {}; +}; + +(function() { + this.set = function(options) { + oop.mixin(this.$options, options); + return this; + }; + this.getOptions = function() { + return lang.copyObject(this.$options); + }; + this.setOptions = function(options) { + this.$options = options; + }; + this.find = function(session) { + var options = this.$options; + var iterator = this.$matchIterator(session, options); + if (!iterator) + return false; + + var firstRange = null; + iterator.forEach(function(sr, sc, er, ec) { + firstRange = new Range(sr, sc, er, ec); + if (sc == ec && options.start && options.start.start + && options.skipCurrent != false && firstRange.isEqual(options.start) + ) { + firstRange = null; + return false; + } + + return true; + }); + + return firstRange; + }; + this.findAll = function(session) { + var options = this.$options; + if (!options.needle) + return []; + this.$assembleRegExp(options); + + var range = options.range; + var lines = range + ? session.getLines(range.start.row, range.end.row) + : session.doc.getAllLines(); + + var ranges = []; + var re = options.re; + if (options.$isMultiLine) { + var len = re.length; + var maxRow = lines.length - len; + var prevRange; + outer: for (var row = re.offset || 0; row <= maxRow; row++) { + for (var j = 0; j < len; j++) + if (lines[row + j].search(re[j]) == -1) + continue outer; + + var startLine = lines[row]; + var line = lines[row + len - 1]; + var startIndex = startLine.length - startLine.match(re[0])[0].length; + var endIndex = line.match(re[len - 1])[0].length; + + if (prevRange && prevRange.end.row === row && + prevRange.end.column > startIndex + ) { + continue; + } + ranges.push(prevRange = new Range( + row, startIndex, row + len - 1, endIndex + )); + if (len > 2) + row = row + len - 2; + } + } else { + for (var i = 0; i < lines.length; i++) { + var matches = lang.getMatchOffsets(lines[i], re); + for (var j = 0; j < matches.length; j++) { + var match = matches[j]; + ranges.push(new Range(i, match.offset, i, match.offset + match.length)); + } + } + } + + if (range) { + var startColumn = range.start.column; + var endColumn = range.start.column; + var i = 0, j = ranges.length - 1; + while (i < j && ranges[i].start.column < startColumn && ranges[i].start.row == range.start.row) + i++; + + while (i < j && ranges[j].end.column > endColumn && ranges[j].end.row == range.end.row) + j--; + + ranges = ranges.slice(i, j + 1); + for (i = 0, j = ranges.length; i < j; i++) { + ranges[i].start.row += range.start.row; + ranges[i].end.row += range.start.row; + } + } + + return ranges; + }; + this.replace = function(input, replacement) { + var options = this.$options; + + var re = this.$assembleRegExp(options); + if (options.$isMultiLine) + return replacement; + + if (!re) + return; + + var match = re.exec(input); + if (!match || match[0].length != input.length) + return null; + + replacement = input.replace(re, replacement); + if (options.preserveCase) { + replacement = replacement.split(""); + for (var i = Math.min(input.length, input.length); i--; ) { + var ch = input[i]; + if (ch && ch.toLowerCase() != ch) + replacement[i] = replacement[i].toUpperCase(); + else + replacement[i] = replacement[i].toLowerCase(); + } + replacement = replacement.join(""); + } + + return replacement; + }; + + this.$assembleRegExp = function(options, $disableFakeMultiline) { + if (options.needle instanceof RegExp) + return options.re = options.needle; + + var needle = options.needle; + + if (!options.needle) + return options.re = false; + + if (!options.regExp) + needle = lang.escapeRegExp(needle); + + if (options.wholeWord) + needle = addWordBoundary(needle, options); + + var modifier = options.caseSensitive ? "gm" : "gmi"; + + options.$isMultiLine = !$disableFakeMultiline && /[\n\r]/.test(needle); + if (options.$isMultiLine) + return options.re = this.$assembleMultilineRegExp(needle, modifier); + + try { + var re = new RegExp(needle, modifier); + } catch(e) { + re = false; + } + return options.re = re; + }; + + this.$assembleMultilineRegExp = function(needle, modifier) { + var parts = needle.replace(/\r\n|\r|\n/g, "$\n^").split("\n"); + var re = []; + for (var i = 0; i < parts.length; i++) try { + re.push(new RegExp(parts[i], modifier)); + } catch(e) { + return false; + } + return re; + }; + + this.$matchIterator = function(session, options) { + var re = this.$assembleRegExp(options); + if (!re) + return false; + var backwards = options.backwards == true; + var skipCurrent = options.skipCurrent != false; + + var range = options.range; + var start = options.start; + if (!start) + start = range ? range[backwards ? "end" : "start"] : session.selection.getRange(); + + if (start.start) + start = start[skipCurrent != backwards ? "end" : "start"]; + + var firstRow = range ? range.start.row : 0; + var lastRow = range ? range.end.row : session.getLength() - 1; + + if (backwards) { + var forEach = function(callback) { + var row = start.row; + if (forEachInLine(row, start.column, callback)) + return; + for (row--; row >= firstRow; row--) + if (forEachInLine(row, Number.MAX_VALUE, callback)) + return; + if (options.wrap == false) + return; + for (row = lastRow, firstRow = start.row; row >= firstRow; row--) + if (forEachInLine(row, Number.MAX_VALUE, callback)) + return; + }; + } + else { + var forEach = function(callback) { + var row = start.row; + if (forEachInLine(row, start.column, callback)) + return; + for (row = row + 1; row <= lastRow; row++) + if (forEachInLine(row, 0, callback)) + return; + if (options.wrap == false) + return; + for (row = firstRow, lastRow = start.row; row <= lastRow; row++) + if (forEachInLine(row, 0, callback)) + return; + }; + } + + if (options.$isMultiLine) { + var len = re.length; + var forEachInLine = function(row, offset, callback) { + var startRow = backwards ? row - len + 1 : row; + if (startRow < 0 || startRow + len > session.getLength()) return; + var line = session.getLine(startRow); + var startIndex = line.search(re[0]); + if (!backwards && startIndex < offset || startIndex === -1) return; + for (var i = 1; i < len; i++) { + line = session.getLine(startRow + i); + if (line.search(re[i]) == -1) + return; + } + var endIndex = line.match(re[len - 1])[0].length; + if (backwards && endIndex > offset) return; + if (callback(startRow, startIndex, startRow + len - 1, endIndex)) + return true; + }; + } + else if (backwards) { + var forEachInLine = function(row, endIndex, callback) { + var line = session.getLine(row); + var matches = []; + var m, last = 0; + re.lastIndex = 0; + while((m = re.exec(line))) { + var length = m[0].length; + last = m.index; + if (!length) { + if (last >= line.length) break; + re.lastIndex = last += 1; + } + if (m.index + length > endIndex) + break; + matches.push(m.index, length); + } + for (var i = matches.length - 1; i >= 0; i -= 2) { + var column = matches[i - 1]; + var length = matches[i]; + if (callback(row, column, row, column + length)) + return true; + } + }; + } + else { + var forEachInLine = function(row, startIndex, callback) { + var line = session.getLine(row); + var last; + var m; + re.lastIndex = startIndex; + while((m = re.exec(line))) { + var length = m[0].length; + last = m.index; + if (callback(row, last, row,last + length)) + return true; + if (!length) { + re.lastIndex = last += 1; + if (last >= line.length) return false; + } + } + }; + } + return {forEach: forEach}; + }; + +}).call(Search.prototype); + +function addWordBoundary(needle, options) { + function wordBoundary(c) { + if (/\w/.test(c) || options.regExp) return "\\b"; + return ""; + } + return wordBoundary(needle[0]) + needle + + wordBoundary(needle[needle.length - 1]); +} + +exports.Search = Search; +}); + +ace.define("ace/keyboard/hash_handler",["require","exports","module","ace/lib/keys","ace/lib/useragent"], function(require, exports, module) { +"use strict"; + +var keyUtil = require("../lib/keys"); +var useragent = require("../lib/useragent"); +var KEY_MODS = keyUtil.KEY_MODS; + +function HashHandler(config, platform) { + this.platform = platform || (useragent.isMac ? "mac" : "win"); + this.commands = {}; + this.commandKeyBinding = {}; + this.addCommands(config); + this.$singleCommand = true; +} + +function MultiHashHandler(config, platform) { + HashHandler.call(this, config, platform); + this.$singleCommand = false; +} + +MultiHashHandler.prototype = HashHandler.prototype; + +(function() { + + + this.addCommand = function(command) { + if (this.commands[command.name]) + this.removeCommand(command); + + this.commands[command.name] = command; + + if (command.bindKey) + this._buildKeyHash(command); + }; + + this.removeCommand = function(command, keepCommand) { + var name = command && (typeof command === 'string' ? command : command.name); + command = this.commands[name]; + if (!keepCommand) + delete this.commands[name]; + var ckb = this.commandKeyBinding; + for (var keyId in ckb) { + var cmdGroup = ckb[keyId]; + if (cmdGroup == command) { + delete ckb[keyId]; + } else if (Array.isArray(cmdGroup)) { + var i = cmdGroup.indexOf(command); + if (i != -1) { + cmdGroup.splice(i, 1); + if (cmdGroup.length == 1) + ckb[keyId] = cmdGroup[0]; + } + } + } + }; + + this.bindKey = function(key, command, position) { + if (typeof key == "object" && key) { + if (position == undefined) + position = key.position; + key = key[this.platform]; + } + if (!key) + return; + if (typeof command == "function") + return this.addCommand({exec: command, bindKey: key, name: command.name || key}); + + key.split("|").forEach(function(keyPart) { + var chain = ""; + if (keyPart.indexOf(" ") != -1) { + var parts = keyPart.split(/\s+/); + keyPart = parts.pop(); + parts.forEach(function(keyPart) { + var binding = this.parseKeys(keyPart); + var id = KEY_MODS[binding.hashId] + binding.key; + chain += (chain ? " " : "") + id; + this._addCommandToBinding(chain, "chainKeys"); + }, this); + chain += " "; + } + var binding = this.parseKeys(keyPart); + var id = KEY_MODS[binding.hashId] + binding.key; + this._addCommandToBinding(chain + id, command, position); + }, this); + }; + + function getPosition(command) { + return typeof command == "object" && command.bindKey + && command.bindKey.position + || (command.isDefault ? -100 : 0); + } + this._addCommandToBinding = function(keyId, command, position) { + var ckb = this.commandKeyBinding, i; + if (!command) { + delete ckb[keyId]; + } else if (!ckb[keyId] || this.$singleCommand) { + ckb[keyId] = command; + } else { + if (!Array.isArray(ckb[keyId])) { + ckb[keyId] = [ckb[keyId]]; + } else if ((i = ckb[keyId].indexOf(command)) != -1) { + ckb[keyId].splice(i, 1); + } + + if (typeof position != "number") { + position = getPosition(command); + } + + var commands = ckb[keyId]; + for (i = 0; i < commands.length; i++) { + var other = commands[i]; + var otherPos = getPosition(other); + if (otherPos > position) + break; + } + commands.splice(i, 0, command); + } + }; + + this.addCommands = function(commands) { + commands && Object.keys(commands).forEach(function(name) { + var command = commands[name]; + if (!command) + return; + + if (typeof command === "string") + return this.bindKey(command, name); + + if (typeof command === "function") + command = { exec: command }; + + if (typeof command !== "object") + return; + + if (!command.name) + command.name = name; + + this.addCommand(command); + }, this); + }; + + this.removeCommands = function(commands) { + Object.keys(commands).forEach(function(name) { + this.removeCommand(commands[name]); + }, this); + }; + + this.bindKeys = function(keyList) { + Object.keys(keyList).forEach(function(key) { + this.bindKey(key, keyList[key]); + }, this); + }; + + this._buildKeyHash = function(command) { + this.bindKey(command.bindKey, command); + }; + this.parseKeys = function(keys) { + var parts = keys.toLowerCase().split(/[\-\+]([\-\+])?/).filter(function(x){return x;}); + var key = parts.pop(); + + var keyCode = keyUtil[key]; + if (keyUtil.FUNCTION_KEYS[keyCode]) + key = keyUtil.FUNCTION_KEYS[keyCode].toLowerCase(); + else if (!parts.length) + return {key: key, hashId: -1}; + else if (parts.length == 1 && parts[0] == "shift") + return {key: key.toUpperCase(), hashId: -1}; + + var hashId = 0; + for (var i = parts.length; i--;) { + var modifier = keyUtil.KEY_MODS[parts[i]]; + if (modifier == null) { + if (typeof console != "undefined") + console.error("invalid modifier " + parts[i] + " in " + keys); + return false; + } + hashId |= modifier; + } + return {key: key, hashId: hashId}; + }; + + this.findKeyCommand = function findKeyCommand(hashId, keyString) { + var key = KEY_MODS[hashId] + keyString; + return this.commandKeyBinding[key]; + }; + + this.handleKeyboard = function(data, hashId, keyString, keyCode) { + if (keyCode < 0) return; + var key = KEY_MODS[hashId] + keyString; + var command = this.commandKeyBinding[key]; + if (data.$keyChain) { + data.$keyChain += " " + key; + command = this.commandKeyBinding[data.$keyChain] || command; + } + + if (command) { + if (command == "chainKeys" || command[command.length - 1] == "chainKeys") { + data.$keyChain = data.$keyChain || key; + return {command: "null"}; + } + } + + if (data.$keyChain) { + if ((!hashId || hashId == 4) && keyString.length == 1) + data.$keyChain = data.$keyChain.slice(0, -key.length - 1); // wait for input + else if (hashId == -1 || keyCode > 0) + data.$keyChain = ""; // reset keyChain + } + return {command: command}; + }; + + this.getStatusText = function(editor, data) { + return data.$keyChain || ""; + }; + +}).call(HashHandler.prototype); + +exports.HashHandler = HashHandler; +exports.MultiHashHandler = MultiHashHandler; +}); + +ace.define("ace/commands/command_manager",["require","exports","module","ace/lib/oop","ace/keyboard/hash_handler","ace/lib/event_emitter"], function(require, exports, module) { +"use strict"; + +var oop = require("../lib/oop"); +var MultiHashHandler = require("../keyboard/hash_handler").MultiHashHandler; +var EventEmitter = require("../lib/event_emitter").EventEmitter; + +var CommandManager = function(platform, commands) { + MultiHashHandler.call(this, commands, platform); + this.byName = this.commands; + this.setDefaultHandler("exec", function(e) { + return e.command.exec(e.editor, e.args || {}); + }); +}; + +oop.inherits(CommandManager, MultiHashHandler); + +(function() { + + oop.implement(this, EventEmitter); + + this.exec = function(command, editor, args) { + if (Array.isArray(command)) { + for (var i = command.length; i--; ) { + if (this.exec(command[i], editor, args)) return true; + } + return false; + } + + if (typeof command === "string") + command = this.commands[command]; + + if (!command) + return false; + + if (editor && editor.$readOnly && !command.readOnly) + return false; + + if (this.$checkCommandState != false && command.isAvailable && !command.isAvailable(editor)) + return false; + + var e = {editor: editor, command: command, args: args}; + e.returnValue = this._emit("exec", e); + this._signal("afterExec", e); + + return e.returnValue === false ? false : true; + }; + + this.toggleRecording = function(editor) { + if (this.$inReplay) + return; + + editor && editor._emit("changeStatus"); + if (this.recording) { + this.macro.pop(); + this.off("exec", this.$addCommandToMacro); + + if (!this.macro.length) + this.macro = this.oldMacro; + + return this.recording = false; + } + if (!this.$addCommandToMacro) { + this.$addCommandToMacro = function(e) { + this.macro.push([e.command, e.args]); + }.bind(this); + } + + this.oldMacro = this.macro; + this.macro = []; + this.on("exec", this.$addCommandToMacro); + return this.recording = true; + }; + + this.replay = function(editor) { + if (this.$inReplay || !this.macro) + return; + + if (this.recording) + return this.toggleRecording(editor); + + try { + this.$inReplay = true; + this.macro.forEach(function(x) { + if (typeof x == "string") + this.exec(x, editor); + else + this.exec(x[0], editor, x[1]); + }, this); + } finally { + this.$inReplay = false; + } + }; + + this.trimMacro = function(m) { + return m.map(function(x){ + if (typeof x[0] != "string") + x[0] = x[0].name; + if (!x[1]) + x = x[0]; + return x; + }); + }; + +}).call(CommandManager.prototype); + +exports.CommandManager = CommandManager; + +}); + +ace.define("ace/commands/default_commands",["require","exports","module","ace/lib/lang","ace/config","ace/range"], function(require, exports, module) { +"use strict"; + +var lang = require("../lib/lang"); +var config = require("../config"); +var Range = require("../range").Range; + +function bindKey(win, mac) { + return {win: win, mac: mac}; +} +exports.commands = [{ + name: "showSettingsMenu", + description: "Show settings menu", + bindKey: bindKey("Ctrl-,", "Command-,"), + exec: function(editor) { + config.loadModule("ace/ext/settings_menu", function(module) { + module.init(editor); + editor.showSettingsMenu(); + }); + }, + readOnly: true +}, { + name: "goToNextError", + description: "Go to next error", + bindKey: bindKey("Alt-E", "F4"), + exec: function(editor) { + config.loadModule("./ext/error_marker", function(module) { + module.showErrorMarker(editor, 1); + }); + }, + scrollIntoView: "animate", + readOnly: true +}, { + name: "goToPreviousError", + description: "Go to previous error", + bindKey: bindKey("Alt-Shift-E", "Shift-F4"), + exec: function(editor) { + config.loadModule("./ext/error_marker", function(module) { + module.showErrorMarker(editor, -1); + }); + }, + scrollIntoView: "animate", + readOnly: true +}, { + name: "selectall", + description: "Select all", + bindKey: bindKey("Ctrl-A", "Command-A"), + exec: function(editor) { editor.selectAll(); }, + readOnly: true +}, { + name: "centerselection", + description: "Center selection", + bindKey: bindKey(null, "Ctrl-L"), + exec: function(editor) { editor.centerSelection(); }, + readOnly: true +}, { + name: "gotoline", + description: "Go to line...", + bindKey: bindKey("Ctrl-L", "Command-L"), + exec: function(editor, line) { + if (typeof line === "number" && !isNaN(line)) + editor.gotoLine(line); + editor.prompt({ $type: "gotoLine" }); + }, + readOnly: true +}, { + name: "fold", + bindKey: bindKey("Alt-L|Ctrl-F1", "Command-Alt-L|Command-F1"), + exec: function(editor) { editor.session.toggleFold(false); }, + multiSelectAction: "forEach", + scrollIntoView: "center", + readOnly: true +}, { + name: "unfold", + bindKey: bindKey("Alt-Shift-L|Ctrl-Shift-F1", "Command-Alt-Shift-L|Command-Shift-F1"), + exec: function(editor) { editor.session.toggleFold(true); }, + multiSelectAction: "forEach", + scrollIntoView: "center", + readOnly: true +}, { + name: "toggleFoldWidget", + description: "Toggle fold widget", + bindKey: bindKey("F2", "F2"), + exec: function(editor) { editor.session.toggleFoldWidget(); }, + multiSelectAction: "forEach", + scrollIntoView: "center", + readOnly: true +}, { + name: "toggleParentFoldWidget", + description: "Toggle parent fold widget", + bindKey: bindKey("Alt-F2", "Alt-F2"), + exec: function(editor) { editor.session.toggleFoldWidget(true); }, + multiSelectAction: "forEach", + scrollIntoView: "center", + readOnly: true +}, { + name: "foldall", + description: "Fold all", + bindKey: bindKey(null, "Ctrl-Command-Option-0"), + exec: function(editor) { editor.session.foldAll(); }, + scrollIntoView: "center", + readOnly: true +}, { + name: "foldAllComments", + description: "Fold all comments", + bindKey: bindKey(null, "Ctrl-Command-Option-0"), + exec: function(editor) { editor.session.foldAllComments(); }, + scrollIntoView: "center", + readOnly: true +}, { + name: "foldOther", + description: "Fold other", + bindKey: bindKey("Alt-0", "Command-Option-0"), + exec: function(editor) { + editor.session.foldAll(); + editor.session.unfold(editor.selection.getAllRanges()); + }, + scrollIntoView: "center", + readOnly: true +}, { + name: "unfoldall", + description: "Unfold all", + bindKey: bindKey("Alt-Shift-0", "Command-Option-Shift-0"), + exec: function(editor) { editor.session.unfold(); }, + scrollIntoView: "center", + readOnly: true +}, { + name: "findnext", + description: "Find next", + bindKey: bindKey("Ctrl-K", "Command-G"), + exec: function(editor) { editor.findNext(); }, + multiSelectAction: "forEach", + scrollIntoView: "center", + readOnly: true +}, { + name: "findprevious", + description: "Find previous", + bindKey: bindKey("Ctrl-Shift-K", "Command-Shift-G"), + exec: function(editor) { editor.findPrevious(); }, + multiSelectAction: "forEach", + scrollIntoView: "center", + readOnly: true +}, { + name: "selectOrFindNext", + description: "Select or find next", + bindKey: bindKey("Alt-K", "Ctrl-G"), + exec: function(editor) { + if (editor.selection.isEmpty()) + editor.selection.selectWord(); + else + editor.findNext(); + }, + readOnly: true +}, { + name: "selectOrFindPrevious", + description: "Select or find previous", + bindKey: bindKey("Alt-Shift-K", "Ctrl-Shift-G"), + exec: function(editor) { + if (editor.selection.isEmpty()) + editor.selection.selectWord(); + else + editor.findPrevious(); + }, + readOnly: true +}, { + name: "find", + description: "Find", + bindKey: bindKey("Ctrl-F", "Command-F"), + exec: function(editor) { + config.loadModule("ace/ext/searchbox", function(e) {e.Search(editor);}); + }, + readOnly: true +}, { + name: "overwrite", + description: "Overwrite", + bindKey: "Insert", + exec: function(editor) { editor.toggleOverwrite(); }, + readOnly: true +}, { + name: "selecttostart", + description: "Select to start", + bindKey: bindKey("Ctrl-Shift-Home", "Command-Shift-Home|Command-Shift-Up"), + exec: function(editor) { editor.getSelection().selectFileStart(); }, + multiSelectAction: "forEach", + readOnly: true, + scrollIntoView: "animate", + aceCommandGroup: "fileJump" +}, { + name: "gotostart", + description: "Go to start", + bindKey: bindKey("Ctrl-Home", "Command-Home|Command-Up"), + exec: function(editor) { editor.navigateFileStart(); }, + multiSelectAction: "forEach", + readOnly: true, + scrollIntoView: "animate", + aceCommandGroup: "fileJump" +}, { + name: "selectup", + description: "Select up", + bindKey: bindKey("Shift-Up", "Shift-Up|Ctrl-Shift-P"), + exec: function(editor) { editor.getSelection().selectUp(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true +}, { + name: "golineup", + description: "Go line up", + bindKey: bindKey("Up", "Up|Ctrl-P"), + exec: function(editor, args) { editor.navigateUp(args.times); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true +}, { + name: "selecttoend", + description: "Select to end", + bindKey: bindKey("Ctrl-Shift-End", "Command-Shift-End|Command-Shift-Down"), + exec: function(editor) { editor.getSelection().selectFileEnd(); }, + multiSelectAction: "forEach", + readOnly: true, + scrollIntoView: "animate", + aceCommandGroup: "fileJump" +}, { + name: "gotoend", + description: "Go to end", + bindKey: bindKey("Ctrl-End", "Command-End|Command-Down"), + exec: function(editor) { editor.navigateFileEnd(); }, + multiSelectAction: "forEach", + readOnly: true, + scrollIntoView: "animate", + aceCommandGroup: "fileJump" +}, { + name: "selectdown", + description: "Select down", + bindKey: bindKey("Shift-Down", "Shift-Down|Ctrl-Shift-N"), + exec: function(editor) { editor.getSelection().selectDown(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true +}, { + name: "golinedown", + description: "Go line down", + bindKey: bindKey("Down", "Down|Ctrl-N"), + exec: function(editor, args) { editor.navigateDown(args.times); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true +}, { + name: "selectwordleft", + description: "Select word left", + bindKey: bindKey("Ctrl-Shift-Left", "Option-Shift-Left"), + exec: function(editor) { editor.getSelection().selectWordLeft(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true +}, { + name: "gotowordleft", + description: "Go to word left", + bindKey: bindKey("Ctrl-Left", "Option-Left"), + exec: function(editor) { editor.navigateWordLeft(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true +}, { + name: "selecttolinestart", + description: "Select to line start", + bindKey: bindKey("Alt-Shift-Left", "Command-Shift-Left|Ctrl-Shift-A"), + exec: function(editor) { editor.getSelection().selectLineStart(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true +}, { + name: "gotolinestart", + description: "Go to line start", + bindKey: bindKey("Alt-Left|Home", "Command-Left|Home|Ctrl-A"), + exec: function(editor) { editor.navigateLineStart(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true +}, { + name: "selectleft", + description: "Select left", + bindKey: bindKey("Shift-Left", "Shift-Left|Ctrl-Shift-B"), + exec: function(editor) { editor.getSelection().selectLeft(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true +}, { + name: "gotoleft", + description: "Go to left", + bindKey: bindKey("Left", "Left|Ctrl-B"), + exec: function(editor, args) { editor.navigateLeft(args.times); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true +}, { + name: "selectwordright", + description: "Select word right", + bindKey: bindKey("Ctrl-Shift-Right", "Option-Shift-Right"), + exec: function(editor) { editor.getSelection().selectWordRight(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true +}, { + name: "gotowordright", + description: "Go to word right", + bindKey: bindKey("Ctrl-Right", "Option-Right"), + exec: function(editor) { editor.navigateWordRight(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true +}, { + name: "selecttolineend", + description: "Select to line end", + bindKey: bindKey("Alt-Shift-Right", "Command-Shift-Right|Shift-End|Ctrl-Shift-E"), + exec: function(editor) { editor.getSelection().selectLineEnd(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true +}, { + name: "gotolineend", + description: "Go to line end", + bindKey: bindKey("Alt-Right|End", "Command-Right|End|Ctrl-E"), + exec: function(editor) { editor.navigateLineEnd(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true +}, { + name: "selectright", + description: "Select right", + bindKey: bindKey("Shift-Right", "Shift-Right"), + exec: function(editor) { editor.getSelection().selectRight(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true +}, { + name: "gotoright", + description: "Go to right", + bindKey: bindKey("Right", "Right|Ctrl-F"), + exec: function(editor, args) { editor.navigateRight(args.times); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true +}, { + name: "selectpagedown", + description: "Select page down", + bindKey: "Shift-PageDown", + exec: function(editor) { editor.selectPageDown(); }, + readOnly: true +}, { + name: "pagedown", + description: "Page down", + bindKey: bindKey(null, "Option-PageDown"), + exec: function(editor) { editor.scrollPageDown(); }, + readOnly: true +}, { + name: "gotopagedown", + description: "Go to page down", + bindKey: bindKey("PageDown", "PageDown|Ctrl-V"), + exec: function(editor) { editor.gotoPageDown(); }, + readOnly: true +}, { + name: "selectpageup", + description: "Select page up", + bindKey: "Shift-PageUp", + exec: function(editor) { editor.selectPageUp(); }, + readOnly: true +}, { + name: "pageup", + description: "Page up", + bindKey: bindKey(null, "Option-PageUp"), + exec: function(editor) { editor.scrollPageUp(); }, + readOnly: true +}, { + name: "gotopageup", + description: "Go to page up", + bindKey: "PageUp", + exec: function(editor) { editor.gotoPageUp(); }, + readOnly: true +}, { + name: "scrollup", + description: "Scroll up", + bindKey: bindKey("Ctrl-Up", null), + exec: function(e) { e.renderer.scrollBy(0, -2 * e.renderer.layerConfig.lineHeight); }, + readOnly: true +}, { + name: "scrolldown", + description: "Scroll down", + bindKey: bindKey("Ctrl-Down", null), + exec: function(e) { e.renderer.scrollBy(0, 2 * e.renderer.layerConfig.lineHeight); }, + readOnly: true +}, { + name: "selectlinestart", + description: "Select line start", + bindKey: "Shift-Home", + exec: function(editor) { editor.getSelection().selectLineStart(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true +}, { + name: "selectlineend", + description: "Select line end", + bindKey: "Shift-End", + exec: function(editor) { editor.getSelection().selectLineEnd(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true +}, { + name: "togglerecording", + description: "Toggle recording", + bindKey: bindKey("Ctrl-Alt-E", "Command-Option-E"), + exec: function(editor) { editor.commands.toggleRecording(editor); }, + readOnly: true +}, { + name: "replaymacro", + description: "Replay macro", + bindKey: bindKey("Ctrl-Shift-E", "Command-Shift-E"), + exec: function(editor) { editor.commands.replay(editor); }, + readOnly: true +}, { + name: "jumptomatching", + description: "Jump to matching", + bindKey: bindKey("Ctrl-\\|Ctrl-P", "Command-\\"), + exec: function(editor) { editor.jumpToMatching(); }, + multiSelectAction: "forEach", + scrollIntoView: "animate", + readOnly: true +}, { + name: "selecttomatching", + description: "Select to matching", + bindKey: bindKey("Ctrl-Shift-\\|Ctrl-Shift-P", "Command-Shift-\\"), + exec: function(editor) { editor.jumpToMatching(true); }, + multiSelectAction: "forEach", + scrollIntoView: "animate", + readOnly: true +}, { + name: "expandToMatching", + description: "Expand to matching", + bindKey: bindKey("Ctrl-Shift-M", "Ctrl-Shift-M"), + exec: function(editor) { editor.jumpToMatching(true, true); }, + multiSelectAction: "forEach", + scrollIntoView: "animate", + readOnly: true +}, { + name: "passKeysToBrowser", + description: "Pass keys to browser", + bindKey: bindKey(null, null), + exec: function() {}, + passEvent: true, + readOnly: true +}, { + name: "copy", + description: "Copy", + exec: function(editor) { + }, + readOnly: true +}, +{ + name: "cut", + description: "Cut", + exec: function(editor) { + var cutLine = editor.$copyWithEmptySelection && editor.selection.isEmpty(); + var range = cutLine ? editor.selection.getLineRange() : editor.selection.getRange(); + editor._emit("cut", range); + + if (!range.isEmpty()) + editor.session.remove(range); + editor.clearSelection(); + }, + scrollIntoView: "cursor", + multiSelectAction: "forEach" +}, { + name: "paste", + description: "Paste", + exec: function(editor, args) { + editor.$handlePaste(args); + }, + scrollIntoView: "cursor" +}, { + name: "removeline", + description: "Remove line", + bindKey: bindKey("Ctrl-D", "Command-D"), + exec: function(editor) { editor.removeLines(); }, + scrollIntoView: "cursor", + multiSelectAction: "forEachLine" +}, { + name: "duplicateSelection", + description: "Duplicate selection", + bindKey: bindKey("Ctrl-Shift-D", "Command-Shift-D"), + exec: function(editor) { editor.duplicateSelection(); }, + scrollIntoView: "cursor", + multiSelectAction: "forEach" +}, { + name: "sortlines", + description: "Sort lines", + bindKey: bindKey("Ctrl-Alt-S", "Command-Alt-S"), + exec: function(editor) { editor.sortLines(); }, + scrollIntoView: "selection", + multiSelectAction: "forEachLine" +}, { + name: "togglecomment", + description: "Toggle comment", + bindKey: bindKey("Ctrl-/", "Command-/"), + exec: function(editor) { editor.toggleCommentLines(); }, + multiSelectAction: "forEachLine", + scrollIntoView: "selectionPart" +}, { + name: "toggleBlockComment", + description: "Toggle block comment", + bindKey: bindKey("Ctrl-Shift-/", "Command-Shift-/"), + exec: function(editor) { editor.toggleBlockComment(); }, + multiSelectAction: "forEach", + scrollIntoView: "selectionPart" +}, { + name: "modifyNumberUp", + description: "Modify number up", + bindKey: bindKey("Ctrl-Shift-Up", "Alt-Shift-Up"), + exec: function(editor) { editor.modifyNumber(1); }, + scrollIntoView: "cursor", + multiSelectAction: "forEach" +}, { + name: "modifyNumberDown", + description: "Modify number down", + bindKey: bindKey("Ctrl-Shift-Down", "Alt-Shift-Down"), + exec: function(editor) { editor.modifyNumber(-1); }, + scrollIntoView: "cursor", + multiSelectAction: "forEach" +}, { + name: "replace", + description: "Replace", + bindKey: bindKey("Ctrl-H", "Command-Option-F"), + exec: function(editor) { + config.loadModule("ace/ext/searchbox", function(e) {e.Search(editor, true);}); + } +}, { + name: "undo", + description: "Undo", + bindKey: bindKey("Ctrl-Z", "Command-Z"), + exec: function(editor) { editor.undo(); } +}, { + name: "redo", + description: "Redo", + bindKey: bindKey("Ctrl-Shift-Z|Ctrl-Y", "Command-Shift-Z|Command-Y"), + exec: function(editor) { editor.redo(); } +}, { + name: "copylinesup", + description: "Copy lines up", + bindKey: bindKey("Alt-Shift-Up", "Command-Option-Up"), + exec: function(editor) { editor.copyLinesUp(); }, + scrollIntoView: "cursor" +}, { + name: "movelinesup", + description: "Move lines up", + bindKey: bindKey("Alt-Up", "Option-Up"), + exec: function(editor) { editor.moveLinesUp(); }, + scrollIntoView: "cursor" +}, { + name: "copylinesdown", + description: "Copy lines down", + bindKey: bindKey("Alt-Shift-Down", "Command-Option-Down"), + exec: function(editor) { editor.copyLinesDown(); }, + scrollIntoView: "cursor" +}, { + name: "movelinesdown", + description: "Move lines down", + bindKey: bindKey("Alt-Down", "Option-Down"), + exec: function(editor) { editor.moveLinesDown(); }, + scrollIntoView: "cursor" +}, { + name: "del", + description: "Delete", + bindKey: bindKey("Delete", "Delete|Ctrl-D|Shift-Delete"), + exec: function(editor) { editor.remove("right"); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" +}, { + name: "backspace", + description: "Backspace", + bindKey: bindKey( + "Shift-Backspace|Backspace", + "Ctrl-Backspace|Shift-Backspace|Backspace|Ctrl-H" + ), + exec: function(editor) { editor.remove("left"); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" +}, { + name: "cut_or_delete", + description: "Cut or delete", + bindKey: bindKey("Shift-Delete", null), + exec: function(editor) { + if (editor.selection.isEmpty()) { + editor.remove("left"); + } else { + return false; + } + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" +}, { + name: "removetolinestart", + description: "Remove to line start", + bindKey: bindKey("Alt-Backspace", "Command-Backspace"), + exec: function(editor) { editor.removeToLineStart(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" +}, { + name: "removetolineend", + description: "Remove to line end", + bindKey: bindKey("Alt-Delete", "Ctrl-K|Command-Delete"), + exec: function(editor) { editor.removeToLineEnd(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" +}, { + name: "removetolinestarthard", + description: "Remove to line start hard", + bindKey: bindKey("Ctrl-Shift-Backspace", null), + exec: function(editor) { + var range = editor.selection.getRange(); + range.start.column = 0; + editor.session.remove(range); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" +}, { + name: "removetolineendhard", + description: "Remove to line end hard", + bindKey: bindKey("Ctrl-Shift-Delete", null), + exec: function(editor) { + var range = editor.selection.getRange(); + range.end.column = Number.MAX_VALUE; + editor.session.remove(range); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" +}, { + name: "removewordleft", + description: "Remove word left", + bindKey: bindKey("Ctrl-Backspace", "Alt-Backspace|Ctrl-Alt-Backspace"), + exec: function(editor) { editor.removeWordLeft(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" +}, { + name: "removewordright", + description: "Remove word right", + bindKey: bindKey("Ctrl-Delete", "Alt-Delete"), + exec: function(editor) { editor.removeWordRight(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" +}, { + name: "outdent", + description: "Outdent", + bindKey: bindKey("Shift-Tab", "Shift-Tab"), + exec: function(editor) { editor.blockOutdent(); }, + multiSelectAction: "forEach", + scrollIntoView: "selectionPart" +}, { + name: "indent", + description: "Indent", + bindKey: bindKey("Tab", "Tab"), + exec: function(editor) { editor.indent(); }, + multiSelectAction: "forEach", + scrollIntoView: "selectionPart" +}, { + name: "blockoutdent", + description: "Block outdent", + bindKey: bindKey("Ctrl-[", "Ctrl-["), + exec: function(editor) { editor.blockOutdent(); }, + multiSelectAction: "forEachLine", + scrollIntoView: "selectionPart" +}, { + name: "blockindent", + description: "Block indent", + bindKey: bindKey("Ctrl-]", "Ctrl-]"), + exec: function(editor) { editor.blockIndent(); }, + multiSelectAction: "forEachLine", + scrollIntoView: "selectionPart" +}, { + name: "insertstring", + description: "Insert string", + exec: function(editor, str) { editor.insert(str); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" +}, { + name: "inserttext", + description: "Insert text", + exec: function(editor, args) { + editor.insert(lang.stringRepeat(args.text || "", args.times || 1)); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" +}, { + name: "splitline", + description: "Split line", + bindKey: bindKey(null, "Ctrl-O"), + exec: function(editor) { editor.splitLine(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" +}, { + name: "transposeletters", + description: "Transpose letters", + bindKey: bindKey("Alt-Shift-X", "Ctrl-T"), + exec: function(editor) { editor.transposeLetters(); }, + multiSelectAction: function(editor) {editor.transposeSelections(1); }, + scrollIntoView: "cursor" +}, { + name: "touppercase", + description: "To uppercase", + bindKey: bindKey("Ctrl-U", "Ctrl-U"), + exec: function(editor) { editor.toUpperCase(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" +}, { + name: "tolowercase", + description: "To lowercase", + bindKey: bindKey("Ctrl-Shift-U", "Ctrl-Shift-U"), + exec: function(editor) { editor.toLowerCase(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" +}, { + name: "autoindent", + description: "Auto Indent", + bindKey: bindKey(null, null), + exec: function(editor) { editor.autoIndent(); }, + multiSelectAction: "forEachLine", + scrollIntoView: "animate" +}, { + name: "expandtoline", + description: "Expand to line", + bindKey: bindKey("Ctrl-Shift-L", "Command-Shift-L"), + exec: function(editor) { + var range = editor.selection.getRange(); + + range.start.column = range.end.column = 0; + range.end.row++; + editor.selection.setRange(range, false); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true +}, { + name: "joinlines", + description: "Join lines", + bindKey: bindKey(null, null), + exec: function(editor) { + var isBackwards = editor.selection.isBackwards(); + var selectionStart = isBackwards ? editor.selection.getSelectionLead() : editor.selection.getSelectionAnchor(); + var selectionEnd = isBackwards ? editor.selection.getSelectionAnchor() : editor.selection.getSelectionLead(); + var firstLineEndCol = editor.session.doc.getLine(selectionStart.row).length; + var selectedText = editor.session.doc.getTextRange(editor.selection.getRange()); + var selectedCount = selectedText.replace(/\n\s*/, " ").length; + var insertLine = editor.session.doc.getLine(selectionStart.row); + + for (var i = selectionStart.row + 1; i <= selectionEnd.row + 1; i++) { + var curLine = lang.stringTrimLeft(lang.stringTrimRight(editor.session.doc.getLine(i))); + if (curLine.length !== 0) { + curLine = " " + curLine; + } + insertLine += curLine; + } + + if (selectionEnd.row + 1 < (editor.session.doc.getLength() - 1)) { + insertLine += editor.session.doc.getNewLineCharacter(); + } + + editor.clearSelection(); + editor.session.doc.replace(new Range(selectionStart.row, 0, selectionEnd.row + 2, 0), insertLine); + + if (selectedCount > 0) { + editor.selection.moveCursorTo(selectionStart.row, selectionStart.column); + editor.selection.selectTo(selectionStart.row, selectionStart.column + selectedCount); + } else { + firstLineEndCol = editor.session.doc.getLine(selectionStart.row).length > firstLineEndCol ? (firstLineEndCol + 1) : firstLineEndCol; + editor.selection.moveCursorTo(selectionStart.row, firstLineEndCol); + } + }, + multiSelectAction: "forEach", + readOnly: true +}, { + name: "invertSelection", + description: "Invert selection", + bindKey: bindKey(null, null), + exec: function(editor) { + var endRow = editor.session.doc.getLength() - 1; + var endCol = editor.session.doc.getLine(endRow).length; + var ranges = editor.selection.rangeList.ranges; + var newRanges = []; + if (ranges.length < 1) { + ranges = [editor.selection.getRange()]; + } + + for (var i = 0; i < ranges.length; i++) { + if (i == (ranges.length - 1)) { + if (!(ranges[i].end.row === endRow && ranges[i].end.column === endCol)) { + newRanges.push(new Range(ranges[i].end.row, ranges[i].end.column, endRow, endCol)); + } + } + + if (i === 0) { + if (!(ranges[i].start.row === 0 && ranges[i].start.column === 0)) { + newRanges.push(new Range(0, 0, ranges[i].start.row, ranges[i].start.column)); + } + } else { + newRanges.push(new Range(ranges[i-1].end.row, ranges[i-1].end.column, ranges[i].start.row, ranges[i].start.column)); + } + } + + editor.exitMultiSelectMode(); + editor.clearSelection(); + + for(var i = 0; i < newRanges.length; i++) { + editor.selection.addRange(newRanges[i], false); + } + }, + readOnly: true, + scrollIntoView: "none" +}, { + name: "addLineAfter", + description: "Add new line after the current line", + exec: function(editor) { + editor.selection.clearSelection(); + editor.navigateLineEnd(); + editor.insert("\n"); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" +}, { + name: "addLineBefore", + description: "Add new line before the current line", + exec: function(editor) { + editor.selection.clearSelection(); + var cursor = editor.getCursorPosition(); + editor.selection.moveTo(cursor.row - 1, Number.MAX_VALUE); + editor.insert("\n"); + if (cursor.row === 0) editor.navigateUp(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" +}, { + name: "openCommandPallete", + description: "Open command pallete", + bindKey: bindKey("F1", "F1"), + exec: function(editor) { + editor.prompt({ $type: "commands" }); + }, + readOnly: true +}, { + name: "modeSelect", + description: "Change language mode...", + bindKey: bindKey(null, null), + exec: function(editor) { + editor.prompt({ $type: "modes" }); + }, + readOnly: true +}]; + +for (var i = 1; i < 9; i++) { + exports.commands.push({ + name: "foldToLevel" + i, + description: "Fold To Level " + i, + level: i, + exec: function(editor) { editor.session.foldToLevel(this.level); }, + scrollIntoView: "center", + readOnly: true + }); +} + +}); + +ace.define("ace/editor",["require","exports","module","ace/lib/fixoldbrowsers","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/useragent","ace/keyboard/textinput","ace/mouse/mouse_handler","ace/mouse/fold_handler","ace/keyboard/keybinding","ace/edit_session","ace/search","ace/range","ace/lib/event_emitter","ace/commands/command_manager","ace/commands/default_commands","ace/config","ace/token_iterator","ace/clipboard"], function(require, exports, module) { +"use strict"; + +require("./lib/fixoldbrowsers"); + +var oop = require("./lib/oop"); +var dom = require("./lib/dom"); +var lang = require("./lib/lang"); +var useragent = require("./lib/useragent"); +var TextInput = require("./keyboard/textinput").TextInput; +var MouseHandler = require("./mouse/mouse_handler").MouseHandler; +var FoldHandler = require("./mouse/fold_handler").FoldHandler; +var KeyBinding = require("./keyboard/keybinding").KeyBinding; +var EditSession = require("./edit_session").EditSession; +var Search = require("./search").Search; +var Range = require("./range").Range; +var EventEmitter = require("./lib/event_emitter").EventEmitter; +var CommandManager = require("./commands/command_manager").CommandManager; +var defaultCommands = require("./commands/default_commands").commands; +var config = require("./config"); +var TokenIterator = require("./token_iterator").TokenIterator; + +var clipboard = require("./clipboard"); +var Editor = function(renderer, session, options) { + this.$toDestroy = []; + var container = renderer.getContainerElement(); + this.container = container; + this.renderer = renderer; + this.id = "editor" + (++Editor.$uid); + + this.commands = new CommandManager(useragent.isMac ? "mac" : "win", defaultCommands); + if (typeof document == "object") { + this.textInput = new TextInput(renderer.getTextAreaContainer(), this); + this.renderer.textarea = this.textInput.getElement(); + this.$mouseHandler = new MouseHandler(this); + new FoldHandler(this); + } + + this.keyBinding = new KeyBinding(this); + + this.$search = new Search().set({ + wrap: true + }); + + this.$historyTracker = this.$historyTracker.bind(this); + this.commands.on("exec", this.$historyTracker); + + this.$initOperationListeners(); + + this._$emitInputEvent = lang.delayedCall(function() { + this._signal("input", {}); + if (this.session && this.session.bgTokenizer) + this.session.bgTokenizer.scheduleStart(); + }.bind(this)); + + this.on("change", function(_, _self) { + _self._$emitInputEvent.schedule(31); + }); + + this.setSession(session || options && options.session || new EditSession("")); + config.resetOptions(this); + if (options) + this.setOptions(options); + config._signal("editor", this); +}; + +Editor.$uid = 0; + +(function(){ + + oop.implement(this, EventEmitter); + + this.$initOperationListeners = function() { + this.commands.on("exec", this.startOperation.bind(this), true); + this.commands.on("afterExec", this.endOperation.bind(this), true); + + this.$opResetTimer = lang.delayedCall(this.endOperation.bind(this, true)); + this.on("change", function() { + if (!this.curOp) { + this.startOperation(); + this.curOp.selectionBefore = this.$lastSel; + } + this.curOp.docChanged = true; + }.bind(this), true); + + this.on("changeSelection", function() { + if (!this.curOp) { + this.startOperation(); + this.curOp.selectionBefore = this.$lastSel; + } + this.curOp.selectionChanged = true; + }.bind(this), true); + }; + + this.curOp = null; + this.prevOp = {}; + this.startOperation = function(commandEvent) { + if (this.curOp) { + if (!commandEvent || this.curOp.command) + return; + this.prevOp = this.curOp; + } + if (!commandEvent) { + this.previousCommand = null; + commandEvent = {}; + } + + this.$opResetTimer.schedule(); + this.curOp = this.session.curOp = { + command: commandEvent.command || {}, + args: commandEvent.args, + scrollTop: this.renderer.scrollTop + }; + this.curOp.selectionBefore = this.selection.toJSON(); + }; + + this.endOperation = function(e) { + if (this.curOp && this.session) { + if (e && e.returnValue === false || !this.session) + return (this.curOp = null); + if (e == true && this.curOp.command && this.curOp.command.name == "mouse") + return; + this._signal("beforeEndOperation"); + if (!this.curOp) return; + var command = this.curOp.command; + var scrollIntoView = command && command.scrollIntoView; + if (scrollIntoView) { + switch (scrollIntoView) { + case "center-animate": + scrollIntoView = "animate"; + case "center": + this.renderer.scrollCursorIntoView(null, 0.5); + break; + case "animate": + case "cursor": + this.renderer.scrollCursorIntoView(); + break; + case "selectionPart": + var range = this.selection.getRange(); + var config = this.renderer.layerConfig; + if (range.start.row >= config.lastRow || range.end.row <= config.firstRow) { + this.renderer.scrollSelectionIntoView(this.selection.anchor, this.selection.lead); + } + break; + default: + break; + } + if (scrollIntoView == "animate") + this.renderer.animateScrolling(this.curOp.scrollTop); + } + var sel = this.selection.toJSON(); + this.curOp.selectionAfter = sel; + this.$lastSel = this.selection.toJSON(); + this.session.getUndoManager().addSelection(sel); + this.prevOp = this.curOp; + this.curOp = null; + } + }; + this.$mergeableCommands = ["backspace", "del", "insertstring"]; + this.$historyTracker = function(e) { + if (!this.$mergeUndoDeltas) + return; + + var prev = this.prevOp; + var mergeableCommands = this.$mergeableCommands; + var shouldMerge = prev.command && (e.command.name == prev.command.name); + if (e.command.name == "insertstring") { + var text = e.args; + if (this.mergeNextCommand === undefined) + this.mergeNextCommand = true; + + shouldMerge = shouldMerge + && this.mergeNextCommand // previous command allows to coalesce with + && (!/\s/.test(text) || /\s/.test(prev.args)); // previous insertion was of same type + + this.mergeNextCommand = true; + } else { + shouldMerge = shouldMerge + && mergeableCommands.indexOf(e.command.name) !== -1; // the command is mergeable + } + + if ( + this.$mergeUndoDeltas != "always" + && Date.now() - this.sequenceStartTime > 2000 + ) { + shouldMerge = false; // the sequence is too long + } + + if (shouldMerge) + this.session.mergeUndoDeltas = true; + else if (mergeableCommands.indexOf(e.command.name) !== -1) + this.sequenceStartTime = Date.now(); + }; + this.setKeyboardHandler = function(keyboardHandler, cb) { + if (keyboardHandler && typeof keyboardHandler === "string" && keyboardHandler != "ace") { + this.$keybindingId = keyboardHandler; + var _self = this; + config.loadModule(["keybinding", keyboardHandler], function(module) { + if (_self.$keybindingId == keyboardHandler) + _self.keyBinding.setKeyboardHandler(module && module.handler); + cb && cb(); + }); + } else { + this.$keybindingId = null; + this.keyBinding.setKeyboardHandler(keyboardHandler); + cb && cb(); + } + }; + this.getKeyboardHandler = function() { + return this.keyBinding.getKeyboardHandler(); + }; + this.setSession = function(session) { + if (this.session == session) + return; + if (this.curOp) this.endOperation(); + this.curOp = {}; + + var oldSession = this.session; + if (oldSession) { + this.session.off("change", this.$onDocumentChange); + this.session.off("changeMode", this.$onChangeMode); + this.session.off("tokenizerUpdate", this.$onTokenizerUpdate); + this.session.off("changeTabSize", this.$onChangeTabSize); + this.session.off("changeWrapLimit", this.$onChangeWrapLimit); + this.session.off("changeWrapMode", this.$onChangeWrapMode); + this.session.off("changeFold", this.$onChangeFold); + this.session.off("changeFrontMarker", this.$onChangeFrontMarker); + this.session.off("changeBackMarker", this.$onChangeBackMarker); + this.session.off("changeBreakpoint", this.$onChangeBreakpoint); + this.session.off("changeAnnotation", this.$onChangeAnnotation); + this.session.off("changeOverwrite", this.$onCursorChange); + this.session.off("changeScrollTop", this.$onScrollTopChange); + this.session.off("changeScrollLeft", this.$onScrollLeftChange); + + var selection = this.session.getSelection(); + selection.off("changeCursor", this.$onCursorChange); + selection.off("changeSelection", this.$onSelectionChange); + } + + this.session = session; + if (session) { + this.$onDocumentChange = this.onDocumentChange.bind(this); + session.on("change", this.$onDocumentChange); + this.renderer.setSession(session); + + this.$onChangeMode = this.onChangeMode.bind(this); + session.on("changeMode", this.$onChangeMode); + + this.$onTokenizerUpdate = this.onTokenizerUpdate.bind(this); + session.on("tokenizerUpdate", this.$onTokenizerUpdate); + + this.$onChangeTabSize = this.renderer.onChangeTabSize.bind(this.renderer); + session.on("changeTabSize", this.$onChangeTabSize); + + this.$onChangeWrapLimit = this.onChangeWrapLimit.bind(this); + session.on("changeWrapLimit", this.$onChangeWrapLimit); + + this.$onChangeWrapMode = this.onChangeWrapMode.bind(this); + session.on("changeWrapMode", this.$onChangeWrapMode); + + this.$onChangeFold = this.onChangeFold.bind(this); + session.on("changeFold", this.$onChangeFold); + + this.$onChangeFrontMarker = this.onChangeFrontMarker.bind(this); + this.session.on("changeFrontMarker", this.$onChangeFrontMarker); + + this.$onChangeBackMarker = this.onChangeBackMarker.bind(this); + this.session.on("changeBackMarker", this.$onChangeBackMarker); + + this.$onChangeBreakpoint = this.onChangeBreakpoint.bind(this); + this.session.on("changeBreakpoint", this.$onChangeBreakpoint); + + this.$onChangeAnnotation = this.onChangeAnnotation.bind(this); + this.session.on("changeAnnotation", this.$onChangeAnnotation); + + this.$onCursorChange = this.onCursorChange.bind(this); + this.session.on("changeOverwrite", this.$onCursorChange); + + this.$onScrollTopChange = this.onScrollTopChange.bind(this); + this.session.on("changeScrollTop", this.$onScrollTopChange); + + this.$onScrollLeftChange = this.onScrollLeftChange.bind(this); + this.session.on("changeScrollLeft", this.$onScrollLeftChange); + + this.selection = session.getSelection(); + this.selection.on("changeCursor", this.$onCursorChange); + + this.$onSelectionChange = this.onSelectionChange.bind(this); + this.selection.on("changeSelection", this.$onSelectionChange); + + this.onChangeMode(); + + this.onCursorChange(); + + this.onScrollTopChange(); + this.onScrollLeftChange(); + this.onSelectionChange(); + this.onChangeFrontMarker(); + this.onChangeBackMarker(); + this.onChangeBreakpoint(); + this.onChangeAnnotation(); + this.session.getUseWrapMode() && this.renderer.adjustWrapLimit(); + this.renderer.updateFull(); + } else { + this.selection = null; + this.renderer.setSession(session); + } + + this._signal("changeSession", { + session: session, + oldSession: oldSession + }); + + this.curOp = null; + + oldSession && oldSession._signal("changeEditor", {oldEditor: this}); + session && session._signal("changeEditor", {editor: this}); + + if (session && session.bgTokenizer) + session.bgTokenizer.scheduleStart(); + }; + this.getSession = function() { + return this.session; + }; + this.setValue = function(val, cursorPos) { + this.session.doc.setValue(val); + + if (!cursorPos) + this.selectAll(); + else if (cursorPos == 1) + this.navigateFileEnd(); + else if (cursorPos == -1) + this.navigateFileStart(); + + return val; + }; + this.getValue = function() { + return this.session.getValue(); + }; + this.getSelection = function() { + return this.selection; + }; + this.resize = function(force) { + this.renderer.onResize(force); + }; + this.setTheme = function(theme, cb) { + this.renderer.setTheme(theme, cb); + }; + this.getTheme = function() { + return this.renderer.getTheme(); + }; + this.setStyle = function(style) { + this.renderer.setStyle(style); + }; + this.unsetStyle = function(style) { + this.renderer.unsetStyle(style); + }; + this.getFontSize = function () { + return this.getOption("fontSize") || + dom.computedStyle(this.container).fontSize; + }; + this.setFontSize = function(size) { + this.setOption("fontSize", size); + }; + + this.$highlightBrackets = function() { + if (this.$highlightPending) { + return; + } + var self = this; + this.$highlightPending = true; + setTimeout(function () { + self.$highlightPending = false; + var session = self.session; + if (!session || !session.bgTokenizer) return; + if (session.$bracketHighlight) { + session.$bracketHighlight.markerIds.forEach(function(id) { + session.removeMarker(id); + }); + session.$bracketHighlight = null; + } + var ranges = session.getMatchingBracketRanges(self.getCursorPosition()); + if (!ranges && session.$mode.getMatching) + ranges = session.$mode.getMatching(self.session); + if (!ranges) + return; + + var markerType = "ace_bracket"; + if (!Array.isArray(ranges)) { + ranges = [ranges]; + } else if (ranges.length == 1) { + markerType = "ace_error_bracket"; + } + if (ranges.length == 2) { + if (Range.comparePoints(ranges[0].end, ranges[1].start) == 0) + ranges = [Range.fromPoints(ranges[0].start, ranges[1].end)]; + else if (Range.comparePoints(ranges[0].start, ranges[1].end) == 0) + ranges = [Range.fromPoints(ranges[1].start, ranges[0].end)]; + } + + session.$bracketHighlight = { + ranges: ranges, + markerIds: ranges.map(function(range) { + return session.addMarker(range, markerType, "text"); + }) + }; + }, 50); + }; + this.$highlightTags = function() { + if (this.$highlightTagPending) + return; + var self = this; + this.$highlightTagPending = true; + setTimeout(function() { + self.$highlightTagPending = false; + + var session = self.session; + if (!session || !session.bgTokenizer) return; + + var pos = self.getCursorPosition(); + var iterator = new TokenIterator(self.session, pos.row, pos.column); + var token = iterator.getCurrentToken(); + + if (!token || !/\b(?:tag-open|tag-name)/.test(token.type)) { + session.removeMarker(session.$tagHighlight); + session.$tagHighlight = null; + return; + } + + if (token.type.indexOf("tag-open") !== -1) { + token = iterator.stepForward(); + if (!token) + return; + } + + var tag = token.value; + var currentTag = token.value; + var depth = 0; + var prevToken = iterator.stepBackward(); + + if (prevToken.value === '<'){ + do { + prevToken = token; + token = iterator.stepForward(); + + if (token) { + if (token.type.indexOf('tag-name') !== -1) { + currentTag = token.value; + if (tag === currentTag) { + if (prevToken.value === '<') { + depth++; + } else if (prevToken.value === '') { // self closing tag + depth--; + } + } + + } while (token && depth >= 0); + } else { + do { + token = prevToken; + prevToken = iterator.stepBackward(); + + if (token) { + if (token.type.indexOf('tag-name') !== -1) { + if (tag === token.value) { + if (prevToken.value === '<') { + depth++; + } else if (prevToken.value === '') { // self closing tag + var stepCount = 0; + var tmpToken = prevToken; + while (tmpToken) { + if (tmpToken.type.indexOf('tag-name') !== -1 && tmpToken.value === tag) { + depth--; + break; + } else if (tmpToken.value === '<') { + break; + } + tmpToken = iterator.stepBackward(); + stepCount++; + } + for (var i = 0; i < stepCount; i++) { + iterator.stepForward(); + } + } + } + } while (prevToken && depth <= 0); + iterator.stepForward(); + } + + if (!token) { + session.removeMarker(session.$tagHighlight); + session.$tagHighlight = null; + return; + } + + var row = iterator.getCurrentTokenRow(); + var column = iterator.getCurrentTokenColumn(); + var range = new Range(row, column, row, column+token.value.length); + var sbm = session.$backMarkers[session.$tagHighlight]; + if (session.$tagHighlight && sbm != undefined && range.compareRange(sbm.range) !== 0) { + session.removeMarker(session.$tagHighlight); + session.$tagHighlight = null; + } + + if (!session.$tagHighlight) + session.$tagHighlight = session.addMarker(range, "ace_bracket", "text"); + }, 50); + }; + this.focus = function() { + var _self = this; + setTimeout(function() { + if (!_self.isFocused()) + _self.textInput.focus(); + }); + this.textInput.focus(); + }; + this.isFocused = function() { + return this.textInput.isFocused(); + }; + this.blur = function() { + this.textInput.blur(); + }; + this.onFocus = function(e) { + if (this.$isFocused) + return; + this.$isFocused = true; + this.renderer.showCursor(); + this.renderer.visualizeFocus(); + this._emit("focus", e); + }; + this.onBlur = function(e) { + if (!this.$isFocused) + return; + this.$isFocused = false; + this.renderer.hideCursor(); + this.renderer.visualizeBlur(); + this._emit("blur", e); + }; + + this.$cursorChange = function() { + this.renderer.updateCursor(); + this.$highlightBrackets(); + this.$highlightTags(); + this.$updateHighlightActiveLine(); + }; + this.onDocumentChange = function(delta) { + var wrap = this.session.$useWrapMode; + var lastRow = (delta.start.row == delta.end.row ? delta.end.row : Infinity); + this.renderer.updateLines(delta.start.row, lastRow, wrap); + + this._signal("change", delta); + this.$cursorChange(); + }; + + this.onTokenizerUpdate = function(e) { + var rows = e.data; + this.renderer.updateLines(rows.first, rows.last); + }; + + + this.onScrollTopChange = function() { + this.renderer.scrollToY(this.session.getScrollTop()); + }; + + this.onScrollLeftChange = function() { + this.renderer.scrollToX(this.session.getScrollLeft()); + }; + this.onCursorChange = function() { + this.$cursorChange(); + this._signal("changeSelection"); + }; + + this.$updateHighlightActiveLine = function() { + var session = this.getSession(); + + var highlight; + if (this.$highlightActiveLine) { + if (this.$selectionStyle != "line" || !this.selection.isMultiLine()) + highlight = this.getCursorPosition(); + if (this.renderer.theme && this.renderer.theme.$selectionColorConflict && !this.selection.isEmpty()) + highlight = false; + if (this.renderer.$maxLines && this.session.getLength() === 1 && !(this.renderer.$minLines > 1)) + highlight = false; + } + + if (session.$highlightLineMarker && !highlight) { + session.removeMarker(session.$highlightLineMarker.id); + session.$highlightLineMarker = null; + } else if (!session.$highlightLineMarker && highlight) { + var range = new Range(highlight.row, highlight.column, highlight.row, Infinity); + range.id = session.addMarker(range, "ace_active-line", "screenLine"); + session.$highlightLineMarker = range; + } else if (highlight) { + session.$highlightLineMarker.start.row = highlight.row; + session.$highlightLineMarker.end.row = highlight.row; + session.$highlightLineMarker.start.column = highlight.column; + session._signal("changeBackMarker"); + } + }; + + this.onSelectionChange = function(e) { + var session = this.session; + + if (session.$selectionMarker) { + session.removeMarker(session.$selectionMarker); + } + session.$selectionMarker = null; + + if (!this.selection.isEmpty()) { + var range = this.selection.getRange(); + var style = this.getSelectionStyle(); + session.$selectionMarker = session.addMarker(range, "ace_selection", style); + } else { + this.$updateHighlightActiveLine(); + } + + var re = this.$highlightSelectedWord && this.$getSelectionHighLightRegexp(); + this.session.highlight(re); + + this._signal("changeSelection"); + }; + + this.$getSelectionHighLightRegexp = function() { + var session = this.session; + + var selection = this.getSelectionRange(); + if (selection.isEmpty() || selection.isMultiLine()) + return; + + var startColumn = selection.start.column; + var endColumn = selection.end.column; + var line = session.getLine(selection.start.row); + + var needle = line.substring(startColumn, endColumn); + if (needle.length > 5000 || !/[\w\d]/.test(needle)) + return; + + var re = this.$search.$assembleRegExp({ + wholeWord: true, + caseSensitive: true, + needle: needle + }); + + var wordWithBoundary = line.substring(startColumn - 1, endColumn + 1); + if (!re.test(wordWithBoundary)) + return; + + return re; + }; + + + this.onChangeFrontMarker = function() { + this.renderer.updateFrontMarkers(); + }; + + this.onChangeBackMarker = function() { + this.renderer.updateBackMarkers(); + }; + + + this.onChangeBreakpoint = function() { + this.renderer.updateBreakpoints(); + }; + + this.onChangeAnnotation = function() { + this.renderer.setAnnotations(this.session.getAnnotations()); + }; + + + this.onChangeMode = function(e) { + this.renderer.updateText(); + this._emit("changeMode", e); + }; + + + this.onChangeWrapLimit = function() { + this.renderer.updateFull(); + }; + + this.onChangeWrapMode = function() { + this.renderer.onResize(true); + }; + + + this.onChangeFold = function() { + this.$updateHighlightActiveLine(); + this.renderer.updateFull(); + }; + this.getSelectedText = function() { + return this.session.getTextRange(this.getSelectionRange()); + }; + this.getCopyText = function() { + var text = this.getSelectedText(); + var nl = this.session.doc.getNewLineCharacter(); + var copyLine= false; + if (!text && this.$copyWithEmptySelection) { + copyLine = true; + var ranges = this.selection.getAllRanges(); + for (var i = 0; i < ranges.length; i++) { + var range = ranges[i]; + if (i && ranges[i - 1].start.row == range.start.row) + continue; + text += this.session.getLine(range.start.row) + nl; + } + } + var e = {text: text}; + this._signal("copy", e); + clipboard.lineMode = copyLine ? e.text : false; + return e.text; + }; + this.onCopy = function() { + this.commands.exec("copy", this); + }; + this.onCut = function() { + this.commands.exec("cut", this); + }; + this.onPaste = function(text, event) { + var e = {text: text, event: event}; + this.commands.exec("paste", this, e); + }; + + this.$handlePaste = function(e) { + if (typeof e == "string") + e = {text: e}; + this._signal("paste", e); + var text = e.text; + + var lineMode = text === clipboard.lineMode; + var session = this.session; + if (!this.inMultiSelectMode || this.inVirtualSelectionMode) { + if (lineMode) + session.insert({ row: this.selection.lead.row, column: 0 }, text); + else + this.insert(text); + } else if (lineMode) { + this.selection.rangeList.ranges.forEach(function(range) { + session.insert({ row: range.start.row, column: 0 }, text); + }); + } else { + var lines = text.split(/\r\n|\r|\n/); + var ranges = this.selection.rangeList.ranges; + + var isFullLine = lines.length == 2 && (!lines[0] || !lines[1]); + if (lines.length != ranges.length || isFullLine) + return this.commands.exec("insertstring", this, text); + + for (var i = ranges.length; i--;) { + var range = ranges[i]; + if (!range.isEmpty()) + session.remove(range); + + session.insert(range.start, lines[i]); + } + } + }; + + this.execCommand = function(command, args) { + return this.commands.exec(command, this, args); + }; + this.insert = function(text, pasted) { + var session = this.session; + var mode = session.getMode(); + var cursor = this.getCursorPosition(); + + if (this.getBehavioursEnabled() && !pasted) { + var transform = mode.transformAction(session.getState(cursor.row), 'insertion', this, session, text); + if (transform) { + if (text !== transform.text) { + if (!this.inVirtualSelectionMode) { + this.session.mergeUndoDeltas = false; + this.mergeNextCommand = false; + } + } + text = transform.text; + + } + } + + if (text == "\t") + text = this.session.getTabString(); + if (!this.selection.isEmpty()) { + var range = this.getSelectionRange(); + cursor = this.session.remove(range); + this.clearSelection(); + } + else if (this.session.getOverwrite() && text.indexOf("\n") == -1) { + var range = new Range.fromPoints(cursor, cursor); + range.end.column += text.length; + this.session.remove(range); + } + + if (text == "\n" || text == "\r\n") { + var line = session.getLine(cursor.row); + if (cursor.column > line.search(/\S|$/)) { + var d = line.substr(cursor.column).search(/\S|$/); + session.doc.removeInLine(cursor.row, cursor.column, cursor.column + d); + } + } + this.clearSelection(); + + var start = cursor.column; + var lineState = session.getState(cursor.row); + var line = session.getLine(cursor.row); + var shouldOutdent = mode.checkOutdent(lineState, line, text); + session.insert(cursor, text); + + if (transform && transform.selection) { + if (transform.selection.length == 2) { // Transform relative to the current column + this.selection.setSelectionRange( + new Range(cursor.row, start + transform.selection[0], + cursor.row, start + transform.selection[1])); + } else { // Transform relative to the current row. + this.selection.setSelectionRange( + new Range(cursor.row + transform.selection[0], + transform.selection[1], + cursor.row + transform.selection[2], + transform.selection[3])); + } + } + if (this.$enableAutoIndent) { + if (session.getDocument().isNewLine(text)) { + var lineIndent = mode.getNextLineIndent(lineState, line.slice(0, cursor.column), session.getTabString()); + + session.insert({row: cursor.row+1, column: 0}, lineIndent); + } + if (shouldOutdent) + mode.autoOutdent(lineState, session, cursor.row); + } + }; + + this.autoIndent = function () { + var session = this.session; + var mode = session.getMode(); + + var startRow, endRow; + if (this.selection.isEmpty()) { + startRow = 0; + endRow = session.doc.getLength() - 1; + } else { + var selectedRange = this.getSelectionRange(); + + startRow = selectedRange.start.row; + endRow = selectedRange.end.row; + } + + var prevLineState = ""; + var prevLine = ""; + var lineIndent = ""; + var line, currIndent, range; + var tab = session.getTabString(); + + for (var row = startRow; row <= endRow; row++) { + if (row > 0) { + prevLineState = session.getState(row - 1); + prevLine = session.getLine(row - 1); + lineIndent = mode.getNextLineIndent(prevLineState, prevLine, tab); + } + + line = session.getLine(row); + currIndent = mode.$getIndent(line); + if (lineIndent !== currIndent) { + if (currIndent.length > 0) { + range = new Range(row, 0, row, currIndent.length); + session.remove(range); + } + if (lineIndent.length > 0) { + session.insert({row: row, column: 0}, lineIndent); + } + } + + mode.autoOutdent(prevLineState, session, row); + } + }; + + + this.onTextInput = function(text, composition) { + if (!composition) + return this.keyBinding.onTextInput(text); + + this.startOperation({command: { name: "insertstring" }}); + var applyComposition = this.applyComposition.bind(this, text, composition); + if (this.selection.rangeCount) + this.forEachSelection(applyComposition); + else + applyComposition(); + this.endOperation(); + }; + + this.applyComposition = function(text, composition) { + if (composition.extendLeft || composition.extendRight) { + var r = this.selection.getRange(); + r.start.column -= composition.extendLeft; + r.end.column += composition.extendRight; + if (r.start.column < 0) { + r.start.row--; + r.start.column += this.session.getLine(r.start.row).length + 1; + } + this.selection.setRange(r); + if (!text && !r.isEmpty()) + this.remove(); + } + if (text || !this.selection.isEmpty()) + this.insert(text, true); + if (composition.restoreStart || composition.restoreEnd) { + var r = this.selection.getRange(); + r.start.column -= composition.restoreStart; + r.end.column -= composition.restoreEnd; + this.selection.setRange(r); + } + }; + + this.onCommandKey = function(e, hashId, keyCode) { + return this.keyBinding.onCommandKey(e, hashId, keyCode); + }; + this.setOverwrite = function(overwrite) { + this.session.setOverwrite(overwrite); + }; + this.getOverwrite = function() { + return this.session.getOverwrite(); + }; + this.toggleOverwrite = function() { + this.session.toggleOverwrite(); + }; + this.setScrollSpeed = function(speed) { + this.setOption("scrollSpeed", speed); + }; + this.getScrollSpeed = function() { + return this.getOption("scrollSpeed"); + }; + this.setDragDelay = function(dragDelay) { + this.setOption("dragDelay", dragDelay); + }; + this.getDragDelay = function() { + return this.getOption("dragDelay"); + }; + this.setSelectionStyle = function(val) { + this.setOption("selectionStyle", val); + }; + this.getSelectionStyle = function() { + return this.getOption("selectionStyle"); + }; + this.setHighlightActiveLine = function(shouldHighlight) { + this.setOption("highlightActiveLine", shouldHighlight); + }; + this.getHighlightActiveLine = function() { + return this.getOption("highlightActiveLine"); + }; + this.setHighlightGutterLine = function(shouldHighlight) { + this.setOption("highlightGutterLine", shouldHighlight); + }; + + this.getHighlightGutterLine = function() { + return this.getOption("highlightGutterLine"); + }; + this.setHighlightSelectedWord = function(shouldHighlight) { + this.setOption("highlightSelectedWord", shouldHighlight); + }; + this.getHighlightSelectedWord = function() { + return this.$highlightSelectedWord; + }; + + this.setAnimatedScroll = function(shouldAnimate){ + this.renderer.setAnimatedScroll(shouldAnimate); + }; + + this.getAnimatedScroll = function(){ + return this.renderer.getAnimatedScroll(); + }; + this.setShowInvisibles = function(showInvisibles) { + this.renderer.setShowInvisibles(showInvisibles); + }; + this.getShowInvisibles = function() { + return this.renderer.getShowInvisibles(); + }; + + this.setDisplayIndentGuides = function(display) { + this.renderer.setDisplayIndentGuides(display); + }; + + this.getDisplayIndentGuides = function() { + return this.renderer.getDisplayIndentGuides(); + }; + this.setShowPrintMargin = function(showPrintMargin) { + this.renderer.setShowPrintMargin(showPrintMargin); + }; + this.getShowPrintMargin = function() { + return this.renderer.getShowPrintMargin(); + }; + this.setPrintMarginColumn = function(showPrintMargin) { + this.renderer.setPrintMarginColumn(showPrintMargin); + }; + this.getPrintMarginColumn = function() { + return this.renderer.getPrintMarginColumn(); + }; + this.setReadOnly = function(readOnly) { + this.setOption("readOnly", readOnly); + }; + this.getReadOnly = function() { + return this.getOption("readOnly"); + }; + this.setBehavioursEnabled = function (enabled) { + this.setOption("behavioursEnabled", enabled); + }; + this.getBehavioursEnabled = function () { + return this.getOption("behavioursEnabled"); + }; + this.setWrapBehavioursEnabled = function (enabled) { + this.setOption("wrapBehavioursEnabled", enabled); + }; + this.getWrapBehavioursEnabled = function () { + return this.getOption("wrapBehavioursEnabled"); + }; + this.setShowFoldWidgets = function(show) { + this.setOption("showFoldWidgets", show); + + }; + this.getShowFoldWidgets = function() { + return this.getOption("showFoldWidgets"); + }; + + this.setFadeFoldWidgets = function(fade) { + this.setOption("fadeFoldWidgets", fade); + }; + + this.getFadeFoldWidgets = function() { + return this.getOption("fadeFoldWidgets"); + }; + this.remove = function(dir) { + if (this.selection.isEmpty()){ + if (dir == "left") + this.selection.selectLeft(); + else + this.selection.selectRight(); + } + + var range = this.getSelectionRange(); + if (this.getBehavioursEnabled()) { + var session = this.session; + var state = session.getState(range.start.row); + var new_range = session.getMode().transformAction(state, 'deletion', this, session, range); + + if (range.end.column === 0) { + var text = session.getTextRange(range); + if (text[text.length - 1] == "\n") { + var line = session.getLine(range.end.row); + if (/^\s+$/.test(line)) { + range.end.column = line.length; + } + } + } + if (new_range) + range = new_range; + } + + this.session.remove(range); + this.clearSelection(); + }; + this.removeWordRight = function() { + if (this.selection.isEmpty()) + this.selection.selectWordRight(); + + this.session.remove(this.getSelectionRange()); + this.clearSelection(); + }; + this.removeWordLeft = function() { + if (this.selection.isEmpty()) + this.selection.selectWordLeft(); + + this.session.remove(this.getSelectionRange()); + this.clearSelection(); + }; + this.removeToLineStart = function() { + if (this.selection.isEmpty()) + this.selection.selectLineStart(); + if (this.selection.isEmpty()) + this.selection.selectLeft(); + this.session.remove(this.getSelectionRange()); + this.clearSelection(); + }; + this.removeToLineEnd = function() { + if (this.selection.isEmpty()) + this.selection.selectLineEnd(); + + var range = this.getSelectionRange(); + if (range.start.column == range.end.column && range.start.row == range.end.row) { + range.end.column = 0; + range.end.row++; + } + + this.session.remove(range); + this.clearSelection(); + }; + this.splitLine = function() { + if (!this.selection.isEmpty()) { + this.session.remove(this.getSelectionRange()); + this.clearSelection(); + } + + var cursor = this.getCursorPosition(); + this.insert("\n"); + this.moveCursorToPosition(cursor); + }; + this.transposeLetters = function() { + if (!this.selection.isEmpty()) { + return; + } + + var cursor = this.getCursorPosition(); + var column = cursor.column; + if (column === 0) + return; + + var line = this.session.getLine(cursor.row); + var swap, range; + if (column < line.length) { + swap = line.charAt(column) + line.charAt(column-1); + range = new Range(cursor.row, column-1, cursor.row, column+1); + } + else { + swap = line.charAt(column-1) + line.charAt(column-2); + range = new Range(cursor.row, column-2, cursor.row, column); + } + this.session.replace(range, swap); + this.session.selection.moveToPosition(range.end); + }; + this.toLowerCase = function() { + var originalRange = this.getSelectionRange(); + if (this.selection.isEmpty()) { + this.selection.selectWord(); + } + + var range = this.getSelectionRange(); + var text = this.session.getTextRange(range); + this.session.replace(range, text.toLowerCase()); + this.selection.setSelectionRange(originalRange); + }; + this.toUpperCase = function() { + var originalRange = this.getSelectionRange(); + if (this.selection.isEmpty()) { + this.selection.selectWord(); + } + + var range = this.getSelectionRange(); + var text = this.session.getTextRange(range); + this.session.replace(range, text.toUpperCase()); + this.selection.setSelectionRange(originalRange); + }; + this.indent = function() { + var session = this.session; + var range = this.getSelectionRange(); + + if (range.start.row < range.end.row) { + var rows = this.$getSelectedRows(); + session.indentRows(rows.first, rows.last, "\t"); + return; + } else if (range.start.column < range.end.column) { + var text = session.getTextRange(range); + if (!/^\s+$/.test(text)) { + var rows = this.$getSelectedRows(); + session.indentRows(rows.first, rows.last, "\t"); + return; + } + } + + var line = session.getLine(range.start.row); + var position = range.start; + var size = session.getTabSize(); + var column = session.documentToScreenColumn(position.row, position.column); + + if (this.session.getUseSoftTabs()) { + var count = (size - column % size); + var indentString = lang.stringRepeat(" ", count); + } else { + var count = column % size; + while (line[range.start.column - 1] == " " && count) { + range.start.column--; + count--; + } + this.selection.setSelectionRange(range); + indentString = "\t"; + } + return this.insert(indentString); + }; + this.blockIndent = function() { + var rows = this.$getSelectedRows(); + this.session.indentRows(rows.first, rows.last, "\t"); + }; + this.blockOutdent = function() { + var selection = this.session.getSelection(); + this.session.outdentRows(selection.getRange()); + }; + this.sortLines = function() { + var rows = this.$getSelectedRows(); + var session = this.session; + + var lines = []; + for (var i = rows.first; i <= rows.last; i++) + lines.push(session.getLine(i)); + + lines.sort(function(a, b) { + if (a.toLowerCase() < b.toLowerCase()) return -1; + if (a.toLowerCase() > b.toLowerCase()) return 1; + return 0; + }); + + var deleteRange = new Range(0, 0, 0, 0); + for (var i = rows.first; i <= rows.last; i++) { + var line = session.getLine(i); + deleteRange.start.row = i; + deleteRange.end.row = i; + deleteRange.end.column = line.length; + session.replace(deleteRange, lines[i-rows.first]); + } + }; + this.toggleCommentLines = function() { + var state = this.session.getState(this.getCursorPosition().row); + var rows = this.$getSelectedRows(); + this.session.getMode().toggleCommentLines(state, this.session, rows.first, rows.last); + }; + + this.toggleBlockComment = function() { + var cursor = this.getCursorPosition(); + var state = this.session.getState(cursor.row); + var range = this.getSelectionRange(); + this.session.getMode().toggleBlockComment(state, this.session, range, cursor); + }; + this.getNumberAt = function(row, column) { + var _numberRx = /[\-]?[0-9]+(?:\.[0-9]+)?/g; + _numberRx.lastIndex = 0; + + var s = this.session.getLine(row); + while (_numberRx.lastIndex < column) { + var m = _numberRx.exec(s); + if(m.index <= column && m.index+m[0].length >= column){ + var number = { + value: m[0], + start: m.index, + end: m.index+m[0].length + }; + return number; + } + } + return null; + }; + this.modifyNumber = function(amount) { + var row = this.selection.getCursor().row; + var column = this.selection.getCursor().column; + var charRange = new Range(row, column-1, row, column); + + var c = this.session.getTextRange(charRange); + if (!isNaN(parseFloat(c)) && isFinite(c)) { + var nr = this.getNumberAt(row, column); + if (nr) { + var fp = nr.value.indexOf(".") >= 0 ? nr.start + nr.value.indexOf(".") + 1 : nr.end; + var decimals = nr.start + nr.value.length - fp; + + var t = parseFloat(nr.value); + t *= Math.pow(10, decimals); + + + if(fp !== nr.end && column < fp){ + amount *= Math.pow(10, nr.end - column - 1); + } else { + amount *= Math.pow(10, nr.end - column); + } + + t += amount; + t /= Math.pow(10, decimals); + var nnr = t.toFixed(decimals); + var replaceRange = new Range(row, nr.start, row, nr.end); + this.session.replace(replaceRange, nnr); + this.moveCursorTo(row, Math.max(nr.start +1, column + nnr.length - nr.value.length)); + + } + } else { + this.toggleWord(); + } + }; + + this.$toggleWordPairs = [ + ["first", "last"], + ["true", "false"], + ["yes", "no"], + ["width", "height"], + ["top", "bottom"], + ["right", "left"], + ["on", "off"], + ["x", "y"], + ["get", "set"], + ["max", "min"], + ["horizontal", "vertical"], + ["show", "hide"], + ["add", "remove"], + ["up", "down"], + ["before", "after"], + ["even", "odd"], + ["in", "out"], + ["inside", "outside"], + ["next", "previous"], + ["increase", "decrease"], + ["attach", "detach"], + ["&&", "||"], + ["==", "!="] + ]; + + this.toggleWord = function () { + var row = this.selection.getCursor().row; + var column = this.selection.getCursor().column; + this.selection.selectWord(); + var currentState = this.getSelectedText(); + var currWordStart = this.selection.getWordRange().start.column; + var wordParts = currentState.replace(/([a-z]+|[A-Z]+)(?=[A-Z_]|$)/g, '$1 ').split(/\s/); + var delta = column - currWordStart - 1; + if (delta < 0) delta = 0; + var curLength = 0, itLength = 0; + var that = this; + if (currentState.match(/[A-Za-z0-9_]+/)) { + wordParts.forEach(function (item, i) { + itLength = curLength + item.length; + if (delta >= curLength && delta <= itLength) { + currentState = item; + that.selection.clearSelection(); + that.moveCursorTo(row, curLength + currWordStart); + that.selection.selectTo(row, itLength + currWordStart); + } + curLength = itLength; + }); + } + + var wordPairs = this.$toggleWordPairs; + var reg; + for (var i = 0; i < wordPairs.length; i++) { + var item = wordPairs[i]; + for (var j = 0; j <= 1; j++) { + var negate = +!j; + var firstCondition = currentState.match(new RegExp('^\\s?_?(' + lang.escapeRegExp(item[j]) + ')\\s?$', 'i')); + if (firstCondition) { + var secondCondition = currentState.match(new RegExp('([_]|^|\\s)(' + lang.escapeRegExp(firstCondition[1]) + ')($|\\s)', 'g')); + if (secondCondition) { + reg = currentState.replace(new RegExp(lang.escapeRegExp(item[j]), 'i'), function (result) { + var res = item[negate]; + if (result.toUpperCase() == result) { + res = res.toUpperCase(); + } else if (result.charAt(0).toUpperCase() == result.charAt(0)) { + res = res.substr(0, 0) + item[negate].charAt(0).toUpperCase() + res.substr(1); + } + return res; + }); + this.insert(reg); + reg = ""; + } + } + } + } + }; + this.removeLines = function() { + var rows = this.$getSelectedRows(); + this.session.removeFullLines(rows.first, rows.last); + this.clearSelection(); + }; + + this.duplicateSelection = function() { + var sel = this.selection; + var doc = this.session; + var range = sel.getRange(); + var reverse = sel.isBackwards(); + if (range.isEmpty()) { + var row = range.start.row; + doc.duplicateLines(row, row); + } else { + var point = reverse ? range.start : range.end; + var endPoint = doc.insert(point, doc.getTextRange(range), false); + range.start = point; + range.end = endPoint; + + sel.setSelectionRange(range, reverse); + } + }; + this.moveLinesDown = function() { + this.$moveLines(1, false); + }; + this.moveLinesUp = function() { + this.$moveLines(-1, false); + }; + this.moveText = function(range, toPosition, copy) { + return this.session.moveText(range, toPosition, copy); + }; + this.copyLinesUp = function() { + this.$moveLines(-1, true); + }; + this.copyLinesDown = function() { + this.$moveLines(1, true); + }; + this.$moveLines = function(dir, copy) { + var rows, moved; + var selection = this.selection; + if (!selection.inMultiSelectMode || this.inVirtualSelectionMode) { + var range = selection.toOrientedRange(); + rows = this.$getSelectedRows(range); + moved = this.session.$moveLines(rows.first, rows.last, copy ? 0 : dir); + if (copy && dir == -1) moved = 0; + range.moveBy(moved, 0); + selection.fromOrientedRange(range); + } else { + var ranges = selection.rangeList.ranges; + selection.rangeList.detach(this.session); + this.inVirtualSelectionMode = true; + + var diff = 0; + var totalDiff = 0; + var l = ranges.length; + for (var i = 0; i < l; i++) { + var rangeIndex = i; + ranges[i].moveBy(diff, 0); + rows = this.$getSelectedRows(ranges[i]); + var first = rows.first; + var last = rows.last; + while (++i < l) { + if (totalDiff) ranges[i].moveBy(totalDiff, 0); + var subRows = this.$getSelectedRows(ranges[i]); + if (copy && subRows.first != last) + break; + else if (!copy && subRows.first > last + 1) + break; + last = subRows.last; + } + i--; + diff = this.session.$moveLines(first, last, copy ? 0 : dir); + if (copy && dir == -1) rangeIndex = i + 1; + while (rangeIndex <= i) { + ranges[rangeIndex].moveBy(diff, 0); + rangeIndex++; + } + if (!copy) diff = 0; + totalDiff += diff; + } + + selection.fromOrientedRange(selection.ranges[0]); + selection.rangeList.attach(this.session); + this.inVirtualSelectionMode = false; + } + }; + this.$getSelectedRows = function(range) { + range = (range || this.getSelectionRange()).collapseRows(); + + return { + first: this.session.getRowFoldStart(range.start.row), + last: this.session.getRowFoldEnd(range.end.row) + }; + }; + + this.onCompositionStart = function(compositionState) { + this.renderer.showComposition(compositionState); + }; + + this.onCompositionUpdate = function(text) { + this.renderer.setCompositionText(text); + }; + + this.onCompositionEnd = function() { + this.renderer.hideComposition(); + }; + this.getFirstVisibleRow = function() { + return this.renderer.getFirstVisibleRow(); + }; + this.getLastVisibleRow = function() { + return this.renderer.getLastVisibleRow(); + }; + this.isRowVisible = function(row) { + return (row >= this.getFirstVisibleRow() && row <= this.getLastVisibleRow()); + }; + this.isRowFullyVisible = function(row) { + return (row >= this.renderer.getFirstFullyVisibleRow() && row <= this.renderer.getLastFullyVisibleRow()); + }; + this.$getVisibleRowCount = function() { + return this.renderer.getScrollBottomRow() - this.renderer.getScrollTopRow() + 1; + }; + + this.$moveByPage = function(dir, select) { + var renderer = this.renderer; + var config = this.renderer.layerConfig; + var rows = dir * Math.floor(config.height / config.lineHeight); + + if (select === true) { + this.selection.$moveSelection(function(){ + this.moveCursorBy(rows, 0); + }); + } else if (select === false) { + this.selection.moveCursorBy(rows, 0); + this.selection.clearSelection(); + } + + var scrollTop = renderer.scrollTop; + + renderer.scrollBy(0, rows * config.lineHeight); + if (select != null) + renderer.scrollCursorIntoView(null, 0.5); + + renderer.animateScrolling(scrollTop); + }; + this.selectPageDown = function() { + this.$moveByPage(1, true); + }; + this.selectPageUp = function() { + this.$moveByPage(-1, true); + }; + this.gotoPageDown = function() { + this.$moveByPage(1, false); + }; + this.gotoPageUp = function() { + this.$moveByPage(-1, false); + }; + this.scrollPageDown = function() { + this.$moveByPage(1); + }; + this.scrollPageUp = function() { + this.$moveByPage(-1); + }; + this.scrollToRow = function(row) { + this.renderer.scrollToRow(row); + }; + this.scrollToLine = function(line, center, animate, callback) { + this.renderer.scrollToLine(line, center, animate, callback); + }; + this.centerSelection = function() { + var range = this.getSelectionRange(); + var pos = { + row: Math.floor(range.start.row + (range.end.row - range.start.row) / 2), + column: Math.floor(range.start.column + (range.end.column - range.start.column) / 2) + }; + this.renderer.alignCursor(pos, 0.5); + }; + this.getCursorPosition = function() { + return this.selection.getCursor(); + }; + this.getCursorPositionScreen = function() { + return this.session.documentToScreenPosition(this.getCursorPosition()); + }; + this.getSelectionRange = function() { + return this.selection.getRange(); + }; + this.selectAll = function() { + this.selection.selectAll(); + }; + this.clearSelection = function() { + this.selection.clearSelection(); + }; + this.moveCursorTo = function(row, column) { + this.selection.moveCursorTo(row, column); + }; + this.moveCursorToPosition = function(pos) { + this.selection.moveCursorToPosition(pos); + }; + this.jumpToMatching = function(select, expand) { + var cursor = this.getCursorPosition(); + var iterator = new TokenIterator(this.session, cursor.row, cursor.column); + var prevToken = iterator.getCurrentToken(); + var token = prevToken || iterator.stepForward(); + + if (!token) return; + var matchType; + var found = false; + var depth = {}; + var i = cursor.column - token.start; + var bracketType; + var brackets = { + ")": "(", + "(": "(", + "]": "[", + "[": "[", + "{": "{", + "}": "{" + }; + + do { + if (token.value.match(/[{}()\[\]]/g)) { + for (; i < token.value.length && !found; i++) { + if (!brackets[token.value[i]]) { + continue; + } + + bracketType = brackets[token.value[i]] + '.' + token.type.replace("rparen", "lparen"); + + if (isNaN(depth[bracketType])) { + depth[bracketType] = 0; + } + + switch (token.value[i]) { + case '(': + case '[': + case '{': + depth[bracketType]++; + break; + case ')': + case ']': + case '}': + depth[bracketType]--; + + if (depth[bracketType] === -1) { + matchType = 'bracket'; + found = true; + } + break; + } + } + } + else if (token.type.indexOf('tag-name') !== -1) { + if (isNaN(depth[token.value])) { + depth[token.value] = 0; + } + + if (prevToken.value === '<') { + depth[token.value]++; + } + else if (prevToken.value === '= 0; --i) { + if(this.$tryReplace(ranges[i], replacement)) { + replaced++; + } + } + + this.selection.setSelectionRange(selection); + + return replaced; + }; + + this.$tryReplace = function(range, replacement) { + var input = this.session.getTextRange(range); + replacement = this.$search.replace(input, replacement); + if (replacement !== null) { + range.end = this.session.replace(range, replacement); + return range; + } else { + return null; + } + }; + this.getLastSearchOptions = function() { + return this.$search.getOptions(); + }; + this.find = function(needle, options, animate) { + if (!options) + options = {}; + + if (typeof needle == "string" || needle instanceof RegExp) + options.needle = needle; + else if (typeof needle == "object") + oop.mixin(options, needle); + + var range = this.selection.getRange(); + if (options.needle == null) { + needle = this.session.getTextRange(range) + || this.$search.$options.needle; + if (!needle) { + range = this.session.getWordRange(range.start.row, range.start.column); + needle = this.session.getTextRange(range); + } + this.$search.set({needle: needle}); + } + + this.$search.set(options); + if (!options.start) + this.$search.set({start: range}); + + var newRange = this.$search.find(this.session); + if (options.preventScroll) + return newRange; + if (newRange) { + this.revealRange(newRange, animate); + return newRange; + } + if (options.backwards) + range.start = range.end; + else + range.end = range.start; + this.selection.setRange(range); + }; + this.findNext = function(options, animate) { + this.find({skipCurrent: true, backwards: false}, options, animate); + }; + this.findPrevious = function(options, animate) { + this.find(options, {skipCurrent: true, backwards: true}, animate); + }; + + this.revealRange = function(range, animate) { + this.session.unfold(range); + this.selection.setSelectionRange(range); + + var scrollTop = this.renderer.scrollTop; + this.renderer.scrollSelectionIntoView(range.start, range.end, 0.5); + if (animate !== false) + this.renderer.animateScrolling(scrollTop); + }; + this.undo = function() { + this.session.getUndoManager().undo(this.session); + this.renderer.scrollCursorIntoView(null, 0.5); + }; + this.redo = function() { + this.session.getUndoManager().redo(this.session); + this.renderer.scrollCursorIntoView(null, 0.5); + }; + this.destroy = function() { + if (this.$toDestroy) { + this.$toDestroy.forEach(function(el) { + el.destroy(); + }); + this.$toDestroy = null; + } + if (this.$mouseHandler) + this.$mouseHandler.destroy(); + this.renderer.destroy(); + this._signal("destroy", this); + if (this.session) + this.session.destroy(); + if (this._$emitInputEvent) + this._$emitInputEvent.cancel(); + this.removeAllListeners(); + }; + this.setAutoScrollEditorIntoView = function(enable) { + if (!enable) + return; + var rect; + var self = this; + var shouldScroll = false; + if (!this.$scrollAnchor) + this.$scrollAnchor = document.createElement("div"); + var scrollAnchor = this.$scrollAnchor; + scrollAnchor.style.cssText = "position:absolute"; + this.container.insertBefore(scrollAnchor, this.container.firstChild); + var onChangeSelection = this.on("changeSelection", function() { + shouldScroll = true; + }); + var onBeforeRender = this.renderer.on("beforeRender", function() { + if (shouldScroll) + rect = self.renderer.container.getBoundingClientRect(); + }); + var onAfterRender = this.renderer.on("afterRender", function() { + if (shouldScroll && rect && (self.isFocused() + || self.searchBox && self.searchBox.isFocused()) + ) { + var renderer = self.renderer; + var pos = renderer.$cursorLayer.$pixelPos; + var config = renderer.layerConfig; + var top = pos.top - config.offset; + if (pos.top >= 0 && top + rect.top < 0) { + shouldScroll = true; + } else if (pos.top < config.height && + pos.top + rect.top + config.lineHeight > window.innerHeight) { + shouldScroll = false; + } else { + shouldScroll = null; + } + if (shouldScroll != null) { + scrollAnchor.style.top = top + "px"; + scrollAnchor.style.left = pos.left + "px"; + scrollAnchor.style.height = config.lineHeight + "px"; + scrollAnchor.scrollIntoView(shouldScroll); + } + shouldScroll = rect = null; + } + }); + this.setAutoScrollEditorIntoView = function(enable) { + if (enable) + return; + delete this.setAutoScrollEditorIntoView; + this.off("changeSelection", onChangeSelection); + this.renderer.off("afterRender", onAfterRender); + this.renderer.off("beforeRender", onBeforeRender); + }; + }; + + + this.$resetCursorStyle = function() { + var style = this.$cursorStyle || "ace"; + var cursorLayer = this.renderer.$cursorLayer; + if (!cursorLayer) + return; + cursorLayer.setSmoothBlinking(/smooth/.test(style)); + cursorLayer.isBlinking = !this.$readOnly && style != "wide"; + dom.setCssClass(cursorLayer.element, "ace_slim-cursors", /slim/.test(style)); + }; + this.prompt = function(message, options, callback) { + var editor = this; + config.loadModule("./ext/prompt", function (module) { + module.prompt(editor, message, options, callback); + }); + }; + +}).call(Editor.prototype); + + + +config.defineOptions(Editor.prototype, "editor", { + selectionStyle: { + set: function(style) { + this.onSelectionChange(); + this._signal("changeSelectionStyle", {data: style}); + }, + initialValue: "line" + }, + highlightActiveLine: { + set: function() {this.$updateHighlightActiveLine();}, + initialValue: true + }, + highlightSelectedWord: { + set: function(shouldHighlight) {this.$onSelectionChange();}, + initialValue: true + }, + readOnly: { + set: function(readOnly) { + this.textInput.setReadOnly(readOnly); + this.$resetCursorStyle(); + }, + initialValue: false + }, + copyWithEmptySelection: { + set: function(value) { + this.textInput.setCopyWithEmptySelection(value); + }, + initialValue: false + }, + cursorStyle: { + set: function(val) { this.$resetCursorStyle(); }, + values: ["ace", "slim", "smooth", "wide"], + initialValue: "ace" + }, + mergeUndoDeltas: { + values: [false, true, "always"], + initialValue: true + }, + behavioursEnabled: {initialValue: true}, + wrapBehavioursEnabled: {initialValue: true}, + enableAutoIndent: {initialValue: true}, + autoScrollEditorIntoView: { + set: function(val) {this.setAutoScrollEditorIntoView(val);} + }, + keyboardHandler: { + set: function(val) { this.setKeyboardHandler(val); }, + get: function() { return this.$keybindingId; }, + handlesSet: true + }, + value: { + set: function(val) { this.session.setValue(val); }, + get: function() { return this.getValue(); }, + handlesSet: true, + hidden: true + }, + session: { + set: function(val) { this.setSession(val); }, + get: function() { return this.session; }, + handlesSet: true, + hidden: true + }, + + showLineNumbers: { + set: function(show) { + this.renderer.$gutterLayer.setShowLineNumbers(show); + this.renderer.$loop.schedule(this.renderer.CHANGE_GUTTER); + if (show && this.$relativeLineNumbers) + relativeNumberRenderer.attach(this); + else + relativeNumberRenderer.detach(this); + }, + initialValue: true + }, + relativeLineNumbers: { + set: function(value) { + if (this.$showLineNumbers && value) + relativeNumberRenderer.attach(this); + else + relativeNumberRenderer.detach(this); + } + }, + placeholder: { + set: function(message) { + if (!this.$updatePlaceholder) { + this.$updatePlaceholder = function() { + var value = this.session && (this.renderer.$composition || this.getValue()); + if (value && this.renderer.placeholderNode) { + this.renderer.off("afterRender", this.$updatePlaceholder); + dom.removeCssClass(this.container, "ace_hasPlaceholder"); + this.renderer.placeholderNode.remove(); + this.renderer.placeholderNode = null; + } else if (!value && !this.renderer.placeholderNode) { + this.renderer.on("afterRender", this.$updatePlaceholder); + dom.addCssClass(this.container, "ace_hasPlaceholder"); + var el = dom.createElement("div"); + el.className = "ace_placeholder"; + el.textContent = this.$placeholder || ""; + this.renderer.placeholderNode = el; + this.renderer.content.appendChild(this.renderer.placeholderNode); + } else if (!value && this.renderer.placeholderNode) { + this.renderer.placeholderNode.textContent = this.$placeholder || ""; + } + }.bind(this); + this.on("input", this.$updatePlaceholder); + } + this.$updatePlaceholder(); + } + }, + + hScrollBarAlwaysVisible: "renderer", + vScrollBarAlwaysVisible: "renderer", + highlightGutterLine: "renderer", + animatedScroll: "renderer", + showInvisibles: "renderer", + showPrintMargin: "renderer", + printMarginColumn: "renderer", + printMargin: "renderer", + fadeFoldWidgets: "renderer", + showFoldWidgets: "renderer", + displayIndentGuides: "renderer", + showGutter: "renderer", + fontSize: "renderer", + fontFamily: "renderer", + maxLines: "renderer", + minLines: "renderer", + scrollPastEnd: "renderer", + fixedWidthGutter: "renderer", + theme: "renderer", + hasCssTransforms: "renderer", + maxPixelHeight: "renderer", + useTextareaForIME: "renderer", + + scrollSpeed: "$mouseHandler", + dragDelay: "$mouseHandler", + dragEnabled: "$mouseHandler", + focusTimeout: "$mouseHandler", + tooltipFollowsMouse: "$mouseHandler", + + firstLineNumber: "session", + overwrite: "session", + newLineMode: "session", + useWorker: "session", + useSoftTabs: "session", + navigateWithinSoftTabs: "session", + tabSize: "session", + wrap: "session", + indentedSoftWrap: "session", + foldStyle: "session", + mode: "session" +}); + + +var relativeNumberRenderer = { + getText: function(session, row) { + return (Math.abs(session.selection.lead.row - row) || (row + 1 + (row < 9 ? "\xb7" : ""))) + ""; + }, + getWidth: function(session, lastLineNumber, config) { + return Math.max( + lastLineNumber.toString().length, + (config.lastRow + 1).toString().length, + 2 + ) * config.characterWidth; + }, + update: function(e, editor) { + editor.renderer.$loop.schedule(editor.renderer.CHANGE_GUTTER); + }, + attach: function(editor) { + editor.renderer.$gutterLayer.$renderer = this; + editor.on("changeSelection", this.update); + this.update(null, editor); + }, + detach: function(editor) { + if (editor.renderer.$gutterLayer.$renderer == this) + editor.renderer.$gutterLayer.$renderer = null; + editor.off("changeSelection", this.update); + this.update(null, editor); + } +}; + +exports.Editor = Editor; +}); + +ace.define("ace/undomanager",["require","exports","module","ace/range"], function(require, exports, module) { +"use strict"; +var UndoManager = function() { + this.$maxRev = 0; + this.$fromUndo = false; + this.reset(); +}; + +(function() { + + this.addSession = function(session) { + this.$session = session; + }; + this.add = function(delta, allowMerge, session) { + if (this.$fromUndo) return; + if (delta == this.$lastDelta) return; + if (!this.$keepRedoStack) this.$redoStack.length = 0; + if (allowMerge === false || !this.lastDeltas) { + this.lastDeltas = []; + this.$undoStack.push(this.lastDeltas); + delta.id = this.$rev = ++this.$maxRev; + } + if (delta.action == "remove" || delta.action == "insert") + this.$lastDelta = delta; + this.lastDeltas.push(delta); + }; + + this.addSelection = function(selection, rev) { + this.selections.push({ + value: selection, + rev: rev || this.$rev + }); + }; + + this.startNewGroup = function() { + this.lastDeltas = null; + return this.$rev; + }; + + this.markIgnored = function(from, to) { + if (to == null) to = this.$rev + 1; + var stack = this.$undoStack; + for (var i = stack.length; i--;) { + var delta = stack[i][0]; + if (delta.id <= from) + break; + if (delta.id < to) + delta.ignore = true; + } + this.lastDeltas = null; + }; + + this.getSelection = function(rev, after) { + var stack = this.selections; + for (var i = stack.length; i--;) { + var selection = stack[i]; + if (selection.rev < rev) { + if (after) + selection = stack[i + 1]; + return selection; + } + } + }; + + this.getRevision = function() { + return this.$rev; + }; + + this.getDeltas = function(from, to) { + if (to == null) to = this.$rev + 1; + var stack = this.$undoStack; + var end = null, start = 0; + for (var i = stack.length; i--;) { + var delta = stack[i][0]; + if (delta.id < to && !end) + end = i+1; + if (delta.id <= from) { + start = i + 1; + break; + } + } + return stack.slice(start, end); + }; + + this.getChangedRanges = function(from, to) { + if (to == null) to = this.$rev + 1; + + }; + + this.getChangedLines = function(from, to) { + if (to == null) to = this.$rev + 1; + + }; + this.undo = function(session, dontSelect) { + this.lastDeltas = null; + var stack = this.$undoStack; + + if (!rearrangeUndoStack(stack, stack.length)) + return; + + if (!session) + session = this.$session; + + if (this.$redoStackBaseRev !== this.$rev && this.$redoStack.length) + this.$redoStack = []; + + this.$fromUndo = true; + + var deltaSet = stack.pop(); + var undoSelectionRange = null; + if (deltaSet) { + undoSelectionRange = session.undoChanges(deltaSet, dontSelect); + this.$redoStack.push(deltaSet); + this.$syncRev(); + } + + this.$fromUndo = false; + + return undoSelectionRange; + }; + this.redo = function(session, dontSelect) { + this.lastDeltas = null; + + if (!session) + session = this.$session; + + this.$fromUndo = true; + if (this.$redoStackBaseRev != this.$rev) { + var diff = this.getDeltas(this.$redoStackBaseRev, this.$rev + 1); + rebaseRedoStack(this.$redoStack, diff); + this.$redoStackBaseRev = this.$rev; + this.$redoStack.forEach(function(x) { + x[0].id = ++this.$maxRev; + }, this); + } + var deltaSet = this.$redoStack.pop(); + var redoSelectionRange = null; + + if (deltaSet) { + redoSelectionRange = session.redoChanges(deltaSet, dontSelect); + this.$undoStack.push(deltaSet); + this.$syncRev(); + } + this.$fromUndo = false; + + return redoSelectionRange; + }; + + this.$syncRev = function() { + var stack = this.$undoStack; + var nextDelta = stack[stack.length - 1]; + var id = nextDelta && nextDelta[0].id || 0; + this.$redoStackBaseRev = id; + this.$rev = id; + }; + this.reset = function() { + this.lastDeltas = null; + this.$lastDelta = null; + this.$undoStack = []; + this.$redoStack = []; + this.$rev = 0; + this.mark = 0; + this.$redoStackBaseRev = this.$rev; + this.selections = []; + }; + this.canUndo = function() { + return this.$undoStack.length > 0; + }; + this.canRedo = function() { + return this.$redoStack.length > 0; + }; + this.bookmark = function(rev) { + if (rev == undefined) + rev = this.$rev; + this.mark = rev; + }; + this.isAtBookmark = function() { + return this.$rev === this.mark; + }; + + this.toJSON = function() { + + }; + + this.fromJSON = function() { + + }; + + this.hasUndo = this.canUndo; + this.hasRedo = this.canRedo; + this.isClean = this.isAtBookmark; + this.markClean = this.bookmark; + + this.$prettyPrint = function(delta) { + if (delta) return stringifyDelta(delta); + return stringifyDelta(this.$undoStack) + "\n---\n" + stringifyDelta(this.$redoStack); + }; +}).call(UndoManager.prototype); + +function rearrangeUndoStack(stack, pos) { + for (var i = pos; i--; ) { + var deltaSet = stack[i]; + if (deltaSet && !deltaSet[0].ignore) { + while(i < pos - 1) { + var swapped = swapGroups(stack[i], stack[i + 1]); + stack[i] = swapped[0]; + stack[i + 1] = swapped[1]; + i++; + } + return true; + } + } +} + +var Range = require("./range").Range; +var cmp = Range.comparePoints; +var comparePoints = Range.comparePoints; + +function $updateMarkers(delta) { + var isInsert = delta.action == "insert"; + var start = delta.start; + var end = delta.end; + var rowShift = (end.row - start.row) * (isInsert ? 1 : -1); + var colShift = (end.column - start.column) * (isInsert ? 1 : -1); + if (isInsert) end = start; + + for (var i in this.marks) { + var point = this.marks[i]; + var cmp = comparePoints(point, start); + if (cmp < 0) { + continue; // delta starts after the range + } + if (cmp === 0) { + if (isInsert) { + if (point.bias == 1) { + cmp = 1; + } + else { + point.bias == -1; + continue; + } + } + } + var cmp2 = isInsert ? cmp : comparePoints(point, end); + if (cmp2 > 0) { + point.row += rowShift; + point.column += point.row == end.row ? colShift : 0; + continue; + } + if (!isInsert && cmp2 <= 0) { + point.row = start.row; + point.column = start.column; + if (cmp2 === 0) + point.bias = 1; + } + } +} + + + +function clonePos(pos) { + return {row: pos.row,column: pos.column}; +} +function cloneDelta(d) { + return { + start: clonePos(d.start), + end: clonePos(d.end), + action: d.action, + lines: d.lines.slice() + }; +} +function stringifyDelta(d) { + d = d || this; + if (Array.isArray(d)) { + return d.map(stringifyDelta).join("\n"); + } + var type = ""; + if (d.action) { + type = d.action == "insert" ? "+" : "-"; + type += "[" + d.lines + "]"; + } else if (d.value) { + if (Array.isArray(d.value)) { + type = d.value.map(stringifyRange).join("\n"); + } else { + type = stringifyRange(d.value); + } + } + if (d.start) { + type += stringifyRange(d); + } + if (d.id || d.rev) { + type += "\t(" + (d.id || d.rev) + ")"; + } + return type; +} +function stringifyRange(r) { + return r.start.row + ":" + r.start.column + + "=>" + r.end.row + ":" + r.end.column; +} + +function swap(d1, d2) { + var i1 = d1.action == "insert"; + var i2 = d2.action == "insert"; + + if (i1 && i2) { + if (cmp(d2.start, d1.end) >= 0) { + shift(d2, d1, -1); + } else if (cmp(d2.start, d1.start) <= 0) { + shift(d1, d2, +1); + } else { + return null; + } + } else if (i1 && !i2) { + if (cmp(d2.start, d1.end) >= 0) { + shift(d2, d1, -1); + } else if (cmp(d2.end, d1.start) <= 0) { + shift(d1, d2, -1); + } else { + return null; + } + } else if (!i1 && i2) { + if (cmp(d2.start, d1.start) >= 0) { + shift(d2, d1, +1); + } else if (cmp(d2.start, d1.start) <= 0) { + shift(d1, d2, +1); + } else { + return null; + } + } else if (!i1 && !i2) { + if (cmp(d2.start, d1.start) >= 0) { + shift(d2, d1, +1); + } else if (cmp(d2.end, d1.start) <= 0) { + shift(d1, d2, -1); + } else { + return null; + } + } + return [d2, d1]; +} +function swapGroups(ds1, ds2) { + for (var i = ds1.length; i--; ) { + for (var j = 0; j < ds2.length; j++) { + if (!swap(ds1[i], ds2[j])) { + while (i < ds1.length) { + while (j--) { + swap(ds2[j], ds1[i]); + } + j = ds2.length; + i++; + } + return [ds1, ds2]; + } + } + } + ds1.selectionBefore = ds2.selectionBefore = + ds1.selectionAfter = ds2.selectionAfter = null; + return [ds2, ds1]; +} +function xform(d1, c1) { + var i1 = d1.action == "insert"; + var i2 = c1.action == "insert"; + + if (i1 && i2) { + if (cmp(d1.start, c1.start) < 0) { + shift(c1, d1, 1); + } else { + shift(d1, c1, 1); + } + } else if (i1 && !i2) { + if (cmp(d1.start, c1.end) >= 0) { + shift(d1, c1, -1); + } else if (cmp(d1.start, c1.start) <= 0) { + shift(c1, d1, +1); + } else { + shift(d1, Range.fromPoints(c1.start, d1.start), -1); + shift(c1, d1, +1); + } + } else if (!i1 && i2) { + if (cmp(c1.start, d1.end) >= 0) { + shift(c1, d1, -1); + } else if (cmp(c1.start, d1.start) <= 0) { + shift(d1, c1, +1); + } else { + shift(c1, Range.fromPoints(d1.start, c1.start), -1); + shift(d1, c1, +1); + } + } else if (!i1 && !i2) { + if (cmp(c1.start, d1.end) >= 0) { + shift(c1, d1, -1); + } else if (cmp(c1.end, d1.start) <= 0) { + shift(d1, c1, -1); + } else { + var before, after; + if (cmp(d1.start, c1.start) < 0) { + before = d1; + d1 = splitDelta(d1, c1.start); + } + if (cmp(d1.end, c1.end) > 0) { + after = splitDelta(d1, c1.end); + } + + shiftPos(c1.end, d1.start, d1.end, -1); + if (after && !before) { + d1.lines = after.lines; + d1.start = after.start; + d1.end = after.end; + after = d1; + } + + return [c1, before, after].filter(Boolean); + } + } + return [c1, d1]; +} + +function shift(d1, d2, dir) { + shiftPos(d1.start, d2.start, d2.end, dir); + shiftPos(d1.end, d2.start, d2.end, dir); +} +function shiftPos(pos, start, end, dir) { + if (pos.row == (dir == 1 ? start : end).row) { + pos.column += dir * (end.column - start.column); + } + pos.row += dir * (end.row - start.row); +} +function splitDelta(c, pos) { + var lines = c.lines; + var end = c.end; + c.end = clonePos(pos); + var rowsBefore = c.end.row - c.start.row; + var otherLines = lines.splice(rowsBefore, lines.length); + + var col = rowsBefore ? pos.column : pos.column - c.start.column; + lines.push(otherLines[0].substring(0, col)); + otherLines[0] = otherLines[0].substr(col) ; + var rest = { + start: clonePos(pos), + end: end, + lines: otherLines, + action: c.action + }; + return rest; +} + +function moveDeltasByOne(redoStack, d) { + d = cloneDelta(d); + for (var j = redoStack.length; j--;) { + var deltaSet = redoStack[j]; + for (var i = 0; i < deltaSet.length; i++) { + var x = deltaSet[i]; + var xformed = xform(x, d); + d = xformed[0]; + if (xformed.length != 2) { + if (xformed[2]) { + deltaSet.splice(i + 1, 1, xformed[1], xformed[2]); + i++; + } else if (!xformed[1]) { + deltaSet.splice(i, 1); + i--; + } + } + } + if (!deltaSet.length) { + redoStack.splice(j, 1); + } + } + return redoStack; +} +function rebaseRedoStack(redoStack, deltaSets) { + for (var i = 0; i < deltaSets.length; i++) { + var deltas = deltaSets[i]; + for (var j = 0; j < deltas.length; j++) { + moveDeltasByOne(redoStack, deltas[j]); + } + } +} + +exports.UndoManager = UndoManager; + +}); + +ace.define("ace/layer/lines",["require","exports","module","ace/lib/dom"], function(require, exports, module) { +"use strict"; + +var dom = require("../lib/dom"); + +var Lines = function(element, canvasHeight) { + this.element = element; + this.canvasHeight = canvasHeight || 500000; + this.element.style.height = (this.canvasHeight * 2) + "px"; + + this.cells = []; + this.cellCache = []; + this.$offsetCoefficient = 0; +}; + +(function() { + + this.moveContainer = function(config) { + dom.translate(this.element, 0, -((config.firstRowScreen * config.lineHeight) % this.canvasHeight) - config.offset * this.$offsetCoefficient); + }; + + this.pageChanged = function(oldConfig, newConfig) { + return ( + Math.floor((oldConfig.firstRowScreen * oldConfig.lineHeight) / this.canvasHeight) !== + Math.floor((newConfig.firstRowScreen * newConfig.lineHeight) / this.canvasHeight) + ); + }; + + this.computeLineTop = function(row, config, session) { + var screenTop = config.firstRowScreen * config.lineHeight; + var screenPage = Math.floor(screenTop / this.canvasHeight); + var lineTop = session.documentToScreenRow(row, 0) * config.lineHeight; + return lineTop - (screenPage * this.canvasHeight); + }; + + this.computeLineHeight = function(row, config, session) { + return config.lineHeight * session.getRowLineCount(row); + }; + + this.getLength = function() { + return this.cells.length; + }; + + this.get = function(index) { + return this.cells[index]; + }; + + this.shift = function() { + this.$cacheCell(this.cells.shift()); + }; + + this.pop = function() { + this.$cacheCell(this.cells.pop()); + }; + + this.push = function(cell) { + if (Array.isArray(cell)) { + this.cells.push.apply(this.cells, cell); + var fragment = dom.createFragment(this.element); + for (var i=0; i foldStart) { + row = fold.end.row + 1; + fold = session.getNextFoldLine(row, fold); + foldStart = fold ? fold.start.row : Infinity; + } + if (row > lastRow) { + while (this.$lines.getLength() > index + 1) + this.$lines.pop(); + + break; + } + + cell = this.$lines.get(++index); + if (cell) { + cell.row = row; + } else { + cell = this.$lines.createCell(row, config, this.session, onCreateCell); + this.$lines.push(cell); + } + + this.$renderCell(cell, config, fold, row); + row++; + } + + this._signal("afterRender"); + this.$updateGutterWidth(config); + }; + + this.$updateGutterWidth = function(config) { + var session = this.session; + + var gutterRenderer = session.gutterRenderer || this.$renderer; + + var firstLineNumber = session.$firstLineNumber; + var lastLineText = this.$lines.last() ? this.$lines.last().text : ""; + + if (this.$fixedWidth || session.$useWrapMode) + lastLineText = session.getLength() + firstLineNumber - 1; + + var gutterWidth = gutterRenderer + ? gutterRenderer.getWidth(session, lastLineText, config) + : lastLineText.toString().length * config.characterWidth; + + var padding = this.$padding || this.$computePadding(); + gutterWidth += padding.left + padding.right; + if (gutterWidth !== this.gutterWidth && !isNaN(gutterWidth)) { + this.gutterWidth = gutterWidth; + this.element.parentNode.style.width = + this.element.style.width = Math.ceil(this.gutterWidth) + "px"; + this._signal("changeGutterWidth", gutterWidth); + } + }; + + this.$updateCursorRow = function() { + if (!this.$highlightGutterLine) + return; + + var position = this.session.selection.getCursor(); + if (this.$cursorRow === position.row) + return; + + this.$cursorRow = position.row; + }; + + this.updateLineHighlight = function() { + if (!this.$highlightGutterLine) + return; + var row = this.session.selection.cursor.row; + this.$cursorRow = row; + + if (this.$cursorCell && this.$cursorCell.row == row) + return; + if (this.$cursorCell) + this.$cursorCell.element.className = this.$cursorCell.element.className.replace("ace_gutter-active-line ", ""); + var cells = this.$lines.cells; + this.$cursorCell = null; + for (var i = 0; i < cells.length; i++) { + var cell = cells[i]; + if (cell.row >= this.$cursorRow) { + if (cell.row > this.$cursorRow) { + var fold = this.session.getFoldLine(this.$cursorRow); + if (i > 0 && fold && fold.start.row == cells[i - 1].row) + cell = cells[i - 1]; + else + break; + } + cell.element.className = "ace_gutter-active-line " + cell.element.className; + this.$cursorCell = cell; + break; + } + } + }; + + this.scrollLines = function(config) { + var oldConfig = this.config; + this.config = config; + + this.$updateCursorRow(); + if (this.$lines.pageChanged(oldConfig, config)) + return this.update(config); + + this.$lines.moveContainer(config); + + var lastRow = Math.min(config.lastRow + config.gutterOffset, // needed to compensate for hor scollbar + this.session.getLength() - 1); + var oldLastRow = this.oldLastRow; + this.oldLastRow = lastRow; + + if (!oldConfig || oldLastRow < config.firstRow) + return this.update(config); + + if (lastRow < oldConfig.firstRow) + return this.update(config); + + if (oldConfig.firstRow < config.firstRow) + for (var row=this.session.getFoldedRowCount(oldConfig.firstRow, config.firstRow - 1); row>0; row--) + this.$lines.shift(); + + if (oldLastRow > lastRow) + for (var row=this.session.getFoldedRowCount(lastRow + 1, oldLastRow); row>0; row--) + this.$lines.pop(); + + if (config.firstRow < oldConfig.firstRow) { + this.$lines.unshift(this.$renderLines(config, config.firstRow, oldConfig.firstRow - 1)); + } + + if (lastRow > oldLastRow) { + this.$lines.push(this.$renderLines(config, oldLastRow + 1, lastRow)); + } + + this.updateLineHighlight(); + + this._signal("afterRender"); + this.$updateGutterWidth(config); + }; + + this.$renderLines = function(config, firstRow, lastRow) { + var fragment = []; + var row = firstRow; + var foldLine = this.session.getNextFoldLine(row); + var foldStart = foldLine ? foldLine.start.row : Infinity; + + while (true) { + if (row > foldStart) { + row = foldLine.end.row+1; + foldLine = this.session.getNextFoldLine(row, foldLine); + foldStart = foldLine ? foldLine.start.row : Infinity; + } + if (row > lastRow) + break; + + var cell = this.$lines.createCell(row, config, this.session, onCreateCell); + this.$renderCell(cell, config, foldLine, row); + fragment.push(cell); + + row++; + } + return fragment; + }; + + this.$renderCell = function(cell, config, fold, row) { + var element = cell.element; + + var session = this.session; + + var textNode = element.childNodes[0]; + var foldWidget = element.childNodes[1]; + + var firstLineNumber = session.$firstLineNumber; + + var breakpoints = session.$breakpoints; + var decorations = session.$decorations; + var gutterRenderer = session.gutterRenderer || this.$renderer; + var foldWidgets = this.$showFoldWidgets && session.foldWidgets; + var foldStart = fold ? fold.start.row : Number.MAX_VALUE; + + var className = "ace_gutter-cell "; + if (this.$highlightGutterLine) { + if (row == this.$cursorRow || (fold && row < this.$cursorRow && row >= foldStart && this.$cursorRow <= fold.end.row)) { + className += "ace_gutter-active-line "; + if (this.$cursorCell != cell) { + if (this.$cursorCell) + this.$cursorCell.element.className = this.$cursorCell.element.className.replace("ace_gutter-active-line ", ""); + this.$cursorCell = cell; + } + } + } + + if (breakpoints[row]) + className += breakpoints[row]; + if (decorations[row]) + className += decorations[row]; + if (this.$annotations[row]) + className += this.$annotations[row].className; + if (element.className != className) + element.className = className; + + if (foldWidgets) { + var c = foldWidgets[row]; + if (c == null) + c = foldWidgets[row] = session.getFoldWidget(row); + } + + if (c) { + var className = "ace_fold-widget ace_" + c; + if (c == "start" && row == foldStart && row < fold.end.row) + className += " ace_closed"; + else + className += " ace_open"; + if (foldWidget.className != className) + foldWidget.className = className; + + var foldHeight = config.lineHeight + "px"; + dom.setStyle(foldWidget.style, "height", foldHeight); + dom.setStyle(foldWidget.style, "display", "inline-block"); + } else { + if (foldWidget) { + dom.setStyle(foldWidget.style, "display", "none"); + } + } + + var text = (gutterRenderer + ? gutterRenderer.getText(session, row) + : row + firstLineNumber).toString(); + + if (text !== textNode.data) { + textNode.data = text; + } + + dom.setStyle(cell.element.style, "height", this.$lines.computeLineHeight(row, config, session) + "px"); + dom.setStyle(cell.element.style, "top", this.$lines.computeLineTop(row, config, session) + "px"); + + cell.text = text; + return cell; + }; + + this.$fixedWidth = false; + + this.$highlightGutterLine = true; + this.$renderer = ""; + this.setHighlightGutterLine = function(highlightGutterLine) { + this.$highlightGutterLine = highlightGutterLine; + }; + + this.$showLineNumbers = true; + this.$renderer = ""; + this.setShowLineNumbers = function(show) { + this.$renderer = !show && { + getWidth: function() {return 0;}, + getText: function() {return "";} + }; + }; + + this.getShowLineNumbers = function() { + return this.$showLineNumbers; + }; + + this.$showFoldWidgets = true; + this.setShowFoldWidgets = function(show) { + if (show) + dom.addCssClass(this.element, "ace_folding-enabled"); + else + dom.removeCssClass(this.element, "ace_folding-enabled"); + + this.$showFoldWidgets = show; + this.$padding = null; + }; + + this.getShowFoldWidgets = function() { + return this.$showFoldWidgets; + }; + + this.$computePadding = function() { + if (!this.element.firstChild) + return {left: 0, right: 0}; + var style = dom.computedStyle(this.element.firstChild); + this.$padding = {}; + this.$padding.left = (parseInt(style.borderLeftWidth) || 0) + + (parseInt(style.paddingLeft) || 0) + 1; + this.$padding.right = (parseInt(style.borderRightWidth) || 0) + + (parseInt(style.paddingRight) || 0); + return this.$padding; + }; + + this.getRegion = function(point) { + var padding = this.$padding || this.$computePadding(); + var rect = this.element.getBoundingClientRect(); + if (point.x < padding.left + rect.left) + return "markers"; + if (this.$showFoldWidgets && point.x > rect.right - padding.right) + return "foldWidgets"; + }; + +}).call(Gutter.prototype); + +function onCreateCell(element) { + var textNode = document.createTextNode(''); + element.appendChild(textNode); + + var foldWidget = dom.createElement("span"); + element.appendChild(foldWidget); + + return element; +} + +exports.Gutter = Gutter; + +}); + +ace.define("ace/layer/marker",["require","exports","module","ace/range","ace/lib/dom"], function(require, exports, module) { +"use strict"; + +var Range = require("../range").Range; +var dom = require("../lib/dom"); + +var Marker = function(parentEl) { + this.element = dom.createElement("div"); + this.element.className = "ace_layer ace_marker-layer"; + parentEl.appendChild(this.element); +}; + +(function() { + + this.$padding = 0; + + this.setPadding = function(padding) { + this.$padding = padding; + }; + this.setSession = function(session) { + this.session = session; + }; + + this.setMarkers = function(markers) { + this.markers = markers; + }; + + this.elt = function(className, css) { + var x = this.i != -1 && this.element.childNodes[this.i]; + if (!x) { + x = document.createElement("div"); + this.element.appendChild(x); + this.i = -1; + } else { + this.i++; + } + x.style.cssText = css; + x.className = className; + }; + + this.update = function(config) { + if (!config) return; + + this.config = config; + + this.i = 0; + var html; + for (var key in this.markers) { + var marker = this.markers[key]; + + if (!marker.range) { + marker.update(html, this, this.session, config); + continue; + } + + var range = marker.range.clipRows(config.firstRow, config.lastRow); + if (range.isEmpty()) continue; + + range = range.toScreenRange(this.session); + if (marker.renderer) { + var top = this.$getTop(range.start.row, config); + var left = this.$padding + range.start.column * config.characterWidth; + marker.renderer(html, range, left, top, config); + } else if (marker.type == "fullLine") { + this.drawFullLineMarker(html, range, marker.clazz, config); + } else if (marker.type == "screenLine") { + this.drawScreenLineMarker(html, range, marker.clazz, config); + } else if (range.isMultiLine()) { + if (marker.type == "text") + this.drawTextMarker(html, range, marker.clazz, config); + else + this.drawMultiLineMarker(html, range, marker.clazz, config); + } else { + this.drawSingleLineMarker(html, range, marker.clazz + " ace_start" + " ace_br15", config); + } + } + if (this.i !=-1) { + while (this.i < this.element.childElementCount) + this.element.removeChild(this.element.lastChild); + } + }; + + this.$getTop = function(row, layerConfig) { + return (row - layerConfig.firstRowScreen) * layerConfig.lineHeight; + }; + + function getBorderClass(tl, tr, br, bl) { + return (tl ? 1 : 0) | (tr ? 2 : 0) | (br ? 4 : 0) | (bl ? 8 : 0); + } + this.drawTextMarker = function(stringBuilder, range, clazz, layerConfig, extraStyle) { + var session = this.session; + var start = range.start.row; + var end = range.end.row; + var row = start; + var prev = 0; + var curr = 0; + var next = session.getScreenLastRowColumn(row); + var lineRange = new Range(row, range.start.column, row, curr); + for (; row <= end; row++) { + lineRange.start.row = lineRange.end.row = row; + lineRange.start.column = row == start ? range.start.column : session.getRowWrapIndent(row); + lineRange.end.column = next; + prev = curr; + curr = next; + next = row + 1 < end ? session.getScreenLastRowColumn(row + 1) : row == end ? 0 : range.end.column; + this.drawSingleLineMarker(stringBuilder, lineRange, + clazz + (row == start ? " ace_start" : "") + " ace_br" + + getBorderClass(row == start || row == start + 1 && range.start.column, prev < curr, curr > next, row == end), + layerConfig, row == end ? 0 : 1, extraStyle); + } + }; + this.drawMultiLineMarker = function(stringBuilder, range, clazz, config, extraStyle) { + var padding = this.$padding; + var height = config.lineHeight; + var top = this.$getTop(range.start.row, config); + var left = padding + range.start.column * config.characterWidth; + extraStyle = extraStyle || ""; + + if (this.session.$bidiHandler.isBidiRow(range.start.row)) { + var range1 = range.clone(); + range1.end.row = range1.start.row; + range1.end.column = this.session.getLine(range1.start.row).length; + this.drawBidiSingleLineMarker(stringBuilder, range1, clazz + " ace_br1 ace_start", config, null, extraStyle); + } else { + this.elt( + clazz + " ace_br1 ace_start", + "height:"+ height+ "px;"+ "right:0;"+ "top:"+top+ "px;left:"+ left+ "px;" + (extraStyle || "") + ); + } + if (this.session.$bidiHandler.isBidiRow(range.end.row)) { + var range1 = range.clone(); + range1.start.row = range1.end.row; + range1.start.column = 0; + this.drawBidiSingleLineMarker(stringBuilder, range1, clazz + " ace_br12", config, null, extraStyle); + } else { + top = this.$getTop(range.end.row, config); + var width = range.end.column * config.characterWidth; + + this.elt( + clazz + " ace_br12", + "height:"+ height+ "px;"+ + "width:"+ width+ "px;"+ + "top:"+ top+ "px;"+ + "left:"+ padding+ "px;"+ (extraStyle || "") + ); + } + height = (range.end.row - range.start.row - 1) * config.lineHeight; + if (height <= 0) + return; + top = this.$getTop(range.start.row + 1, config); + + var radiusClass = (range.start.column ? 1 : 0) | (range.end.column ? 0 : 8); + + this.elt( + clazz + (radiusClass ? " ace_br" + radiusClass : ""), + "height:"+ height+ "px;"+ + "right:0;"+ + "top:"+ top+ "px;"+ + "left:"+ padding+ "px;"+ (extraStyle || "") + ); + }; + this.drawSingleLineMarker = function(stringBuilder, range, clazz, config, extraLength, extraStyle) { + if (this.session.$bidiHandler.isBidiRow(range.start.row)) + return this.drawBidiSingleLineMarker(stringBuilder, range, clazz, config, extraLength, extraStyle); + var height = config.lineHeight; + var width = (range.end.column + (extraLength || 0) - range.start.column) * config.characterWidth; + + var top = this.$getTop(range.start.row, config); + var left = this.$padding + range.start.column * config.characterWidth; + + this.elt( + clazz, + "height:"+ height+ "px;"+ + "width:"+ width+ "px;"+ + "top:"+ top+ "px;"+ + "left:"+ left+ "px;"+ (extraStyle || "") + ); + }; + this.drawBidiSingleLineMarker = function(stringBuilder, range, clazz, config, extraLength, extraStyle) { + var height = config.lineHeight, top = this.$getTop(range.start.row, config), padding = this.$padding; + var selections = this.session.$bidiHandler.getSelections(range.start.column, range.end.column); + + selections.forEach(function(selection) { + this.elt( + clazz, + "height:" + height + "px;" + + "width:" + selection.width + (extraLength || 0) + "px;" + + "top:" + top + "px;" + + "left:" + (padding + selection.left) + "px;" + (extraStyle || "") + ); + }, this); + }; + + this.drawFullLineMarker = function(stringBuilder, range, clazz, config, extraStyle) { + var top = this.$getTop(range.start.row, config); + var height = config.lineHeight; + if (range.start.row != range.end.row) + height += this.$getTop(range.end.row, config) - top; + + this.elt( + clazz, + "height:"+ height+ "px;"+ + "top:"+ top+ "px;"+ + "left:0;right:0;"+ (extraStyle || "") + ); + }; + + this.drawScreenLineMarker = function(stringBuilder, range, clazz, config, extraStyle) { + var top = this.$getTop(range.start.row, config); + var height = config.lineHeight; + + this.elt( + clazz, + "height:"+ height+ "px;"+ + "top:"+ top+ "px;"+ + "left:0;right:0;"+ (extraStyle || "") + ); + }; + +}).call(Marker.prototype); + +exports.Marker = Marker; + +}); + +ace.define("ace/layer/text",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/layer/lines","ace/lib/event_emitter"], function(require, exports, module) { +"use strict"; + +var oop = require("../lib/oop"); +var dom = require("../lib/dom"); +var lang = require("../lib/lang"); +var Lines = require("./lines").Lines; +var EventEmitter = require("../lib/event_emitter").EventEmitter; + +var Text = function(parentEl) { + this.dom = dom; + this.element = this.dom.createElement("div"); + this.element.className = "ace_layer ace_text-layer"; + parentEl.appendChild(this.element); + this.$updateEolChar = this.$updateEolChar.bind(this); + this.$lines = new Lines(this.element); +}; + +(function() { + + oop.implement(this, EventEmitter); + + this.EOF_CHAR = "\xB6"; + this.EOL_CHAR_LF = "\xAC"; + this.EOL_CHAR_CRLF = "\xa4"; + this.EOL_CHAR = this.EOL_CHAR_LF; + this.TAB_CHAR = "\u2014"; //"\u21E5"; + this.SPACE_CHAR = "\xB7"; + this.$padding = 0; + this.MAX_LINE_LENGTH = 10000; + + this.$updateEolChar = function() { + var doc = this.session.doc; + var unixMode = doc.getNewLineCharacter() == "\n" && doc.getNewLineMode() != "windows"; + var EOL_CHAR = unixMode ? this.EOL_CHAR_LF : this.EOL_CHAR_CRLF; + if (this.EOL_CHAR != EOL_CHAR) { + this.EOL_CHAR = EOL_CHAR; + return true; + } + }; + + this.setPadding = function(padding) { + this.$padding = padding; + this.element.style.margin = "0 " + padding + "px"; + }; + + this.getLineHeight = function() { + return this.$fontMetrics.$characterSize.height || 0; + }; + + this.getCharacterWidth = function() { + return this.$fontMetrics.$characterSize.width || 0; + }; + + this.$setFontMetrics = function(measure) { + this.$fontMetrics = measure; + this.$fontMetrics.on("changeCharacterSize", function(e) { + this._signal("changeCharacterSize", e); + }.bind(this)); + this.$pollSizeChanges(); + }; + + this.checkForSizeChanges = function() { + this.$fontMetrics.checkForSizeChanges(); + }; + this.$pollSizeChanges = function() { + return this.$pollSizeChangesTimer = this.$fontMetrics.$pollSizeChanges(); + }; + this.setSession = function(session) { + this.session = session; + if (session) + this.$computeTabString(); + }; + + this.showInvisibles = false; + this.showSpaces = false; + this.showTabs = false; + this.showEOL = false; + this.setShowInvisibles = function(showInvisibles) { + if (this.showInvisibles == showInvisibles) + return false; + + this.showInvisibles = showInvisibles; + if (typeof showInvisibles == "string") { + this.showSpaces = /tab/i.test(showInvisibles); + this.showTabs = /space/i.test(showInvisibles); + this.showEOL = /eol/i.test(showInvisibles); + } else { + this.showSpaces = this.showTabs = this.showEOL = showInvisibles; + } + this.$computeTabString(); + return true; + }; + + this.displayIndentGuides = true; + this.setDisplayIndentGuides = function(display) { + if (this.displayIndentGuides == display) + return false; + + this.displayIndentGuides = display; + this.$computeTabString(); + return true; + }; + + this.$tabStrings = []; + this.onChangeTabSize = + this.$computeTabString = function() { + var tabSize = this.session.getTabSize(); + this.tabSize = tabSize; + var tabStr = this.$tabStrings = [0]; + for (var i = 1; i < tabSize + 1; i++) { + if (this.showTabs) { + var span = this.dom.createElement("span"); + span.className = "ace_invisible ace_invisible_tab"; + span.textContent = lang.stringRepeat(this.TAB_CHAR, i); + tabStr.push(span); + } else { + tabStr.push(this.dom.createTextNode(lang.stringRepeat(" ", i), this.element)); + } + } + if (this.displayIndentGuides) { + this.$indentGuideRe = /\s\S| \t|\t |\s$/; + var className = "ace_indent-guide"; + var spaceClass = this.showSpaces ? " ace_invisible ace_invisible_space" : ""; + var spaceContent = this.showSpaces + ? lang.stringRepeat(this.SPACE_CHAR, this.tabSize) + : lang.stringRepeat(" ", this.tabSize); + + var tabClass = this.showTabs ? " ace_invisible ace_invisible_tab" : ""; + var tabContent = this.showTabs + ? lang.stringRepeat(this.TAB_CHAR, this.tabSize) + : spaceContent; + + var span = this.dom.createElement("span"); + span.className = className + spaceClass; + span.textContent = spaceContent; + this.$tabStrings[" "] = span; + + var span = this.dom.createElement("span"); + span.className = className + tabClass; + span.textContent = tabContent; + this.$tabStrings["\t"] = span; + } + }; + + this.updateLines = function(config, firstRow, lastRow) { + if (this.config.lastRow != config.lastRow || + this.config.firstRow != config.firstRow) { + return this.update(config); + } + + this.config = config; + + var first = Math.max(firstRow, config.firstRow); + var last = Math.min(lastRow, config.lastRow); + + var lineElements = this.element.childNodes; + var lineElementsIdx = 0; + + for (var row = config.firstRow; row < first; row++) { + var foldLine = this.session.getFoldLine(row); + if (foldLine) { + if (foldLine.containsRow(first)) { + first = foldLine.start.row; + break; + } else { + row = foldLine.end.row; + } + } + lineElementsIdx ++; + } + + var heightChanged = false; + var row = first; + var foldLine = this.session.getNextFoldLine(row); + var foldStart = foldLine ? foldLine.start.row : Infinity; + + while (true) { + if (row > foldStart) { + row = foldLine.end.row+1; + foldLine = this.session.getNextFoldLine(row, foldLine); + foldStart = foldLine ? foldLine.start.row :Infinity; + } + if (row > last) + break; + + var lineElement = lineElements[lineElementsIdx++]; + if (lineElement) { + this.dom.removeChildren(lineElement); + this.$renderLine( + lineElement, row, row == foldStart ? foldLine : false + ); + + if (heightChanged) + lineElement.style.top = this.$lines.computeLineTop(row, config, this.session) + "px"; + + var height = (config.lineHeight * this.session.getRowLength(row)) + "px"; + if (lineElement.style.height != height) { + heightChanged = true; + lineElement.style.height = height; + } + } + row++; + } + if (heightChanged) { + while (lineElementsIdx < this.$lines.cells.length) { + var cell = this.$lines.cells[lineElementsIdx++]; + cell.element.style.top = this.$lines.computeLineTop(cell.row, config, this.session) + "px"; + } + } + }; + + this.scrollLines = function(config) { + var oldConfig = this.config; + this.config = config; + + if (this.$lines.pageChanged(oldConfig, config)) + return this.update(config); + + this.$lines.moveContainer(config); + + var lastRow = config.lastRow; + var oldLastRow = oldConfig ? oldConfig.lastRow : -1; + + if (!oldConfig || oldLastRow < config.firstRow) + return this.update(config); + + if (lastRow < oldConfig.firstRow) + return this.update(config); + + if (!oldConfig || oldConfig.lastRow < config.firstRow) + return this.update(config); + + if (config.lastRow < oldConfig.firstRow) + return this.update(config); + + if (oldConfig.firstRow < config.firstRow) + for (var row=this.session.getFoldedRowCount(oldConfig.firstRow, config.firstRow - 1); row>0; row--) + this.$lines.shift(); + + if (oldConfig.lastRow > config.lastRow) + for (var row=this.session.getFoldedRowCount(config.lastRow + 1, oldConfig.lastRow); row>0; row--) + this.$lines.pop(); + + if (config.firstRow < oldConfig.firstRow) { + this.$lines.unshift(this.$renderLinesFragment(config, config.firstRow, oldConfig.firstRow - 1)); + } + + if (config.lastRow > oldConfig.lastRow) { + this.$lines.push(this.$renderLinesFragment(config, oldConfig.lastRow + 1, config.lastRow)); + } + }; + + this.$renderLinesFragment = function(config, firstRow, lastRow) { + var fragment = []; + var row = firstRow; + var foldLine = this.session.getNextFoldLine(row); + var foldStart = foldLine ? foldLine.start.row : Infinity; + + while (true) { + if (row > foldStart) { + row = foldLine.end.row+1; + foldLine = this.session.getNextFoldLine(row, foldLine); + foldStart = foldLine ? foldLine.start.row : Infinity; + } + if (row > lastRow) + break; + + var line = this.$lines.createCell(row, config, this.session); + + var lineEl = line.element; + this.dom.removeChildren(lineEl); + dom.setStyle(lineEl.style, "height", this.$lines.computeLineHeight(row, config, this.session) + "px"); + dom.setStyle(lineEl.style, "top", this.$lines.computeLineTop(row, config, this.session) + "px"); + this.$renderLine(lineEl, row, row == foldStart ? foldLine : false); + + if (this.$useLineGroups()) { + lineEl.className = "ace_line_group"; + } else { + lineEl.className = "ace_line"; + } + fragment.push(line); + + row++; + } + return fragment; + }; + + this.update = function(config) { + this.$lines.moveContainer(config); + + this.config = config; + + var firstRow = config.firstRow; + var lastRow = config.lastRow; + + var lines = this.$lines; + while (lines.getLength()) + lines.pop(); + + lines.push(this.$renderLinesFragment(config, firstRow, lastRow)); + }; + + this.$textToken = { + "text": true, + "rparen": true, + "lparen": true + }; + + this.$renderToken = function(parent, screenColumn, token, value) { + var self = this; + var re = /(\t)|( +)|([\x00-\x1f\x80-\xa0\xad\u1680\u180E\u2000-\u200f\u2028\u2029\u202F\u205F\uFEFF\uFFF9-\uFFFC\u2066\u2067\u2068\u202A\u202B\u202D\u202E\u202C\u2069]+)|(\u3000)|([\u1100-\u115F\u11A3-\u11A7\u11FA-\u11FF\u2329-\u232A\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3001-\u303E\u3041-\u3096\u3099-\u30FF\u3105-\u312D\u3131-\u318E\u3190-\u31BA\u31C0-\u31E3\u31F0-\u321E\u3220-\u3247\u3250-\u32FE\u3300-\u4DBF\u4E00-\uA48C\uA490-\uA4C6\uA960-\uA97C\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFAFF\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE66\uFE68-\uFE6B\uFF01-\uFF60\uFFE0-\uFFE6]|[\uD800-\uDBFF][\uDC00-\uDFFF])/g; + + var valueFragment = this.dom.createFragment(this.element); + + var m; + var i = 0; + while (m = re.exec(value)) { + var tab = m[1]; + var simpleSpace = m[2]; + var controlCharacter = m[3]; + var cjkSpace = m[4]; + var cjk = m[5]; + + if (!self.showSpaces && simpleSpace) + continue; + + var before = i != m.index ? value.slice(i, m.index) : ""; + + i = m.index + m[0].length; + + if (before) { + valueFragment.appendChild(this.dom.createTextNode(before, this.element)); + } + + if (tab) { + var tabSize = self.session.getScreenTabSize(screenColumn + m.index); + valueFragment.appendChild(self.$tabStrings[tabSize].cloneNode(true)); + screenColumn += tabSize - 1; + } else if (simpleSpace) { + if (self.showSpaces) { + var span = this.dom.createElement("span"); + span.className = "ace_invisible ace_invisible_space"; + span.textContent = lang.stringRepeat(self.SPACE_CHAR, simpleSpace.length); + valueFragment.appendChild(span); + } else { + valueFragment.appendChild(this.com.createTextNode(simpleSpace, this.element)); + } + } else if (controlCharacter) { + var span = this.dom.createElement("span"); + span.className = "ace_invisible ace_invisible_space ace_invalid"; + span.textContent = lang.stringRepeat(self.SPACE_CHAR, controlCharacter.length); + valueFragment.appendChild(span); + } else if (cjkSpace) { + screenColumn += 1; + + var span = this.dom.createElement("span"); + span.style.width = (self.config.characterWidth * 2) + "px"; + span.className = self.showSpaces ? "ace_cjk ace_invisible ace_invisible_space" : "ace_cjk"; + span.textContent = self.showSpaces ? self.SPACE_CHAR : cjkSpace; + valueFragment.appendChild(span); + } else if (cjk) { + screenColumn += 1; + var span = this.dom.createElement("span"); + span.style.width = (self.config.characterWidth * 2) + "px"; + span.className = "ace_cjk"; + span.textContent = cjk; + valueFragment.appendChild(span); + } + } + + valueFragment.appendChild(this.dom.createTextNode(i ? value.slice(i) : value, this.element)); + + if (!this.$textToken[token.type]) { + var classes = "ace_" + token.type.replace(/\./g, " ace_"); + var span = this.dom.createElement("span"); + if (token.type == "fold") + span.style.width = (token.value.length * this.config.characterWidth) + "px"; + + span.className = classes; + span.appendChild(valueFragment); + + parent.appendChild(span); + } + else { + parent.appendChild(valueFragment); + } + + return screenColumn + value.length; + }; + + this.renderIndentGuide = function(parent, value, max) { + var cols = value.search(this.$indentGuideRe); + if (cols <= 0 || cols >= max) + return value; + if (value[0] == " ") { + cols -= cols % this.tabSize; + var count = cols/this.tabSize; + for (var i=0; i= splitChars) { + screenColumn = this.$renderToken( + lineEl, screenColumn, + token, value.substring(0, splitChars - chars) + ); + value = value.substring(splitChars - chars); + chars = splitChars; + + lineEl = this.$createLineElement(); + parent.appendChild(lineEl); + + lineEl.appendChild(this.dom.createTextNode(lang.stringRepeat("\xa0", splits.indent), this.element)); + + split ++; + screenColumn = 0; + splitChars = splits[split] || Number.MAX_VALUE; + } + if (value.length != 0) { + chars += value.length; + screenColumn = this.$renderToken( + lineEl, screenColumn, token, value + ); + } + } + } + + if (splits[splits.length - 1] > this.MAX_LINE_LENGTH) + this.$renderOverflowMessage(lineEl, screenColumn, null, "", true); + }; + + this.$renderSimpleLine = function(parent, tokens) { + var screenColumn = 0; + var token = tokens[0]; + var value = token.value; + if (this.displayIndentGuides) + value = this.renderIndentGuide(parent, value); + if (value) + screenColumn = this.$renderToken(parent, screenColumn, token, value); + for (var i = 1; i < tokens.length; i++) { + token = tokens[i]; + value = token.value; + if (screenColumn + value.length > this.MAX_LINE_LENGTH) + return this.$renderOverflowMessage(parent, screenColumn, token, value); + screenColumn = this.$renderToken(parent, screenColumn, token, value); + } + }; + + this.$renderOverflowMessage = function(parent, screenColumn, token, value, hide) { + token && this.$renderToken(parent, screenColumn, token, + value.slice(0, this.MAX_LINE_LENGTH - screenColumn)); + + var overflowEl = this.dom.createElement("span"); + overflowEl.className = "ace_inline_button ace_keyword ace_toggle_wrap"; + overflowEl.textContent = hide ? "" : ""; + + parent.appendChild(overflowEl); + }; + this.$renderLine = function(parent, row, foldLine) { + if (!foldLine && foldLine != false) + foldLine = this.session.getFoldLine(row); + + if (foldLine) + var tokens = this.$getFoldLineTokens(row, foldLine); + else + var tokens = this.session.getTokens(row); + + var lastLineEl = parent; + if (tokens.length) { + var splits = this.session.getRowSplitData(row); + if (splits && splits.length) { + this.$renderWrappedLine(parent, tokens, splits); + var lastLineEl = parent.lastChild; + } else { + var lastLineEl = parent; + if (this.$useLineGroups()) { + lastLineEl = this.$createLineElement(); + parent.appendChild(lastLineEl); + } + this.$renderSimpleLine(lastLineEl, tokens); + } + } else if (this.$useLineGroups()) { + lastLineEl = this.$createLineElement(); + parent.appendChild(lastLineEl); + } + + if (this.showEOL && lastLineEl) { + if (foldLine) + row = foldLine.end.row; + + var invisibleEl = this.dom.createElement("span"); + invisibleEl.className = "ace_invisible ace_invisible_eol"; + invisibleEl.textContent = row == this.session.getLength() - 1 ? this.EOF_CHAR : this.EOL_CHAR; + + lastLineEl.appendChild(invisibleEl); + } + }; + + this.$getFoldLineTokens = function(row, foldLine) { + var session = this.session; + var renderTokens = []; + + function addTokens(tokens, from, to) { + var idx = 0, col = 0; + while ((col + tokens[idx].value.length) < from) { + col += tokens[idx].value.length; + idx++; + + if (idx == tokens.length) + return; + } + if (col != from) { + var value = tokens[idx].value.substring(from - col); + if (value.length > (to - from)) + value = value.substring(0, to - from); + + renderTokens.push({ + type: tokens[idx].type, + value: value + }); + + col = from + value.length; + idx += 1; + } + + while (col < to && idx < tokens.length) { + var value = tokens[idx].value; + if (value.length + col > to) { + renderTokens.push({ + type: tokens[idx].type, + value: value.substring(0, to - col) + }); + } else + renderTokens.push(tokens[idx]); + col += value.length; + idx += 1; + } + } + + var tokens = session.getTokens(row); + foldLine.walk(function(placeholder, row, column, lastColumn, isNewRow) { + if (placeholder != null) { + renderTokens.push({ + type: "fold", + value: placeholder + }); + } else { + if (isNewRow) + tokens = session.getTokens(row); + + if (tokens.length) + addTokens(tokens, lastColumn, column); + } + }, foldLine.end.row, this.session.getLine(foldLine.end.row).length); + + return renderTokens; + }; + + this.$useLineGroups = function() { + return this.session.getUseWrapMode(); + }; + + this.destroy = function() {}; +}).call(Text.prototype); + +exports.Text = Text; + +}); + +ace.define("ace/layer/cursor",["require","exports","module","ace/lib/dom"], function(require, exports, module) { +"use strict"; + +var dom = require("../lib/dom"); + +var Cursor = function(parentEl) { + this.element = dom.createElement("div"); + this.element.className = "ace_layer ace_cursor-layer"; + parentEl.appendChild(this.element); + + this.isVisible = false; + this.isBlinking = true; + this.blinkInterval = 1000; + this.smoothBlinking = false; + + this.cursors = []; + this.cursor = this.addCursor(); + dom.addCssClass(this.element, "ace_hidden-cursors"); + this.$updateCursors = this.$updateOpacity.bind(this); +}; + +(function() { + + this.$updateOpacity = function(val) { + var cursors = this.cursors; + for (var i = cursors.length; i--; ) + dom.setStyle(cursors[i].style, "opacity", val ? "" : "0"); + }; + + this.$startCssAnimation = function() { + var cursors = this.cursors; + for (var i = cursors.length; i--; ) + cursors[i].style.animationDuration = this.blinkInterval + "ms"; + + this.$isAnimating = true; + setTimeout(function() { + if (this.$isAnimating) { + dom.addCssClass(this.element, "ace_animate-blinking"); + } + }.bind(this)); + }; + + this.$stopCssAnimation = function() { + this.$isAnimating = false; + dom.removeCssClass(this.element, "ace_animate-blinking"); + }; + + this.$padding = 0; + this.setPadding = function(padding) { + this.$padding = padding; + }; + + this.setSession = function(session) { + this.session = session; + }; + + this.setBlinking = function(blinking) { + if (blinking != this.isBlinking) { + this.isBlinking = blinking; + this.restartTimer(); + } + }; + + this.setBlinkInterval = function(blinkInterval) { + if (blinkInterval != this.blinkInterval) { + this.blinkInterval = blinkInterval; + this.restartTimer(); + } + }; + + this.setSmoothBlinking = function(smoothBlinking) { + if (smoothBlinking != this.smoothBlinking) { + this.smoothBlinking = smoothBlinking; + dom.setCssClass(this.element, "ace_smooth-blinking", smoothBlinking); + this.$updateCursors(true); + this.restartTimer(); + } + }; + + this.addCursor = function() { + var el = dom.createElement("div"); + el.className = "ace_cursor"; + this.element.appendChild(el); + this.cursors.push(el); + return el; + }; + + this.removeCursor = function() { + if (this.cursors.length > 1) { + var el = this.cursors.pop(); + el.parentNode.removeChild(el); + return el; + } + }; + + this.hideCursor = function() { + this.isVisible = false; + dom.addCssClass(this.element, "ace_hidden-cursors"); + this.restartTimer(); + }; + + this.showCursor = function() { + this.isVisible = true; + dom.removeCssClass(this.element, "ace_hidden-cursors"); + this.restartTimer(); + }; + + this.restartTimer = function() { + var update = this.$updateCursors; + clearInterval(this.intervalId); + clearTimeout(this.timeoutId); + this.$stopCssAnimation(); + + if (this.smoothBlinking) { + this.$isSmoothBlinking = false; + dom.removeCssClass(this.element, "ace_smooth-blinking"); + } + + update(true); + + if (!this.isBlinking || !this.blinkInterval || !this.isVisible) { + this.$stopCssAnimation(); + return; + } + + if (this.smoothBlinking) { + this.$isSmoothBlinking = true; + setTimeout(function() { + if (this.$isSmoothBlinking) { + dom.addCssClass(this.element, "ace_smooth-blinking"); + } + }.bind(this)); + } + + if (dom.HAS_CSS_ANIMATION) { + this.$startCssAnimation(); + } else { + var blink = function(){ + this.timeoutId = setTimeout(function() { + update(false); + }, 0.6 * this.blinkInterval); + }.bind(this); + + this.intervalId = setInterval(function() { + update(true); + blink(); + }, this.blinkInterval); + blink(); + } + }; + + this.getPixelPosition = function(position, onScreen) { + if (!this.config || !this.session) + return {left : 0, top : 0}; + + if (!position) + position = this.session.selection.getCursor(); + var pos = this.session.documentToScreenPosition(position); + var cursorLeft = this.$padding + (this.session.$bidiHandler.isBidiRow(pos.row, position.row) + ? this.session.$bidiHandler.getPosLeft(pos.column) + : pos.column * this.config.characterWidth); + + var cursorTop = (pos.row - (onScreen ? this.config.firstRowScreen : 0)) * + this.config.lineHeight; + + return {left : cursorLeft, top : cursorTop}; + }; + + this.isCursorInView = function(pixelPos, config) { + return pixelPos.top >= 0 && pixelPos.top < config.maxHeight; + }; + + this.update = function(config) { + this.config = config; + + var selections = this.session.$selectionMarkers; + var i = 0, cursorIndex = 0; + + if (selections === undefined || selections.length === 0){ + selections = [{cursor: null}]; + } + + for (var i = 0, n = selections.length; i < n; i++) { + var pixelPos = this.getPixelPosition(selections[i].cursor, true); + if ((pixelPos.top > config.height + config.offset || + pixelPos.top < 0) && i > 1) { + continue; + } + + var element = this.cursors[cursorIndex++] || this.addCursor(); + var style = element.style; + + if (!this.drawCursor) { + if (!this.isCursorInView(pixelPos, config)) { + dom.setStyle(style, "display", "none"); + } else { + dom.setStyle(style, "display", "block"); + dom.translate(element, pixelPos.left, pixelPos.top); + dom.setStyle(style, "width", Math.round(config.characterWidth) + "px"); + dom.setStyle(style, "height", config.lineHeight + "px"); + } + } else { + this.drawCursor(element, pixelPos, config, selections[i], this.session); + } + } + while (this.cursors.length > cursorIndex) + this.removeCursor(); + + var overwrite = this.session.getOverwrite(); + this.$setOverwrite(overwrite); + this.$pixelPos = pixelPos; + this.restartTimer(); + }; + + this.drawCursor = null; + + this.$setOverwrite = function(overwrite) { + if (overwrite != this.overwrite) { + this.overwrite = overwrite; + if (overwrite) + dom.addCssClass(this.element, "ace_overwrite-cursors"); + else + dom.removeCssClass(this.element, "ace_overwrite-cursors"); + } + }; + + this.destroy = function() { + clearInterval(this.intervalId); + clearTimeout(this.timeoutId); + }; + +}).call(Cursor.prototype); + +exports.Cursor = Cursor; + +}); + +ace.define("ace/scrollbar",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/event","ace/lib/event_emitter"], function(require, exports, module) { +"use strict"; + +var oop = require("./lib/oop"); +var dom = require("./lib/dom"); +var event = require("./lib/event"); +var EventEmitter = require("./lib/event_emitter").EventEmitter; +var MAX_SCROLL_H = 0x8000; +var ScrollBar = function(parent) { + this.element = dom.createElement("div"); + this.element.className = "ace_scrollbar ace_scrollbar" + this.classSuffix; + + this.inner = dom.createElement("div"); + this.inner.className = "ace_scrollbar-inner"; + this.inner.textContent = "\xa0"; + this.element.appendChild(this.inner); + + parent.appendChild(this.element); + + this.setVisible(false); + this.skipEvent = false; + + event.addListener(this.element, "scroll", this.onScroll.bind(this)); + event.addListener(this.element, "mousedown", event.preventDefault); +}; + +(function() { + oop.implement(this, EventEmitter); + + this.setVisible = function(isVisible) { + this.element.style.display = isVisible ? "" : "none"; + this.isVisible = isVisible; + this.coeff = 1; + }; +}).call(ScrollBar.prototype); +var VScrollBar = function(parent, renderer) { + ScrollBar.call(this, parent); + this.scrollTop = 0; + this.scrollHeight = 0; + renderer.$scrollbarWidth = + this.width = dom.scrollbarWidth(parent.ownerDocument); + this.inner.style.width = + this.element.style.width = (this.width || 15) + 5 + "px"; + this.$minWidth = 0; +}; + +oop.inherits(VScrollBar, ScrollBar); + +(function() { + + this.classSuffix = '-v'; + this.onScroll = function() { + if (!this.skipEvent) { + this.scrollTop = this.element.scrollTop; + if (this.coeff != 1) { + var h = this.element.clientHeight / this.scrollHeight; + this.scrollTop = this.scrollTop * (1 - h) / (this.coeff - h); + } + this._emit("scroll", {data: this.scrollTop}); + } + this.skipEvent = false; + }; + this.getWidth = function() { + return Math.max(this.isVisible ? this.width : 0, this.$minWidth || 0); + }; + this.setHeight = function(height) { + this.element.style.height = height + "px"; + }; + this.setInnerHeight = + this.setScrollHeight = function(height) { + this.scrollHeight = height; + if (height > MAX_SCROLL_H) { + this.coeff = MAX_SCROLL_H / height; + height = MAX_SCROLL_H; + } else if (this.coeff != 1) { + this.coeff = 1; + } + this.inner.style.height = height + "px"; + }; + this.setScrollTop = function(scrollTop) { + if (this.scrollTop != scrollTop) { + this.skipEvent = true; + this.scrollTop = scrollTop; + this.element.scrollTop = scrollTop * this.coeff; + } + }; + +}).call(VScrollBar.prototype); +var HScrollBar = function(parent, renderer) { + ScrollBar.call(this, parent); + this.scrollLeft = 0; + this.height = renderer.$scrollbarWidth; + this.inner.style.height = + this.element.style.height = (this.height || 15) + 5 + "px"; +}; + +oop.inherits(HScrollBar, ScrollBar); + +(function() { + + this.classSuffix = '-h'; + this.onScroll = function() { + if (!this.skipEvent) { + this.scrollLeft = this.element.scrollLeft; + this._emit("scroll", {data: this.scrollLeft}); + } + this.skipEvent = false; + }; + this.getHeight = function() { + return this.isVisible ? this.height : 0; + }; + this.setWidth = function(width) { + this.element.style.width = width + "px"; + }; + this.setInnerWidth = function(width) { + this.inner.style.width = width + "px"; + }; + this.setScrollWidth = function(width) { + this.inner.style.width = width + "px"; + }; + this.setScrollLeft = function(scrollLeft) { + if (this.scrollLeft != scrollLeft) { + this.skipEvent = true; + this.scrollLeft = this.element.scrollLeft = scrollLeft; + } + }; + +}).call(HScrollBar.prototype); + + +exports.ScrollBar = VScrollBar; // backward compatibility +exports.ScrollBarV = VScrollBar; // backward compatibility +exports.ScrollBarH = HScrollBar; // backward compatibility + +exports.VScrollBar = VScrollBar; +exports.HScrollBar = HScrollBar; +}); + +ace.define("ace/renderloop",["require","exports","module","ace/lib/event"], function(require, exports, module) { +"use strict"; + +var event = require("./lib/event"); + + +var RenderLoop = function(onRender, win) { + this.onRender = onRender; + this.pending = false; + this.changes = 0; + this.$recursionLimit = 2; + this.window = win || window; + var _self = this; + this._flush = function(ts) { + _self.pending = false; + var changes = _self.changes; + + if (changes) { + event.blockIdle(100); + _self.changes = 0; + _self.onRender(changes); + } + + if (_self.changes) { + if (_self.$recursionLimit-- < 0) return; + _self.schedule(); + } else { + _self.$recursionLimit = 2; + } + }; +}; + +(function() { + + this.schedule = function(change) { + this.changes = this.changes | change; + if (this.changes && !this.pending) { + event.nextFrame(this._flush); + this.pending = true; + } + }; + + this.clear = function(change) { + var changes = this.changes; + this.changes = 0; + return changes; + }; + +}).call(RenderLoop.prototype); + +exports.RenderLoop = RenderLoop; +}); + +ace.define("ace/layer/font_metrics",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/event","ace/lib/useragent","ace/lib/event_emitter"], function(require, exports, module) { + +var oop = require("../lib/oop"); +var dom = require("../lib/dom"); +var lang = require("../lib/lang"); +var event = require("../lib/event"); +var useragent = require("../lib/useragent"); +var EventEmitter = require("../lib/event_emitter").EventEmitter; + +var CHAR_COUNT = 256; +var USE_OBSERVER = typeof ResizeObserver == "function"; +var L = 200; + +var FontMetrics = exports.FontMetrics = function(parentEl) { + this.el = dom.createElement("div"); + this.$setMeasureNodeStyles(this.el.style, true); + + this.$main = dom.createElement("div"); + this.$setMeasureNodeStyles(this.$main.style); + + this.$measureNode = dom.createElement("div"); + this.$setMeasureNodeStyles(this.$measureNode.style); + + + this.el.appendChild(this.$main); + this.el.appendChild(this.$measureNode); + parentEl.appendChild(this.el); + + this.$measureNode.textContent = lang.stringRepeat("X", CHAR_COUNT); + + this.$characterSize = {width: 0, height: 0}; + + + if (USE_OBSERVER) + this.$addObserver(); + else + this.checkForSizeChanges(); +}; + +(function() { + + oop.implement(this, EventEmitter); + + this.$characterSize = {width: 0, height: 0}; + + this.$setMeasureNodeStyles = function(style, isRoot) { + style.width = style.height = "auto"; + style.left = style.top = "0px"; + style.visibility = "hidden"; + style.position = "absolute"; + style.whiteSpace = "pre"; + + if (useragent.isIE < 8) { + style["font-family"] = "inherit"; + } else { + style.font = "inherit"; + } + style.overflow = isRoot ? "hidden" : "visible"; + }; + + this.checkForSizeChanges = function(size) { + if (size === undefined) + size = this.$measureSizes(); + if (size && (this.$characterSize.width !== size.width || this.$characterSize.height !== size.height)) { + this.$measureNode.style.fontWeight = "bold"; + var boldSize = this.$measureSizes(); + this.$measureNode.style.fontWeight = ""; + this.$characterSize = size; + this.charSizes = Object.create(null); + this.allowBoldFonts = boldSize && boldSize.width === size.width && boldSize.height === size.height; + this._emit("changeCharacterSize", {data: size}); + } + }; + + this.$addObserver = function() { + var self = this; + this.$observer = new window.ResizeObserver(function(e) { + self.checkForSizeChanges(); + }); + this.$observer.observe(this.$measureNode); + }; + + this.$pollSizeChanges = function() { + if (this.$pollSizeChangesTimer || this.$observer) + return this.$pollSizeChangesTimer; + var self = this; + + return this.$pollSizeChangesTimer = event.onIdle(function cb() { + self.checkForSizeChanges(); + event.onIdle(cb, 500); + }, 500); + }; + + this.setPolling = function(val) { + if (val) { + this.$pollSizeChanges(); + } else if (this.$pollSizeChangesTimer) { + clearInterval(this.$pollSizeChangesTimer); + this.$pollSizeChangesTimer = 0; + } + }; + + this.$measureSizes = function(node) { + var size = { + height: (node || this.$measureNode).clientHeight, + width: (node || this.$measureNode).clientWidth / CHAR_COUNT + }; + if (size.width === 0 || size.height === 0) + return null; + return size; + }; + + this.$measureCharWidth = function(ch) { + this.$main.textContent = lang.stringRepeat(ch, CHAR_COUNT); + var rect = this.$main.getBoundingClientRect(); + return rect.width / CHAR_COUNT; + }; + + this.getCharacterWidth = function(ch) { + var w = this.charSizes[ch]; + if (w === undefined) { + w = this.charSizes[ch] = this.$measureCharWidth(ch) / this.$characterSize.width; + } + return w; + }; + + this.destroy = function() { + clearInterval(this.$pollSizeChangesTimer); + if (this.$observer) + this.$observer.disconnect(); + if (this.el && this.el.parentNode) + this.el.parentNode.removeChild(this.el); + }; + + + this.$getZoom = function getZoom(element) { + if (!element || !element.parentElement) return 1; + return (window.getComputedStyle(element).zoom || 1) * getZoom(element.parentElement); + }; + this.$initTransformMeasureNodes = function() { + var t = function(t, l) { + return ["div", { + style: "position: absolute;top:" + t + "px;left:" + l + "px;" + }]; + }; + this.els = dom.buildDom([t(0, 0), t(L, 0), t(0, L), t(L, L)], this.el); + }; + this.transformCoordinates = function(clientPos, elPos) { + if (clientPos) { + var zoom = this.$getZoom(this.el); + clientPos = mul(1 / zoom, clientPos); + } + function solve(l1, l2, r) { + var det = l1[1] * l2[0] - l1[0] * l2[1]; + return [ + (-l2[1] * r[0] + l2[0] * r[1]) / det, + (+l1[1] * r[0] - l1[0] * r[1]) / det + ]; + } + function sub(a, b) { return [a[0] - b[0], a[1] - b[1]]; } + function add(a, b) { return [a[0] + b[0], a[1] + b[1]]; } + function mul(a, b) { return [a * b[0], a * b[1]]; } + + if (!this.els) + this.$initTransformMeasureNodes(); + + function p(el) { + var r = el.getBoundingClientRect(); + return [r.left, r.top]; + } + + var a = p(this.els[0]); + var b = p(this.els[1]); + var c = p(this.els[2]); + var d = p(this.els[3]); + + var h = solve(sub(d, b), sub(d, c), sub(add(b, c), add(d, a))); + + var m1 = mul(1 + h[0], sub(b, a)); + var m2 = mul(1 + h[1], sub(c, a)); + + if (elPos) { + var x = elPos; + var k = h[0] * x[0] / L + h[1] * x[1] / L + 1; + var ut = add(mul(x[0], m1), mul(x[1], m2)); + return add(mul(1 / k / L, ut), a); + } + var u = sub(clientPos, a); + var f = solve(sub(m1, mul(h[0], u)), sub(m2, mul(h[1], u)), u); + return mul(L, f); + }; + +}).call(FontMetrics.prototype); + +}); + +ace.define("ace/virtual_renderer",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/config","ace/layer/gutter","ace/layer/marker","ace/layer/text","ace/layer/cursor","ace/scrollbar","ace/scrollbar","ace/renderloop","ace/layer/font_metrics","ace/lib/event_emitter","ace/lib/useragent"], function(require, exports, module) { +"use strict"; + +var oop = require("./lib/oop"); +var dom = require("./lib/dom"); +var config = require("./config"); +var GutterLayer = require("./layer/gutter").Gutter; +var MarkerLayer = require("./layer/marker").Marker; +var TextLayer = require("./layer/text").Text; +var CursorLayer = require("./layer/cursor").Cursor; +var HScrollBar = require("./scrollbar").HScrollBar; +var VScrollBar = require("./scrollbar").VScrollBar; +var RenderLoop = require("./renderloop").RenderLoop; +var FontMetrics = require("./layer/font_metrics").FontMetrics; +var EventEmitter = require("./lib/event_emitter").EventEmitter; +var editorCss = "\ +.ace_br1 {border-top-left-radius : 3px;}\ +.ace_br2 {border-top-right-radius : 3px;}\ +.ace_br3 {border-top-left-radius : 3px; border-top-right-radius: 3px;}\ +.ace_br4 {border-bottom-right-radius: 3px;}\ +.ace_br5 {border-top-left-radius : 3px; border-bottom-right-radius: 3px;}\ +.ace_br6 {border-top-right-radius : 3px; border-bottom-right-radius: 3px;}\ +.ace_br7 {border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px;}\ +.ace_br8 {border-bottom-left-radius : 3px;}\ +.ace_br9 {border-top-left-radius : 3px; border-bottom-left-radius: 3px;}\ +.ace_br10{border-top-right-radius : 3px; border-bottom-left-radius: 3px;}\ +.ace_br11{border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-left-radius: 3px;}\ +.ace_br12{border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}\ +.ace_br13{border-top-left-radius : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}\ +.ace_br14{border-top-right-radius : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}\ +.ace_br15{border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}\ +.ace_editor {\ +position: relative;\ +overflow: hidden;\ +padding: 0;\ +font: 12px/normal 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace;\ +direction: ltr;\ +text-align: left;\ +-webkit-tap-highlight-color: rgba(0, 0, 0, 0);\ +}\ +.ace_scroller {\ +position: absolute;\ +overflow: hidden;\ +top: 0;\ +bottom: 0;\ +background-color: inherit;\ +-ms-user-select: none;\ +-moz-user-select: none;\ +-webkit-user-select: none;\ +user-select: none;\ +cursor: text;\ +}\ +.ace_content {\ +position: absolute;\ +box-sizing: border-box;\ +min-width: 100%;\ +contain: style size layout;\ +font-variant-ligatures: no-common-ligatures;\ +}\ +.ace_dragging .ace_scroller:before{\ +position: absolute;\ +top: 0;\ +left: 0;\ +right: 0;\ +bottom: 0;\ +content: '';\ +background: rgba(250, 250, 250, 0.01);\ +z-index: 1000;\ +}\ +.ace_dragging.ace_dark .ace_scroller:before{\ +background: rgba(0, 0, 0, 0.01);\ +}\ +.ace_selecting, .ace_selecting * {\ +cursor: text !important;\ +}\ +.ace_gutter {\ +position: absolute;\ +overflow : hidden;\ +width: auto;\ +top: 0;\ +bottom: 0;\ +left: 0;\ +cursor: default;\ +z-index: 4;\ +-ms-user-select: none;\ +-moz-user-select: none;\ +-webkit-user-select: none;\ +user-select: none;\ +contain: style size layout;\ +}\ +.ace_gutter-active-line {\ +position: absolute;\ +left: 0;\ +right: 0;\ +}\ +.ace_scroller.ace_scroll-left {\ +box-shadow: 17px 0 16px -16px rgba(0, 0, 0, 0.4) inset;\ +}\ +.ace_gutter-cell {\ +position: absolute;\ +top: 0;\ +left: 0;\ +right: 0;\ +padding-left: 19px;\ +padding-right: 6px;\ +background-repeat: no-repeat;\ +}\ +.ace_gutter-cell.ace_error {\ +background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABOFBMVEX/////////QRswFAb/Ui4wFAYwFAYwFAaWGAfDRymzOSH/PxswFAb/SiUwFAYwFAbUPRvjQiDllog5HhHdRybsTi3/Tyv9Tir+Syj/UC3////XurebMBIwFAb/RSHbPx/gUzfdwL3kzMivKBAwFAbbvbnhPx66NhowFAYwFAaZJg8wFAaxKBDZurf/RB6mMxb/SCMwFAYwFAbxQB3+RB4wFAb/Qhy4Oh+4QifbNRcwFAYwFAYwFAb/QRzdNhgwFAYwFAbav7v/Uy7oaE68MBK5LxLewr/r2NXewLswFAaxJw4wFAbkPRy2PyYwFAaxKhLm1tMwFAazPiQwFAaUGAb/QBrfOx3bvrv/VC/maE4wFAbRPBq6MRO8Qynew8Dp2tjfwb0wFAbx6eju5+by6uns4uH9/f36+vr/GkHjAAAAYnRSTlMAGt+64rnWu/bo8eAA4InH3+DwoN7j4eLi4xP99Nfg4+b+/u9B/eDs1MD1mO7+4PHg2MXa347g7vDizMLN4eG+Pv7i5evs/v79yu7S3/DV7/498Yv24eH+4ufQ3Ozu/v7+y13sRqwAAADLSURBVHjaZc/XDsFgGIBhtDrshlitmk2IrbHFqL2pvXf/+78DPokj7+Fz9qpU/9UXJIlhmPaTaQ6QPaz0mm+5gwkgovcV6GZzd5JtCQwgsxoHOvJO15kleRLAnMgHFIESUEPmawB9ngmelTtipwwfASilxOLyiV5UVUyVAfbG0cCPHig+GBkzAENHS0AstVF6bacZIOzgLmxsHbt2OecNgJC83JERmePUYq8ARGkJx6XtFsdddBQgZE2nPR6CICZhawjA4Fb/chv+399kfR+MMMDGOQAAAABJRU5ErkJggg==\");\ +background-repeat: no-repeat;\ +background-position: 2px center;\ +}\ +.ace_gutter-cell.ace_warning {\ +background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAmVBMVEX///8AAAD///8AAAAAAABPSzb/5sAAAAB/blH/73z/ulkAAAAAAAD85pkAAAAAAAACAgP/vGz/rkDerGbGrV7/pkQICAf////e0IsAAAD/oED/qTvhrnUAAAD/yHD/njcAAADuv2r/nz//oTj/p064oGf/zHAAAAA9Nir/tFIAAAD/tlTiuWf/tkIAAACynXEAAAAAAAAtIRW7zBpBAAAAM3RSTlMAABR1m7RXO8Ln31Z36zT+neXe5OzooRDfn+TZ4p3h2hTf4t3k3ucyrN1K5+Xaks52Sfs9CXgrAAAAjklEQVR42o3PbQ+CIBQFYEwboPhSYgoYunIqqLn6/z8uYdH8Vmdnu9vz4WwXgN/xTPRD2+sgOcZjsge/whXZgUaYYvT8QnuJaUrjrHUQreGczuEafQCO/SJTufTbroWsPgsllVhq3wJEk2jUSzX3CUEDJC84707djRc5MTAQxoLgupWRwW6UB5fS++NV8AbOZgnsC7BpEAAAAABJRU5ErkJggg==\");\ +background-position: 2px center;\ +}\ +.ace_gutter-cell.ace_info {\ +background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAJ0Uk5TAAB2k804AAAAPklEQVQY02NgIB68QuO3tiLznjAwpKTgNyDbMegwisCHZUETUZV0ZqOquBpXj2rtnpSJT1AEnnRmL2OgGgAAIKkRQap2htgAAAAASUVORK5CYII=\");\ +background-position: 2px center;\ +}\ +.ace_dark .ace_gutter-cell.ace_info {\ +background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAJFBMVEUAAAChoaGAgIAqKiq+vr6tra1ZWVmUlJSbm5s8PDxubm56enrdgzg3AAAAAXRSTlMAQObYZgAAAClJREFUeNpjYMAPdsMYHegyJZFQBlsUlMFVCWUYKkAZMxZAGdxlDMQBAG+TBP4B6RyJAAAAAElFTkSuQmCC\");\ +}\ +.ace_scrollbar {\ +contain: strict;\ +position: absolute;\ +right: 0;\ +bottom: 0;\ +z-index: 6;\ +}\ +.ace_scrollbar-inner {\ +position: absolute;\ +cursor: text;\ +left: 0;\ +top: 0;\ +}\ +.ace_scrollbar-v{\ +overflow-x: hidden;\ +overflow-y: scroll;\ +top: 0;\ +}\ +.ace_scrollbar-h {\ +overflow-x: scroll;\ +overflow-y: hidden;\ +left: 0;\ +}\ +.ace_print-margin {\ +position: absolute;\ +height: 100%;\ +}\ +.ace_text-input {\ +position: absolute;\ +z-index: 0;\ +width: 0.5em;\ +height: 1em;\ +opacity: 0;\ +background: transparent;\ +-moz-appearance: none;\ +appearance: none;\ +border: none;\ +resize: none;\ +outline: none;\ +overflow: hidden;\ +font: inherit;\ +padding: 0 1px;\ +margin: 0 -1px;\ +contain: strict;\ +-ms-user-select: text;\ +-moz-user-select: text;\ +-webkit-user-select: text;\ +user-select: text;\ +white-space: pre!important;\ +}\ +.ace_text-input.ace_composition {\ +background: transparent;\ +color: inherit;\ +z-index: 1000;\ +opacity: 1;\ +}\ +.ace_composition_placeholder { color: transparent }\ +.ace_composition_marker { \ +border-bottom: 1px solid;\ +position: absolute;\ +border-radius: 0;\ +margin-top: 1px;\ +}\ +[ace_nocontext=true] {\ +transform: none!important;\ +filter: none!important;\ +clip-path: none!important;\ +mask : none!important;\ +contain: none!important;\ +perspective: none!important;\ +mix-blend-mode: initial!important;\ +z-index: auto;\ +}\ +.ace_layer {\ +z-index: 1;\ +position: absolute;\ +overflow: hidden;\ +word-wrap: normal;\ +white-space: pre;\ +height: 100%;\ +width: 100%;\ +box-sizing: border-box;\ +pointer-events: none;\ +}\ +.ace_gutter-layer {\ +position: relative;\ +width: auto;\ +text-align: right;\ +pointer-events: auto;\ +height: 1000000px;\ +contain: style size layout;\ +}\ +.ace_text-layer {\ +font: inherit !important;\ +position: absolute;\ +height: 1000000px;\ +width: 1000000px;\ +contain: style size layout;\ +}\ +.ace_text-layer > .ace_line, .ace_text-layer > .ace_line_group {\ +contain: style size layout;\ +position: absolute;\ +top: 0;\ +left: 0;\ +right: 0;\ +}\ +.ace_hidpi .ace_text-layer,\ +.ace_hidpi .ace_gutter-layer,\ +.ace_hidpi .ace_content,\ +.ace_hidpi .ace_gutter {\ +contain: strict;\ +will-change: transform;\ +}\ +.ace_hidpi .ace_text-layer > .ace_line, \ +.ace_hidpi .ace_text-layer > .ace_line_group {\ +contain: strict;\ +}\ +.ace_cjk {\ +display: inline-block;\ +text-align: center;\ +}\ +.ace_cursor-layer {\ +z-index: 4;\ +}\ +.ace_cursor {\ +z-index: 4;\ +position: absolute;\ +box-sizing: border-box;\ +border-left: 2px solid;\ +transform: translatez(0);\ +}\ +.ace_multiselect .ace_cursor {\ +border-left-width: 1px;\ +}\ +.ace_slim-cursors .ace_cursor {\ +border-left-width: 1px;\ +}\ +.ace_overwrite-cursors .ace_cursor {\ +border-left-width: 0;\ +border-bottom: 1px solid;\ +}\ +.ace_hidden-cursors .ace_cursor {\ +opacity: 0.2;\ +}\ +.ace_hasPlaceholder .ace_hidden-cursors .ace_cursor {\ +opacity: 0;\ +}\ +.ace_smooth-blinking .ace_cursor {\ +transition: opacity 0.18s;\ +}\ +.ace_animate-blinking .ace_cursor {\ +animation-duration: 1000ms;\ +animation-timing-function: step-end;\ +animation-name: blink-ace-animate;\ +animation-iteration-count: infinite;\ +}\ +.ace_animate-blinking.ace_smooth-blinking .ace_cursor {\ +animation-duration: 1000ms;\ +animation-timing-function: ease-in-out;\ +animation-name: blink-ace-animate-smooth;\ +}\ +@keyframes blink-ace-animate {\ +from, to { opacity: 1; }\ +60% { opacity: 0; }\ +}\ +@keyframes blink-ace-animate-smooth {\ +from, to { opacity: 1; }\ +45% { opacity: 1; }\ +60% { opacity: 0; }\ +85% { opacity: 0; }\ +}\ +.ace_marker-layer .ace_step, .ace_marker-layer .ace_stack {\ +position: absolute;\ +z-index: 3;\ +}\ +.ace_marker-layer .ace_selection {\ +position: absolute;\ +z-index: 5;\ +}\ +.ace_marker-layer .ace_bracket {\ +position: absolute;\ +z-index: 6;\ +}\ +.ace_marker-layer .ace_error_bracket {\ +position: absolute;\ +border-bottom: 1px solid #DE5555;\ +border-radius: 0;\ +}\ +.ace_marker-layer .ace_active-line {\ +position: absolute;\ +z-index: 2;\ +}\ +.ace_marker-layer .ace_selected-word {\ +position: absolute;\ +z-index: 4;\ +box-sizing: border-box;\ +}\ +.ace_line .ace_fold {\ +box-sizing: border-box;\ +display: inline-block;\ +height: 11px;\ +margin-top: -2px;\ +vertical-align: middle;\ +background-image:\ +url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII=\"),\ +url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACJJREFUeNpi+P//fxgTAwPDBxDxD078RSX+YeEyDFMCIMAAI3INmXiwf2YAAAAASUVORK5CYII=\");\ +background-repeat: no-repeat, repeat-x;\ +background-position: center center, top left;\ +color: transparent;\ +border: 1px solid black;\ +border-radius: 2px;\ +cursor: pointer;\ +pointer-events: auto;\ +}\ +.ace_dark .ace_fold {\ +}\ +.ace_fold:hover{\ +background-image:\ +url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII=\"),\ +url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACBJREFUeNpi+P//fz4TAwPDZxDxD5X4i5fLMEwJgAADAEPVDbjNw87ZAAAAAElFTkSuQmCC\");\ +}\ +.ace_tooltip {\ +background-color: #FFF;\ +background-image: linear-gradient(to bottom, transparent, rgba(0, 0, 0, 0.1));\ +border: 1px solid gray;\ +border-radius: 1px;\ +box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);\ +color: black;\ +max-width: 100%;\ +padding: 3px 4px;\ +position: fixed;\ +z-index: 999999;\ +box-sizing: border-box;\ +cursor: default;\ +white-space: pre;\ +word-wrap: break-word;\ +line-height: normal;\ +font-style: normal;\ +font-weight: normal;\ +letter-spacing: normal;\ +pointer-events: none;\ +}\ +.ace_folding-enabled > .ace_gutter-cell {\ +padding-right: 13px;\ +}\ +.ace_fold-widget {\ +box-sizing: border-box;\ +margin: 0 -12px 0 1px;\ +display: none;\ +width: 11px;\ +vertical-align: top;\ +background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42mWKsQ0AMAzC8ixLlrzQjzmBiEjp0A6WwBCSPgKAXoLkqSot7nN3yMwR7pZ32NzpKkVoDBUxKAAAAABJRU5ErkJggg==\");\ +background-repeat: no-repeat;\ +background-position: center;\ +border-radius: 3px;\ +border: 1px solid transparent;\ +cursor: pointer;\ +}\ +.ace_folding-enabled .ace_fold-widget {\ +display: inline-block; \ +}\ +.ace_fold-widget.ace_end {\ +background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42m3HwQkAMAhD0YzsRchFKI7sAikeWkrxwScEB0nh5e7KTPWimZki4tYfVbX+MNl4pyZXejUO1QAAAABJRU5ErkJggg==\");\ +}\ +.ace_fold-widget.ace_closed {\ +background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAGCAYAAAAG5SQMAAAAOUlEQVR42jXKwQkAMAgDwKwqKD4EwQ26sSOkVWjgIIHAzPiCgaqiqnJHZnKICBERHN194O5b9vbLuAVRL+l0YWnZAAAAAElFTkSuQmCCXA==\");\ +}\ +.ace_fold-widget:hover {\ +border: 1px solid rgba(0, 0, 0, 0.3);\ +background-color: rgba(255, 255, 255, 0.2);\ +box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);\ +}\ +.ace_fold-widget:active {\ +border: 1px solid rgba(0, 0, 0, 0.4);\ +background-color: rgba(0, 0, 0, 0.05);\ +box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);\ +}\ +.ace_dark .ace_fold-widget {\ +background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHklEQVQIW2P4//8/AzoGEQ7oGCaLLAhWiSwB146BAQCSTPYocqT0AAAAAElFTkSuQmCC\");\ +}\ +.ace_dark .ace_fold-widget.ace_end {\ +background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAH0lEQVQIW2P4//8/AxQ7wNjIAjDMgC4AxjCVKBirIAAF0kz2rlhxpAAAAABJRU5ErkJggg==\");\ +}\ +.ace_dark .ace_fold-widget.ace_closed {\ +background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAHElEQVQIW2P4//+/AxAzgDADlOOAznHAKgPWAwARji8UIDTfQQAAAABJRU5ErkJggg==\");\ +}\ +.ace_dark .ace_fold-widget:hover {\ +box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);\ +background-color: rgba(255, 255, 255, 0.1);\ +}\ +.ace_dark .ace_fold-widget:active {\ +box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);\ +}\ +.ace_inline_button {\ +border: 1px solid lightgray;\ +display: inline-block;\ +margin: -1px 8px;\ +padding: 0 5px;\ +pointer-events: auto;\ +cursor: pointer;\ +}\ +.ace_inline_button:hover {\ +border-color: gray;\ +background: rgba(200,200,200,0.2);\ +display: inline-block;\ +pointer-events: auto;\ +}\ +.ace_fold-widget.ace_invalid {\ +background-color: #FFB4B4;\ +border-color: #DE5555;\ +}\ +.ace_fade-fold-widgets .ace_fold-widget {\ +transition: opacity 0.4s ease 0.05s;\ +opacity: 0;\ +}\ +.ace_fade-fold-widgets:hover .ace_fold-widget {\ +transition: opacity 0.05s ease 0.05s;\ +opacity:1;\ +}\ +.ace_underline {\ +text-decoration: underline;\ +}\ +.ace_bold {\ +font-weight: bold;\ +}\ +.ace_nobold .ace_bold {\ +font-weight: normal;\ +}\ +.ace_italic {\ +font-style: italic;\ +}\ +.ace_error-marker {\ +background-color: rgba(255, 0, 0,0.2);\ +position: absolute;\ +z-index: 9;\ +}\ +.ace_highlight-marker {\ +background-color: rgba(255, 255, 0,0.2);\ +position: absolute;\ +z-index: 8;\ +}\ +.ace_mobile-menu {\ +position: absolute;\ +line-height: 1.5;\ +border-radius: 4px;\ +-ms-user-select: none;\ +-moz-user-select: none;\ +-webkit-user-select: none;\ +user-select: none;\ +background: white;\ +box-shadow: 1px 3px 2px grey;\ +border: 1px solid #dcdcdc;\ +color: black;\ +}\ +.ace_dark > .ace_mobile-menu {\ +background: #333;\ +color: #ccc;\ +box-shadow: 1px 3px 2px grey;\ +border: 1px solid #444;\ +}\ +.ace_mobile-button {\ +padding: 2px;\ +cursor: pointer;\ +overflow: hidden;\ +}\ +.ace_mobile-button:hover {\ +background-color: #eee;\ +opacity:1;\ +}\ +.ace_mobile-button:active {\ +background-color: #ddd;\ +}\ +.ace_placeholder {\ +font-family: arial;\ +transform: scale(0.9);\ +transform-origin: left;\ +white-space: pre;\ +opacity: 0.7;\ +margin: 0 10px;\ +}"; + +var useragent = require("./lib/useragent"); +var HIDE_TEXTAREA = useragent.isIE; + +dom.importCssString(editorCss, "ace_editor.css", false); + +var VirtualRenderer = function(container, theme) { + var _self = this; + + this.container = container || dom.createElement("div"); + + dom.addCssClass(this.container, "ace_editor"); + if (dom.HI_DPI) dom.addCssClass(this.container, "ace_hidpi"); + + this.setTheme(theme); + if (config.get("useStrictCSP") == null) + config.set("useStrictCSP", false); + + this.$gutter = dom.createElement("div"); + this.$gutter.className = "ace_gutter"; + this.container.appendChild(this.$gutter); + this.$gutter.setAttribute("aria-hidden", true); + + this.scroller = dom.createElement("div"); + this.scroller.className = "ace_scroller"; + + this.container.appendChild(this.scroller); + + this.content = dom.createElement("div"); + this.content.className = "ace_content"; + this.scroller.appendChild(this.content); + + this.$gutterLayer = new GutterLayer(this.$gutter); + this.$gutterLayer.on("changeGutterWidth", this.onGutterResize.bind(this)); + + this.$markerBack = new MarkerLayer(this.content); + + var textLayer = this.$textLayer = new TextLayer(this.content); + this.canvas = textLayer.element; + + this.$markerFront = new MarkerLayer(this.content); + + this.$cursorLayer = new CursorLayer(this.content); + this.$horizScroll = false; + this.$vScroll = false; + + this.scrollBar = + this.scrollBarV = new VScrollBar(this.container, this); + this.scrollBarH = new HScrollBar(this.container, this); + this.scrollBarV.on("scroll", function(e) { + if (!_self.$scrollAnimation) + _self.session.setScrollTop(e.data - _self.scrollMargin.top); + }); + this.scrollBarH.on("scroll", function(e) { + if (!_self.$scrollAnimation) + _self.session.setScrollLeft(e.data - _self.scrollMargin.left); + }); + + this.scrollTop = 0; + this.scrollLeft = 0; + + this.cursorPos = { + row : 0, + column : 0 + }; + + this.$fontMetrics = new FontMetrics(this.container); + this.$textLayer.$setFontMetrics(this.$fontMetrics); + this.$textLayer.on("changeCharacterSize", function(e) { + _self.updateCharacterSize(); + _self.onResize(true, _self.gutterWidth, _self.$size.width, _self.$size.height); + _self._signal("changeCharacterSize", e); + }); + + this.$size = { + width: 0, + height: 0, + scrollerHeight: 0, + scrollerWidth: 0, + $dirty: true + }; + + this.layerConfig = { + width : 1, + padding : 0, + firstRow : 0, + firstRowScreen: 0, + lastRow : 0, + lineHeight : 0, + characterWidth : 0, + minHeight : 1, + maxHeight : 1, + offset : 0, + height : 1, + gutterOffset: 1 + }; + + this.scrollMargin = { + left: 0, + right: 0, + top: 0, + bottom: 0, + v: 0, + h: 0 + }; + + this.margin = { + left: 0, + right: 0, + top: 0, + bottom: 0, + v: 0, + h: 0 + }; + + this.$keepTextAreaAtCursor = !useragent.isIOS; + + this.$loop = new RenderLoop( + this.$renderChanges.bind(this), + this.container.ownerDocument.defaultView + ); + this.$loop.schedule(this.CHANGE_FULL); + + this.updateCharacterSize(); + this.setPadding(4); + config.resetOptions(this); + config._signal("renderer", this); +}; + +(function() { + + this.CHANGE_CURSOR = 1; + this.CHANGE_MARKER = 2; + this.CHANGE_GUTTER = 4; + this.CHANGE_SCROLL = 8; + this.CHANGE_LINES = 16; + this.CHANGE_TEXT = 32; + this.CHANGE_SIZE = 64; + this.CHANGE_MARKER_BACK = 128; + this.CHANGE_MARKER_FRONT = 256; + this.CHANGE_FULL = 512; + this.CHANGE_H_SCROLL = 1024; + + oop.implement(this, EventEmitter); + + this.updateCharacterSize = function() { + if (this.$textLayer.allowBoldFonts != this.$allowBoldFonts) { + this.$allowBoldFonts = this.$textLayer.allowBoldFonts; + this.setStyle("ace_nobold", !this.$allowBoldFonts); + } + + this.layerConfig.characterWidth = + this.characterWidth = this.$textLayer.getCharacterWidth(); + this.layerConfig.lineHeight = + this.lineHeight = this.$textLayer.getLineHeight(); + this.$updatePrintMargin(); + dom.setStyle(this.scroller.style, "line-height", this.lineHeight + "px"); + }; + this.setSession = function(session) { + if (this.session) + this.session.doc.off("changeNewLineMode", this.onChangeNewLineMode); + + this.session = session; + if (session && this.scrollMargin.top && session.getScrollTop() <= 0) + session.setScrollTop(-this.scrollMargin.top); + + this.$cursorLayer.setSession(session); + this.$markerBack.setSession(session); + this.$markerFront.setSession(session); + this.$gutterLayer.setSession(session); + this.$textLayer.setSession(session); + if (!session) + return; + + this.$loop.schedule(this.CHANGE_FULL); + this.session.$setFontMetrics(this.$fontMetrics); + this.scrollBarH.scrollLeft = this.scrollBarV.scrollTop = null; + + this.onChangeNewLineMode = this.onChangeNewLineMode.bind(this); + this.onChangeNewLineMode(); + this.session.doc.on("changeNewLineMode", this.onChangeNewLineMode); + }; + this.updateLines = function(firstRow, lastRow, force) { + if (lastRow === undefined) + lastRow = Infinity; + + if (!this.$changedLines) { + this.$changedLines = { + firstRow: firstRow, + lastRow: lastRow + }; + } + else { + if (this.$changedLines.firstRow > firstRow) + this.$changedLines.firstRow = firstRow; + + if (this.$changedLines.lastRow < lastRow) + this.$changedLines.lastRow = lastRow; + } + if (this.$changedLines.lastRow < this.layerConfig.firstRow) { + if (force) + this.$changedLines.lastRow = this.layerConfig.lastRow; + else + return; + } + if (this.$changedLines.firstRow > this.layerConfig.lastRow) + return; + this.$loop.schedule(this.CHANGE_LINES); + }; + + this.onChangeNewLineMode = function() { + this.$loop.schedule(this.CHANGE_TEXT); + this.$textLayer.$updateEolChar(); + this.session.$bidiHandler.setEolChar(this.$textLayer.EOL_CHAR); + }; + + this.onChangeTabSize = function() { + this.$loop.schedule(this.CHANGE_TEXT | this.CHANGE_MARKER); + this.$textLayer.onChangeTabSize(); + }; + this.updateText = function() { + this.$loop.schedule(this.CHANGE_TEXT); + }; + this.updateFull = function(force) { + if (force) + this.$renderChanges(this.CHANGE_FULL, true); + else + this.$loop.schedule(this.CHANGE_FULL); + }; + this.updateFontSize = function() { + this.$textLayer.checkForSizeChanges(); + }; + + this.$changes = 0; + this.$updateSizeAsync = function() { + if (this.$loop.pending) + this.$size.$dirty = true; + else + this.onResize(); + }; + this.onResize = function(force, gutterWidth, width, height) { + if (this.resizing > 2) + return; + else if (this.resizing > 0) + this.resizing++; + else + this.resizing = force ? 1 : 0; + var el = this.container; + if (!height) + height = el.clientHeight || el.scrollHeight; + if (!width) + width = el.clientWidth || el.scrollWidth; + var changes = this.$updateCachedSize(force, gutterWidth, width, height); + + + if (!this.$size.scrollerHeight || (!width && !height)) + return this.resizing = 0; + + if (force) + this.$gutterLayer.$padding = null; + + if (force) + this.$renderChanges(changes | this.$changes, true); + else + this.$loop.schedule(changes | this.$changes); + + if (this.resizing) + this.resizing = 0; + this.scrollBarH.scrollLeft = this.scrollBarV.scrollTop = null; + }; + + this.$updateCachedSize = function(force, gutterWidth, width, height) { + height -= (this.$extraHeight || 0); + var changes = 0; + var size = this.$size; + var oldSize = { + width: size.width, + height: size.height, + scrollerHeight: size.scrollerHeight, + scrollerWidth: size.scrollerWidth + }; + if (height && (force || size.height != height)) { + size.height = height; + changes |= this.CHANGE_SIZE; + + size.scrollerHeight = size.height; + if (this.$horizScroll) + size.scrollerHeight -= this.scrollBarH.getHeight(); + this.scrollBarV.element.style.bottom = this.scrollBarH.getHeight() + "px"; + + changes = changes | this.CHANGE_SCROLL; + } + + if (width && (force || size.width != width)) { + changes |= this.CHANGE_SIZE; + size.width = width; + + if (gutterWidth == null) + gutterWidth = this.$showGutter ? this.$gutter.offsetWidth : 0; + + this.gutterWidth = gutterWidth; + + dom.setStyle(this.scrollBarH.element.style, "left", gutterWidth + "px"); + dom.setStyle(this.scroller.style, "left", gutterWidth + this.margin.left + "px"); + size.scrollerWidth = Math.max(0, width - gutterWidth - this.scrollBarV.getWidth() - this.margin.h); + dom.setStyle(this.$gutter.style, "left", this.margin.left + "px"); + + var right = this.scrollBarV.getWidth() + "px"; + dom.setStyle(this.scrollBarH.element.style, "right", right); + dom.setStyle(this.scroller.style, "right", right); + dom.setStyle(this.scroller.style, "bottom", this.scrollBarH.getHeight()); + + if (this.session && this.session.getUseWrapMode() && this.adjustWrapLimit() || force) { + changes |= this.CHANGE_FULL; + } + } + + size.$dirty = !width || !height; + + if (changes) + this._signal("resize", oldSize); + + return changes; + }; + + this.onGutterResize = function(width) { + var gutterWidth = this.$showGutter ? width : 0; + if (gutterWidth != this.gutterWidth) + this.$changes |= this.$updateCachedSize(true, gutterWidth, this.$size.width, this.$size.height); + + if (this.session.getUseWrapMode() && this.adjustWrapLimit()) { + this.$loop.schedule(this.CHANGE_FULL); + } else if (this.$size.$dirty) { + this.$loop.schedule(this.CHANGE_FULL); + } else { + this.$computeLayerConfig(); + } + }; + this.adjustWrapLimit = function() { + var availableWidth = this.$size.scrollerWidth - this.$padding * 2; + var limit = Math.floor(availableWidth / this.characterWidth); + return this.session.adjustWrapLimit(limit, this.$showPrintMargin && this.$printMarginColumn); + }; + this.setAnimatedScroll = function(shouldAnimate){ + this.setOption("animatedScroll", shouldAnimate); + }; + this.getAnimatedScroll = function() { + return this.$animatedScroll; + }; + this.setShowInvisibles = function(showInvisibles) { + this.setOption("showInvisibles", showInvisibles); + this.session.$bidiHandler.setShowInvisibles(showInvisibles); + }; + this.getShowInvisibles = function() { + return this.getOption("showInvisibles"); + }; + this.getDisplayIndentGuides = function() { + return this.getOption("displayIndentGuides"); + }; + + this.setDisplayIndentGuides = function(display) { + this.setOption("displayIndentGuides", display); + }; + this.setShowPrintMargin = function(showPrintMargin) { + this.setOption("showPrintMargin", showPrintMargin); + }; + this.getShowPrintMargin = function() { + return this.getOption("showPrintMargin"); + }; + this.setPrintMarginColumn = function(showPrintMargin) { + this.setOption("printMarginColumn", showPrintMargin); + }; + this.getPrintMarginColumn = function() { + return this.getOption("printMarginColumn"); + }; + this.getShowGutter = function(){ + return this.getOption("showGutter"); + }; + this.setShowGutter = function(show){ + return this.setOption("showGutter", show); + }; + + this.getFadeFoldWidgets = function(){ + return this.getOption("fadeFoldWidgets"); + }; + + this.setFadeFoldWidgets = function(show) { + this.setOption("fadeFoldWidgets", show); + }; + + this.setHighlightGutterLine = function(shouldHighlight) { + this.setOption("highlightGutterLine", shouldHighlight); + }; + + this.getHighlightGutterLine = function() { + return this.getOption("highlightGutterLine"); + }; + + this.$updatePrintMargin = function() { + if (!this.$showPrintMargin && !this.$printMarginEl) + return; + + if (!this.$printMarginEl) { + var containerEl = dom.createElement("div"); + containerEl.className = "ace_layer ace_print-margin-layer"; + this.$printMarginEl = dom.createElement("div"); + this.$printMarginEl.className = "ace_print-margin"; + containerEl.appendChild(this.$printMarginEl); + this.content.insertBefore(containerEl, this.content.firstChild); + } + + var style = this.$printMarginEl.style; + style.left = Math.round(this.characterWidth * this.$printMarginColumn + this.$padding) + "px"; + style.visibility = this.$showPrintMargin ? "visible" : "hidden"; + + if (this.session && this.session.$wrap == -1) + this.adjustWrapLimit(); + }; + this.getContainerElement = function() { + return this.container; + }; + this.getMouseEventTarget = function() { + return this.scroller; + }; + this.getTextAreaContainer = function() { + return this.container; + }; + this.$moveTextAreaToCursor = function() { + if (this.$isMousePressed) return; + var style = this.textarea.style; + var composition = this.$composition; + if (!this.$keepTextAreaAtCursor && !composition) { + dom.translate(this.textarea, -100, 0); + return; + } + var pixelPos = this.$cursorLayer.$pixelPos; + if (!pixelPos) + return; + if (composition && composition.markerRange) + pixelPos = this.$cursorLayer.getPixelPosition(composition.markerRange.start, true); + + var config = this.layerConfig; + var posTop = pixelPos.top; + var posLeft = pixelPos.left; + posTop -= config.offset; + + var h = composition && composition.useTextareaForIME ? this.lineHeight : HIDE_TEXTAREA ? 0 : 1; + if (posTop < 0 || posTop > config.height - h) { + dom.translate(this.textarea, 0, 0); + return; + } + + var w = 1; + var maxTop = this.$size.height - h; + if (!composition) { + posTop += this.lineHeight; + } + else { + if (composition.useTextareaForIME) { + var val = this.textarea.value; + w = this.characterWidth * (this.session.$getStringScreenWidth(val)[0]); + } + else { + posTop += this.lineHeight + 2; + } + } + + posLeft -= this.scrollLeft; + if (posLeft > this.$size.scrollerWidth - w) + posLeft = this.$size.scrollerWidth - w; + + posLeft += this.gutterWidth + this.margin.left; + + dom.setStyle(style, "height", h + "px"); + dom.setStyle(style, "width", w + "px"); + dom.translate(this.textarea, Math.min(posLeft, this.$size.scrollerWidth - w), Math.min(posTop, maxTop)); + }; + this.getFirstVisibleRow = function() { + return this.layerConfig.firstRow; + }; + this.getFirstFullyVisibleRow = function() { + return this.layerConfig.firstRow + (this.layerConfig.offset === 0 ? 0 : 1); + }; + this.getLastFullyVisibleRow = function() { + var config = this.layerConfig; + var lastRow = config.lastRow; + var top = this.session.documentToScreenRow(lastRow, 0) * config.lineHeight; + if (top - this.session.getScrollTop() > config.height - config.lineHeight) + return lastRow - 1; + return lastRow; + }; + this.getLastVisibleRow = function() { + return this.layerConfig.lastRow; + }; + + this.$padding = null; + this.setPadding = function(padding) { + this.$padding = padding; + this.$textLayer.setPadding(padding); + this.$cursorLayer.setPadding(padding); + this.$markerFront.setPadding(padding); + this.$markerBack.setPadding(padding); + this.$loop.schedule(this.CHANGE_FULL); + this.$updatePrintMargin(); + }; + + this.setScrollMargin = function(top, bottom, left, right) { + var sm = this.scrollMargin; + sm.top = top|0; + sm.bottom = bottom|0; + sm.right = right|0; + sm.left = left|0; + sm.v = sm.top + sm.bottom; + sm.h = sm.left + sm.right; + if (sm.top && this.scrollTop <= 0 && this.session) + this.session.setScrollTop(-sm.top); + this.updateFull(); + }; + + this.setMargin = function(top, bottom, left, right) { + var sm = this.margin; + sm.top = top|0; + sm.bottom = bottom|0; + sm.right = right|0; + sm.left = left|0; + sm.v = sm.top + sm.bottom; + sm.h = sm.left + sm.right; + this.$updateCachedSize(true, this.gutterWidth, this.$size.width, this.$size.height); + this.updateFull(); + }; + this.getHScrollBarAlwaysVisible = function() { + return this.$hScrollBarAlwaysVisible; + }; + this.setHScrollBarAlwaysVisible = function(alwaysVisible) { + this.setOption("hScrollBarAlwaysVisible", alwaysVisible); + }; + this.getVScrollBarAlwaysVisible = function() { + return this.$vScrollBarAlwaysVisible; + }; + this.setVScrollBarAlwaysVisible = function(alwaysVisible) { + this.setOption("vScrollBarAlwaysVisible", alwaysVisible); + }; + + this.$updateScrollBarV = function() { + var scrollHeight = this.layerConfig.maxHeight; + var scrollerHeight = this.$size.scrollerHeight; + if (!this.$maxLines && this.$scrollPastEnd) { + scrollHeight -= (scrollerHeight - this.lineHeight) * this.$scrollPastEnd; + if (this.scrollTop > scrollHeight - scrollerHeight) { + scrollHeight = this.scrollTop + scrollerHeight; + this.scrollBarV.scrollTop = null; + } + } + this.scrollBarV.setScrollHeight(scrollHeight + this.scrollMargin.v); + this.scrollBarV.setScrollTop(this.scrollTop + this.scrollMargin.top); + }; + this.$updateScrollBarH = function() { + this.scrollBarH.setScrollWidth(this.layerConfig.width + 2 * this.$padding + this.scrollMargin.h); + this.scrollBarH.setScrollLeft(this.scrollLeft + this.scrollMargin.left); + }; + + this.$frozen = false; + this.freeze = function() { + this.$frozen = true; + }; + + this.unfreeze = function() { + this.$frozen = false; + }; + + this.$renderChanges = function(changes, force) { + if (this.$changes) { + changes |= this.$changes; + this.$changes = 0; + } + if ((!this.session || !this.container.offsetWidth || this.$frozen) || (!changes && !force)) { + this.$changes |= changes; + return; + } + if (this.$size.$dirty) { + this.$changes |= changes; + return this.onResize(true); + } + if (!this.lineHeight) { + this.$textLayer.checkForSizeChanges(); + } + + this._signal("beforeRender", changes); + + if (this.session && this.session.$bidiHandler) + this.session.$bidiHandler.updateCharacterWidths(this.$fontMetrics); + + var config = this.layerConfig; + if (changes & this.CHANGE_FULL || + changes & this.CHANGE_SIZE || + changes & this.CHANGE_TEXT || + changes & this.CHANGE_LINES || + changes & this.CHANGE_SCROLL || + changes & this.CHANGE_H_SCROLL + ) { + changes |= this.$computeLayerConfig() | this.$loop.clear(); + if (config.firstRow != this.layerConfig.firstRow && config.firstRowScreen == this.layerConfig.firstRowScreen) { + var st = this.scrollTop + (config.firstRow - this.layerConfig.firstRow) * this.lineHeight; + if (st > 0) { + this.scrollTop = st; + changes = changes | this.CHANGE_SCROLL; + changes |= this.$computeLayerConfig() | this.$loop.clear(); + } + } + config = this.layerConfig; + this.$updateScrollBarV(); + if (changes & this.CHANGE_H_SCROLL) + this.$updateScrollBarH(); + + dom.translate(this.content, -this.scrollLeft, -config.offset); + + var width = config.width + 2 * this.$padding + "px"; + var height = config.minHeight + "px"; + + dom.setStyle(this.content.style, "width", width); + dom.setStyle(this.content.style, "height", height); + } + if (changes & this.CHANGE_H_SCROLL) { + dom.translate(this.content, -this.scrollLeft, -config.offset); + this.scroller.className = this.scrollLeft <= 0 ? "ace_scroller" : "ace_scroller ace_scroll-left"; + } + if (changes & this.CHANGE_FULL) { + this.$changedLines = null; + this.$textLayer.update(config); + if (this.$showGutter) + this.$gutterLayer.update(config); + this.$markerBack.update(config); + this.$markerFront.update(config); + this.$cursorLayer.update(config); + this.$moveTextAreaToCursor(); + this._signal("afterRender", changes); + return; + } + if (changes & this.CHANGE_SCROLL) { + this.$changedLines = null; + if (changes & this.CHANGE_TEXT || changes & this.CHANGE_LINES) + this.$textLayer.update(config); + else + this.$textLayer.scrollLines(config); + + if (this.$showGutter) { + if (changes & this.CHANGE_GUTTER || changes & this.CHANGE_LINES) + this.$gutterLayer.update(config); + else + this.$gutterLayer.scrollLines(config); + } + this.$markerBack.update(config); + this.$markerFront.update(config); + this.$cursorLayer.update(config); + this.$moveTextAreaToCursor(); + this._signal("afterRender", changes); + return; + } + + if (changes & this.CHANGE_TEXT) { + this.$changedLines = null; + this.$textLayer.update(config); + if (this.$showGutter) + this.$gutterLayer.update(config); + } + else if (changes & this.CHANGE_LINES) { + if (this.$updateLines() || (changes & this.CHANGE_GUTTER) && this.$showGutter) + this.$gutterLayer.update(config); + } + else if (changes & this.CHANGE_TEXT || changes & this.CHANGE_GUTTER) { + if (this.$showGutter) + this.$gutterLayer.update(config); + } + else if (changes & this.CHANGE_CURSOR) { + if (this.$highlightGutterLine) + this.$gutterLayer.updateLineHighlight(config); + } + + if (changes & this.CHANGE_CURSOR) { + this.$cursorLayer.update(config); + this.$moveTextAreaToCursor(); + } + + if (changes & (this.CHANGE_MARKER | this.CHANGE_MARKER_FRONT)) { + this.$markerFront.update(config); + } + + if (changes & (this.CHANGE_MARKER | this.CHANGE_MARKER_BACK)) { + this.$markerBack.update(config); + } + + this._signal("afterRender", changes); + }; + + + this.$autosize = function() { + var height = this.session.getScreenLength() * this.lineHeight; + var maxHeight = this.$maxLines * this.lineHeight; + var desiredHeight = Math.min(maxHeight, + Math.max((this.$minLines || 1) * this.lineHeight, height) + ) + this.scrollMargin.v + (this.$extraHeight || 0); + if (this.$horizScroll) + desiredHeight += this.scrollBarH.getHeight(); + if (this.$maxPixelHeight && desiredHeight > this.$maxPixelHeight) + desiredHeight = this.$maxPixelHeight; + + var hideScrollbars = desiredHeight <= 2 * this.lineHeight; + var vScroll = !hideScrollbars && height > maxHeight; + + if (desiredHeight != this.desiredHeight || + this.$size.height != this.desiredHeight || vScroll != this.$vScroll) { + if (vScroll != this.$vScroll) { + this.$vScroll = vScroll; + this.scrollBarV.setVisible(vScroll); + } + + var w = this.container.clientWidth; + this.container.style.height = desiredHeight + "px"; + this.$updateCachedSize(true, this.$gutterWidth, w, desiredHeight); + this.desiredHeight = desiredHeight; + + this._signal("autosize"); + } + }; + + this.$computeLayerConfig = function() { + var session = this.session; + var size = this.$size; + + var hideScrollbars = size.height <= 2 * this.lineHeight; + var screenLines = this.session.getScreenLength(); + var maxHeight = screenLines * this.lineHeight; + + var longestLine = this.$getLongestLine(); + + var horizScroll = !hideScrollbars && (this.$hScrollBarAlwaysVisible || + size.scrollerWidth - longestLine - 2 * this.$padding < 0); + + var hScrollChanged = this.$horizScroll !== horizScroll; + if (hScrollChanged) { + this.$horizScroll = horizScroll; + this.scrollBarH.setVisible(horizScroll); + } + var vScrollBefore = this.$vScroll; // autosize can change vscroll value in which case we need to update longestLine + if (this.$maxLines && this.lineHeight > 1) + this.$autosize(); + + var minHeight = size.scrollerHeight + this.lineHeight; + + var scrollPastEnd = !this.$maxLines && this.$scrollPastEnd + ? (size.scrollerHeight - this.lineHeight) * this.$scrollPastEnd + : 0; + maxHeight += scrollPastEnd; + + var sm = this.scrollMargin; + this.session.setScrollTop(Math.max(-sm.top, + Math.min(this.scrollTop, maxHeight - size.scrollerHeight + sm.bottom))); + + this.session.setScrollLeft(Math.max(-sm.left, Math.min(this.scrollLeft, + longestLine + 2 * this.$padding - size.scrollerWidth + sm.right))); + + var vScroll = !hideScrollbars && (this.$vScrollBarAlwaysVisible || + size.scrollerHeight - maxHeight + scrollPastEnd < 0 || this.scrollTop > sm.top); + var vScrollChanged = vScrollBefore !== vScroll; + if (vScrollChanged) { + this.$vScroll = vScroll; + this.scrollBarV.setVisible(vScroll); + } + + var offset = this.scrollTop % this.lineHeight; + var lineCount = Math.ceil(minHeight / this.lineHeight) - 1; + var firstRow = Math.max(0, Math.round((this.scrollTop - offset) / this.lineHeight)); + var lastRow = firstRow + lineCount; + var firstRowScreen, firstRowHeight; + var lineHeight = this.lineHeight; + firstRow = session.screenToDocumentRow(firstRow, 0); + var foldLine = session.getFoldLine(firstRow); + if (foldLine) { + firstRow = foldLine.start.row; + } + + firstRowScreen = session.documentToScreenRow(firstRow, 0); + firstRowHeight = session.getRowLength(firstRow) * lineHeight; + + lastRow = Math.min(session.screenToDocumentRow(lastRow, 0), session.getLength() - 1); + minHeight = size.scrollerHeight + session.getRowLength(lastRow) * lineHeight + + firstRowHeight; + + offset = this.scrollTop - firstRowScreen * lineHeight; + + var changes = 0; + if (this.layerConfig.width != longestLine || hScrollChanged) + changes = this.CHANGE_H_SCROLL; + if (hScrollChanged || vScrollChanged) { + changes |= this.$updateCachedSize(true, this.gutterWidth, size.width, size.height); + this._signal("scrollbarVisibilityChanged"); + if (vScrollChanged) + longestLine = this.$getLongestLine(); + } + + this.layerConfig = { + width : longestLine, + padding : this.$padding, + firstRow : firstRow, + firstRowScreen: firstRowScreen, + lastRow : lastRow, + lineHeight : lineHeight, + characterWidth : this.characterWidth, + minHeight : minHeight, + maxHeight : maxHeight, + offset : offset, + gutterOffset : lineHeight ? Math.max(0, Math.ceil((offset + size.height - size.scrollerHeight) / lineHeight)) : 0, + height : this.$size.scrollerHeight + }; + + if (this.session.$bidiHandler) + this.session.$bidiHandler.setContentWidth(longestLine - this.$padding); + + return changes; + }; + + this.$updateLines = function() { + if (!this.$changedLines) return; + var firstRow = this.$changedLines.firstRow; + var lastRow = this.$changedLines.lastRow; + this.$changedLines = null; + + var layerConfig = this.layerConfig; + + if (firstRow > layerConfig.lastRow + 1) { return; } + if (lastRow < layerConfig.firstRow) { return; } + if (lastRow === Infinity) { + if (this.$showGutter) + this.$gutterLayer.update(layerConfig); + this.$textLayer.update(layerConfig); + return; + } + this.$textLayer.updateLines(layerConfig, firstRow, lastRow); + return true; + }; + + this.$getLongestLine = function() { + var charCount = this.session.getScreenWidth(); + if (this.showInvisibles && !this.session.$useWrapMode) + charCount += 1; + + if (this.$textLayer && charCount > this.$textLayer.MAX_LINE_LENGTH) + charCount = this.$textLayer.MAX_LINE_LENGTH + 30; + + return Math.max(this.$size.scrollerWidth - 2 * this.$padding, Math.round(charCount * this.characterWidth)); + }; + this.updateFrontMarkers = function() { + this.$markerFront.setMarkers(this.session.getMarkers(true)); + this.$loop.schedule(this.CHANGE_MARKER_FRONT); + }; + this.updateBackMarkers = function() { + this.$markerBack.setMarkers(this.session.getMarkers()); + this.$loop.schedule(this.CHANGE_MARKER_BACK); + }; + this.addGutterDecoration = function(row, className){ + this.$gutterLayer.addGutterDecoration(row, className); + }; + this.removeGutterDecoration = function(row, className){ + this.$gutterLayer.removeGutterDecoration(row, className); + }; + this.updateBreakpoints = function(rows) { + this.$loop.schedule(this.CHANGE_GUTTER); + }; + this.setAnnotations = function(annotations) { + this.$gutterLayer.setAnnotations(annotations); + this.$loop.schedule(this.CHANGE_GUTTER); + }; + this.updateCursor = function() { + this.$loop.schedule(this.CHANGE_CURSOR); + }; + this.hideCursor = function() { + this.$cursorLayer.hideCursor(); + }; + this.showCursor = function() { + this.$cursorLayer.showCursor(); + }; + + this.scrollSelectionIntoView = function(anchor, lead, offset) { + this.scrollCursorIntoView(anchor, offset); + this.scrollCursorIntoView(lead, offset); + }; + this.scrollCursorIntoView = function(cursor, offset, $viewMargin) { + if (this.$size.scrollerHeight === 0) + return; + + var pos = this.$cursorLayer.getPixelPosition(cursor); + + var left = pos.left; + var top = pos.top; + + var topMargin = $viewMargin && $viewMargin.top || 0; + var bottomMargin = $viewMargin && $viewMargin.bottom || 0; + + var scrollTop = this.$scrollAnimation ? this.session.getScrollTop() : this.scrollTop; + + if (scrollTop + topMargin > top) { + if (offset && scrollTop + topMargin > top + this.lineHeight) + top -= offset * this.$size.scrollerHeight; + if (top === 0) + top = -this.scrollMargin.top; + this.session.setScrollTop(top); + } else if (scrollTop + this.$size.scrollerHeight - bottomMargin < top + this.lineHeight) { + if (offset && scrollTop + this.$size.scrollerHeight - bottomMargin < top - this.lineHeight) + top += offset * this.$size.scrollerHeight; + this.session.setScrollTop(top + this.lineHeight + bottomMargin - this.$size.scrollerHeight); + } + + var scrollLeft = this.scrollLeft; + + if (scrollLeft > left) { + if (left < this.$padding + 2 * this.layerConfig.characterWidth) + left = -this.scrollMargin.left; + this.session.setScrollLeft(left); + } else if (scrollLeft + this.$size.scrollerWidth < left + this.characterWidth) { + this.session.setScrollLeft(Math.round(left + this.characterWidth - this.$size.scrollerWidth)); + } else if (scrollLeft <= this.$padding && left - scrollLeft < this.characterWidth) { + this.session.setScrollLeft(0); + } + }; + this.getScrollTop = function() { + return this.session.getScrollTop(); + }; + this.getScrollLeft = function() { + return this.session.getScrollLeft(); + }; + this.getScrollTopRow = function() { + return this.scrollTop / this.lineHeight; + }; + this.getScrollBottomRow = function() { + return Math.max(0, Math.floor((this.scrollTop + this.$size.scrollerHeight) / this.lineHeight) - 1); + }; + this.scrollToRow = function(row) { + this.session.setScrollTop(row * this.lineHeight); + }; + + this.alignCursor = function(cursor, alignment) { + if (typeof cursor == "number") + cursor = {row: cursor, column: 0}; + + var pos = this.$cursorLayer.getPixelPosition(cursor); + var h = this.$size.scrollerHeight - this.lineHeight; + var offset = pos.top - h * (alignment || 0); + + this.session.setScrollTop(offset); + return offset; + }; + + this.STEPS = 8; + this.$calcSteps = function(fromValue, toValue){ + var i = 0; + var l = this.STEPS; + var steps = []; + + var func = function(t, x_min, dx) { + return dx * (Math.pow(t - 1, 3) + 1) + x_min; + }; + + for (i = 0; i < l; ++i) + steps.push(func(i / this.STEPS, fromValue, toValue - fromValue)); + + return steps; + }; + this.scrollToLine = function(line, center, animate, callback) { + var pos = this.$cursorLayer.getPixelPosition({row: line, column: 0}); + var offset = pos.top; + if (center) + offset -= this.$size.scrollerHeight / 2; + + var initialScroll = this.scrollTop; + this.session.setScrollTop(offset); + if (animate !== false) + this.animateScrolling(initialScroll, callback); + }; + + this.animateScrolling = function(fromValue, callback) { + var toValue = this.scrollTop; + if (!this.$animatedScroll) + return; + var _self = this; + + if (fromValue == toValue) + return; + + if (this.$scrollAnimation) { + var oldSteps = this.$scrollAnimation.steps; + if (oldSteps.length) { + fromValue = oldSteps[0]; + if (fromValue == toValue) + return; + } + } + + var steps = _self.$calcSteps(fromValue, toValue); + this.$scrollAnimation = {from: fromValue, to: toValue, steps: steps}; + + clearInterval(this.$timer); + + _self.session.setScrollTop(steps.shift()); + _self.session.$scrollTop = toValue; + this.$timer = setInterval(function() { + if (!_self.session) + return clearInterval(_self.$timer); + if (steps.length) { + _self.session.setScrollTop(steps.shift()); + _self.session.$scrollTop = toValue; + } else if (toValue != null) { + _self.session.$scrollTop = -1; + _self.session.setScrollTop(toValue); + toValue = null; + } else { + _self.$timer = clearInterval(_self.$timer); + _self.$scrollAnimation = null; + callback && callback(); + } + }, 10); + }; + this.scrollToY = function(scrollTop) { + if (this.scrollTop !== scrollTop) { + this.$loop.schedule(this.CHANGE_SCROLL); + this.scrollTop = scrollTop; + } + }; + this.scrollToX = function(scrollLeft) { + if (this.scrollLeft !== scrollLeft) + this.scrollLeft = scrollLeft; + this.$loop.schedule(this.CHANGE_H_SCROLL); + }; + this.scrollTo = function(x, y) { + this.session.setScrollTop(y); + this.session.setScrollLeft(x); + }; + this.scrollBy = function(deltaX, deltaY) { + deltaY && this.session.setScrollTop(this.session.getScrollTop() + deltaY); + deltaX && this.session.setScrollLeft(this.session.getScrollLeft() + deltaX); + }; + this.isScrollableBy = function(deltaX, deltaY) { + if (deltaY < 0 && this.session.getScrollTop() >= 1 - this.scrollMargin.top) + return true; + if (deltaY > 0 && this.session.getScrollTop() + this.$size.scrollerHeight + - this.layerConfig.maxHeight < -1 + this.scrollMargin.bottom) + return true; + if (deltaX < 0 && this.session.getScrollLeft() >= 1 - this.scrollMargin.left) + return true; + if (deltaX > 0 && this.session.getScrollLeft() + this.$size.scrollerWidth + - this.layerConfig.width < -1 + this.scrollMargin.right) + return true; + }; + + this.pixelToScreenCoordinates = function(x, y) { + var canvasPos; + if (this.$hasCssTransforms) { + canvasPos = {top:0, left: 0}; + var p = this.$fontMetrics.transformCoordinates([x, y]); + x = p[1] - this.gutterWidth - this.margin.left; + y = p[0]; + } else { + canvasPos = this.scroller.getBoundingClientRect(); + } + + var offsetX = x + this.scrollLeft - canvasPos.left - this.$padding; + var offset = offsetX / this.characterWidth; + var row = Math.floor((y + this.scrollTop - canvasPos.top) / this.lineHeight); + var col = this.$blockCursor ? Math.floor(offset) : Math.round(offset); + + return {row: row, column: col, side: offset - col > 0 ? 1 : -1, offsetX: offsetX}; + }; + + this.screenToTextCoordinates = function(x, y) { + var canvasPos; + if (this.$hasCssTransforms) { + canvasPos = {top:0, left: 0}; + var p = this.$fontMetrics.transformCoordinates([x, y]); + x = p[1] - this.gutterWidth - this.margin.left; + y = p[0]; + } else { + canvasPos = this.scroller.getBoundingClientRect(); + } + + var offsetX = x + this.scrollLeft - canvasPos.left - this.$padding; + var offset = offsetX / this.characterWidth; + var col = this.$blockCursor ? Math.floor(offset) : Math.round(offset); + + var row = Math.floor((y + this.scrollTop - canvasPos.top) / this.lineHeight); + + return this.session.screenToDocumentPosition(row, Math.max(col, 0), offsetX); + }; + this.textToScreenCoordinates = function(row, column) { + var canvasPos = this.scroller.getBoundingClientRect(); + var pos = this.session.documentToScreenPosition(row, column); + + var x = this.$padding + (this.session.$bidiHandler.isBidiRow(pos.row, row) + ? this.session.$bidiHandler.getPosLeft(pos.column) + : Math.round(pos.column * this.characterWidth)); + + var y = pos.row * this.lineHeight; + + return { + pageX: canvasPos.left + x - this.scrollLeft, + pageY: canvasPos.top + y - this.scrollTop + }; + }; + this.visualizeFocus = function() { + dom.addCssClass(this.container, "ace_focus"); + }; + this.visualizeBlur = function() { + dom.removeCssClass(this.container, "ace_focus"); + }; + this.showComposition = function(composition) { + this.$composition = composition; + if (!composition.cssText) { + composition.cssText = this.textarea.style.cssText; + } + if (composition.useTextareaForIME == undefined) + composition.useTextareaForIME = this.$useTextareaForIME; + + if (this.$useTextareaForIME) { + dom.addCssClass(this.textarea, "ace_composition"); + this.textarea.style.cssText = ""; + this.$moveTextAreaToCursor(); + this.$cursorLayer.element.style.display = "none"; + } + else { + composition.markerId = this.session.addMarker(composition.markerRange, "ace_composition_marker", "text"); + } + }; + this.setCompositionText = function(text) { + var cursor = this.session.selection.cursor; + this.addToken(text, "composition_placeholder", cursor.row, cursor.column); + this.$moveTextAreaToCursor(); + }; + this.hideComposition = function() { + if (!this.$composition) + return; + + if (this.$composition.markerId) + this.session.removeMarker(this.$composition.markerId); + + dom.removeCssClass(this.textarea, "ace_composition"); + this.textarea.style.cssText = this.$composition.cssText; + var cursor = this.session.selection.cursor; + this.removeExtraToken(cursor.row, cursor.column); + this.$composition = null; + this.$cursorLayer.element.style.display = ""; + }; + + this.addToken = function(text, type, row, column) { + var session = this.session; + session.bgTokenizer.lines[row] = null; + var newToken = {type: type, value: text}; + var tokens = session.getTokens(row); + if (column == null) { + tokens.push(newToken); + } else { + var l = 0; + for (var i =0; i < tokens.length; i++) { + var token = tokens[i]; + l += token.value.length; + if (column <= l) { + var diff = token.value.length - (l - column); + var before = token.value.slice(0, diff); + var after = token.value.slice(diff); + + tokens.splice(i, 1, {type: token.type, value: before}, newToken, {type: token.type, value: after}); + break; + } + } + } + this.updateLines(row, row); + }; + + this.removeExtraToken = function(row, column) { + this.updateLines(row, row); + }; + this.setTheme = function(theme, cb) { + var _self = this; + this.$themeId = theme; + _self._dispatchEvent('themeChange',{theme:theme}); + + if (!theme || typeof theme == "string") { + var moduleName = theme || this.$options.theme.initialValue; + config.loadModule(["theme", moduleName], afterLoad); + } else { + afterLoad(theme); + } + + function afterLoad(module) { + if (_self.$themeId != theme) + return cb && cb(); + if (!module || !module.cssClass) + throw new Error("couldn't load module " + theme + " or it didn't call define"); + if (module.$id) + _self.$themeId = module.$id; + dom.importCssString( + module.cssText, + module.cssClass, + _self.container + ); + + if (_self.theme) + dom.removeCssClass(_self.container, _self.theme.cssClass); + + var padding = "padding" in module ? module.padding + : "padding" in (_self.theme || {}) ? 4 : _self.$padding; + if (_self.$padding && padding != _self.$padding) + _self.setPadding(padding); + _self.$theme = module.cssClass; + + _self.theme = module; + dom.addCssClass(_self.container, module.cssClass); + dom.setCssClass(_self.container, "ace_dark", module.isDark); + if (_self.$size) { + _self.$size.width = 0; + _self.$updateSizeAsync(); + } + + _self._dispatchEvent('themeLoaded', {theme:module}); + cb && cb(); + } + }; + this.getTheme = function() { + return this.$themeId; + }; + this.setStyle = function(style, include) { + dom.setCssClass(this.container, style, include !== false); + }; + this.unsetStyle = function(style) { + dom.removeCssClass(this.container, style); + }; + + this.setCursorStyle = function(style) { + dom.setStyle(this.scroller.style, "cursor", style); + }; + this.setMouseCursor = function(cursorStyle) { + dom.setStyle(this.scroller.style, "cursor", cursorStyle); + }; + + this.attachToShadowRoot = function() { + dom.importCssString(editorCss, "ace_editor.css", this.container); + }; + this.destroy = function() { + this.freeze(); + this.$fontMetrics.destroy(); + this.$cursorLayer.destroy(); + this.removeAllListeners(); + this.container.textContent = ""; + }; + +}).call(VirtualRenderer.prototype); + + +config.defineOptions(VirtualRenderer.prototype, "renderer", { + animatedScroll: {initialValue: false}, + showInvisibles: { + set: function(value) { + if (this.$textLayer.setShowInvisibles(value)) + this.$loop.schedule(this.CHANGE_TEXT); + }, + initialValue: false + }, + showPrintMargin: { + set: function() { this.$updatePrintMargin(); }, + initialValue: true + }, + printMarginColumn: { + set: function() { this.$updatePrintMargin(); }, + initialValue: 80 + }, + printMargin: { + set: function(val) { + if (typeof val == "number") + this.$printMarginColumn = val; + this.$showPrintMargin = !!val; + this.$updatePrintMargin(); + }, + get: function() { + return this.$showPrintMargin && this.$printMarginColumn; + } + }, + showGutter: { + set: function(show){ + this.$gutter.style.display = show ? "block" : "none"; + this.$loop.schedule(this.CHANGE_FULL); + this.onGutterResize(); + }, + initialValue: true + }, + fadeFoldWidgets: { + set: function(show) { + dom.setCssClass(this.$gutter, "ace_fade-fold-widgets", show); + }, + initialValue: false + }, + showFoldWidgets: { + set: function(show) { + this.$gutterLayer.setShowFoldWidgets(show); + this.$loop.schedule(this.CHANGE_GUTTER); + }, + initialValue: true + }, + displayIndentGuides: { + set: function(show) { + if (this.$textLayer.setDisplayIndentGuides(show)) + this.$loop.schedule(this.CHANGE_TEXT); + }, + initialValue: true + }, + highlightGutterLine: { + set: function(shouldHighlight) { + this.$gutterLayer.setHighlightGutterLine(shouldHighlight); + this.$loop.schedule(this.CHANGE_GUTTER); + }, + initialValue: true + }, + hScrollBarAlwaysVisible: { + set: function(val) { + if (!this.$hScrollBarAlwaysVisible || !this.$horizScroll) + this.$loop.schedule(this.CHANGE_SCROLL); + }, + initialValue: false + }, + vScrollBarAlwaysVisible: { + set: function(val) { + if (!this.$vScrollBarAlwaysVisible || !this.$vScroll) + this.$loop.schedule(this.CHANGE_SCROLL); + }, + initialValue: false + }, + fontSize: { + set: function(size) { + if (typeof size == "number") + size = size + "px"; + this.container.style.fontSize = size; + this.updateFontSize(); + }, + initialValue: 12 + }, + fontFamily: { + set: function(name) { + this.container.style.fontFamily = name; + this.updateFontSize(); + } + }, + maxLines: { + set: function(val) { + this.updateFull(); + } + }, + minLines: { + set: function(val) { + if (!(this.$minLines < 0x1ffffffffffff)) + this.$minLines = 0; + this.updateFull(); + } + }, + maxPixelHeight: { + set: function(val) { + this.updateFull(); + }, + initialValue: 0 + }, + scrollPastEnd: { + set: function(val) { + val = +val || 0; + if (this.$scrollPastEnd == val) + return; + this.$scrollPastEnd = val; + this.$loop.schedule(this.CHANGE_SCROLL); + }, + initialValue: 0, + handlesSet: true + }, + fixedWidthGutter: { + set: function(val) { + this.$gutterLayer.$fixedWidth = !!val; + this.$loop.schedule(this.CHANGE_GUTTER); + } + }, + theme: { + set: function(val) { this.setTheme(val); }, + get: function() { return this.$themeId || this.theme; }, + initialValue: "./theme/textmate", + handlesSet: true + }, + hasCssTransforms: { + }, + useTextareaForIME: { + initialValue: !useragent.isMobile && !useragent.isIE + } +}); + +exports.VirtualRenderer = VirtualRenderer; +}); + +ace.define("ace/worker/worker_client",["require","exports","module","ace/lib/oop","ace/lib/net","ace/lib/event_emitter","ace/config"], function(require, exports, module) { +"use strict"; + +var oop = require("../lib/oop"); +var net = require("../lib/net"); +var EventEmitter = require("../lib/event_emitter").EventEmitter; +var config = require("../config"); + +function $workerBlob(workerUrl) { + var script = "importScripts('" + net.qualifyURL(workerUrl) + "');"; + try { + return new Blob([script], {"type": "application/javascript"}); + } catch (e) { // Backwards-compatibility + var BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder; + var blobBuilder = new BlobBuilder(); + blobBuilder.append(script); + return blobBuilder.getBlob("application/javascript"); + } +} + +function createWorker(workerUrl) { + if (typeof Worker == "undefined") + return { postMessage: function() {}, terminate: function() {} }; + if (config.get("loadWorkerFromBlob")) { + var blob = $workerBlob(workerUrl); + var URL = window.URL || window.webkitURL; + var blobURL = URL.createObjectURL(blob); + return new Worker(blobURL); + } + return new Worker(workerUrl); +} + +var WorkerClient = function(worker) { + if (!worker.postMessage) + worker = this.$createWorkerFromOldConfig.apply(this, arguments); + + this.$worker = worker; + this.$sendDeltaQueue = this.$sendDeltaQueue.bind(this); + this.changeListener = this.changeListener.bind(this); + this.onMessage = this.onMessage.bind(this); + + this.callbackId = 1; + this.callbacks = {}; + + this.$worker.onmessage = this.onMessage; +}; + +(function(){ + + oop.implement(this, EventEmitter); + + this.$createWorkerFromOldConfig = function(topLevelNamespaces, mod, classname, workerUrl, importScripts) { + if (require.nameToUrl && !require.toUrl) + require.toUrl = require.nameToUrl; + + if (config.get("packaged") || !require.toUrl) { + workerUrl = workerUrl || config.moduleUrl(mod, "worker"); + } else { + var normalizePath = this.$normalizePath; + workerUrl = workerUrl || normalizePath(require.toUrl("ace/worker/worker.js", null, "_")); + + var tlns = {}; + topLevelNamespaces.forEach(function(ns) { + tlns[ns] = normalizePath(require.toUrl(ns, null, "_").replace(/(\.js)?(\?.*)?$/, "")); + }); + } + + this.$worker = createWorker(workerUrl); + if (importScripts) { + this.send("importScripts", importScripts); + } + this.$worker.postMessage({ + init : true, + tlns : tlns, + module : mod, + classname : classname + }); + return this.$worker; + }; + + this.onMessage = function(e) { + var msg = e.data; + switch (msg.type) { + case "event": + this._signal(msg.name, {data: msg.data}); + break; + case "call": + var callback = this.callbacks[msg.id]; + if (callback) { + callback(msg.data); + delete this.callbacks[msg.id]; + } + break; + case "error": + this.reportError(msg.data); + break; + case "log": + window.console && console.log && console.log.apply(console, msg.data); + break; + } + }; + + this.reportError = function(err) { + window.console && console.error && console.error(err); + }; + + this.$normalizePath = function(path) { + return net.qualifyURL(path); + }; + + this.terminate = function() { + this._signal("terminate", {}); + this.deltaQueue = null; + this.$worker.terminate(); + this.$worker = null; + if (this.$doc) + this.$doc.off("change", this.changeListener); + this.$doc = null; + }; + + this.send = function(cmd, args) { + this.$worker.postMessage({command: cmd, args: args}); + }; + + this.call = function(cmd, args, callback) { + if (callback) { + var id = this.callbackId++; + this.callbacks[id] = callback; + args.push(id); + } + this.send(cmd, args); + }; + + this.emit = function(event, data) { + try { + if (data.data && data.data.err) + data.data.err = {message: data.data.err.message, stack: data.data.err.stack, code: data.data.err.code}; + this.$worker.postMessage({event: event, data: {data: data.data}}); + } + catch(ex) { + console.error(ex.stack); + } + }; + + this.attachToDocument = function(doc) { + if (this.$doc) + this.terminate(); + + this.$doc = doc; + this.call("setValue", [doc.getValue()]); + doc.on("change", this.changeListener); + }; + + this.changeListener = function(delta) { + if (!this.deltaQueue) { + this.deltaQueue = []; + setTimeout(this.$sendDeltaQueue, 0); + } + if (delta.action == "insert") + this.deltaQueue.push(delta.start, delta.lines); + else + this.deltaQueue.push(delta.start, delta.end); + }; + + this.$sendDeltaQueue = function() { + var q = this.deltaQueue; + if (!q) return; + this.deltaQueue = null; + if (q.length > 50 && q.length > this.$doc.getLength() >> 1) { + this.call("setValue", [this.$doc.getValue()]); + } else + this.emit("change", {data: q}); + }; + +}).call(WorkerClient.prototype); + + +var UIWorkerClient = function(topLevelNamespaces, mod, classname) { + var main = null; + var emitSync = false; + var sender = Object.create(EventEmitter); + + var messageBuffer = []; + var workerClient = new WorkerClient({ + messageBuffer: messageBuffer, + terminate: function() {}, + postMessage: function(e) { + messageBuffer.push(e); + if (!main) return; + if (emitSync) + setTimeout(processNext); + else + processNext(); + } + }); + + workerClient.setEmitSync = function(val) { emitSync = val; }; + + var processNext = function() { + var msg = messageBuffer.shift(); + if (msg.command) + main[msg.command].apply(main, msg.args); + else if (msg.event) + sender._signal(msg.event, msg.data); + }; + + sender.postMessage = function(msg) { + workerClient.onMessage({data: msg}); + }; + sender.callback = function(data, callbackId) { + this.postMessage({type: "call", id: callbackId, data: data}); + }; + sender.emit = function(name, data) { + this.postMessage({type: "event", name: name, data: data}); + }; + + config.loadModule(["worker", mod], function(Main) { + main = new Main[classname](sender); + while (messageBuffer.length) + processNext(); + }); + + return workerClient; +}; + +exports.UIWorkerClient = UIWorkerClient; +exports.WorkerClient = WorkerClient; +exports.createWorker = createWorker; + + +}); + +ace.define("ace/placeholder",["require","exports","module","ace/range","ace/lib/event_emitter","ace/lib/oop"], function(require, exports, module) { +"use strict"; + +var Range = require("./range").Range; +var EventEmitter = require("./lib/event_emitter").EventEmitter; +var oop = require("./lib/oop"); + +var PlaceHolder = function(session, length, pos, others, mainClass, othersClass) { + var _self = this; + this.length = length; + this.session = session; + this.doc = session.getDocument(); + this.mainClass = mainClass; + this.othersClass = othersClass; + this.$onUpdate = this.onUpdate.bind(this); + this.doc.on("change", this.$onUpdate); + this.$others = others; + + this.$onCursorChange = function() { + setTimeout(function() { + _self.onCursorChange(); + }); + }; + + this.$pos = pos; + var undoStack = session.getUndoManager().$undoStack || session.getUndoManager().$undostack || {length: -1}; + this.$undoStackDepth = undoStack.length; + this.setup(); + + session.selection.on("changeCursor", this.$onCursorChange); +}; + +(function() { + + oop.implement(this, EventEmitter); + this.setup = function() { + var _self = this; + var doc = this.doc; + var session = this.session; + + this.selectionBefore = session.selection.toJSON(); + if (session.selection.inMultiSelectMode) + session.selection.toSingleRange(); + + this.pos = doc.createAnchor(this.$pos.row, this.$pos.column); + var pos = this.pos; + pos.$insertRight = true; + pos.detach(); + pos.markerId = session.addMarker(new Range(pos.row, pos.column, pos.row, pos.column + this.length), this.mainClass, null, false); + this.others = []; + this.$others.forEach(function(other) { + var anchor = doc.createAnchor(other.row, other.column); + anchor.$insertRight = true; + anchor.detach(); + _self.others.push(anchor); + }); + session.setUndoSelect(false); + }; + this.showOtherMarkers = function() { + if (this.othersActive) return; + var session = this.session; + var _self = this; + this.othersActive = true; + this.others.forEach(function(anchor) { + anchor.markerId = session.addMarker(new Range(anchor.row, anchor.column, anchor.row, anchor.column+_self.length), _self.othersClass, null, false); + }); + }; + this.hideOtherMarkers = function() { + if (!this.othersActive) return; + this.othersActive = false; + for (var i = 0; i < this.others.length; i++) { + this.session.removeMarker(this.others[i].markerId); + } + }; + this.onUpdate = function(delta) { + if (this.$updating) + return this.updateAnchors(delta); + + var range = delta; + if (range.start.row !== range.end.row) return; + if (range.start.row !== this.pos.row) return; + this.$updating = true; + var lengthDiff = delta.action === "insert" ? range.end.column - range.start.column : range.start.column - range.end.column; + var inMainRange = range.start.column >= this.pos.column && range.start.column <= this.pos.column + this.length + 1; + var distanceFromStart = range.start.column - this.pos.column; + + this.updateAnchors(delta); + + if (inMainRange) + this.length += lengthDiff; + + if (inMainRange && !this.session.$fromUndo) { + if (delta.action === 'insert') { + for (var i = this.others.length - 1; i >= 0; i--) { + var otherPos = this.others[i]; + var newPos = {row: otherPos.row, column: otherPos.column + distanceFromStart}; + this.doc.insertMergedLines(newPos, delta.lines); + } + } else if (delta.action === 'remove') { + for (var i = this.others.length - 1; i >= 0; i--) { + var otherPos = this.others[i]; + var newPos = {row: otherPos.row, column: otherPos.column + distanceFromStart}; + this.doc.remove(new Range(newPos.row, newPos.column, newPos.row, newPos.column - lengthDiff)); + } + } + } + + this.$updating = false; + this.updateMarkers(); + }; + + this.updateAnchors = function(delta) { + this.pos.onChange(delta); + for (var i = this.others.length; i--;) + this.others[i].onChange(delta); + this.updateMarkers(); + }; + + this.updateMarkers = function() { + if (this.$updating) + return; + var _self = this; + var session = this.session; + var updateMarker = function(pos, className) { + session.removeMarker(pos.markerId); + pos.markerId = session.addMarker(new Range(pos.row, pos.column, pos.row, pos.column+_self.length), className, null, false); + }; + updateMarker(this.pos, this.mainClass); + for (var i = this.others.length; i--;) + updateMarker(this.others[i], this.othersClass); + }; + + this.onCursorChange = function(event) { + if (this.$updating || !this.session) return; + var pos = this.session.selection.getCursor(); + if (pos.row === this.pos.row && pos.column >= this.pos.column && pos.column <= this.pos.column + this.length) { + this.showOtherMarkers(); + this._emit("cursorEnter", event); + } else { + this.hideOtherMarkers(); + this._emit("cursorLeave", event); + } + }; + this.detach = function() { + this.session.removeMarker(this.pos && this.pos.markerId); + this.hideOtherMarkers(); + this.doc.off("change", this.$onUpdate); + this.session.selection.off("changeCursor", this.$onCursorChange); + this.session.setUndoSelect(true); + this.session = null; + }; + this.cancel = function() { + if (this.$undoStackDepth === -1) + return; + var undoManager = this.session.getUndoManager(); + var undosRequired = (undoManager.$undoStack || undoManager.$undostack).length - this.$undoStackDepth; + for (var i = 0; i < undosRequired; i++) { + undoManager.undo(this.session, true); + } + if (this.selectionBefore) + this.session.selection.fromJSON(this.selectionBefore); + }; +}).call(PlaceHolder.prototype); + + +exports.PlaceHolder = PlaceHolder; +}); + +ace.define("ace/mouse/multi_select_handler",["require","exports","module","ace/lib/event","ace/lib/useragent"], function(require, exports, module) { + +var event = require("../lib/event"); +var useragent = require("../lib/useragent"); +function isSamePoint(p1, p2) { + return p1.row == p2.row && p1.column == p2.column; +} + +function onMouseDown(e) { + var ev = e.domEvent; + var alt = ev.altKey; + var shift = ev.shiftKey; + var ctrl = ev.ctrlKey; + var accel = e.getAccelKey(); + var button = e.getButton(); + + if (ctrl && useragent.isMac) + button = ev.button; + + if (e.editor.inMultiSelectMode && button == 2) { + e.editor.textInput.onContextMenu(e.domEvent); + return; + } + + if (!ctrl && !alt && !accel) { + if (button === 0 && e.editor.inMultiSelectMode) + e.editor.exitMultiSelectMode(); + return; + } + + if (button !== 0) + return; + + var editor = e.editor; + var selection = editor.selection; + var isMultiSelect = editor.inMultiSelectMode; + var pos = e.getDocumentPosition(); + var cursor = selection.getCursor(); + var inSelection = e.inSelection() || (selection.isEmpty() && isSamePoint(pos, cursor)); + + var mouseX = e.x, mouseY = e.y; + var onMouseSelection = function(e) { + mouseX = e.clientX; + mouseY = e.clientY; + }; + + var session = editor.session; + var screenAnchor = editor.renderer.pixelToScreenCoordinates(mouseX, mouseY); + var screenCursor = screenAnchor; + + var selectionMode; + if (editor.$mouseHandler.$enableJumpToDef) { + if (ctrl && alt || accel && alt) + selectionMode = shift ? "block" : "add"; + else if (alt && editor.$blockSelectEnabled) + selectionMode = "block"; + } else { + if (accel && !alt) { + selectionMode = "add"; + if (!isMultiSelect && shift) + return; + } else if (alt && editor.$blockSelectEnabled) { + selectionMode = "block"; + } + } + + if (selectionMode && useragent.isMac && ev.ctrlKey) { + editor.$mouseHandler.cancelContextMenu(); + } + + if (selectionMode == "add") { + if (!isMultiSelect && inSelection) + return; // dragging + + if (!isMultiSelect) { + var range = selection.toOrientedRange(); + editor.addSelectionMarker(range); + } + + var oldRange = selection.rangeList.rangeAtPoint(pos); + + editor.inVirtualSelectionMode = true; + + if (shift) { + oldRange = null; + range = selection.ranges[0] || range; + editor.removeSelectionMarker(range); + } + editor.once("mouseup", function() { + var tmpSel = selection.toOrientedRange(); + + if (oldRange && tmpSel.isEmpty() && isSamePoint(oldRange.cursor, tmpSel.cursor)) + selection.substractPoint(tmpSel.cursor); + else { + if (shift) { + selection.substractPoint(range.cursor); + } else if (range) { + editor.removeSelectionMarker(range); + selection.addRange(range); + } + selection.addRange(tmpSel); + } + editor.inVirtualSelectionMode = false; + }); + + } else if (selectionMode == "block") { + e.stop(); + editor.inVirtualSelectionMode = true; + var initialRange; + var rectSel = []; + var blockSelect = function() { + var newCursor = editor.renderer.pixelToScreenCoordinates(mouseX, mouseY); + var cursor = session.screenToDocumentPosition(newCursor.row, newCursor.column, newCursor.offsetX); + + if (isSamePoint(screenCursor, newCursor) && isSamePoint(cursor, selection.lead)) + return; + screenCursor = newCursor; + + editor.selection.moveToPosition(cursor); + editor.renderer.scrollCursorIntoView(); + + editor.removeSelectionMarkers(rectSel); + rectSel = selection.rectangularRangeBlock(screenCursor, screenAnchor); + if (editor.$mouseHandler.$clickSelection && rectSel.length == 1 && rectSel[0].isEmpty()) + rectSel[0] = editor.$mouseHandler.$clickSelection.clone(); + rectSel.forEach(editor.addSelectionMarker, editor); + editor.updateSelectionMarkers(); + }; + if (isMultiSelect && !accel) { + selection.toSingleRange(); + } else if (!isMultiSelect && accel) { + initialRange = selection.toOrientedRange(); + editor.addSelectionMarker(initialRange); + } + + if (shift) + screenAnchor = session.documentToScreenPosition(selection.lead); + else + selection.moveToPosition(pos); + + screenCursor = {row: -1, column: -1}; + + var onMouseSelectionEnd = function(e) { + blockSelect(); + clearInterval(timerId); + editor.removeSelectionMarkers(rectSel); + if (!rectSel.length) + rectSel = [selection.toOrientedRange()]; + if (initialRange) { + editor.removeSelectionMarker(initialRange); + selection.toSingleRange(initialRange); + } + for (var i = 0; i < rectSel.length; i++) + selection.addRange(rectSel[i]); + editor.inVirtualSelectionMode = false; + editor.$mouseHandler.$clickSelection = null; + }; + + var onSelectionInterval = blockSelect; + + event.capture(editor.container, onMouseSelection, onMouseSelectionEnd); + var timerId = setInterval(function() {onSelectionInterval();}, 20); + + return e.preventDefault(); + } +} + + +exports.onMouseDown = onMouseDown; + +}); + +ace.define("ace/commands/multi_select_commands",["require","exports","module","ace/keyboard/hash_handler"], function(require, exports, module) { +exports.defaultCommands = [{ + name: "addCursorAbove", + description: "Add cursor above", + exec: function(editor) { editor.selectMoreLines(-1); }, + bindKey: {win: "Ctrl-Alt-Up", mac: "Ctrl-Alt-Up"}, + scrollIntoView: "cursor", + readOnly: true +}, { + name: "addCursorBelow", + description: "Add cursor below", + exec: function(editor) { editor.selectMoreLines(1); }, + bindKey: {win: "Ctrl-Alt-Down", mac: "Ctrl-Alt-Down"}, + scrollIntoView: "cursor", + readOnly: true +}, { + name: "addCursorAboveSkipCurrent", + description: "Add cursor above (skip current)", + exec: function(editor) { editor.selectMoreLines(-1, true); }, + bindKey: {win: "Ctrl-Alt-Shift-Up", mac: "Ctrl-Alt-Shift-Up"}, + scrollIntoView: "cursor", + readOnly: true +}, { + name: "addCursorBelowSkipCurrent", + description: "Add cursor below (skip current)", + exec: function(editor) { editor.selectMoreLines(1, true); }, + bindKey: {win: "Ctrl-Alt-Shift-Down", mac: "Ctrl-Alt-Shift-Down"}, + scrollIntoView: "cursor", + readOnly: true +}, { + name: "selectMoreBefore", + description: "Select more before", + exec: function(editor) { editor.selectMore(-1); }, + bindKey: {win: "Ctrl-Alt-Left", mac: "Ctrl-Alt-Left"}, + scrollIntoView: "cursor", + readOnly: true +}, { + name: "selectMoreAfter", + description: "Select more after", + exec: function(editor) { editor.selectMore(1); }, + bindKey: {win: "Ctrl-Alt-Right", mac: "Ctrl-Alt-Right"}, + scrollIntoView: "cursor", + readOnly: true +}, { + name: "selectNextBefore", + description: "Select next before", + exec: function(editor) { editor.selectMore(-1, true); }, + bindKey: {win: "Ctrl-Alt-Shift-Left", mac: "Ctrl-Alt-Shift-Left"}, + scrollIntoView: "cursor", + readOnly: true +}, { + name: "selectNextAfter", + description: "Select next after", + exec: function(editor) { editor.selectMore(1, true); }, + bindKey: {win: "Ctrl-Alt-Shift-Right", mac: "Ctrl-Alt-Shift-Right"}, + scrollIntoView: "cursor", + readOnly: true +}, { + name: "toggleSplitSelectionIntoLines", + description: "Split into lines", + exec: function(editor) { + if (editor.multiSelect.rangeCount > 1) + editor.multiSelect.joinSelections(); + else + editor.multiSelect.splitIntoLines(); + }, + bindKey: {win: "Ctrl-Alt-L", mac: "Ctrl-Alt-L"}, + readOnly: true +}, { + name: "splitSelectionIntoLines", + description: "Split into lines", + exec: function(editor) { editor.multiSelect.splitIntoLines(); }, + readOnly: true +}, { + name: "alignCursors", + description: "Align cursors", + exec: function(editor) { editor.alignCursors(); }, + bindKey: {win: "Ctrl-Alt-A", mac: "Ctrl-Alt-A"}, + scrollIntoView: "cursor" +}, { + name: "findAll", + description: "Find all", + exec: function(editor) { editor.findAll(); }, + bindKey: {win: "Ctrl-Alt-K", mac: "Ctrl-Alt-G"}, + scrollIntoView: "cursor", + readOnly: true +}]; +exports.multiSelectCommands = [{ + name: "singleSelection", + description: "Single selection", + bindKey: "esc", + exec: function(editor) { editor.exitMultiSelectMode(); }, + scrollIntoView: "cursor", + readOnly: true, + isAvailable: function(editor) {return editor && editor.inMultiSelectMode;} +}]; + +var HashHandler = require("../keyboard/hash_handler").HashHandler; +exports.keyboardHandler = new HashHandler(exports.multiSelectCommands); + +}); + +ace.define("ace/multi_select",["require","exports","module","ace/range_list","ace/range","ace/selection","ace/mouse/multi_select_handler","ace/lib/event","ace/lib/lang","ace/commands/multi_select_commands","ace/search","ace/edit_session","ace/editor","ace/config"], function(require, exports, module) { + +var RangeList = require("./range_list").RangeList; +var Range = require("./range").Range; +var Selection = require("./selection").Selection; +var onMouseDown = require("./mouse/multi_select_handler").onMouseDown; +var event = require("./lib/event"); +var lang = require("./lib/lang"); +var commands = require("./commands/multi_select_commands"); +exports.commands = commands.defaultCommands.concat(commands.multiSelectCommands); +var Search = require("./search").Search; +var search = new Search(); + +function find(session, needle, dir) { + search.$options.wrap = true; + search.$options.needle = needle; + search.$options.backwards = dir == -1; + return search.find(session); +} +var EditSession = require("./edit_session").EditSession; +(function() { + this.getSelectionMarkers = function() { + return this.$selectionMarkers; + }; +}).call(EditSession.prototype); +(function() { + this.ranges = null; + this.rangeList = null; + this.addRange = function(range, $blockChangeEvents) { + if (!range) + return; + + if (!this.inMultiSelectMode && this.rangeCount === 0) { + var oldRange = this.toOrientedRange(); + this.rangeList.add(oldRange); + this.rangeList.add(range); + if (this.rangeList.ranges.length != 2) { + this.rangeList.removeAll(); + return $blockChangeEvents || this.fromOrientedRange(range); + } + this.rangeList.removeAll(); + this.rangeList.add(oldRange); + this.$onAddRange(oldRange); + } + + if (!range.cursor) + range.cursor = range.end; + + var removed = this.rangeList.add(range); + + this.$onAddRange(range); + + if (removed.length) + this.$onRemoveRange(removed); + + if (this.rangeCount > 1 && !this.inMultiSelectMode) { + this._signal("multiSelect"); + this.inMultiSelectMode = true; + this.session.$undoSelect = false; + this.rangeList.attach(this.session); + } + + return $blockChangeEvents || this.fromOrientedRange(range); + }; + this.toSingleRange = function(range) { + range = range || this.ranges[0]; + var removed = this.rangeList.removeAll(); + if (removed.length) + this.$onRemoveRange(removed); + + range && this.fromOrientedRange(range); + }; + this.substractPoint = function(pos) { + var removed = this.rangeList.substractPoint(pos); + if (removed) { + this.$onRemoveRange(removed); + return removed[0]; + } + }; + this.mergeOverlappingRanges = function() { + var removed = this.rangeList.merge(); + if (removed.length) + this.$onRemoveRange(removed); + }; + + this.$onAddRange = function(range) { + this.rangeCount = this.rangeList.ranges.length; + this.ranges.unshift(range); + this._signal("addRange", {range: range}); + }; + + this.$onRemoveRange = function(removed) { + this.rangeCount = this.rangeList.ranges.length; + if (this.rangeCount == 1 && this.inMultiSelectMode) { + var lastRange = this.rangeList.ranges.pop(); + removed.push(lastRange); + this.rangeCount = 0; + } + + for (var i = removed.length; i--; ) { + var index = this.ranges.indexOf(removed[i]); + this.ranges.splice(index, 1); + } + + this._signal("removeRange", {ranges: removed}); + + if (this.rangeCount === 0 && this.inMultiSelectMode) { + this.inMultiSelectMode = false; + this._signal("singleSelect"); + this.session.$undoSelect = true; + this.rangeList.detach(this.session); + } + + lastRange = lastRange || this.ranges[0]; + if (lastRange && !lastRange.isEqual(this.getRange())) + this.fromOrientedRange(lastRange); + }; + this.$initRangeList = function() { + if (this.rangeList) + return; + + this.rangeList = new RangeList(); + this.ranges = []; + this.rangeCount = 0; + }; + this.getAllRanges = function() { + return this.rangeCount ? this.rangeList.ranges.concat() : [this.getRange()]; + }; + this.splitIntoLines = function () { + var ranges = this.ranges.length ? this.ranges : [this.getRange()]; + var newRanges = []; + for (var i = 0; i < ranges.length; i++) { + var range = ranges[i]; + var row = range.start.row; + var endRow = range.end.row; + if (row === endRow) { + newRanges.push(range.clone()); + } else { + newRanges.push(new Range(row, range.start.column, row, this.session.getLine(row).length)); + while (++row < endRow) + newRanges.push(this.getLineRange(row, true)); + newRanges.push(new Range(endRow, 0, endRow, range.end.column)); + } + if (i == 0 && !this.isBackwards()) + newRanges = newRanges.reverse(); + } + this.toSingleRange(); + for (var i = newRanges.length; i--;) + this.addRange(newRanges[i]); + }; + + this.joinSelections = function () { + var ranges = this.rangeList.ranges; + var lastRange = ranges[ranges.length - 1]; + var range = Range.fromPoints(ranges[0].start, lastRange.end); + + this.toSingleRange(); + this.setSelectionRange(range, lastRange.cursor == lastRange.start); + }; + this.toggleBlockSelection = function () { + if (this.rangeCount > 1) { + var ranges = this.rangeList.ranges; + var lastRange = ranges[ranges.length - 1]; + var range = Range.fromPoints(ranges[0].start, lastRange.end); + + this.toSingleRange(); + this.setSelectionRange(range, lastRange.cursor == lastRange.start); + } else { + var cursor = this.session.documentToScreenPosition(this.cursor); + var anchor = this.session.documentToScreenPosition(this.anchor); + + var rectSel = this.rectangularRangeBlock(cursor, anchor); + rectSel.forEach(this.addRange, this); + } + }; + this.rectangularRangeBlock = function(screenCursor, screenAnchor, includeEmptyLines) { + var rectSel = []; + + var xBackwards = screenCursor.column < screenAnchor.column; + if (xBackwards) { + var startColumn = screenCursor.column; + var endColumn = screenAnchor.column; + var startOffsetX = screenCursor.offsetX; + var endOffsetX = screenAnchor.offsetX; + } else { + var startColumn = screenAnchor.column; + var endColumn = screenCursor.column; + var startOffsetX = screenAnchor.offsetX; + var endOffsetX = screenCursor.offsetX; + } + + var yBackwards = screenCursor.row < screenAnchor.row; + if (yBackwards) { + var startRow = screenCursor.row; + var endRow = screenAnchor.row; + } else { + var startRow = screenAnchor.row; + var endRow = screenCursor.row; + } + + if (startColumn < 0) + startColumn = 0; + if (startRow < 0) + startRow = 0; + + if (startRow == endRow) + includeEmptyLines = true; + + var docEnd; + for (var row = startRow; row <= endRow; row++) { + var range = Range.fromPoints( + this.session.screenToDocumentPosition(row, startColumn, startOffsetX), + this.session.screenToDocumentPosition(row, endColumn, endOffsetX) + ); + if (range.isEmpty()) { + if (docEnd && isSamePoint(range.end, docEnd)) + break; + docEnd = range.end; + } + range.cursor = xBackwards ? range.start : range.end; + rectSel.push(range); + } + + if (yBackwards) + rectSel.reverse(); + + if (!includeEmptyLines) { + var end = rectSel.length - 1; + while (rectSel[end].isEmpty() && end > 0) + end--; + if (end > 0) { + var start = 0; + while (rectSel[start].isEmpty()) + start++; + } + for (var i = end; i >= start; i--) { + if (rectSel[i].isEmpty()) + rectSel.splice(i, 1); + } + } + + return rectSel; + }; +}).call(Selection.prototype); +var Editor = require("./editor").Editor; +(function() { + this.updateSelectionMarkers = function() { + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + }; + this.addSelectionMarker = function(orientedRange) { + if (!orientedRange.cursor) + orientedRange.cursor = orientedRange.end; + + var style = this.getSelectionStyle(); + orientedRange.marker = this.session.addMarker(orientedRange, "ace_selection", style); + + this.session.$selectionMarkers.push(orientedRange); + this.session.selectionMarkerCount = this.session.$selectionMarkers.length; + return orientedRange; + }; + this.removeSelectionMarker = function(range) { + if (!range.marker) + return; + this.session.removeMarker(range.marker); + var index = this.session.$selectionMarkers.indexOf(range); + if (index != -1) + this.session.$selectionMarkers.splice(index, 1); + this.session.selectionMarkerCount = this.session.$selectionMarkers.length; + }; + + this.removeSelectionMarkers = function(ranges) { + var markerList = this.session.$selectionMarkers; + for (var i = ranges.length; i--; ) { + var range = ranges[i]; + if (!range.marker) + continue; + this.session.removeMarker(range.marker); + var index = markerList.indexOf(range); + if (index != -1) + markerList.splice(index, 1); + } + this.session.selectionMarkerCount = markerList.length; + }; + + this.$onAddRange = function(e) { + this.addSelectionMarker(e.range); + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + }; + + this.$onRemoveRange = function(e) { + this.removeSelectionMarkers(e.ranges); + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + }; + + this.$onMultiSelect = function(e) { + if (this.inMultiSelectMode) + return; + this.inMultiSelectMode = true; + + this.setStyle("ace_multiselect"); + this.keyBinding.addKeyboardHandler(commands.keyboardHandler); + this.commands.setDefaultHandler("exec", this.$onMultiSelectExec); + + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + }; + + this.$onSingleSelect = function(e) { + if (this.session.multiSelect.inVirtualMode) + return; + this.inMultiSelectMode = false; + + this.unsetStyle("ace_multiselect"); + this.keyBinding.removeKeyboardHandler(commands.keyboardHandler); + + this.commands.removeDefaultHandler("exec", this.$onMultiSelectExec); + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + this._emit("changeSelection"); + }; + + this.$onMultiSelectExec = function(e) { + var command = e.command; + var editor = e.editor; + if (!editor.multiSelect) + return; + if (!command.multiSelectAction) { + var result = command.exec(editor, e.args || {}); + editor.multiSelect.addRange(editor.multiSelect.toOrientedRange()); + editor.multiSelect.mergeOverlappingRanges(); + } else if (command.multiSelectAction == "forEach") { + result = editor.forEachSelection(command, e.args); + } else if (command.multiSelectAction == "forEachLine") { + result = editor.forEachSelection(command, e.args, true); + } else if (command.multiSelectAction == "single") { + editor.exitMultiSelectMode(); + result = command.exec(editor, e.args || {}); + } else { + result = command.multiSelectAction(editor, e.args || {}); + } + return result; + }; + this.forEachSelection = function(cmd, args, options) { + if (this.inVirtualSelectionMode) + return; + var keepOrder = options && options.keepOrder; + var $byLines = options == true || options && options.$byLines; + var session = this.session; + var selection = this.selection; + var rangeList = selection.rangeList; + var ranges = (keepOrder ? selection : rangeList).ranges; + var result; + + if (!ranges.length) + return cmd.exec ? cmd.exec(this, args || {}) : cmd(this, args || {}); + + var reg = selection._eventRegistry; + selection._eventRegistry = {}; + + var tmpSel = new Selection(session); + this.inVirtualSelectionMode = true; + for (var i = ranges.length; i--;) { + if ($byLines) { + while (i > 0 && ranges[i].start.row == ranges[i - 1].end.row) + i--; + } + tmpSel.fromOrientedRange(ranges[i]); + tmpSel.index = i; + this.selection = session.selection = tmpSel; + var cmdResult = cmd.exec ? cmd.exec(this, args || {}) : cmd(this, args || {}); + if (!result && cmdResult !== undefined) + result = cmdResult; + tmpSel.toOrientedRange(ranges[i]); + } + tmpSel.detach(); + + this.selection = session.selection = selection; + this.inVirtualSelectionMode = false; + selection._eventRegistry = reg; + selection.mergeOverlappingRanges(); + if (selection.ranges[0]) + selection.fromOrientedRange(selection.ranges[0]); + + var anim = this.renderer.$scrollAnimation; + this.onCursorChange(); + this.onSelectionChange(); + if (anim && anim.from == anim.to) + this.renderer.animateScrolling(anim.from); + + return result; + }; + this.exitMultiSelectMode = function() { + if (!this.inMultiSelectMode || this.inVirtualSelectionMode) + return; + this.multiSelect.toSingleRange(); + }; + + this.getSelectedText = function() { + var text = ""; + if (this.inMultiSelectMode && !this.inVirtualSelectionMode) { + var ranges = this.multiSelect.rangeList.ranges; + var buf = []; + for (var i = 0; i < ranges.length; i++) { + buf.push(this.session.getTextRange(ranges[i])); + } + var nl = this.session.getDocument().getNewLineCharacter(); + text = buf.join(nl); + if (text.length == (buf.length - 1) * nl.length) + text = ""; + } else if (!this.selection.isEmpty()) { + text = this.session.getTextRange(this.getSelectionRange()); + } + return text; + }; + + this.$checkMultiselectChange = function(e, anchor) { + if (this.inMultiSelectMode && !this.inVirtualSelectionMode) { + var range = this.multiSelect.ranges[0]; + if (this.multiSelect.isEmpty() && anchor == this.multiSelect.anchor) + return; + var pos = anchor == this.multiSelect.anchor + ? range.cursor == range.start ? range.end : range.start + : range.cursor; + if (pos.row != anchor.row + || this.session.$clipPositionToDocument(pos.row, pos.column).column != anchor.column) + this.multiSelect.toSingleRange(this.multiSelect.toOrientedRange()); + else + this.multiSelect.mergeOverlappingRanges(); + } + }; + this.findAll = function(needle, options, additive) { + options = options || {}; + options.needle = needle || options.needle; + if (options.needle == undefined) { + var range = this.selection.isEmpty() + ? this.selection.getWordRange() + : this.selection.getRange(); + options.needle = this.session.getTextRange(range); + } + this.$search.set(options); + + var ranges = this.$search.findAll(this.session); + if (!ranges.length) + return 0; + + var selection = this.multiSelect; + + if (!additive) + selection.toSingleRange(ranges[0]); + + for (var i = ranges.length; i--; ) + selection.addRange(ranges[i], true); + if (range && selection.rangeList.rangeAtPoint(range.start)) + selection.addRange(range, true); + + return ranges.length; + }; + this.selectMoreLines = function(dir, skip) { + var range = this.selection.toOrientedRange(); + var isBackwards = range.cursor == range.end; + + var screenLead = this.session.documentToScreenPosition(range.cursor); + if (this.selection.$desiredColumn) + screenLead.column = this.selection.$desiredColumn; + + var lead = this.session.screenToDocumentPosition(screenLead.row + dir, screenLead.column); + + if (!range.isEmpty()) { + var screenAnchor = this.session.documentToScreenPosition(isBackwards ? range.end : range.start); + var anchor = this.session.screenToDocumentPosition(screenAnchor.row + dir, screenAnchor.column); + } else { + var anchor = lead; + } + + if (isBackwards) { + var newRange = Range.fromPoints(lead, anchor); + newRange.cursor = newRange.start; + } else { + var newRange = Range.fromPoints(anchor, lead); + newRange.cursor = newRange.end; + } + + newRange.desiredColumn = screenLead.column; + if (!this.selection.inMultiSelectMode) { + this.selection.addRange(range); + } else { + if (skip) + var toRemove = range.cursor; + } + + this.selection.addRange(newRange); + if (toRemove) + this.selection.substractPoint(toRemove); + }; + this.transposeSelections = function(dir) { + var session = this.session; + var sel = session.multiSelect; + var all = sel.ranges; + + for (var i = all.length; i--; ) { + var range = all[i]; + if (range.isEmpty()) { + var tmp = session.getWordRange(range.start.row, range.start.column); + range.start.row = tmp.start.row; + range.start.column = tmp.start.column; + range.end.row = tmp.end.row; + range.end.column = tmp.end.column; + } + } + sel.mergeOverlappingRanges(); + + var words = []; + for (var i = all.length; i--; ) { + var range = all[i]; + words.unshift(session.getTextRange(range)); + } + + if (dir < 0) + words.unshift(words.pop()); + else + words.push(words.shift()); + + for (var i = all.length; i--; ) { + var range = all[i]; + var tmp = range.clone(); + session.replace(range, words[i]); + range.start.row = tmp.start.row; + range.start.column = tmp.start.column; + } + sel.fromOrientedRange(sel.ranges[0]); + }; + this.selectMore = function(dir, skip, stopAtFirst) { + var session = this.session; + var sel = session.multiSelect; + + var range = sel.toOrientedRange(); + if (range.isEmpty()) { + range = session.getWordRange(range.start.row, range.start.column); + range.cursor = dir == -1 ? range.start : range.end; + this.multiSelect.addRange(range); + if (stopAtFirst) + return; + } + var needle = session.getTextRange(range); + + var newRange = find(session, needle, dir); + if (newRange) { + newRange.cursor = dir == -1 ? newRange.start : newRange.end; + this.session.unfold(newRange); + this.multiSelect.addRange(newRange); + this.renderer.scrollCursorIntoView(null, 0.5); + } + if (skip) + this.multiSelect.substractPoint(range.cursor); + }; + this.alignCursors = function() { + var session = this.session; + var sel = session.multiSelect; + var ranges = sel.ranges; + var row = -1; + var sameRowRanges = ranges.filter(function(r) { + if (r.cursor.row == row) + return true; + row = r.cursor.row; + }); + + if (!ranges.length || sameRowRanges.length == ranges.length - 1) { + var range = this.selection.getRange(); + var fr = range.start.row, lr = range.end.row; + var guessRange = fr == lr; + if (guessRange) { + var max = this.session.getLength(); + var line; + do { + line = this.session.getLine(lr); + } while (/[=:]/.test(line) && ++lr < max); + do { + line = this.session.getLine(fr); + } while (/[=:]/.test(line) && --fr > 0); + + if (fr < 0) fr = 0; + if (lr >= max) lr = max - 1; + } + var lines = this.session.removeFullLines(fr, lr); + lines = this.$reAlignText(lines, guessRange); + this.session.insert({row: fr, column: 0}, lines.join("\n") + "\n"); + if (!guessRange) { + range.start.column = 0; + range.end.column = lines[lines.length - 1].length; + } + this.selection.setRange(range); + } else { + sameRowRanges.forEach(function(r) { + sel.substractPoint(r.cursor); + }); + + var maxCol = 0; + var minSpace = Infinity; + var spaceOffsets = ranges.map(function(r) { + var p = r.cursor; + var line = session.getLine(p.row); + var spaceOffset = line.substr(p.column).search(/\S/g); + if (spaceOffset == -1) + spaceOffset = 0; + + if (p.column > maxCol) + maxCol = p.column; + if (spaceOffset < minSpace) + minSpace = spaceOffset; + return spaceOffset; + }); + ranges.forEach(function(r, i) { + var p = r.cursor; + var l = maxCol - p.column; + var d = spaceOffsets[i] - minSpace; + if (l > d) + session.insert(p, lang.stringRepeat(" ", l - d)); + else + session.remove(new Range(p.row, p.column, p.row, p.column - l + d)); + + r.start.column = r.end.column = maxCol; + r.start.row = r.end.row = p.row; + r.cursor = r.end; + }); + sel.fromOrientedRange(ranges[0]); + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + } + }; + + this.$reAlignText = function(lines, forceLeft) { + var isLeftAligned = true, isRightAligned = true; + var startW, textW, endW; + + return lines.map(function(line) { + var m = line.match(/(\s*)(.*?)(\s*)([=:].*)/); + if (!m) + return [line]; + + if (startW == null) { + startW = m[1].length; + textW = m[2].length; + endW = m[3].length; + return m; + } + + if (startW + textW + endW != m[1].length + m[2].length + m[3].length) + isRightAligned = false; + if (startW != m[1].length) + isLeftAligned = false; + + if (startW > m[1].length) + startW = m[1].length; + if (textW < m[2].length) + textW = m[2].length; + if (endW > m[3].length) + endW = m[3].length; + + return m; + }).map(forceLeft ? alignLeft : + isLeftAligned ? isRightAligned ? alignRight : alignLeft : unAlign); + + function spaces(n) { + return lang.stringRepeat(" ", n); + } + + function alignLeft(m) { + return !m[2] ? m[0] : spaces(startW) + m[2] + + spaces(textW - m[2].length + endW) + + m[4].replace(/^([=:])\s+/, "$1 "); + } + function alignRight(m) { + return !m[2] ? m[0] : spaces(startW + textW - m[2].length) + m[2] + + spaces(endW) + + m[4].replace(/^([=:])\s+/, "$1 "); + } + function unAlign(m) { + return !m[2] ? m[0] : spaces(startW) + m[2] + + spaces(endW) + + m[4].replace(/^([=:])\s+/, "$1 "); + } + }; +}).call(Editor.prototype); + + +function isSamePoint(p1, p2) { + return p1.row == p2.row && p1.column == p2.column; +} +exports.onSessionChange = function(e) { + var session = e.session; + if (session && !session.multiSelect) { + session.$selectionMarkers = []; + session.selection.$initRangeList(); + session.multiSelect = session.selection; + } + this.multiSelect = session && session.multiSelect; + + var oldSession = e.oldSession; + if (oldSession) { + oldSession.multiSelect.off("addRange", this.$onAddRange); + oldSession.multiSelect.off("removeRange", this.$onRemoveRange); + oldSession.multiSelect.off("multiSelect", this.$onMultiSelect); + oldSession.multiSelect.off("singleSelect", this.$onSingleSelect); + oldSession.multiSelect.lead.off("change", this.$checkMultiselectChange); + oldSession.multiSelect.anchor.off("change", this.$checkMultiselectChange); + } + + if (session) { + session.multiSelect.on("addRange", this.$onAddRange); + session.multiSelect.on("removeRange", this.$onRemoveRange); + session.multiSelect.on("multiSelect", this.$onMultiSelect); + session.multiSelect.on("singleSelect", this.$onSingleSelect); + session.multiSelect.lead.on("change", this.$checkMultiselectChange); + session.multiSelect.anchor.on("change", this.$checkMultiselectChange); + } + + if (session && this.inMultiSelectMode != session.selection.inMultiSelectMode) { + if (session.selection.inMultiSelectMode) + this.$onMultiSelect(); + else + this.$onSingleSelect(); + } +}; +function MultiSelect(editor) { + if (editor.$multiselectOnSessionChange) + return; + editor.$onAddRange = editor.$onAddRange.bind(editor); + editor.$onRemoveRange = editor.$onRemoveRange.bind(editor); + editor.$onMultiSelect = editor.$onMultiSelect.bind(editor); + editor.$onSingleSelect = editor.$onSingleSelect.bind(editor); + editor.$multiselectOnSessionChange = exports.onSessionChange.bind(editor); + editor.$checkMultiselectChange = editor.$checkMultiselectChange.bind(editor); + + editor.$multiselectOnSessionChange(editor); + editor.on("changeSession", editor.$multiselectOnSessionChange); + + editor.on("mousedown", onMouseDown); + editor.commands.addCommands(commands.defaultCommands); + + addAltCursorListeners(editor); +} + +function addAltCursorListeners(editor){ + if (!editor.textInput) return; + var el = editor.textInput.getElement(); + var altCursor = false; + event.addListener(el, "keydown", function(e) { + var altDown = e.keyCode == 18 && !(e.ctrlKey || e.shiftKey || e.metaKey); + if (editor.$blockSelectEnabled && altDown) { + if (!altCursor) { + editor.renderer.setMouseCursor("crosshair"); + altCursor = true; + } + } else if (altCursor) { + reset(); + } + }, editor); + + event.addListener(el, "keyup", reset, editor); + event.addListener(el, "blur", reset, editor); + function reset(e) { + if (altCursor) { + editor.renderer.setMouseCursor(""); + altCursor = false; + } + } +} + +exports.MultiSelect = MultiSelect; + + +require("./config").defineOptions(Editor.prototype, "editor", { + enableMultiselect: { + set: function(val) { + MultiSelect(this); + if (val) { + this.on("changeSession", this.$multiselectOnSessionChange); + this.on("mousedown", onMouseDown); + } else { + this.off("changeSession", this.$multiselectOnSessionChange); + this.off("mousedown", onMouseDown); + } + }, + value: true + }, + enableBlockSelect: { + set: function(val) { + this.$blockSelectEnabled = val; + }, + value: true + } +}); + + + +}); + +ace.define("ace/mode/folding/fold_mode",["require","exports","module","ace/range"], function(require, exports, module) { +"use strict"; + +var Range = require("../../range").Range; + +var FoldMode = exports.FoldMode = function() {}; + +(function() { + + this.foldingStartMarker = null; + this.foldingStopMarker = null; + this.getFoldWidget = function(session, foldStyle, row) { + var line = session.getLine(row); + if (this.foldingStartMarker.test(line)) + return "start"; + if (foldStyle == "markbeginend" + && this.foldingStopMarker + && this.foldingStopMarker.test(line)) + return "end"; + return ""; + }; + + this.getFoldWidgetRange = function(session, foldStyle, row) { + return null; + }; + + this.indentationBlock = function(session, row, column) { + var re = /\S/; + var line = session.getLine(row); + var startLevel = line.search(re); + if (startLevel == -1) + return; + + var startColumn = column || line.length; + var maxRow = session.getLength(); + var startRow = row; + var endRow = row; + + while (++row < maxRow) { + var level = session.getLine(row).search(re); + + if (level == -1) + continue; + + if (level <= startLevel) { + var token = session.getTokenAt(row, 0); + if (!token || token.type !== "string") + break; + } + + endRow = row; + } + + if (endRow > startRow) { + var endColumn = session.getLine(endRow).length; + return new Range(startRow, startColumn, endRow, endColumn); + } + }; + + this.openingBracketBlock = function(session, bracket, row, column, typeRe) { + var start = {row: row, column: column + 1}; + var end = session.$findClosingBracket(bracket, start, typeRe); + if (!end) + return; + + var fw = session.foldWidgets[end.row]; + if (fw == null) + fw = session.getFoldWidget(end.row); + + if (fw == "start" && end.row > start.row) { + end.row --; + end.column = session.getLine(end.row).length; + } + return Range.fromPoints(start, end); + }; + + this.closingBracketBlock = function(session, bracket, row, column, typeRe) { + var end = {row: row, column: column}; + var start = session.$findOpeningBracket(bracket, end); + + if (!start) + return; + + start.column++; + end.column--; + + return Range.fromPoints(start, end); + }; +}).call(FoldMode.prototype); + +}); + +ace.define("ace/theme/textmate",["require","exports","module","ace/lib/dom"], function(require, exports, module) { +"use strict"; + +exports.isDark = false; +exports.cssClass = "ace-tm"; +exports.cssText = ".ace-tm .ace_gutter {\ +background: #f0f0f0;\ +color: #333;\ +}\ +.ace-tm .ace_print-margin {\ +width: 1px;\ +background: #e8e8e8;\ +}\ +.ace-tm .ace_fold {\ +background-color: #6B72E6;\ +}\ +.ace-tm {\ +background-color: #FFFFFF;\ +color: black;\ +}\ +.ace-tm .ace_cursor {\ +color: black;\ +}\ +.ace-tm .ace_invisible {\ +color: rgb(191, 191, 191);\ +}\ +.ace-tm .ace_storage,\ +.ace-tm .ace_keyword {\ +color: blue;\ +}\ +.ace-tm .ace_constant {\ +color: rgb(197, 6, 11);\ +}\ +.ace-tm .ace_constant.ace_buildin {\ +color: rgb(88, 72, 246);\ +}\ +.ace-tm .ace_constant.ace_language {\ +color: rgb(88, 92, 246);\ +}\ +.ace-tm .ace_constant.ace_library {\ +color: rgb(6, 150, 14);\ +}\ +.ace-tm .ace_invalid {\ +background-color: rgba(255, 0, 0, 0.1);\ +color: red;\ +}\ +.ace-tm .ace_support.ace_function {\ +color: rgb(60, 76, 114);\ +}\ +.ace-tm .ace_support.ace_constant {\ +color: rgb(6, 150, 14);\ +}\ +.ace-tm .ace_support.ace_type,\ +.ace-tm .ace_support.ace_class {\ +color: rgb(109, 121, 222);\ +}\ +.ace-tm .ace_keyword.ace_operator {\ +color: rgb(104, 118, 135);\ +}\ +.ace-tm .ace_string {\ +color: rgb(3, 106, 7);\ +}\ +.ace-tm .ace_comment {\ +color: rgb(76, 136, 107);\ +}\ +.ace-tm .ace_comment.ace_doc {\ +color: rgb(0, 102, 255);\ +}\ +.ace-tm .ace_comment.ace_doc.ace_tag {\ +color: rgb(128, 159, 191);\ +}\ +.ace-tm .ace_constant.ace_numeric {\ +color: rgb(0, 0, 205);\ +}\ +.ace-tm .ace_variable {\ +color: rgb(49, 132, 149);\ +}\ +.ace-tm .ace_xml-pe {\ +color: rgb(104, 104, 91);\ +}\ +.ace-tm .ace_entity.ace_name.ace_function {\ +color: #0000A2;\ +}\ +.ace-tm .ace_heading {\ +color: rgb(12, 7, 255);\ +}\ +.ace-tm .ace_list {\ +color:rgb(185, 6, 144);\ +}\ +.ace-tm .ace_meta.ace_tag {\ +color:rgb(0, 22, 142);\ +}\ +.ace-tm .ace_string.ace_regex {\ +color: rgb(255, 0, 0)\ +}\ +.ace-tm .ace_marker-layer .ace_selection {\ +background: rgb(181, 213, 255);\ +}\ +.ace-tm.ace_multiselect .ace_selection.ace_start {\ +box-shadow: 0 0 3px 0px white;\ +}\ +.ace-tm .ace_marker-layer .ace_step {\ +background: rgb(252, 255, 0);\ +}\ +.ace-tm .ace_marker-layer .ace_stack {\ +background: rgb(164, 229, 101);\ +}\ +.ace-tm .ace_marker-layer .ace_bracket {\ +margin: -1px 0 0 -1px;\ +border: 1px solid rgb(192, 192, 192);\ +}\ +.ace-tm .ace_marker-layer .ace_active-line {\ +background: rgba(0, 0, 0, 0.07);\ +}\ +.ace-tm .ace_gutter-active-line {\ +background-color : #dcdcdc;\ +}\ +.ace-tm .ace_marker-layer .ace_selected-word {\ +background: rgb(250, 250, 255);\ +border: 1px solid rgb(200, 200, 250);\ +}\ +.ace-tm .ace_indent-guide {\ +background: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==\") right repeat-y;\ +}\ +"; +exports.$id = "ace/theme/textmate"; + +var dom = require("../lib/dom"); +dom.importCssString(exports.cssText, exports.cssClass, false); +}); + +ace.define("ace/line_widgets",["require","exports","module","ace/lib/dom"], function(require, exports, module) { +"use strict"; + +var dom = require("./lib/dom"); + +function LineWidgets(session) { + this.session = session; + this.session.widgetManager = this; + this.session.getRowLength = this.getRowLength; + this.session.$getWidgetScreenLength = this.$getWidgetScreenLength; + this.updateOnChange = this.updateOnChange.bind(this); + this.renderWidgets = this.renderWidgets.bind(this); + this.measureWidgets = this.measureWidgets.bind(this); + this.session._changedWidgets = []; + this.$onChangeEditor = this.$onChangeEditor.bind(this); + + this.session.on("change", this.updateOnChange); + this.session.on("changeFold", this.updateOnFold); + this.session.on("changeEditor", this.$onChangeEditor); +} + +(function() { + this.getRowLength = function(row) { + var h; + if (this.lineWidgets) + h = this.lineWidgets[row] && this.lineWidgets[row].rowCount || 0; + else + h = 0; + if (!this.$useWrapMode || !this.$wrapData[row]) { + return 1 + h; + } else { + return this.$wrapData[row].length + 1 + h; + } + }; + + this.$getWidgetScreenLength = function() { + var screenRows = 0; + this.lineWidgets.forEach(function(w){ + if (w && w.rowCount && !w.hidden) + screenRows += w.rowCount; + }); + return screenRows; + }; + + this.$onChangeEditor = function(e) { + this.attach(e.editor); + }; + + this.attach = function(editor) { + if (editor && editor.widgetManager && editor.widgetManager != this) + editor.widgetManager.detach(); + + if (this.editor == editor) + return; + + this.detach(); + this.editor = editor; + + if (editor) { + editor.widgetManager = this; + editor.renderer.on("beforeRender", this.measureWidgets); + editor.renderer.on("afterRender", this.renderWidgets); + } + }; + this.detach = function(e) { + var editor = this.editor; + if (!editor) + return; + + this.editor = null; + editor.widgetManager = null; + + editor.renderer.off("beforeRender", this.measureWidgets); + editor.renderer.off("afterRender", this.renderWidgets); + var lineWidgets = this.session.lineWidgets; + lineWidgets && lineWidgets.forEach(function(w) { + if (w && w.el && w.el.parentNode) { + w._inDocument = false; + w.el.parentNode.removeChild(w.el); + } + }); + }; + + this.updateOnFold = function(e, session) { + var lineWidgets = session.lineWidgets; + if (!lineWidgets || !e.action) + return; + var fold = e.data; + var start = fold.start.row; + var end = fold.end.row; + var hide = e.action == "add"; + for (var i = start + 1; i < end; i++) { + if (lineWidgets[i]) + lineWidgets[i].hidden = hide; + } + if (lineWidgets[end]) { + if (hide) { + if (!lineWidgets[start]) + lineWidgets[start] = lineWidgets[end]; + else + lineWidgets[end].hidden = hide; + } else { + if (lineWidgets[start] == lineWidgets[end]) + lineWidgets[start] = undefined; + lineWidgets[end].hidden = hide; + } + } + }; + + this.updateOnChange = function(delta) { + var lineWidgets = this.session.lineWidgets; + if (!lineWidgets) return; + + var startRow = delta.start.row; + var len = delta.end.row - startRow; + + if (len === 0) { + } else if (delta.action == "remove") { + var removed = lineWidgets.splice(startRow + 1, len); + if (!lineWidgets[startRow] && removed[removed.length - 1]) { + lineWidgets[startRow] = removed.pop(); + } + removed.forEach(function(w) { + w && this.removeLineWidget(w); + }, this); + this.$updateRows(); + } else { + var args = new Array(len); + if (lineWidgets[startRow] && lineWidgets[startRow].column != null) { + if (delta.start.column > lineWidgets[startRow].column) + startRow++; + } + args.unshift(startRow, 0); + lineWidgets.splice.apply(lineWidgets, args); + this.$updateRows(); + } + }; + + this.$updateRows = function() { + var lineWidgets = this.session.lineWidgets; + if (!lineWidgets) return; + var noWidgets = true; + lineWidgets.forEach(function(w, i) { + if (w) { + noWidgets = false; + w.row = i; + while (w.$oldWidget) { + w.$oldWidget.row = i; + w = w.$oldWidget; + } + } + }); + if (noWidgets) + this.session.lineWidgets = null; + }; + + this.$registerLineWidget = function(w) { + if (!this.session.lineWidgets) + this.session.lineWidgets = new Array(this.session.getLength()); + + var old = this.session.lineWidgets[w.row]; + if (old) { + w.$oldWidget = old; + if (old.el && old.el.parentNode) { + old.el.parentNode.removeChild(old.el); + old._inDocument = false; + } + } + + this.session.lineWidgets[w.row] = w; + return w; + }; + + this.addLineWidget = function(w) { + this.$registerLineWidget(w); + w.session = this.session; + + if (!this.editor) return w; + + var renderer = this.editor.renderer; + if (w.html && !w.el) { + w.el = dom.createElement("div"); + w.el.innerHTML = w.html; + } + if (w.el) { + dom.addCssClass(w.el, "ace_lineWidgetContainer"); + w.el.style.position = "absolute"; + w.el.style.zIndex = 5; + renderer.container.appendChild(w.el); + w._inDocument = true; + + if (!w.coverGutter) { + w.el.style.zIndex = 3; + } + if (w.pixelHeight == null) { + w.pixelHeight = w.el.offsetHeight; + } + } + if (w.rowCount == null) { + w.rowCount = w.pixelHeight / renderer.layerConfig.lineHeight; + } + + var fold = this.session.getFoldAt(w.row, 0); + w.$fold = fold; + if (fold) { + var lineWidgets = this.session.lineWidgets; + if (w.row == fold.end.row && !lineWidgets[fold.start.row]) + lineWidgets[fold.start.row] = w; + else + w.hidden = true; + } + + this.session._emit("changeFold", {data:{start:{row: w.row}}}); + + this.$updateRows(); + this.renderWidgets(null, renderer); + this.onWidgetChanged(w); + return w; + }; + + this.removeLineWidget = function(w) { + w._inDocument = false; + w.session = null; + if (w.el && w.el.parentNode) + w.el.parentNode.removeChild(w.el); + if (w.editor && w.editor.destroy) try { + w.editor.destroy(); + } catch(e){} + if (this.session.lineWidgets) { + var w1 = this.session.lineWidgets[w.row]; + if (w1 == w) { + this.session.lineWidgets[w.row] = w.$oldWidget; + if (w.$oldWidget) + this.onWidgetChanged(w.$oldWidget); + } else { + while (w1) { + if (w1.$oldWidget == w) { + w1.$oldWidget = w.$oldWidget; + break; + } + w1 = w1.$oldWidget; + } + } + } + this.session._emit("changeFold", {data:{start:{row: w.row}}}); + this.$updateRows(); + }; + + this.getWidgetsAtRow = function(row) { + var lineWidgets = this.session.lineWidgets; + var w = lineWidgets && lineWidgets[row]; + var list = []; + while (w) { + list.push(w); + w = w.$oldWidget; + } + return list; + }; + + this.onWidgetChanged = function(w) { + this.session._changedWidgets.push(w); + this.editor && this.editor.renderer.updateFull(); + }; + + this.measureWidgets = function(e, renderer) { + var changedWidgets = this.session._changedWidgets; + var config = renderer.layerConfig; + + if (!changedWidgets || !changedWidgets.length) return; + var min = Infinity; + for (var i = 0; i < changedWidgets.length; i++) { + var w = changedWidgets[i]; + if (!w || !w.el) continue; + if (w.session != this.session) continue; + if (!w._inDocument) { + if (this.session.lineWidgets[w.row] != w) + continue; + w._inDocument = true; + renderer.container.appendChild(w.el); + } + + w.h = w.el.offsetHeight; + + if (!w.fixedWidth) { + w.w = w.el.offsetWidth; + w.screenWidth = Math.ceil(w.w / config.characterWidth); + } + + var rowCount = w.h / config.lineHeight; + if (w.coverLine) { + rowCount -= this.session.getRowLineCount(w.row); + if (rowCount < 0) + rowCount = 0; + } + if (w.rowCount != rowCount) { + w.rowCount = rowCount; + if (w.row < min) + min = w.row; + } + } + if (min != Infinity) { + this.session._emit("changeFold", {data:{start:{row: min}}}); + this.session.lineWidgetWidth = null; + } + this.session._changedWidgets = []; + }; + + this.renderWidgets = function(e, renderer) { + var config = renderer.layerConfig; + var lineWidgets = this.session.lineWidgets; + if (!lineWidgets) + return; + var first = Math.min(this.firstRow, config.firstRow); + var last = Math.max(this.lastRow, config.lastRow, lineWidgets.length); + + while (first > 0 && !lineWidgets[first]) + first--; + + this.firstRow = config.firstRow; + this.lastRow = config.lastRow; + + renderer.$cursorLayer.config = config; + for (var i = first; i <= last; i++) { + var w = lineWidgets[i]; + if (!w || !w.el) continue; + if (w.hidden) { + w.el.style.top = -100 - (w.pixelHeight || 0) + "px"; + continue; + } + if (!w._inDocument) { + w._inDocument = true; + renderer.container.appendChild(w.el); + } + var top = renderer.$cursorLayer.getPixelPosition({row: i, column:0}, true).top; + if (!w.coverLine) + top += config.lineHeight * this.session.getRowLineCount(w.row); + w.el.style.top = top - config.offset + "px"; + + var left = w.coverGutter ? 0 : renderer.gutterWidth; + if (!w.fixedWidth) + left -= renderer.scrollLeft; + w.el.style.left = left + "px"; + + if (w.fullWidth && w.screenWidth) { + w.el.style.minWidth = config.width + 2 * config.padding + "px"; + } + + if (w.fixedWidth) { + w.el.style.right = renderer.scrollBar.getWidth() + "px"; + } else { + w.el.style.right = ""; + } + } + }; + +}).call(LineWidgets.prototype); + + +exports.LineWidgets = LineWidgets; + +}); + +ace.define("ace/ext/error_marker",["require","exports","module","ace/line_widgets","ace/lib/dom","ace/range"], function(require, exports, module) { +"use strict"; +var LineWidgets = require("../line_widgets").LineWidgets; +var dom = require("../lib/dom"); +var Range = require("../range").Range; + +function binarySearch(array, needle, comparator) { + var first = 0; + var last = array.length - 1; + + while (first <= last) { + var mid = (first + last) >> 1; + var c = comparator(needle, array[mid]); + if (c > 0) + first = mid + 1; + else if (c < 0) + last = mid - 1; + else + return mid; + } + return -(first + 1); +} + +function findAnnotations(session, row, dir) { + var annotations = session.getAnnotations().sort(Range.comparePoints); + if (!annotations.length) + return; + + var i = binarySearch(annotations, {row: row, column: -1}, Range.comparePoints); + if (i < 0) + i = -i - 1; + + if (i >= annotations.length) + i = dir > 0 ? 0 : annotations.length - 1; + else if (i === 0 && dir < 0) + i = annotations.length - 1; + + var annotation = annotations[i]; + if (!annotation || !dir) + return; + + if (annotation.row === row) { + do { + annotation = annotations[i += dir]; + } while (annotation && annotation.row === row); + if (!annotation) + return annotations.slice(); + } + + + var matched = []; + row = annotation.row; + do { + matched[dir < 0 ? "unshift" : "push"](annotation); + annotation = annotations[i += dir]; + } while (annotation && annotation.row == row); + return matched.length && matched; +} + +exports.showErrorMarker = function(editor, dir) { + var session = editor.session; + if (!session.widgetManager) { + session.widgetManager = new LineWidgets(session); + session.widgetManager.attach(editor); + } + + var pos = editor.getCursorPosition(); + var row = pos.row; + var oldWidget = session.widgetManager.getWidgetsAtRow(row).filter(function(w) { + return w.type == "errorMarker"; + })[0]; + if (oldWidget) { + oldWidget.destroy(); + } else { + row -= dir; + } + var annotations = findAnnotations(session, row, dir); + var gutterAnno; + if (annotations) { + var annotation = annotations[0]; + pos.column = (annotation.pos && typeof annotation.column != "number" + ? annotation.pos.sc + : annotation.column) || 0; + pos.row = annotation.row; + gutterAnno = editor.renderer.$gutterLayer.$annotations[pos.row]; + } else if (oldWidget) { + return; + } else { + gutterAnno = { + text: ["Looks good!"], + className: "ace_ok" + }; + } + editor.session.unfold(pos.row); + editor.selection.moveToPosition(pos); + + var w = { + row: pos.row, + fixedWidth: true, + coverGutter: true, + el: dom.createElement("div"), + type: "errorMarker" + }; + var el = w.el.appendChild(dom.createElement("div")); + var arrow = w.el.appendChild(dom.createElement("div")); + arrow.className = "error_widget_arrow " + gutterAnno.className; + + var left = editor.renderer.$cursorLayer + .getPixelPosition(pos).left; + arrow.style.left = left + editor.renderer.gutterWidth - 5 + "px"; + + w.el.className = "error_widget_wrapper"; + el.className = "error_widget " + gutterAnno.className; + el.innerHTML = gutterAnno.text.join("
"); + + el.appendChild(dom.createElement("div")); + + var kb = function(_, hashId, keyString) { + if (hashId === 0 && (keyString === "esc" || keyString === "return")) { + w.destroy(); + return {command: "null"}; + } + }; + + w.destroy = function() { + if (editor.$mouseHandler.isMousePressed) + return; + editor.keyBinding.removeKeyboardHandler(kb); + session.widgetManager.removeLineWidget(w); + editor.off("changeSelection", w.destroy); + editor.off("changeSession", w.destroy); + editor.off("mouseup", w.destroy); + editor.off("change", w.destroy); + }; + + editor.keyBinding.addKeyboardHandler(kb); + editor.on("changeSelection", w.destroy); + editor.on("changeSession", w.destroy); + editor.on("mouseup", w.destroy); + editor.on("change", w.destroy); + + editor.session.widgetManager.addLineWidget(w); + + w.el.onmousedown = editor.focus.bind(editor); + + editor.renderer.scrollCursorIntoView(null, 0.5, {bottom: w.el.offsetHeight}); +}; + + +dom.importCssString("\ + .error_widget_wrapper {\ + background: inherit;\ + color: inherit;\ + border:none\ + }\ + .error_widget {\ + border-top: solid 2px;\ + border-bottom: solid 2px;\ + margin: 5px 0;\ + padding: 10px 40px;\ + white-space: pre-wrap;\ + }\ + .error_widget.ace_error, .error_widget_arrow.ace_error{\ + border-color: #ff5a5a\ + }\ + .error_widget.ace_warning, .error_widget_arrow.ace_warning{\ + border-color: #F1D817\ + }\ + .error_widget.ace_info, .error_widget_arrow.ace_info{\ + border-color: #5a5a5a\ + }\ + .error_widget.ace_ok, .error_widget_arrow.ace_ok{\ + border-color: #5aaa5a\ + }\ + .error_widget_arrow {\ + position: absolute;\ + border: solid 5px;\ + border-top-color: transparent!important;\ + border-right-color: transparent!important;\ + border-left-color: transparent!important;\ + top: -5px;\ + }\ +", "error_marker.css", false); + +}); + +ace.define("ace/ace",["require","exports","module","ace/lib/fixoldbrowsers","ace/lib/dom","ace/lib/event","ace/range","ace/editor","ace/edit_session","ace/undomanager","ace/virtual_renderer","ace/worker/worker_client","ace/keyboard/hash_handler","ace/placeholder","ace/multi_select","ace/mode/folding/fold_mode","ace/theme/textmate","ace/ext/error_marker","ace/config"], function(require, exports, module) { +"use strict"; + +require("./lib/fixoldbrowsers"); + +var dom = require("./lib/dom"); +var event = require("./lib/event"); + +var Range = require("./range").Range; +var Editor = require("./editor").Editor; +var EditSession = require("./edit_session").EditSession; +var UndoManager = require("./undomanager").UndoManager; +var Renderer = require("./virtual_renderer").VirtualRenderer; +require("./worker/worker_client"); +require("./keyboard/hash_handler"); +require("./placeholder"); +require("./multi_select"); +require("./mode/folding/fold_mode"); +require("./theme/textmate"); +require("./ext/error_marker"); + +exports.config = require("./config"); +exports.require = require; + +if (typeof define === "function") + exports.define = define; +exports.edit = function(el, options) { + if (typeof el == "string") { + var _id = el; + el = document.getElementById(_id); + if (!el) + throw new Error("ace.edit can't find div #" + _id); + } + + if (el && el.env && el.env.editor instanceof Editor) + return el.env.editor; + + var value = ""; + if (el && /input|textarea/i.test(el.tagName)) { + var oldNode = el; + value = oldNode.value; + el = dom.createElement("pre"); + oldNode.parentNode.replaceChild(el, oldNode); + } else if (el) { + value = el.textContent; + el.innerHTML = ""; + } + + var doc = exports.createEditSession(value); + + var editor = new Editor(new Renderer(el), doc, options); + + var env = { + document: doc, + editor: editor, + onResize: editor.resize.bind(editor, null) + }; + if (oldNode) env.textarea = oldNode; + event.addListener(window, "resize", env.onResize); + editor.on("destroy", function() { + event.removeListener(window, "resize", env.onResize); + env.editor.container.env = null; // prevent memory leak on old ie + }); + editor.container.env = editor.env = env; + return editor; +}; +exports.createEditSession = function(text, mode) { + var doc = new EditSession(text, mode); + doc.setUndoManager(new UndoManager()); + return doc; +}; +exports.Range = Range; +exports.Editor = Editor; +exports.EditSession = EditSession; +exports.UndoManager = UndoManager; +exports.VirtualRenderer = Renderer; +exports.version = exports.config.version; +}); (function() { ace.require(["ace/ace"], function(a) { if (a) { a.config.init(true); diff --git a/public/static/libs/ace/mode-sh.js b/public/static/libs/ace/mode-sh.js deleted file mode 100755 index 1debff2..0000000 --- a/public/static/libs/ace/mode-sh.js +++ /dev/null @@ -1 +0,0 @@ -ace.define("ace/mode/sh",["require","exports","module","ace/lib/oop","ace/mode/text","ace/tokenizer","ace/mode/sh_highlight_rules","ace/range"],function(e,t,n){var r=e("../lib/oop"),i=e("./text").Mode,s=e("../tokenizer").Tokenizer,o=e("./sh_highlight_rules").ShHighlightRules,u=e("../range").Range,a=function(){this.$tokenizer=new s((new o).getRules())};r.inherits(a,i),function(){this.toggleCommentLines=function(e,t,n,r){var i=!0,s=/^(\s*)#/;for(var o=n;o<=r;o++)if(!s.test(t.getLine(o))){i=!1;break}if(i){var a=new u(0,0,0,0);for(var o=n;o<=r;o++){var f=t.getLine(o),l=f.match(s);a.start.row=o,a.end.row=o,a.end.column=l[0].length,t.replace(a,l[1])}}else t.indentRows(n,r,"#")},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.$tokenizer.getLineTokens(t,e),s=i.tokens;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"){var o=t.match(/^.*[\{\(\[\:]\s*$/);o&&(r+=n)}return r};var e={pass:1,"return":1,raise:1,"break":1,"continue":1};this.checkOutdent=function(t,n,r){if(r!=="\r\n"&&r!=="\r"&&r!=="\n")return!1;var i=this.$tokenizer.getLineTokens(n.trim(),t).tokens;if(!i)return!1;do var s=i.pop();while(s&&(s.type=="comment"||s.type=="text"&&s.value.match(/^\s+$/)));return s?s.type=="keyword"&&e[s.value]:!1},this.autoOutdent=function(e,t,n){n+=1;var r=this.$getIndent(t.getLine(n)),i=t.getTabString();r.slice(-i.length)==i&&t.remove(new u(n,r.length-i.length,n,r.length))}}.call(a.prototype),t.Mode=a}),ace.define("ace/mode/sh_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=t.reservedKeywords="!|{|}|case|do|done|elif|else|esac|fi|for|if|in|then|until|while|&|;|export|local|read|typeset|unset|elif|select|set",o=t.languageConstructs="[|]|alias|bg|bind|break|builtin|cd|command|compgen|complete|continue|dirs|disown|echo|enable|eval|exec|exit|fc|fg|getopts|hash|help|history|jobs|kill|let|logout|popd|printf|pushd|pwd|return|set|shift|shopt|source|suspend|test|times|trap|type|ulimit|umask|unalias|wait",u=function(){var e=this.createKeywordMapper({keyword:s,"support.function.builtin":o,"invalid.deprecated":"debugger"},"identifier"),t="(?:(?:[1-9]\\d*)|(?:0))",n="(?:\\.\\d+)",r="(?:\\d+)",i="(?:(?:"+r+"?"+n+")|(?:"+r+"\\.))",u="(?:(?:"+i+"|"+r+")"+")",a="(?:"+u+"|"+i+")",f="(?:&"+r+")",l="[a-zA-Z][a-zA-Z0-9_]*",c="(?:(?:\\$"+l+")|(?:"+l+"=))",h="(?:\\$(?:SHLVL|\\$|\\!|\\?))",p="(?:"+l+"\\s*\\(\\))";this.$rules={start:[{token:"comment",regex:"#.*$"},{token:"string",regex:'"(?:[^\\\\]|\\\\.)*?"'},{token:"variable.language",regex:h},{token:"variable",regex:c},{token:"support.function",regex:p},{token:"support.function",regex:f},{token:"string",regex:"'(?:[^\\\\]|\\\\.)*?'"},{token:"constant.numeric",regex:a},{token:"constant.numeric",regex:t+"\\b"},{token:e,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"\\+|\\-|\\*|\\*\\*|\\/|\\/\\/|~|<|>|<=|=>|=|!="},{token:"paren.lparen",regex:"[\\[\\(\\{]"},{token:"paren.rparen",regex:"[\\]\\)\\}]"},{token:"text",regex:"\\s+"}]}};r.inherits(u,i),t.ShHighlightRules=u}) \ No newline at end of file diff --git a/public/static/libs/ace/theme-github.js b/public/static/libs/ace/theme-github.js deleted file mode 100755 index e5dd93d..0000000 --- a/public/static/libs/ace/theme-github.js +++ /dev/null @@ -1,5 +0,0 @@ -ace.define("ace/theme/github", ["require", "exports", "module", "ace/lib/dom"], function (e, t, n) { - t.isDark = !1, t.cssClass = "ace-github", t.cssText = '/* CSS style content from github\'s default pygments highlighter template.Cursor and selection styles from textmate.css. */.ace-github .ace_gutter {background: #e8e8e8;color: #AAA;}.ace-github .ace_scroller {background: #fff;}.ace-github .ace_keyword {font-weight: bold;}.ace-github .ace_string {color: #D14;}.ace-github .ace_variable.ace_class {color: teal;}.ace-github .ace_constant.ace_numeric {color: #099;}.ace-github .ace_constant.ace_buildin {color: #0086B3;}.ace-github .ace_support.ace_function {color: #0086B3;}.ace-github .ace_comment {color: #998;font-style: italic;}.ace-github .ace_variable.ace_language {color: #0086B3;}.ace-github .ace_paren {font-weight: bold;}.ace-github .ace_boolean {font-weight: bold;}.ace-github .ace_string.ace_regexp {color: #009926;font-weight: normal;}.ace-github .ace_variable.ace_instance {color: teal;}.ace-github .ace_constant.ace_language {font-weight: bold;}.ace-github .ace_text-layer {}.ace-github .ace_cursor {border-left: 2px solid black;}.ace-github .ace_overwrite-cursors .ace_cursor {border-left: 0px;border-bottom: 1px solid black;}.ace-github .ace_marker-layer .ace_active-line {background: rgb(255, 255, 204);}.ace-github .ace_marker-layer .ace_selection {background: rgb(181, 213, 255);}.ace-github.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px white;border-radius: 2px;}/* bold keywords cause cursor issues for some fonts *//* this disables bold style for editor and keeps for static highlighter */.ace-github.ace_nobold .ace_line > span {font-weight: normal !important;}.ace-github .ace_marker-layer .ace_step {background: rgb(252, 255, 0);}.ace-github .ace_marker-layer .ace_stack {background: rgb(164, 229, 101);}.ace-github .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgb(192, 192, 192);}.ace-github .ace_gutter-active-line {background-color : rgba(0, 0, 0, 0.07);}.ace-github .ace_marker-layer .ace_selected-word {background: rgb(250, 250, 255);border: 1px solid rgb(200, 200, 250);}.ace-github .ace_print-margin {width: 1px;background: #e8e8e8;}.ace-github .ace_indent-guide {background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y;}'; - var r = e("../lib/dom"); - r.importCssString(t.cssText, t.cssClass) -}) diff --git a/public/static/libs/ace/theme-one-dark.js b/public/static/libs/ace/theme-one-dark.js deleted file mode 100644 index eb2ec1d..0000000 --- a/public/static/libs/ace/theme-one-dark.js +++ /dev/null @@ -1,5 +0,0 @@ -ace.define("ace/theme/one-dark", ["require", "exports", "module", "ace/lib/dom"], function (e, t, n) { - t.isDark = !1, t.cssClass = "ace-one-dark", t.cssText = '/* CSS style content from one-dark\'s default pygments highlighter template.Cursor and selection styles from textmate.css. */.ace-one-dark .ace_gutter{background:#282c34;color:#6a6f7a}.ace-one-dark .ace_print-margin{width:1px;background:#e8e8e8}.ace-one-dark{background-color:#282c34;color:#abb2bf}.ace-one-dark .ace_cursor{color:#528bff}.ace-one-dark .ace_marker-layer .ace_selection{background:#3d4350}.ace-one-dark.ace_multiselect .ace_selection.ace_start{box-shadow:0 0 3px 0 #282c34;border-radius:2px}.ace-one-dark .ace_marker-layer .ace_step{background:#c6dbae}.ace-one-dark .ace_marker-layer .ace_bracket{margin:-1px 0 0 -1px;border:1px solid #747369}.ace-one-dark .ace_marker-layer .ace_active-line{background:rgba(76,87,103,.19)}.ace-one-dark .ace_gutter-active-line{background-color:rgba(76,87,103,.19)}.ace-one-dark .ace_marker-layer .ace_selected-word{border:1px solid #3d4350}.ace-one-dark .ace_fold{background-color:#61afef;border-color:#abb2bf}.ace-one-dark .ace_keyword{color:#c678dd}.ace-one-dark .ace_keyword.ace_operator{color:#c678dd}.ace-one-dark .ace_keyword.ace_other.ace_unit{color:#d19a66}.ace-one-dark .ace_constant.ace_language{color:#d19a66}.ace-one-dark .ace_constant.ace_numeric{color:#d19a66}.ace-one-dark .ace_constant.ace_character{color:#56b6c2}.ace-one-dark .ace_constant.ace_other{color:#56b6c2}.ace-one-dark .ace_support.ace_function{color:#61afef}.ace-one-dark .ace_support.ace_constant{color:#d19a66}.ace-one-dark .ace_support.ace_class{color:#e5c07b}.ace-one-dark .ace_support.ace_type{color:#e5c07b}.ace-one-dark .ace_storage{color:#c678dd}.ace-one-dark .ace_storage.ace_type{color:#c678dd}.ace-one-dark .ace_invalid{color:#fff;background-color:#f2777a}.ace-one-dark .ace_invalid.ace_deprecated{color:#272b33;background-color:#d27b53}.ace-one-dark .ace_string{color:#98c379}.ace-one-dark .ace_string.ace_regexp{color:#e06c75}.ace-one-dark .ace_comment{font-style:italic;color:#5c6370}.ace-one-dark .ace_variable{color:#e06c75}.ace-one-dark .ace_variable.ace_parameter{color:#d19a66}.ace-one-dark .ace_meta.ace_tag{color:#e06c75}.ace-one-dark .ace_entity.ace_other.ace_attribute-name{color:#e06c75}.ace-one-dark .ace_entity.ace_name.ace_function{color:#61afef}.ace-one-dark .ace_entity.ace_name.ace_tag{color:#e06c75}.ace-one-dark .ace_markup.ace_heading{color:#98c379}.ace-one-dark .ace_indent-guide{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWPQ09NrYAgMjP4PAAtGAwchHMyAAAAAAElFTkSuQmCC) right repeat-y}'; - var r = e("../lib/dom"); - r.importCssString(t.cssText, t.cssClass) -}) diff --git a/resources/commands/installation/install-requirements.sh b/resources/commands/installation/install-requirements.sh deleted file mode 100755 index b71731f..0000000 --- a/resources/commands/installation/install-requirements.sh +++ /dev/null @@ -1,3 +0,0 @@ -sudo DEBIAN_FRONTEND=noninteractive apt install -y software-properties-common curl zip unzip git gcc -git config --global user.email "__email__" -git config --global user.name "__name__" diff --git a/resources/commands/php/install-php.sh b/resources/commands/php/install-php.sh deleted file mode 100755 index d18c1b5..0000000 --- a/resources/commands/php/install-php.sh +++ /dev/null @@ -1,13 +0,0 @@ -sudo add-apt-repository ppa:ondrej/php -y - -sudo DEBIAN_FRONTEND=noninteractive apt update - -sudo DEBIAN_FRONTEND=noninteractive apt install -y php__version__ php__version__-fpm php__version__-mbstring php__version__-mysql php__version__-mcrypt php__version__-gd php__version__-xml php__version__-curl php__version__-gettext php__version__-zip php__version__-bcmath php__version__-soap php__version__-redis - -if ! sudo sed -i 's/www-data/__user__/g' /etc/php/__version__/fpm/pool.d/www.conf; then - echo 'VITO_SSH_ERROR' && exit 1 -fi - -sudo service php__version__-fpm enable - -sudo service php__version__-fpm start diff --git a/resources/commands/php/uninstall-php.sh b/resources/commands/php/uninstall-php.sh deleted file mode 100755 index 750ebf6..0000000 --- a/resources/commands/php/uninstall-php.sh +++ /dev/null @@ -1,5 +0,0 @@ -sudo service php__version__-fpm stop - -if ! sudo DEBIAN_FRONTEND=noninteractive apt remove -y php__version__ php__version__-fpm php__version__-mbstring php__version__-mysql php__version__-mcrypt php__version__-gd php__version__-xml php__version__-curl php__version__-gettext php__version__-zip php__version__-bcmath php__version__-soap php__version__-redis; then - echo 'VITO_SSH_ERROR' && exit 1 -fi diff --git a/resources/commands/system/read-file.sh b/resources/commands/system/read-file.sh deleted file mode 100644 index 3eca512..0000000 --- a/resources/commands/system/read-file.sh +++ /dev/null @@ -1 +0,0 @@ -cat __path__; diff --git a/resources/commands/system/upgrade.sh b/resources/commands/system/upgrade.sh deleted file mode 100755 index 8f30eb9..0000000 --- a/resources/commands/system/upgrade.sh +++ /dev/null @@ -1,7 +0,0 @@ -sudo DEBIAN_FRONTEND=noninteractive apt clean - -sudo DEBIAN_FRONTEND=noninteractive apt update - -sudo DEBIAN_FRONTEND=noninteractive apt upgrade -y - -sudo DEBIAN_FRONTEND=noninteractive apt autoremove -y diff --git a/resources/commands/webserver/nginx/install-nginx.sh b/resources/commands/webserver/nginx/install-nginx.sh deleted file mode 100755 index 9908ab6..0000000 --- a/resources/commands/webserver/nginx/install-nginx.sh +++ /dev/null @@ -1,8 +0,0 @@ -sudo DEBIAN_FRONTEND=noninteractive apt install nginx -y - -if ! echo '__config__' | sudo tee /etc/nginx/nginx.conf; then - echo 'VITO_SSH_ERROR' && exit 1 -fi - -sudo service nginx start - diff --git a/resources/commands/website/get-env.sh b/resources/commands/website/get-env.sh deleted file mode 100644 index a7ded68..0000000 --- a/resources/commands/website/get-env.sh +++ /dev/null @@ -1 +0,0 @@ -[ -f /home/vito/__domain__/.env ] && cat /home/vito/__domain__/.env diff --git a/resources/views/application/auto-deployment.blade.php b/resources/views/application/auto-deployment.blade.php index 11ca19b..8d7eb96 100644 --- a/resources/views/application/auto-deployment.blade.php +++ b/resources/views/application/auto-deployment.blade.php @@ -1,5 +1,5 @@
- @if ($site->deploymentScript?->content) + @if ($site->deploymentScript) @@ -13,9 +13,10 @@ class="cursor-pointer" hx-post="{{ route('servers.sites.application.auto-deployment', ['server' => $server, 'site' => $site]) }}" hx-swap="outerHTML" hx-target="#auto-deployment" + hx-select="#auto-deployment" > {{ __("Enable") }} - @if ($site->auto_deployment) + @if ($site->isAutoDeployment()) @endif @@ -26,7 +27,7 @@ class="cursor-pointer" hx-target="#auto-deployment" > {{ __("Disable") }} - @if (! $site->auto_deployment) + @if (! $site->isAutoDeployment()) @endif diff --git a/resources/views/application/deploy.blade.php b/resources/views/application/deploy.blade.php index f4e4434..5afc280 100644 --- a/resources/views/application/deploy.blade.php +++ b/resources/views/application/deploy.blade.php @@ -1,5 +1,5 @@
- @if ($site->deploymentScript?->content) + @if ($site->deploymentScript)
$server, "site" => $site]) }}" diff --git a/resources/views/application/deployment-script.blade.php b/resources/views/application/deployment-script.blade.php index 62a7139..a273f5c 100644 --- a/resources/views/application/deployment-script.blade.php +++ b/resources/views/application/deployment-script.blade.php @@ -1,9 +1,10 @@
- + $server, "site" => $site]) }}" hx-select="#deployment-script-form" + hx-target="#deployment-script-form" hx-swap="outerHTML" class="p-6" > @@ -13,9 +14,9 @@ class="p-6"
- - {{ old("script", $site->deployment_script_text) }} - + + {{ old("script", $site->deploymentScript?->content) }} + @error("script") @enderror diff --git a/resources/views/application/deployments-list.blade.php b/resources/views/application/deployments-list.blade.php index bbba91f..1e87edd 100644 --- a/resources/views/application/deployments-list.blade.php +++ b/resources/views/application/deployments-list.blade.php @@ -19,11 +19,11 @@ @foreach ($deployments as $deployment) - + {{ $deployment->commit_data["message"] }} @@ -38,10 +38,12 @@ class="font-semibold text-primary-600" diff --git a/resources/views/application/env.blade.php b/resources/views/application/env.blade.php index d4b1cf2..f72f45c 100644 --- a/resources/views/application/env.blade.php +++ b/resources/views/application/env.blade.php @@ -1,5 +1,5 @@
- + - - {{ old("env", session()->get("env") ?? "Loading...") }} - +
+ + {{ old("env", session()->get("env") ?? "Loading...") }} + +
@error("env") @enderror diff --git a/resources/views/components/alert-danger.blade.php b/resources/views/components/alert-danger.blade.php new file mode 100644 index 0000000..bda0339 --- /dev/null +++ b/resources/views/components/alert-danger.blade.php @@ -0,0 +1,5 @@ +
+ {{ $slot }} +
diff --git a/resources/views/components/alert-success.blade.php b/resources/views/components/alert-success.blade.php new file mode 100644 index 0000000..bf10d65 --- /dev/null +++ b/resources/views/components/alert-success.blade.php @@ -0,0 +1,5 @@ +
+ {{ $slot }} +
diff --git a/resources/views/components/alert-warning.blade.php b/resources/views/components/alert-warning.blade.php new file mode 100644 index 0000000..802b9a6 --- /dev/null +++ b/resources/views/components/alert-warning.blade.php @@ -0,0 +1,5 @@ +
+ {{ $slot }} +
diff --git a/resources/views/components/alert.blade.php b/resources/views/components/alert.blade.php new file mode 100644 index 0000000..e19b4b3 --- /dev/null +++ b/resources/views/components/alert.blade.php @@ -0,0 +1,5 @@ +
+ {{ $slot }} +
diff --git a/resources/views/components/code-editor.blade.php b/resources/views/components/code-editor.blade.php new file mode 100644 index 0000000..6723e53 --- /dev/null +++ b/resources/views/components/code-editor.blade.php @@ -0,0 +1,45 @@ +@props([ + "id", + "name", + "disabled" => false, + "lang" => "text", +]) + +
+
+ +
diff --git a/resources/views/components/console-view.blade.php b/resources/views/components/console-view.blade.php index 12ffc3f..ab36b91 100644 --- a/resources/views/components/console-view.blade.php +++ b/resources/views/components/console-view.blade.php @@ -1,5 +1,5 @@
{{ $slot }}
diff --git a/resources/views/components/live.blade.php b/resources/views/components/live.blade.php index 8f03529..dc11bd5 100644 --- a/resources/views/components/live.blade.php +++ b/resources/views/components/live.blade.php @@ -1,13 +1,19 @@ @props([ "interval" => "30s", "id", + "target" => null, ])
{{ $slot }} diff --git a/resources/views/cronjobs/index.blade.php b/resources/views/cronjobs/index.blade.php index 6dc94db..494a2d3 100644 --- a/resources/views/cronjobs/index.blade.php +++ b/resources/views/cronjobs/index.blade.php @@ -1,5 +1,5 @@ {{ __("Cronjobs") }} - @include("cronjobs.partrials.cronjobs-list") + @include("cronjobs.partials.cronjobs-list") diff --git a/resources/views/cronjobs/partrials/create-cronjob.blade.php b/resources/views/cronjobs/partials/create-cronjob.blade.php similarity index 96% rename from resources/views/cronjobs/partrials/create-cronjob.blade.php rename to resources/views/cronjobs/partials/create-cronjob.blade.php index ecf1b6a..4e63371 100644 --- a/resources/views/cronjobs/partrials/create-cronjob.blade.php +++ b/resources/views/cronjobs/partials/create-cronjob.blade.php @@ -46,8 +46,8 @@ class="mt-1 w-full" {{ __("Select") }} - @error("user") diff --git a/resources/views/cronjobs/partrials/cronjobs-list.blade.php b/resources/views/cronjobs/partials/cronjobs-list.blade.php similarity index 90% rename from resources/views/cronjobs/partrials/cronjobs-list.blade.php rename to resources/views/cronjobs/partials/cronjobs-list.blade.php index 282ea56..7062968 100644 --- a/resources/views/cronjobs/partrials/cronjobs-list.blade.php +++ b/resources/views/cronjobs/partials/cronjobs-list.blade.php @@ -5,7 +5,7 @@ {{ __("Your server's Cronjobs are here. You can manage them") }} - @include("cronjobs.partrials.create-cronjob") + @include("cronjobs.partials.create-cronjob") @@ -18,11 +18,11 @@ {{ $cronjob->command }} - {{ $cronjob->frequency_label }} + {{ $cronjob->frequencyLabel() }}
- @include("cronjobs.partrials.status", ["status" => $cronjob->status]) + @include("cronjobs.partials.status", ["status" => $cronjob->status])
- {{ __("Daemons") }} - diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index 8298993..5223bd3 100644 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -1,3 +1,7 @@ +@php + use App\Enums\ServerStatus; +@endphp + @props([ "server", ]) @@ -18,9 +22,6 @@ - - - @include("layouts.partials.favicon") @@ -32,7 +33,6 @@ class="min-h-screen min-w-max bg-gray-100 font-sans antialiased dark:bg-gray-900 dark:text-gray-300" x-data="" x-cloak - hx-ext="source" >
- @include("layouts.partials.server-select", ["server" => isset($server) ? $server : null]) + @include("layouts.partials.server-select", ["server" => $server ?? null])
@if (isset($server)) @@ -66,222 +66,93 @@ class="left-0 top-0 min-h-screen w-64 flex-none bg-gray-800 p-3 dark:border-r-2 :href="route('servers.show', ['server' => $server])" :active="request()->routeIs('servers.show')" > - - - + {{ __("Overview") }} - @if ($server->isReady()) - @if ($server->webserver()) - - - - - - {{ __("Sites") }} - - - @endif - - @if ($server->database()) - - - - - - {{ __("Databases") }} - - - @endif - - @if ($server->php()) - - - - - - {{ __("PHP") }} - - - @endif - - @if ($server->firewall()) - - - - - - - {{ __("Firewall") }} - - - @endif - + @if ($server->webserver()) - - - + - {{ __("Cronjobs") }} + {{ __("Sites") }} + @endif + + @if ($server->database()) - - - + - {{ __("SSH Keys") }} + {{ __("Databases") }} + @endif + + @if ($server->php()) - - - - + - {{ __("Services") }} + {{ __("PHP") }} + + + @endif + + @if ($server->firewall()) + + + + {{ __("Firewall") }} @endif + + + + {{ __("Cronjobs") }} + + + + + + {{ __("SSH Keys") }} + + + + + + {{ __("Services") }} + + + - - - + {{ __("Settings") }} @@ -290,20 +161,7 @@ class="h-6 w-6" :href="route('servers.logs', ['server' => $server])" :active="request()->routeIs('servers.logs')" > - - - + {{ __("Logs") }} diff --git a/resources/views/layouts/partials/color-scheme.blade.php b/resources/views/layouts/partials/color-scheme.blade.php index 8d4c727..68ff9d4 100644 --- a/resources/views/layouts/partials/color-scheme.blade.php +++ b/resources/views/layouts/partials/color-scheme.blade.php @@ -15,6 +15,11 @@ class="flex items-center text-gray-600 dark:text-gray-300" this.theme = theme localStorage.theme = theme this.updateDocument() + document.body.dispatchEvent( + new CustomEvent('color-scheme-changed', { + detail: { theme: this.isDark() ? 'dark' : 'light' }, + }), + ) }, updateDocument() { if (this.isDark()) { diff --git a/resources/views/layouts/site.blade.php b/resources/views/layouts/site.blade.php index 6d0812d..b41b200 100644 --- a/resources/views/layouts/site.blade.php +++ b/resources/views/layouts/site.blade.php @@ -9,33 +9,20 @@

- domain }}" target="_blank"> + {{ $site->domain }}

- - - + @include("sites.partials.site-status")
- - - +
{{ $site->domain }}
@@ -69,20 +43,7 @@ class="h-4 w-4 font-bold text-primary-600 dark:text-white"
- - - + @include("servers.partials.server-status", ["server" => $site->server])
- - - +
{{ $site->server->ip }}
@@ -133,22 +81,25 @@ class="h-4 w-4 font-bold text-primary-600 dark:text-white" :href="route('servers.sites.show', ['server' => $site->server, 'site' => $site])" :active="request()->routeIs('servers.sites.show')" > + {{ __("Application") }} - @if ($site->isReady() && $site->hasFeature(SiteFeature::SSL)) + @if ($site->hasFeature(SiteFeature::SSL)) + {{ __("SSL") }} @endif - @if ($site->isReady() && $site->hasFeature(SiteFeature::QUEUES)) + @if ($site->hasFeature(SiteFeature::QUEUES)) + {{ __("Queues") }} @endif @@ -157,12 +108,14 @@ class="h-4 w-4 font-bold text-primary-600 dark:text-white" :href="route('servers.sites.settings', ['server' => $site->server, 'site' => $site])" :active="request()->routeIs('servers.sites.settings')" > + {{ __("Settings") }} + {{ __("Logs") }}
diff --git a/resources/views/php/index.blade.php b/resources/views/php/index.blade.php index 01da497..8696634 100644 --- a/resources/views/php/index.blade.php +++ b/resources/views/php/index.blade.php @@ -1,6 +1,12 @@ {{ __("PHP") }} + @error("version") + + + + @enderror + @include("php.partials.installed-versions") @if ($server->defaultService("php")) diff --git a/resources/views/php/partials/install-extension.blade.php b/resources/views/php/partials/install-extension.blade.php index 22a7e86..7371889 100644 --- a/resources/views/php/partials/install-extension.blade.php +++ b/resources/views/php/partials/install-extension.blade.php @@ -17,13 +17,11 @@ class="p-6" @foreach (config("core.php_extensions") as $extension) - @endforeach diff --git a/resources/views/php/partials/installed-versions.blade.php b/resources/views/php/partials/installed-versions.blade.php index 59adaff..2bb3dda 100644 --- a/resources/views/php/partials/installed-versions.blade.php +++ b/resources/views/php/partials/installed-versions.blade.php @@ -1,4 +1,4 @@ -
+
{{ __("Installed PHPs") }} @@ -33,7 +33,7 @@ {{ __("Install Extension") }} diff --git a/resources/views/queues/partials/create-queue.blade.php b/resources/views/queues/partials/create-queue.blade.php index 6ae2b76..1188764 100644 --- a/resources/views/queues/partials/create-queue.blade.php +++ b/resources/views/queues/partials/create-queue.blade.php @@ -36,8 +36,11 @@ class="mt-1 w-full" {{ __("Select") }} - @error("user") diff --git a/resources/views/queues/partials/status.blade.php b/resources/views/queues/partials/status.blade.php index 04cb7ee..6f48c91 100644 --- a/resources/views/queues/partials/status.blade.php +++ b/resources/views/queues/partials/status.blade.php @@ -26,10 +26,6 @@ {{ $status }} @endif -@if ($status == \App\Enums\QueueStatus::FAILED) - {{ $status }} -@endif - @if ($status == \App\Enums\QueueStatus::DELETING) {{ $status }} @endif diff --git a/resources/views/server-ssh-keys/partials/add-existing-key.blade.php b/resources/views/server-ssh-keys/partials/add-existing-key.blade.php index beaca8f..ba8ea78 100644 --- a/resources/views/server-ssh-keys/partials/add-existing-key.blade.php +++ b/resources/views/server-ssh-keys/partials/add-existing-key.blade.php @@ -21,7 +21,7 @@ class="p-6" - @foreach ($keys as $key) + @foreach (auth()->user()->sshKeys as $key) diff --git a/resources/views/servers/partials/create-server.blade.php b/resources/views/servers/partials/create-server.blade.php index 70afe0b..ae7c83b 100644 --- a/resources/views/servers/partials/create-server.blade.php +++ b/resources/views/servers/partials/create-server.blade.php @@ -1,3 +1,8 @@ +@php + use App\Enums\Database; + use App\Enums\Webserver; +@endphp + {{ __("Create new Server") }} @@ -205,7 +210,7 @@ class="mt-1 block w-full" @foreach (config("core.webservers") as $ws) - @endforeach @@ -218,7 +223,7 @@ class="mt-1 block w-full" @foreach (config("core.databases") as $db) - @endforeach @@ -231,7 +236,7 @@ class="mt-1 block w-full" @foreach (config("core.php_versions") as $p) - @endforeach diff --git a/resources/views/services/partials/services-list.blade.php b/resources/views/services/partials/services-list.blade.php index fe07cf7..12fb581 100644 --- a/resources/views/services/partials/services-list.blade.php +++ b/resources/views/services/partials/services-list.blade.php @@ -7,7 +7,7 @@ - +
@foreach ($services as $service) diff --git a/resources/views/site-settings/partials/change-php-version.blade.php b/resources/views/site-settings/partials/change-php-version.blade.php index b4ba8e3..80ad3d6 100644 --- a/resources/views/site-settings/partials/change-php-version.blade.php +++ b/resources/views/site-settings/partials/change-php-version.blade.php @@ -10,6 +10,8 @@ hx-post="{{ route("servers.sites.settings.php", ["server" => $server, "site" => $site]) }}" hx-swap="outerHTML" hx-select="#change-php-version" + hx-ext="disable-element" + hx-disable-element="#btn-change-php-version" class="space-y-6" >
@@ -29,7 +31,7 @@ class="space-y-6" - + {{ __("Save") }} diff --git a/resources/views/site-settings/partials/update-v-host.blade.php b/resources/views/site-settings/partials/update-v-host.blade.php index 886134b..21836eb 100644 --- a/resources/views/site-settings/partials/update-v-host.blade.php +++ b/resources/views/site-settings/partials/update-v-host.blade.php @@ -5,35 +5,35 @@ {{ __("You can change your site's VHost configuration") }} -
$server, "site" => $site]) }}" - hx-trigger="load" - hx-target="#vhost" - hx-select="#vhost" +
$server, "site" => $site]) }}" hx-swap="outerHTML" + hx-select="#update-vhost" + class="space-y-6" + hx-ext="disable-element" + hx-disable-element="#btn-update-vhost" > - $server, "site" => $site]) }}" +
$server, "site" => $site]) }}" + hx-trigger="load" + hx-target="#vhost-container" + hx-select="#vhost-container" hx-swap="outerHTML" - hx-target="#update-vhost-container" - hx-select="#update-vhost-container" - class="space-y-6" > -
- +
+ {{ session()->has("vhost") ? session()->get("vhost") : "Loading..." }} - - @error("vhost") - - @enderror +
- -
+ @error("vhost") + + @enderror +
+ - + {{ __("Save") }} diff --git a/resources/views/sites/partials/create-site.blade.php b/resources/views/sites/partials/create-site.blade.php index 4f0843a..a45ce8e 100644 --- a/resources/views/sites/partials/create-site.blade.php +++ b/resources/views/sites/partials/create-site.blade.php @@ -10,6 +10,8 @@ hx-trigger="submit" hx-select="#create-site-form" hx-swap="outerHTML" + hx-ext="disable-element" + hx-disable-element="#btn-create-site" class="space-y-6" >
@@ -72,7 +74,7 @@ class="mt-1 block w-full" @include("sites.partials.create." . $type) - + {{ __("Create") }} diff --git a/tests/Feature/ApplicationTest.php b/tests/Feature/ApplicationTest.php index fa983e3..8b407db 100644 --- a/tests/Feature/ApplicationTest.php +++ b/tests/Feature/ApplicationTest.php @@ -2,10 +2,10 @@ namespace Tests\Feature; -use App\Jobs\Site\UpdateBranch; +use App\Enums\DeploymentStatus; +use App\Facades\SSH; use App\Models\GitHook; use Illuminate\Foundation\Testing\RefreshDatabase; -use Illuminate\Support\Facades\Bus; use Illuminate\Support\Facades\Http; use Tests\TestCase; @@ -47,9 +47,64 @@ public function test_update_deployment_script() ]); } + public function test_deploy(): void + { + SSH::fake(); + Http::fake([ + 'github.com/*' => Http::response([ + 'sha' => '123', + 'commit' => [ + 'message' => 'test commit message', + 'name' => 'test commit name', + 'email' => 'test@example.com', + 'url' => 'https://github.com/commit-url', + ], + ], 200), + ]); + + $this->site->deploymentScript->update([ + 'content' => 'git pull', + ]); + + $this->actingAs($this->user); + + $response = $this->post(route('servers.sites.application.deploy', [ + 'server' => $this->server, + 'site' => $this->site, + ]))->assertSessionDoesntHaveErrors(); + + $response->assertSessionHas('toast.type', 'success'); + $response->assertSessionHas('toast.message', 'Deployment started!'); + + $this->assertDatabaseHas('deployments', [ + 'site_id' => $this->site->id, + 'status' => DeploymentStatus::FINISHED, + ]); + + SSH::assertExecutedContains('cd /home/vito/'.$this->site->domain); + SSH::assertExecutedContains('git pull'); + + $this->get(route('servers.sites.show', [ + 'server' => $this->server, + 'site' => $this->site, + ])) + ->assertOk() + ->assertSee('test commit message'); + + $deployment = $this->site->deployments()->first(); + + $this->get(route('servers.sites.application.deployment.log', [ + 'server' => $this->server, + 'site' => $this->site, + 'deployment' => $deployment, + ])) + ->assertRedirect() + ->assertSessionHas('content', 'fake output'); + } + public function test_change_branch() { - Bus::fake(); + SSH::fake(); $this->actingAs($this->user); @@ -58,9 +113,14 @@ public function test_change_branch() 'site' => $this->site, ]), [ 'branch' => 'master', - ])->assertSessionDoesntHaveErrors(); + ]) + ->assertSessionDoesntHaveErrors() + ->assertSessionHas('toast.type', 'success'); - Bus::assertDispatched(UpdateBranch::class); + $this->site->refresh(); + $this->assertEquals('master', $this->site->branch); + + SSH::assertExecutedContains('git checkout -f master'); } public function test_enable_auto_deployment() @@ -68,7 +128,7 @@ public function test_enable_auto_deployment() Http::fake([ 'github.com/*' => Http::response([ 'id' => '123', - ], 201), + ], 200), ]); $this->actingAs($this->user); @@ -80,18 +140,21 @@ public function test_enable_auto_deployment() $this->site->refresh(); - $this->assertTrue($this->site->auto_deployment); + $this->assertTrue($this->site->isAutoDeployment()); } public function test_disable_auto_deployment() { Http::fake([ - 'github.com/*' => Http::response([], 204), + 'api.github.com/repos/organization/repository' => Http::response([ + 'id' => '123', + ], 200), + 'api.github.com/repos/organization/repository/hooks/*' => Http::response([], 204), ]); $this->actingAs($this->user); - GitHook::factory()->create([ + $hook = GitHook::factory()->create([ 'site_id' => $this->site->id, 'source_control_id' => $this->site->source_control_id, ]); @@ -103,6 +166,22 @@ public function test_disable_auto_deployment() $this->site->refresh(); - $this->assertFalse($this->site->auto_deployment); + $this->assertFalse($this->site->isAutoDeployment()); + } + + public function test_update_env_file(): void + { + SSH::fake(); + + $this->actingAs($this->user); + + $this->post(route('servers.sites.application.env', [ + 'server' => $this->server, + 'site' => $this->site, + ]), [ + 'env' => 'APP_ENV=production', + ])->assertSessionDoesntHaveErrors(); + + SSH::assertExecutedContains('echo "APP_ENV=production" | tee /home/vito/'.$this->site->domain.'/.env'); } } diff --git a/tests/Feature/Auth/PasswordConfirmationTest.php b/tests/Feature/Auth/PasswordConfirmationTest.php index 44b97a7..e0ed330 100644 --- a/tests/Feature/Auth/PasswordConfirmationTest.php +++ b/tests/Feature/Auth/PasswordConfirmationTest.php @@ -3,7 +3,6 @@ namespace Tests\Feature\Auth; use Illuminate\Foundation\Testing\RefreshDatabase; -use JsonException; use Tests\TestCase; class PasswordConfirmationTest extends TestCase @@ -19,9 +18,6 @@ public function test_confirm_password_screen_can_be_rendered(): void $response->assertStatus(200); } - /** - * @throws JsonException - */ public function test_password_can_be_confirmed(): void { $this->actingAs($this->user); @@ -31,7 +27,7 @@ public function test_password_can_be_confirmed(): void ]); $response->assertRedirect(); - $response->assertSessionHasNoErrors(); + $response->assertSessionDoesntHaveErrors(); } public function test_password_is_not_confirmed_with_invalid_password(): void diff --git a/tests/Feature/Auth/PasswordResetTest.php b/tests/Feature/Auth/PasswordResetTest.php index 2e65f8d..be3e1f2 100644 --- a/tests/Feature/Auth/PasswordResetTest.php +++ b/tests/Feature/Auth/PasswordResetTest.php @@ -56,7 +56,7 @@ public function test_password_can_be_reset_with_valid_token(): void 'password_confirmation' => 'password', ]); - $response->assertSessionHasNoErrors(); + $response->assertSessionDoesntHaveErrors(); return true; }); diff --git a/tests/Feature/CronjobTest.php b/tests/Feature/CronjobTest.php index cca8055..614646c 100644 --- a/tests/Feature/CronjobTest.php +++ b/tests/Feature/CronjobTest.php @@ -22,7 +22,7 @@ public function test_see_cronjobs_list() ]); $this->get(route('servers.cronjobs', $this->server)) - ->assertSeeText($cronjob->frequency_label); + ->assertSeeText($cronjob->frequencyLabel()); } public function test_delete_cronjob() @@ -34,16 +34,20 @@ public function test_delete_cronjob() /** @var CronJob $cronjob */ $cronjob = CronJob::factory()->create([ 'server_id' => $this->server->id, + 'user' => 'vito', ]); $this->delete(route('servers.cronjobs.destroy', [ 'server' => $this->server, 'cronJob' => $cronjob, - ]))->assertSessionHasNoErrors(); + ]))->assertSessionDoesntHaveErrors(); $this->assertDatabaseMissing('cron_jobs', [ 'id' => $cronjob->id, ]); + + SSH::assertExecutedContains("echo '' | sudo -u vito crontab -"); + SSH::assertExecutedContains('sudo -u vito crontab -l'); } public function test_create_cronjob() @@ -56,7 +60,7 @@ public function test_create_cronjob() 'command' => 'ls -la', 'user' => 'vito', 'frequency' => '* * * * *', - ])->assertSessionHasNoErrors(); + ])->assertSessionDoesntHaveErrors(); $this->assertDatabaseHas('cron_jobs', [ 'server_id' => $this->server->id, @@ -65,5 +69,33 @@ public function test_create_cronjob() 'frequency' => '* * * * *', 'status' => CronjobStatus::READY, ]); + + SSH::assertExecutedContains("echo '* * * * * ls -la' | sudo -u vito crontab -"); + SSH::assertExecutedContains('sudo -u vito crontab -l'); + } + + public function test_create_custom_cronjob() + { + SSH::fake(); + + $this->actingAs($this->user); + + $this->post(route('servers.cronjobs.store', $this->server), [ + 'command' => 'ls -la', + 'user' => 'vito', + 'frequency' => 'custom', + 'custom' => '* * * 1 1', + ])->assertSessionDoesntHaveErrors(); + + $this->assertDatabaseHas('cron_jobs', [ + 'server_id' => $this->server->id, + 'command' => 'ls -la', + 'user' => 'vito', + 'frequency' => '* * * 1 1', + 'status' => CronjobStatus::READY, + ]); + + SSH::assertExecutedContains("echo '* * * 1 1 ls -la' | sudo -u vito crontab -"); + SSH::assertExecutedContains('sudo -u vito crontab -l'); } } diff --git a/tests/Feature/DatabaseBackupTest.php b/tests/Feature/DatabaseBackupTest.php index 9924b4b..3302cc1 100644 --- a/tests/Feature/DatabaseBackupTest.php +++ b/tests/Feature/DatabaseBackupTest.php @@ -2,14 +2,14 @@ namespace Tests\Feature; +use App\Enums\BackupFileStatus; use App\Enums\BackupStatus; use App\Facades\SSH; -use App\Jobs\Backup\RunBackup; use App\Models\Backup; use App\Models\Database; use App\Models\StorageProvider; use Illuminate\Foundation\Testing\RefreshDatabase; -use Illuminate\Support\Facades\Bus; +use Illuminate\Support\Facades\Http; use Tests\TestCase; class DatabaseBackupTest extends TestCase @@ -18,12 +18,11 @@ class DatabaseBackupTest extends TestCase public function test_create_backup(): void { + SSH::fake(); + Http::fake(); + $this->actingAs($this->user); - Bus::fake(); - - SSH::fake()->outputShouldBe('test'); - $database = Database::factory()->create([ 'server_id' => $this->server, ]); @@ -38,13 +37,15 @@ public function test_create_backup(): void 'backup_storage' => $storage->id, 'backup_interval' => '0 * * * *', 'backup_keep' => '10', - ])->assertSessionHasNoErrors(); - - Bus::assertDispatched(RunBackup::class); + ])->assertSessionDoesntHaveErrors(); $this->assertDatabaseHas('backups', [ 'status' => BackupStatus::RUNNING, ]); + + $this->assertDatabaseHas('backup_files', [ + 'status' => BackupFileStatus::CREATED, + ]); } public function test_see_backups_list(): void @@ -70,7 +71,7 @@ public function test_see_backups_list(): void ->assertSee($backup->database->name); } - public function test_delete_database(): void + public function test_delete_backup(): void { $this->actingAs($this->user); @@ -90,7 +91,7 @@ public function test_delete_database(): void ]); $this->delete(route('servers.databases.backups.destroy', [$this->server, $backup])) - ->assertSessionHasNoErrors(); + ->assertSessionDoesntHaveErrors(); $this->assertDatabaseMissing('backups', [ 'id' => $backup->id, diff --git a/tests/Feature/DatabaseTest.php b/tests/Feature/DatabaseTest.php index 16066e8..e3e5625 100644 --- a/tests/Feature/DatabaseTest.php +++ b/tests/Feature/DatabaseTest.php @@ -16,11 +16,11 @@ public function test_create_database(): void { $this->actingAs($this->user); - SSH::fake()->outputShouldBe('test'); + SSH::fake(); $this->post(route('servers.databases.store', $this->server), [ 'name' => 'database', - ])->assertSessionHasNoErrors(); + ])->assertSessionDoesntHaveErrors(); $this->assertDatabaseHas('databases', [ 'name' => 'database', @@ -44,14 +44,14 @@ public function test_delete_database(): void { $this->actingAs($this->user); - SSH::fake()->outputShouldBe('test'); + SSH::fake(); $database = Database::factory()->create([ 'server_id' => $this->server, ]); $this->delete(route('servers.databases.destroy', [$this->server, $database])) - ->assertSessionHasNoErrors(); + ->assertSessionDoesntHaveErrors(); $this->assertDatabaseMissing('databases', [ 'id' => $database->id, diff --git a/tests/Feature/DatabaseUserTest.php b/tests/Feature/DatabaseUserTest.php index 4e586cb..fe4ffbc 100644 --- a/tests/Feature/DatabaseUserTest.php +++ b/tests/Feature/DatabaseUserTest.php @@ -21,7 +21,7 @@ public function test_create_database_user(): void $this->post(route('servers.databases.users.store', $this->server), [ 'username' => 'user', 'password' => 'password', - ])->assertSessionHasNoErrors(); + ])->assertSessionDoesntHaveErrors(); $this->assertDatabaseHas('database_users', [ 'username' => 'user', @@ -52,7 +52,7 @@ public function test_delete_database_user(): void ]); $this->delete(route('servers.databases.users.destroy', [$this->server, $databaseUser])) - ->assertSessionHasNoErrors(); + ->assertSessionDoesntHaveErrors(); $this->assertDatabaseMissing('database_users', [ 'id' => $databaseUser->id, diff --git a/tests/Feature/FirewallTest.php b/tests/Feature/FirewallTest.php index ff8c9b3..cbe0920 100644 --- a/tests/Feature/FirewallTest.php +++ b/tests/Feature/FirewallTest.php @@ -24,7 +24,7 @@ public function test_create_firewall_rule(): void 'port' => '1234', 'source' => '0.0.0.0', 'mask' => '0', - ])->assertSessionHasNoErrors(); + ])->assertSessionDoesntHaveErrors(); $this->assertDatabaseHas('firewall_rules', [ 'port' => '1234', @@ -58,7 +58,7 @@ public function test_delete_firewall_rule(): void $this->delete(route('servers.firewall.destroy', [ 'server' => $this->server, 'firewallRule' => $rule, - ]))->assertSessionHasNoErrors(); + ]))->assertSessionDoesntHaveErrors(); $this->assertDatabaseMissing('firewall_rules', [ 'id' => $rule->id, diff --git a/tests/Feature/NotificationChannelsTest.php b/tests/Feature/NotificationChannelsTest.php index d4361cd..4b772da 100644 --- a/tests/Feature/NotificationChannelsTest.php +++ b/tests/Feature/NotificationChannelsTest.php @@ -6,7 +6,6 @@ use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Foundation\Testing\WithFaker; use Illuminate\Support\Facades\Http; -use JsonException; use Tests\TestCase; class NotificationChannelsTest extends TestCase @@ -14,9 +13,6 @@ class NotificationChannelsTest extends TestCase use RefreshDatabase; use WithFaker; - /** - * @throws JsonException - */ public function test_add_email_channel(): void { $this->actingAs($this->user); @@ -25,7 +21,7 @@ public function test_add_email_channel(): void 'provider' => NotificationChannel::EMAIL, 'email' => 'email@example.com', 'label' => 'Email', - ])->assertSessionHasNoErrors(); + ])->assertSessionDoesntHaveErrors(); /** @var \App\Models\NotificationChannel $channel */ $channel = \App\Models\NotificationChannel::query() @@ -36,9 +32,6 @@ public function test_add_email_channel(): void $this->assertTrue($channel->connected); } - /** - * @throws JsonException - */ public function test_add_slack_channel(): void { $this->actingAs($this->user); @@ -49,7 +42,7 @@ public function test_add_slack_channel(): void 'provider' => NotificationChannel::SLACK, 'webhook_url' => 'https://hooks.slack.com/services/123/token', 'label' => 'Slack', - ])->assertSessionHasNoErrors(); + ])->assertSessionDoesntHaveErrors(); /** @var \App\Models\NotificationChannel $channel */ $channel = \App\Models\NotificationChannel::query() @@ -60,9 +53,6 @@ public function test_add_slack_channel(): void $this->assertTrue($channel->connected); } - /** - * @throws JsonException - */ public function test_add_discord_channel(): void { $this->actingAs($this->user); @@ -73,7 +63,7 @@ public function test_add_discord_channel(): void 'provider' => NotificationChannel::DISCORD, 'webhook_url' => 'https://discord.com/api/webhooks/123/token', 'label' => 'Discord', - ])->assertSessionHasNoErrors(); + ])->assertSessionDoesntHaveErrors(); /** @var \App\Models\NotificationChannel $channel */ $channel = \App\Models\NotificationChannel::query() @@ -87,9 +77,7 @@ public function test_add_discord_channel(): void /* * @TODO fix json comparison */ - /** - * @throws JsonException - */ + public function test_add_telegram_channel(): void { $this->actingAs($this->user); @@ -101,7 +89,7 @@ public function test_add_telegram_channel(): void 'bot_token' => 'token', 'chat_id' => '123', 'label' => 'Telegram', - ])->assertSessionHasNoErrors(); + ])->assertSessionDoesntHaveErrors(); /** @var \App\Models\NotificationChannel $channel */ $channel = \App\Models\NotificationChannel::query() @@ -123,9 +111,6 @@ public function test_see_channels_list(): void ->assertSee($channel->provider); } - /** - * @throws JsonException - */ public function test_delete_channel(): void { $this->actingAs($this->user); @@ -133,7 +118,7 @@ public function test_delete_channel(): void $channel = \App\Models\NotificationChannel::factory()->create(); $this->delete(route('notification-channels.delete', $channel->id)) - ->assertSessionHasNoErrors(); + ->assertSessionDoesntHaveErrors(); $this->assertDatabaseMissing('notification_channels', [ 'id' => $channel->id, diff --git a/tests/Feature/PHPTest.php b/tests/Feature/PHPTest.php index 9e5bcb6..6726653 100644 --- a/tests/Feature/PHPTest.php +++ b/tests/Feature/PHPTest.php @@ -3,12 +3,9 @@ namespace Tests\Feature; use App\Enums\ServiceStatus; -use App\Jobs\Installation\InstallPHP; -use App\Jobs\Installation\UninstallPHP; -use App\Jobs\PHP\InstallPHPExtension; +use App\Facades\SSH; use App\Models\Service; use Illuminate\Foundation\Testing\RefreshDatabase; -use Illuminate\Support\Facades\Bus; use Tests\TestCase; class PHPTest extends TestCase @@ -17,21 +14,26 @@ class PHPTest extends TestCase public function test_install_new_php(): void { - Bus::fake(); + SSH::fake(); $this->actingAs($this->user); $this->post(route('servers.php.install', [ 'server' => $this->server, 'version' => '8.1', - ]))->assertSessionHasNoErrors(); + ]))->assertSessionDoesntHaveErrors(); - Bus::assertDispatched(InstallPHP::class); + $this->assertDatabaseHas('services', [ + 'server_id' => $this->server->id, + 'type' => 'php', + 'version' => '8.1', + 'status' => ServiceStatus::READY, + ]); } public function test_uninstall_php(): void { - Bus::fake(); + SSH::fake(); $this->actingAs($this->user); @@ -52,9 +54,11 @@ public function test_uninstall_php(): void $this->delete(route('servers.php.uninstall', [ 'server' => $this->server, 'version' => '8.1', - ]))->assertSessionHasNoErrors(); + ]))->assertSessionDoesntHaveErrors(); - Bus::assertDispatched(UninstallPHP::class); + $this->assertDatabaseMissing('services', [ + 'id' => $php->id, + ]); } public function test_cannot_uninstall_php(): void @@ -69,6 +73,8 @@ public function test_cannot_uninstall_php(): void public function test_change_default_php_cli(): void { + SSH::fake(); + $this->actingAs($this->user); $php = Service::factory()->create([ @@ -85,7 +91,7 @@ public function test_change_default_php_cli(): void $this->post(route('servers.php.default-cli', [ 'server' => $this->server, 'version' => '8.1', - ]))->assertSessionHasNoErrors(); + ]))->assertSessionDoesntHaveErrors(); $php->refresh(); @@ -94,7 +100,7 @@ public function test_change_default_php_cli(): void public function test_install_extension(): void { - Bus::fake(); + SSH::fake('output... [PHP Modules] gmp'); $this->actingAs($this->user); @@ -102,14 +108,16 @@ public function test_install_extension(): void 'server' => $this->server, 'version' => '8.2', 'extension' => 'gmp', - ]))->assertSessionHasNoErrors(); + ]))->assertSessionDoesntHaveErrors(); - Bus::assertDispatched(InstallPHPExtension::class); + $php = $this->server->php('8.2'); + + $this->assertContains('gmp', $php->type_data['extensions']); } public function test_extension_already_installed(): void { - Bus::fake(); + SSH::fake(); $this->actingAs($this->user); @@ -126,7 +134,5 @@ public function test_extension_already_installed(): void 'version' => '8.2', 'extension' => 'gmp', ]))->assertSessionHasErrors(); - - Bus::assertNotDispatched(InstallPHPExtension::class); } } diff --git a/tests/Feature/ProjectsTest.php b/tests/Feature/ProjectsTest.php index 809901e..919a15f 100644 --- a/tests/Feature/ProjectsTest.php +++ b/tests/Feature/ProjectsTest.php @@ -4,23 +4,19 @@ use App\Models\Project; use Illuminate\Foundation\Testing\RefreshDatabase; -use JsonException; use Tests\TestCase; class ProjectsTest extends TestCase { use RefreshDatabase; - /** - * @throws JsonException - */ public function test_create_project(): void { $this->actingAs($this->user); $this->post(route('projects.create'), [ 'name' => 'test', - ])->assertSessionHasNoErrors(); + ])->assertSessionDoesntHaveErrors(); $this->assertDatabaseHas('projects', [ 'name' => 'test', @@ -39,9 +35,6 @@ public function test_see_projects_list(): void ->assertSee($project->name); } - /** - * @throws JsonException - */ public function test_delete_project(): void { $this->actingAs($this->user); @@ -51,16 +44,13 @@ public function test_delete_project(): void ]); $this->delete(route('projects.delete', $project)) - ->assertSessionHasNoErrors(); + ->assertSessionDoesntHaveErrors(); $this->assertDatabaseMissing('projects', [ 'id' => $project->id, ]); } - /** - * @throws JsonException - */ public function test_edit_project(): void { $this->actingAs($this->user); @@ -71,7 +61,7 @@ public function test_edit_project(): void $this->post(route('projects.update', $project), [ 'name' => 'new-name', - ])->assertSessionHasNoErrors(); + ])->assertSessionDoesntHaveErrors(); $this->assertDatabaseHas('projects', [ 'id' => $project->id, diff --git a/tests/Feature/QueuesTest.php b/tests/Feature/QueuesTest.php index 909a246..b75a0a0 100644 --- a/tests/Feature/QueuesTest.php +++ b/tests/Feature/QueuesTest.php @@ -3,6 +3,7 @@ namespace Tests\Feature; use App\Enums\QueueStatus; +use App\Facades\SSH; use App\Models\Queue; use Illuminate\Foundation\Testing\RefreshDatabase; use Tests\TestCase; @@ -32,6 +33,8 @@ public function test_see_queues() public function test_delete_queue() { + SSH::fake(); + $this->actingAs($this->user); $queue = Queue::factory()->create([ @@ -47,14 +50,15 @@ public function test_delete_queue() ]) )->assertRedirect(); - $this->assertDatabaseHas('queues', [ + $this->assertDatabaseMissing('queues', [ 'id' => $queue->id, - 'status' => QueueStatus::DELETING, ]); } public function test_create_queue() { + SSH::fake(); + $this->actingAs($this->user); $this->post( @@ -79,7 +83,7 @@ public function test_create_queue() 'auto_start' => 1, 'auto_restart' => 1, 'numprocs' => 1, - 'status' => QueueStatus::CREATING, + 'status' => QueueStatus::RUNNING, ]); } } diff --git a/tests/Feature/ServerKeysTest.php b/tests/Feature/ServerKeysTest.php index 5182c5f..2d40d84 100644 --- a/tests/Feature/ServerKeysTest.php +++ b/tests/Feature/ServerKeysTest.php @@ -3,11 +3,9 @@ namespace Tests\Feature; use App\Enums\SshKeyStatus; -use App\Jobs\SshKey\DeleteSshKeyFromServer; -use App\Jobs\SshKey\DeploySshKeyToServer; +use App\Facades\SSH; use App\Models\SshKey; use Illuminate\Foundation\Testing\RefreshDatabase; -use Illuminate\Support\Facades\Bus; use Tests\TestCase; class ServerKeysTest extends TestCase @@ -34,7 +32,7 @@ public function test_see_server_keys() public function test_delete_ssh_key() { - Bus::fake(); + SSH::fake(); $this->actingAs($this->user); @@ -50,18 +48,15 @@ public function test_delete_ssh_key() $this->delete(route('servers.ssh-keys.destroy', [$this->server, $sshKey])); - $this->assertDatabaseHas('server_ssh_keys', [ + $this->assertDatabaseMissing('server_ssh_keys', [ 'server_id' => $this->server->id, 'ssh_key_id' => $sshKey->id, - 'status' => SshKeyStatus::DELETING, ]); - - Bus::assertDispatched(DeleteSshKeyFromServer::class); } public function test_add_new_ssh_key() { - Bus::fake(); + SSH::fake(); $this->actingAs($this->user); @@ -72,15 +67,13 @@ public function test_add_new_ssh_key() $this->assertDatabaseHas('server_ssh_keys', [ 'server_id' => $this->server->id, - 'status' => SshKeyStatus::ADDING, + 'status' => SshKeyStatus::ADDED, ]); - - Bus::assertDispatched(DeploySshKeyToServer::class); } public function test_add_existing_key() { - Bus::fake(); + SSH::fake(); $this->actingAs($this->user); @@ -96,9 +89,7 @@ public function test_add_existing_key() $this->assertDatabaseHas('server_ssh_keys', [ 'server_id' => $this->server->id, - 'status' => SshKeyStatus::ADDING, + 'status' => SshKeyStatus::ADDED, ]); - - Bus::assertDispatched(DeploySshKeyToServer::class); } } diff --git a/tests/Feature/ServerProvidersTest.php b/tests/Feature/ServerProvidersTest.php index d884f06..1646a2c 100644 --- a/tests/Feature/ServerProvidersTest.php +++ b/tests/Feature/ServerProvidersTest.php @@ -5,16 +5,12 @@ use App\Enums\ServerProvider; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Facades\Http; -use JsonException; use Tests\TestCase; class ServerProvidersTest extends TestCase { use RefreshDatabase; - /** - * @throws JsonException - */ public function test_connect_hetzner(): void { $this->actingAs($this->user); @@ -25,7 +21,7 @@ public function test_connect_hetzner(): void 'provider' => ServerProvider::HETZNER, 'name' => 'profile', 'token' => 'token', - ])->assertSessionHasNoErrors(); + ])->assertSessionDoesntHaveErrors(); $this->assertDatabaseHas('server_providers', [ 'provider' => ServerProvider::HETZNER, @@ -45,9 +41,6 @@ public function test_see_providers_list(): void ->assertSee($provider->profile); } - /** - * @throws JsonException - */ public function test_delete_provider(): void { $this->actingAs($this->user); @@ -57,7 +50,7 @@ public function test_delete_provider(): void ]); $this->delete(route('server-providers.delete', $provider->id)) - ->assertSessionHasNoErrors(); + ->assertSessionDoesntHaveErrors(); $this->assertDatabaseMissing('server_providers', [ 'id' => $provider->id, diff --git a/tests/Feature/ServerTest.php b/tests/Feature/ServerTest.php index 739d77f..5063aee 100644 --- a/tests/Feature/ServerTest.php +++ b/tests/Feature/ServerTest.php @@ -7,11 +7,10 @@ use App\Enums\ServerProvider; use App\Enums\ServerStatus; use App\Enums\ServerType; +use App\Enums\ServiceStatus; use App\Enums\Webserver; -use App\Jobs\Installation\Initialize; +use App\Facades\SSH; use Illuminate\Foundation\Testing\RefreshDatabase; -use Illuminate\Support\Facades\Bus; -use JsonException; use Tests\TestCase; /** @@ -21,14 +20,11 @@ class ServerTest extends TestCase { use RefreshDatabase; - /** - * @throws JsonException - */ public function test_create_custom_server(): void { $this->actingAs($this->user); - Bus::fake(); + SSH::fake('Active: active'); // fake output for service installations $this->post(route('servers.create'), [ 'type' => ServerType::REGULAR, @@ -40,14 +36,57 @@ public function test_create_custom_server(): void 'webserver' => Webserver::NGINX, 'database' => Database::MYSQL80, 'php' => '8.2', - ])->assertSessionHasNoErrors(); + ])->assertSessionDoesntHaveErrors(); $this->assertDatabaseHas('servers', [ 'name' => 'test', 'ip' => '1.1.1.1', - 'status' => ServerStatus::INSTALLING, + 'status' => ServerStatus::READY, ]); - Bus::assertDispatched(Initialize::class); + $this->assertDatabaseHas('services', [ + 'server_id' => 1, + 'type' => 'php', + 'version' => '8.2', + 'status' => ServiceStatus::READY, + ]); + + $this->assertDatabaseHas('services', [ + 'server_id' => 1, + 'type' => 'webserver', + 'name' => 'nginx', + 'version' => 'latest', + 'status' => ServiceStatus::READY, + ]); + + $this->assertDatabaseHas('services', [ + 'server_id' => 1, + 'type' => 'database', + 'name' => 'mysql', + 'version' => '8.0', + 'status' => ServiceStatus::READY, + ]); + + $this->assertDatabaseHas('services', [ + 'server_id' => 1, + 'type' => 'firewall', + 'name' => 'ufw', + 'version' => 'latest', + 'status' => ServiceStatus::READY, + ]); + } + + public function test_delete_server(): void + { + $this->actingAs($this->user); + + SSH::fake(); + + $this->delete(route('servers.delete', $this->server)) + ->assertSessionDoesntHaveErrors(); + + $this->assertDatabaseMissing('servers', [ + 'id' => $this->server->id, + ]); } } diff --git a/tests/Feature/ServicesTest.php b/tests/Feature/ServicesTest.php index e997951..3a7783e 100644 --- a/tests/Feature/ServicesTest.php +++ b/tests/Feature/ServicesTest.php @@ -3,12 +3,8 @@ namespace Tests\Feature; use App\Enums\ServiceStatus; -use App\Jobs\Installation\InstallPHPMyAdmin as InstallationInstallPHPMyAdmin; -use App\Jobs\Installation\UninstallPHPMyAdmin; -use App\Jobs\Service\Manage; -use App\Models\Service; +use App\Facades\SSH; use Illuminate\Foundation\Testing\RefreshDatabase; -use Illuminate\Support\Facades\Bus; use Tests\TestCase; class ServicesTest extends TestCase @@ -36,14 +32,16 @@ public function test_restart_service(string $name): void $service = $this->server->services()->where('name', $name)->firstOrFail(); - Bus::fake(); + SSH::fake('Active: active'); $this->get(route('servers.services.restart', [ 'server' => $this->server, 'service' => $service, - ]))->assertSessionHasNoErrors(); + ]))->assertSessionDoesntHaveErrors(); - Bus::assertDispatched(Manage::class); + $service->refresh(); + + $this->assertEquals(ServiceStatus::READY, $service->status); } /** @@ -53,16 +51,18 @@ public function test_stop_service(string $name): void { $this->actingAs($this->user); - $service = $this->server->services()->where('name', $name)->first(); + $service = $this->server->services()->where('name', $name)->firstOrFail(); - Bus::fake(); + SSH::fake('Active: inactive'); $this->get(route('servers.services.stop', [ 'server' => $this->server, 'service' => $service, - ]))->assertSessionHasNoErrors(); + ]))->assertSessionDoesntHaveErrors(); - Bus::assertDispatched(Manage::class); + $service->refresh(); + + $this->assertEquals(ServiceStatus::STOPPED, $service->status); } /** @@ -72,52 +72,81 @@ public function test_start_service(string $name): void { $this->actingAs($this->user); - $service = $this->server->services()->where('name', $name)->first(); + $service = $this->server->services()->where('name', $name)->firstOrFail(); - $service->status = ServiceStatus::STOPPED; - $service->save(); - - Bus::fake(); + SSH::fake('Active: active'); $this->get(route('servers.services.start', [ 'server' => $this->server, 'service' => $service, - ]))->assertSessionHasNoErrors(); + ]))->assertSessionDoesntHaveErrors(); - Bus::assertDispatched(Manage::class); + $service->refresh(); + + $this->assertEquals(ServiceStatus::READY, $service->status); } - public function test_install_phpmyadmin(): void + /** + * @dataProvider data + */ + public function test_failed_to_start_service(string $name): void { - $this->markTestSkipped('PHPMyAdmin is depricated'); + $this->actingAs($this->user); - Bus::fake(); + $service = $this->server->services()->where('name', $name)->firstOrFail(); - Bus::assertDispatched(InstallationInstallPHPMyAdmin::class); + SSH::fake('Active: inactive'); + + $this->get(route('servers.services.start', [ + 'server' => $this->server, + 'service' => $service, + ]))->assertSessionDoesntHaveErrors(); + + $service->refresh(); + + $this->assertEquals(ServiceStatus::FAILED, $service->status); } - public function test_uninstall_phpmyadmin(): void + /** + * @dataProvider data + */ + public function test_failed_to_restart_service(string $name): void { - $this->markTestSkipped('PHPMyAdmin is depricated'); + $this->actingAs($this->user); - $service = Service::factory()->create([ - 'server_id' => $this->server->id, - 'type' => 'phpmyadmin', - 'type_data' => [ - 'allowed_ip' => '0.0.0.0', - 'port' => '5433', - 'php' => '8.1', - ], - 'name' => 'phpmyadmin', - 'version' => '5.1.2', - 'status' => ServiceStatus::READY, - 'is_default' => 1, + $service = $this->server->services()->where('name', $name)->firstOrFail(); - ]); + SSH::fake('Active: inactive'); - Bus::fake(); + $this->get(route('servers.services.restart', [ + 'server' => $this->server, + 'service' => $service, + ]))->assertSessionDoesntHaveErrors(); - Bus::assertDispatched(UninstallPHPMyAdmin::class); + $service->refresh(); + + $this->assertEquals(ServiceStatus::FAILED, $service->status); + } + + /** + * @dataProvider data + */ + public function test_failed_to_stop_service(string $name): void + { + $this->actingAs($this->user); + + $service = $this->server->services()->where('name', $name)->firstOrFail(); + + SSH::fake('Active: active'); + + $this->get(route('servers.services.stop', [ + 'server' => $this->server, + 'service' => $service, + ]))->assertSessionDoesntHaveErrors(); + + $service->refresh(); + + $this->assertEquals(ServiceStatus::FAILED, $service->status); } public static function data(): array diff --git a/tests/Feature/SitesTest.php b/tests/Feature/SitesTest.php index d6d1cbf..e1cfe19 100644 --- a/tests/Feature/SitesTest.php +++ b/tests/Feature/SitesTest.php @@ -6,10 +6,8 @@ use App\Enums\SiteType; use App\Enums\SourceControl; use App\Facades\SSH; -use App\Jobs\Site\CreateVHost; use App\Models\Site; use Illuminate\Foundation\Testing\RefreshDatabase; -use Illuminate\Support\Facades\Bus; use Illuminate\Support\Facades\Http; use Tests\TestCase; @@ -22,9 +20,12 @@ class SitesTest extends TestCase */ public function test_create_site(array $inputs): void { - Bus::fake(); + SSH::fake(); - Http::fake(); + Http::fake([ + 'https://api.github.com/repos/*' => Http::response([ + ], 201), + ]); $this->actingAs($this->user); @@ -39,11 +40,9 @@ public function test_create_site(array $inputs): void 'server' => $this->server, ]), $inputs)->assertSessionDoesntHaveErrors(); - Bus::assertDispatched(CreateVHost::class); - $this->assertDatabaseHas('sites', [ 'domain' => 'example.com', - 'status' => SiteStatus::INSTALLING, + 'status' => SiteStatus::READY, ]); } @@ -64,7 +63,7 @@ public function test_see_sites_list(): void public function test_delete_site(): void { - Bus::fake(); + SSH::fake(); $this->actingAs($this->user); @@ -77,15 +76,15 @@ public function test_delete_site(): void 'site' => $site, ]))->assertRedirect(); - Bus::assertDispatched(\App\Jobs\Site\DeleteSite::class); - - $site->refresh(); - - $this->assertEquals(SiteStatus::DELETING, $site->status); + $this->assertDatabaseMissing('sites', [ + 'id' => $site->id, + ]); } public function test_change_php_version(): void { + SSH::fake(); + $this->actingAs($this->user); $site = Site::factory()->create([ @@ -117,7 +116,7 @@ public function test_update_v_host(): void $this->get(route('servers.sites.settings.vhost', [ 'server' => $this->server, 'site' => $site, - ]))->assertSessionHasNoErrors(); + ]))->assertSessionDoesntHaveErrors(); } public static function create_data(): array diff --git a/tests/Feature/SourceControlsTest.php b/tests/Feature/SourceControlsTest.php index 9a042e6..3b50b28 100644 --- a/tests/Feature/SourceControlsTest.php +++ b/tests/Feature/SourceControlsTest.php @@ -33,7 +33,7 @@ public function test_connect_provider(string $provider, ?string $customUrl): voi $input['url'] = $customUrl; } $this->post(route('source-controls.connect'), $input) - ->assertSessionHasNoErrors(); + ->assertSessionDoesntHaveErrors(); $this->assertDatabaseHas('source_controls', [ 'provider' => $provider, @@ -57,7 +57,7 @@ public function test_delete_provider(string $provider): void ]); $this->delete(route('source-controls.delete', $sourceControl->id)) - ->assertSessionHasNoErrors(); + ->assertSessionDoesntHaveErrors(); $this->assertDatabaseMissing('source_controls', [ 'id' => $sourceControl->id, diff --git a/tests/Feature/SshKeysTest.php b/tests/Feature/SshKeysTest.php index 31a69ce..51e3e21 100644 --- a/tests/Feature/SshKeysTest.php +++ b/tests/Feature/SshKeysTest.php @@ -4,16 +4,12 @@ use App\Models\SshKey; use Illuminate\Foundation\Testing\RefreshDatabase; -use JsonException; use Tests\TestCase; class SshKeysTest extends TestCase { use RefreshDatabase; - /** - * @throws JsonException - */ public function test_create_ssh_key(): void { $this->actingAs($this->user); @@ -21,7 +17,7 @@ public function test_create_ssh_key(): void $this->post(route('ssh-keys.add'), [ 'name' => 'test', 'public_key' => 'ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom/BWDSUGPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3Pbv7kOdJ/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK/7XAt3FaoJoAsncM1Q9x5+3V0Ww68/eIFmb1zuUFljQJKprrX88XypNDvjYNby6vw/Pb0rwert/EnmZ+AW4OZPnTPI89ZPmVMLuayrD2cE86Z/il8b+gw3r3+1nKatmIkjn2so1d01QraTlMqVSsbxNrRFi9wrf+M7Q== test@test.local', - ])->assertSessionHasNoErrors(); + ])->assertSessionDoesntHaveErrors(); } public function test_get_public_keys_list(): void @@ -36,9 +32,6 @@ public function test_get_public_keys_list(): void ->assertSee($key->name); } - /** - * @throws JsonException - */ public function test_delete_key(): void { $this->actingAs($this->user); @@ -48,7 +41,7 @@ public function test_delete_key(): void ]); $this->delete(route('ssh-keys.delete', $key->id)) - ->assertSessionHasNoErrors(); + ->assertSessionDoesntHaveErrors(); $this->assertDatabaseMissing('ssh_keys', [ 'id' => $key->id, diff --git a/tests/Feature/SslTest.php b/tests/Feature/SslTest.php index 0f560a7..5113304 100644 --- a/tests/Feature/SslTest.php +++ b/tests/Feature/SslTest.php @@ -4,11 +4,9 @@ use App\Enums\SslStatus; use App\Enums\SslType; -use App\Jobs\Ssl\Deploy; -use App\Jobs\Ssl\Remove; +use App\Facades\SSH; use App\Models\Ssl; use Illuminate\Foundation\Testing\RefreshDatabase; -use Illuminate\Support\Facades\Bus; use Tests\TestCase; class SslTest extends TestCase @@ -45,7 +43,7 @@ public function test_see_ssls_list_with_no_ssls() public function test_create_ssl() { - Bus::fake(); + SSH::fake('Successfully received certificate'); $this->actingAs($this->user); @@ -59,15 +57,13 @@ public function test_create_ssl() $this->assertDatabaseHas('ssls', [ 'site_id' => $this->site->id, 'type' => SslType::LETSENCRYPT, - 'status' => SslStatus::CREATING, + 'status' => SslStatus::CREATED, ]); - - Bus::assertDispatched(Deploy::class); } public function test_delete_ssl() { - Bus::fake(); + SSH::fake(); $this->actingAs($this->user); @@ -81,11 +77,8 @@ public function test_delete_ssl() 'ssl' => $ssl, ]))->assertRedirect(); - $this->assertDatabaseHas('ssls', [ + $this->assertDatabaseMissing('ssls', [ 'id' => $ssl->id, - 'status' => SslStatus::DELETING, ]); - - Bus::assertDispatched(Remove::class); } } diff --git a/tests/Feature/StorageProvidersTest.php b/tests/Feature/StorageProvidersTest.php index 3e2f705..a612e63 100644 --- a/tests/Feature/StorageProvidersTest.php +++ b/tests/Feature/StorageProvidersTest.php @@ -5,16 +5,12 @@ use App\Enums\StorageProvider; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Facades\Http; -use JsonException; use Tests\TestCase; class StorageProvidersTest extends TestCase { use RefreshDatabase; - /** - * @throws JsonException - */ public function test_connect_dropbox(): void { $this->actingAs($this->user); @@ -25,7 +21,7 @@ public function test_connect_dropbox(): void 'provider' => StorageProvider::DROPBOX, 'name' => 'profile', 'token' => 'token', - ])->assertSessionHasNoErrors(); + ])->assertSessionDoesntHaveErrors(); $this->assertDatabaseHas('storage_providers', [ 'provider' => StorageProvider::DROPBOX, @@ -46,9 +42,6 @@ public function test_see_providers_list(): void ->assertSee($provider->profile); } - /** - * @throws JsonException - */ public function test_delete_provider(): void { $this->actingAs($this->user); @@ -58,7 +51,7 @@ public function test_delete_provider(): void ]); $this->delete(route('storage-providers.delete', $provider->id)) - ->assertSessionHasNoErrors(); + ->assertSessionDoesntHaveErrors(); $this->assertDatabaseMissing('storage_providers', [ 'id' => $provider->id, diff --git a/tests/TestCase.php b/tests/TestCase.php index 5871525..90a40fd 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -26,6 +26,9 @@ public function setUp(): void { parent::setUp(); + config()->set('queue.connections.ssh.driver', 'sync'); + config()->set('filesystems.disks.key-pairs.root', storage_path('app/key-pairs-test')); + $this->user = User::factory()->create(); $this->setupServer(); @@ -41,6 +44,11 @@ private function setupServer(): void 'user_id' => $this->user->id, ]); + $keys = $this->server->sshKey(); + if (! File::exists($keys['public_key_path']) || ! File::exists($keys['private_key_path'])) { + $this->server->provider()->generateKeyPair(); + } + $this->server->type()->createServices([ 'webserver' => Webserver::NGINX, 'database' => Database::MYSQL80, @@ -57,21 +65,25 @@ private function setupSite(): void /** @var SourceControl $sourceControl */ $sourceControl = SourceControl::factory()->github()->create(); $this->site = Site::factory()->create([ + 'domain' => 'vito.test', + 'aliases' => ['www.vito.test'], 'server_id' => $this->server->id, 'source_control_id' => $sourceControl->id, 'repository' => 'organization/repository', + 'path' => '/home/vito/vito.test', + 'web_directory' => 'public', + 'branch' => 'main', ]); } private function setupKeys(): void { - config()->set('core.ssh_public_key_name', 'ssh-public.key'); - config()->set('core.ssh_private_key_name', 'ssh-private.pem'); - if (! File::exists(storage_path(config('core.ssh_public_key_name')))) { - File::put(storage_path(config('core.ssh_public_key_name')), 'public_key'); - } - if (! File::exists(storage_path(config('core.ssh_private_key_name')))) { - File::put(storage_path(config('core.ssh_private_key_name')), 'private_key'); + config()->set('core.ssh_public_key_name', 'test-key.pub'); + config()->set('core.ssh_private_key_name', 'test-key'); + $publicKeypath = storage_path(config('core.ssh_public_key_name')); + $privateKeyPath = storage_path(config('core.ssh_private_key_name')); + if (! File::exists($publicKeypath) || ! File::exists($privateKeyPath)) { + generate_key_pair(storage_path('test-key')); } } } diff --git a/tests/Unit/NotificationChannels/EmailTest.php b/tests/Unit/NotificationChannels/EmailTest.php index d0c7c53..d6b06fc 100644 --- a/tests/Unit/NotificationChannels/EmailTest.php +++ b/tests/Unit/NotificationChannels/EmailTest.php @@ -2,9 +2,9 @@ namespace Tests\Unit\NotificationChannels; -use App\Mail\NotificationMail; use App\Models\NotificationChannel; use App\NotificationChannels\Email; +use App\NotificationChannels\Email\NotificationMail; use Illuminate\Support\Facades\Mail; use Tests\TestCase; diff --git a/tests/Unit/SSHCommands/CronJob/UpdateCronJobsCommandTest.php b/tests/Unit/SSHCommands/CronJob/UpdateCronJobsCommandTest.php deleted file mode 100644 index c0dc997..0000000 --- a/tests/Unit/SSHCommands/CronJob/UpdateCronJobsCommandTest.php +++ /dev/null @@ -1,18 +0,0 @@ -assertStringContainsString("echo 'ls -la' | sudo -u vito crontab -;", $command->content()); - - $this->assertStringContainsString('sudo -u vito crontab -l;', $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Database/BackupDatabaseCommandTest.php b/tests/Unit/SSHCommands/Database/BackupDatabaseCommandTest.php deleted file mode 100644 index c564956..0000000 --- a/tests/Unit/SSHCommands/Database/BackupDatabaseCommandTest.php +++ /dev/null @@ -1,20 +0,0 @@ -assertStringContainsString('sudo DEBIAN_FRONTEND=noninteractive mysqldump -u root test > test.sql;', $command->content()); - - $this->assertStringContainsString('DEBIAN_FRONTEND=noninteractive zip test.zip test.sql;', $command->content()); - - $this->assertStringContainsString('rm test.sql;', $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Database/CreateCommandTest.php b/tests/Unit/SSHCommands/Database/CreateCommandTest.php deleted file mode 100644 index 6bf8e98..0000000 --- a/tests/Unit/SSHCommands/Database/CreateCommandTest.php +++ /dev/null @@ -1,18 +0,0 @@ -assertStringContainsString('sudo mysql -e "CREATE DATABASE IF NOT EXISTS test CHARACTER SET utf8 COLLATE utf8_general_ci";', $command->content()); - - $this->assertStringContainsString('echo "Command executed"', $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Database/CreateUserCommandTest.php b/tests/Unit/SSHCommands/Database/CreateUserCommandTest.php deleted file mode 100644 index 139cfe2..0000000 --- a/tests/Unit/SSHCommands/Database/CreateUserCommandTest.php +++ /dev/null @@ -1,18 +0,0 @@ -assertStringContainsString('sudo mysql -e "CREATE USER IF NOT EXISTS \'user\'@\'%\' IDENTIFIED BY \'password\'";', $command->content()); - - $this->assertStringContainsString('sudo mysql -e "FLUSH PRIVILEGES";', $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Database/DeleteCommandTest.php b/tests/Unit/SSHCommands/Database/DeleteCommandTest.php deleted file mode 100644 index 940983f..0000000 --- a/tests/Unit/SSHCommands/Database/DeleteCommandTest.php +++ /dev/null @@ -1,16 +0,0 @@ -assertStringContainsString('sudo mysql -e "DROP DATABASE IF EXISTS test";', $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Database/DeleteUserCommandTest.php b/tests/Unit/SSHCommands/Database/DeleteUserCommandTest.php deleted file mode 100644 index d2776e0..0000000 --- a/tests/Unit/SSHCommands/Database/DeleteUserCommandTest.php +++ /dev/null @@ -1,18 +0,0 @@ -assertStringContainsString('sudo mysql -e "DROP USER IF EXISTS \'user\'@\'%\'";', $command->content()); - - $this->assertStringContainsString('sudo mysql -e "FLUSH PRIVILEGES";', $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Database/InstallMariadbCommandTest.php b/tests/Unit/SSHCommands/Database/InstallMariadbCommandTest.php deleted file mode 100644 index 19878dd..0000000 --- a/tests/Unit/SSHCommands/Database/InstallMariadbCommandTest.php +++ /dev/null @@ -1,31 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Database/InstallMysqlCommandTest.php b/tests/Unit/SSHCommands/Database/InstallMysqlCommandTest.php deleted file mode 100644 index 21f462f..0000000 --- a/tests/Unit/SSHCommands/Database/InstallMysqlCommandTest.php +++ /dev/null @@ -1,61 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } - - public function test_generate_command_mysql5() - { - $command = new InstallMysqlCommand('5.7'); - - $expected = <<<'EOD' - sudo DEBIAN_FRONTEND=noninteractive apt install mysql-server -y - - sudo service mysql enable - - sudo service mysql start - - if ! sudo mysql -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH auth_socket;"; then - echo 'VITO_SSH_ERROR' && exit 1 - fi - - if ! sudo mysql -e "FLUSH PRIVILEGES"; then - echo 'VITO_SSH_ERROR' && exit 1 - fi - EOD; - - $this->assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Database/LinkCommandTest.php b/tests/Unit/SSHCommands/Database/LinkCommandTest.php deleted file mode 100644 index a0ce73c..0000000 --- a/tests/Unit/SSHCommands/Database/LinkCommandTest.php +++ /dev/null @@ -1,28 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Database/UnlinkCommandTest.php b/tests/Unit/SSHCommands/Database/UnlinkCommandTest.php deleted file mode 100644 index 3429333..0000000 --- a/tests/Unit/SSHCommands/Database/UnlinkCommandTest.php +++ /dev/null @@ -1,24 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Firewall/AddRuleCommandTest.php b/tests/Unit/SSHCommands/Firewall/AddRuleCommandTest.php deleted file mode 100644 index d380e86..0000000 --- a/tests/Unit/SSHCommands/Firewall/AddRuleCommandTest.php +++ /dev/null @@ -1,30 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Firewall/InstallUfwCommandTest.php b/tests/Unit/SSHCommands/Firewall/InstallUfwCommandTest.php deleted file mode 100644 index 36d3292..0000000 --- a/tests/Unit/SSHCommands/Firewall/InstallUfwCommandTest.php +++ /dev/null @@ -1,46 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Firewall/RemoveRuleCommandTest.php b/tests/Unit/SSHCommands/Firewall/RemoveRuleCommandTest.php deleted file mode 100644 index 534f6c5..0000000 --- a/tests/Unit/SSHCommands/Firewall/RemoveRuleCommandTest.php +++ /dev/null @@ -1,31 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Installation/InstallNodejsCommandTest.php b/tests/Unit/SSHCommands/Installation/InstallNodejsCommandTest.php deleted file mode 100644 index be967b2..0000000 --- a/tests/Unit/SSHCommands/Installation/InstallNodejsCommandTest.php +++ /dev/null @@ -1,24 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Installation/InstallRedisCommandTest.php b/tests/Unit/SSHCommands/Installation/InstallRedisCommandTest.php deleted file mode 100644 index a1a1ccf..0000000 --- a/tests/Unit/SSHCommands/Installation/InstallRedisCommandTest.php +++ /dev/null @@ -1,26 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Installation/InstallRequirementsCommandTest.php b/tests/Unit/SSHCommands/Installation/InstallRequirementsCommandTest.php deleted file mode 100644 index 39ba4d8..0000000 --- a/tests/Unit/SSHCommands/Installation/InstallRequirementsCommandTest.php +++ /dev/null @@ -1,22 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Nginx/ChangeNginxPHPVersionCommandTest.php b/tests/Unit/SSHCommands/Nginx/ChangeNginxPHPVersionCommandTest.php deleted file mode 100644 index f991e9c..0000000 --- a/tests/Unit/SSHCommands/Nginx/ChangeNginxPHPVersionCommandTest.php +++ /dev/null @@ -1,28 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Nginx/CreateNginxVHostCommandTest.php b/tests/Unit/SSHCommands/Nginx/CreateNginxVHostCommandTest.php deleted file mode 100644 index 7a62e3c..0000000 --- a/tests/Unit/SSHCommands/Nginx/CreateNginxVHostCommandTest.php +++ /dev/null @@ -1,46 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Nginx/DeleteNginxSiteCommandTest.php b/tests/Unit/SSHCommands/Nginx/DeleteNginxSiteCommandTest.php deleted file mode 100644 index e1087da..0000000 --- a/tests/Unit/SSHCommands/Nginx/DeleteNginxSiteCommandTest.php +++ /dev/null @@ -1,26 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Nginx/InstallNginxCommandTest.php b/tests/Unit/SSHCommands/Nginx/InstallNginxCommandTest.php deleted file mode 100644 index 5da03fb..0000000 --- a/tests/Unit/SSHCommands/Nginx/InstallNginxCommandTest.php +++ /dev/null @@ -1,115 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Nginx/UpdateNginxRedirectsCommandTest.php b/tests/Unit/SSHCommands/Nginx/UpdateNginxRedirectsCommandTest.php deleted file mode 100644 index 3fb7088..0000000 --- a/tests/Unit/SSHCommands/Nginx/UpdateNginxRedirectsCommandTest.php +++ /dev/null @@ -1,26 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Nginx/UpdateNginxVHostCommandTest.php b/tests/Unit/SSHCommands/Nginx/UpdateNginxVHostCommandTest.php deleted file mode 100644 index e64b978..0000000 --- a/tests/Unit/SSHCommands/Nginx/UpdateNginxVHostCommandTest.php +++ /dev/null @@ -1,26 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/PHP/ChangeDefaultPHPCommandTest.php b/tests/Unit/SSHCommands/PHP/ChangeDefaultPHPCommandTest.php deleted file mode 100644 index f47b4d1..0000000 --- a/tests/Unit/SSHCommands/PHP/ChangeDefaultPHPCommandTest.php +++ /dev/null @@ -1,30 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/PHP/GetPHPIniCommandTest.php b/tests/Unit/SSHCommands/PHP/GetPHPIniCommandTest.php deleted file mode 100644 index 51c3d26..0000000 --- a/tests/Unit/SSHCommands/PHP/GetPHPIniCommandTest.php +++ /dev/null @@ -1,20 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/PHP/InstallComposerCommandTest.php b/tests/Unit/SSHCommands/PHP/InstallComposerCommandTest.php deleted file mode 100644 index c87982c..0000000 --- a/tests/Unit/SSHCommands/PHP/InstallComposerCommandTest.php +++ /dev/null @@ -1,26 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/PHP/InstallPHPCommandTest.php b/tests/Unit/SSHCommands/PHP/InstallPHPCommandTest.php deleted file mode 100644 index 84c4dc7..0000000 --- a/tests/Unit/SSHCommands/PHP/InstallPHPCommandTest.php +++ /dev/null @@ -1,32 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/PHP/InstallPHPExtensionCommandTest.php b/tests/Unit/SSHCommands/PHP/InstallPHPExtensionCommandTest.php deleted file mode 100644 index bd78f74..0000000 --- a/tests/Unit/SSHCommands/PHP/InstallPHPExtensionCommandTest.php +++ /dev/null @@ -1,24 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/PHP/UninstallPHPCommandTest.php b/tests/Unit/SSHCommands/PHP/UninstallPHPCommandTest.php deleted file mode 100644 index 0ecb2b2..0000000 --- a/tests/Unit/SSHCommands/PHP/UninstallPHPCommandTest.php +++ /dev/null @@ -1,24 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/PHPMyAdmin/CreateNginxPHPMyAdminVHostCommandTest.php b/tests/Unit/SSHCommands/PHPMyAdmin/CreateNginxPHPMyAdminVHostCommandTest.php deleted file mode 100644 index ce00870..0000000 --- a/tests/Unit/SSHCommands/PHPMyAdmin/CreateNginxPHPMyAdminVHostCommandTest.php +++ /dev/null @@ -1,36 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/PHPMyAdmin/DeleteNginxPHPMyAdminVHostCommandTest.php b/tests/Unit/SSHCommands/PHPMyAdmin/DeleteNginxPHPMyAdminVHostCommandTest.php deleted file mode 100644 index bd764ca..0000000 --- a/tests/Unit/SSHCommands/PHPMyAdmin/DeleteNginxPHPMyAdminVHostCommandTest.php +++ /dev/null @@ -1,30 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/PHPMyAdmin/DownloadPHPMyAdminCommandTest.php b/tests/Unit/SSHCommands/PHPMyAdmin/DownloadPHPMyAdminCommandTest.php deleted file mode 100644 index 5c20310..0000000 --- a/tests/Unit/SSHCommands/PHPMyAdmin/DownloadPHPMyAdminCommandTest.php +++ /dev/null @@ -1,40 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/SSL/CreateCustomSSLCommandTest.php b/tests/Unit/SSHCommands/SSL/CreateCustomSSLCommandTest.php deleted file mode 100644 index bc258eb..0000000 --- a/tests/Unit/SSHCommands/SSL/CreateCustomSSLCommandTest.php +++ /dev/null @@ -1,32 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/SSL/CreateLetsencryptSSLCommandTest.php b/tests/Unit/SSHCommands/SSL/CreateLetsencryptSSLCommandTest.php deleted file mode 100644 index 1c5f6a6..0000000 --- a/tests/Unit/SSHCommands/SSL/CreateLetsencryptSSLCommandTest.php +++ /dev/null @@ -1,22 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/SSL/InstallCertbotCommandTest.php b/tests/Unit/SSHCommands/SSL/InstallCertbotCommandTest.php deleted file mode 100644 index ced78e9..0000000 --- a/tests/Unit/SSHCommands/SSL/InstallCertbotCommandTest.php +++ /dev/null @@ -1,20 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/SSL/RemoveSSLCommandTest.php b/tests/Unit/SSHCommands/SSL/RemoveSSLCommandTest.php deleted file mode 100644 index ccbae05..0000000 --- a/tests/Unit/SSHCommands/SSL/RemoveSSLCommandTest.php +++ /dev/null @@ -1,20 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Service/RestartServiceCommandTest.php b/tests/Unit/SSHCommands/Service/RestartServiceCommandTest.php deleted file mode 100644 index 3308fb4..0000000 --- a/tests/Unit/SSHCommands/Service/RestartServiceCommandTest.php +++ /dev/null @@ -1,22 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Service/ServiceStatusCommandTest.php b/tests/Unit/SSHCommands/Service/ServiceStatusCommandTest.php deleted file mode 100644 index 5492e0d..0000000 --- a/tests/Unit/SSHCommands/Service/ServiceStatusCommandTest.php +++ /dev/null @@ -1,20 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Service/StartServiceCommandTest.php b/tests/Unit/SSHCommands/Service/StartServiceCommandTest.php deleted file mode 100644 index fd504e8..0000000 --- a/tests/Unit/SSHCommands/Service/StartServiceCommandTest.php +++ /dev/null @@ -1,22 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Service/StopServiceCommandTest.php b/tests/Unit/SSHCommands/Service/StopServiceCommandTest.php deleted file mode 100644 index fa03b12..0000000 --- a/tests/Unit/SSHCommands/Service/StopServiceCommandTest.php +++ /dev/null @@ -1,22 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Storage/DownloadFromDropboxCommandTest.php b/tests/Unit/SSHCommands/Storage/DownloadFromDropboxCommandTest.php deleted file mode 100644 index e33b272..0000000 --- a/tests/Unit/SSHCommands/Storage/DownloadFromDropboxCommandTest.php +++ /dev/null @@ -1,23 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Storage/DownloadFromFTPCommandTest.php b/tests/Unit/SSHCommands/Storage/DownloadFromFTPCommandTest.php deleted file mode 100644 index 6c51bab..0000000 --- a/tests/Unit/SSHCommands/Storage/DownloadFromFTPCommandTest.php +++ /dev/null @@ -1,29 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Storage/UploadToDropboxCommandTest.php b/tests/Unit/SSHCommands/Storage/UploadToDropboxCommandTest.php deleted file mode 100644 index 7ae25cf..0000000 --- a/tests/Unit/SSHCommands/Storage/UploadToDropboxCommandTest.php +++ /dev/null @@ -1,25 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Storage/UploadToFTPCommandTest.php b/tests/Unit/SSHCommands/Storage/UploadToFTPCommandTest.php deleted file mode 100644 index e567773..0000000 --- a/tests/Unit/SSHCommands/Storage/UploadToFTPCommandTest.php +++ /dev/null @@ -1,29 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Supervisor/CreateWorkerCommandTest.php b/tests/Unit/SSHCommands/Supervisor/CreateWorkerCommandTest.php deleted file mode 100644 index 8e708d6..0000000 --- a/tests/Unit/SSHCommands/Supervisor/CreateWorkerCommandTest.php +++ /dev/null @@ -1,40 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Supervisor/DeleteWorkerCommandTest.php b/tests/Unit/SSHCommands/Supervisor/DeleteWorkerCommandTest.php deleted file mode 100644 index 4765ad8..0000000 --- a/tests/Unit/SSHCommands/Supervisor/DeleteWorkerCommandTest.php +++ /dev/null @@ -1,38 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Supervisor/InstallSupervisorCommandTest.php b/tests/Unit/SSHCommands/Supervisor/InstallSupervisorCommandTest.php deleted file mode 100644 index 4fdcba4..0000000 --- a/tests/Unit/SSHCommands/Supervisor/InstallSupervisorCommandTest.php +++ /dev/null @@ -1,24 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Supervisor/RestartWorkerCommandTest.php b/tests/Unit/SSHCommands/Supervisor/RestartWorkerCommandTest.php deleted file mode 100644 index efb895d..0000000 --- a/tests/Unit/SSHCommands/Supervisor/RestartWorkerCommandTest.php +++ /dev/null @@ -1,22 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Supervisor/StartWorkerCommandTest.php b/tests/Unit/SSHCommands/Supervisor/StartWorkerCommandTest.php deleted file mode 100644 index a7d2fcd..0000000 --- a/tests/Unit/SSHCommands/Supervisor/StartWorkerCommandTest.php +++ /dev/null @@ -1,22 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Supervisor/StopWorkerCommandTest.php b/tests/Unit/SSHCommands/Supervisor/StopWorkerCommandTest.php deleted file mode 100644 index d808bf5..0000000 --- a/tests/Unit/SSHCommands/Supervisor/StopWorkerCommandTest.php +++ /dev/null @@ -1,22 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/System/CreateUserCommandTest.php b/tests/Unit/SSHCommands/System/CreateUserCommandTest.php deleted file mode 100644 index 9122f12..0000000 --- a/tests/Unit/SSHCommands/System/CreateUserCommandTest.php +++ /dev/null @@ -1,30 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/System/DeleteSshKeyCommandTest.php b/tests/Unit/SSHCommands/System/DeleteSshKeyCommandTest.php deleted file mode 100644 index 28f55eb..0000000 --- a/tests/Unit/SSHCommands/System/DeleteSshKeyCommandTest.php +++ /dev/null @@ -1,20 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/System/DeploySshKeyCommandTest.php b/tests/Unit/SSHCommands/System/DeploySshKeyCommandTest.php deleted file mode 100644 index 3e66d42..0000000 --- a/tests/Unit/SSHCommands/System/DeploySshKeyCommandTest.php +++ /dev/null @@ -1,22 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/System/EditFileCommandTest.php b/tests/Unit/SSHCommands/System/EditFileCommandTest.php deleted file mode 100644 index ef93c49..0000000 --- a/tests/Unit/SSHCommands/System/EditFileCommandTest.php +++ /dev/null @@ -1,22 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/System/GenerateSshKeyCommandTest.php b/tests/Unit/SSHCommands/System/GenerateSshKeyCommandTest.php deleted file mode 100644 index 859ccde..0000000 --- a/tests/Unit/SSHCommands/System/GenerateSshKeyCommandTest.php +++ /dev/null @@ -1,20 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/System/GetPublicKeyCommandTest.php b/tests/Unit/SSHCommands/System/GetPublicKeyCommandTest.php deleted file mode 100644 index 8fef9bb..0000000 --- a/tests/Unit/SSHCommands/System/GetPublicKeyCommandTest.php +++ /dev/null @@ -1,20 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/System/ReadFileCommandTest.php b/tests/Unit/SSHCommands/System/ReadFileCommandTest.php deleted file mode 100644 index cecf643..0000000 --- a/tests/Unit/SSHCommands/System/ReadFileCommandTest.php +++ /dev/null @@ -1,20 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/System/ReadSshKeyCommandTest.php b/tests/Unit/SSHCommands/System/ReadSshKeyCommandTest.php deleted file mode 100644 index b279d66..0000000 --- a/tests/Unit/SSHCommands/System/ReadSshKeyCommandTest.php +++ /dev/null @@ -1,20 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/System/RebootCommandTest.php b/tests/Unit/SSHCommands/System/RebootCommandTest.php deleted file mode 100644 index 2438199..0000000 --- a/tests/Unit/SSHCommands/System/RebootCommandTest.php +++ /dev/null @@ -1,22 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/System/RunScriptCommandTest.php b/tests/Unit/SSHCommands/System/RunScriptCommandTest.php deleted file mode 100644 index b2cef9a..0000000 --- a/tests/Unit/SSHCommands/System/RunScriptCommandTest.php +++ /dev/null @@ -1,24 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/System/UpgradeCommandTest.php b/tests/Unit/SSHCommands/System/UpgradeCommandTest.php deleted file mode 100644 index 4fa838f..0000000 --- a/tests/Unit/SSHCommands/System/UpgradeCommandTest.php +++ /dev/null @@ -1,26 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Website/CloneRepositoryCommandTest.php b/tests/Unit/SSHCommands/Website/CloneRepositoryCommandTest.php deleted file mode 100644 index d203960..0000000 --- a/tests/Unit/SSHCommands/Website/CloneRepositoryCommandTest.php +++ /dev/null @@ -1,46 +0,0 @@ -> ~/.ssh/config - - ssh-keyscan -H github.com >> ~/.ssh/known_hosts - - rm -rf path - - if ! git config --global core.fileMode false; then - echo 'VITO_SSH_ERROR' && exit 1 - fi - - if ! git clone -b main git@github.com-key:vitodeploy/vito.git path; then - echo 'VITO_SSH_ERROR' && exit 1 - fi - - if ! find path -type d -exec chmod 755 {} \;; then - echo 'VITO_SSH_ERROR' && exit 1 - fi - - if ! find path -type f -exec chmod 644 {} \;; then - echo 'VITO_SSH_ERROR' && exit 1 - fi - - if ! cd path && git config core.fileMode false; then - echo 'VITO_SSH_ERROR' && exit 1 - fi - EOD; - - $this->assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Website/ComposerInstallCommandTest.php b/tests/Unit/SSHCommands/Website/ComposerInstallCommandTest.php deleted file mode 100644 index 485e2ba..0000000 --- a/tests/Unit/SSHCommands/Website/ComposerInstallCommandTest.php +++ /dev/null @@ -1,26 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -} diff --git a/tests/Unit/SSHCommands/Website/UpdateBranchCommandTest.php b/tests/Unit/SSHCommands/Website/UpdateBranchCommandTest.php deleted file mode 100644 index 16ff687..0000000 --- a/tests/Unit/SSHCommands/Website/UpdateBranchCommandTest.php +++ /dev/null @@ -1,26 +0,0 @@ -assertStringContainsString($expected, $command->content()); - } -}