commit eb5b65cec62957aa90873dbcdac4a112c729e303 Author: mowetentertainment1 <79593070+mowetentertainment1@users.noreply.github.com> Date: Sat Nov 1 19:45:11 2025 -0400 initial commit diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..a041471 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +root = true + +[*] +indent_style = space +indent_size = 4 +tab_width = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false + +[*.{md,nix,yml,yaml}] +indent_size = 2 +tab_width = 2 diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..0105014 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,6 @@ +public +node_modules +resources/views +babel.config.js +tailwind.config.js +webpack.config.js diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..a0bb5e9 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,51 @@ +/** @type {import('eslint').Linter.Config} */ +module.exports = { + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 6, + ecmaFeatures: { + jsx: true, + }, + project: './tsconfig.json', + tsconfigRootDir: './', + }, + settings: { + react: { + pragma: 'React', + version: 'detect', + }, + linkComponents: [ + { name: 'Link', linkAttribute: 'to' }, + { name: 'NavLink', linkAttribute: 'to' }, + ], + }, + env: { + browser: true, + es6: true, + }, + plugins: ['react', 'react-hooks', 'prettier', '@typescript-eslint'], + extends: [ + // 'standard', + 'eslint:recommended', + 'plugin:react/recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:jest-dom/recommended', + ], + rules: { + eqeqeq: 'error', + 'prettier/prettier': ['error', {}, { usePrettierrc: true }], + // TypeScript can infer this significantly better than eslint ever can. + 'react/prop-types': 0, + 'react/display-name': 0, + '@typescript-eslint/no-explicit-any': 0, + '@typescript-eslint/no-non-null-assertion': 0, + // This setup is required to avoid a spam of errors when running eslint about React being + // used before it is defined. + // + // @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-use-before-define.md#how-to-use + 'no-use-before-define': 0, + '@typescript-eslint/no-use-before-define': 'warn', + '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }], + '@typescript-eslint/ban-ts-comment': ['error', { 'ts-expect-error': 'allow-with-description' }], + }, +}; diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..a21d391 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [matthewpi] diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 0000000..c7bc63a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -0,0 +1,87 @@ +name: Bug Report +description: Something isn't working quite right in the software. +labels: [not confirmed] +body: +- type: markdown + attributes: + value: | + Bug reports should only be used for reporting issues with how the software works. For assistance installing this software, as well as debugging issues with dependencies, please use our [Discord server](https://discord.gg/pterodactyl). + +- type: textarea + attributes: + label: Current Behavior + description: Please provide a clear & concise description of the issue. + validations: + required: true + +- type: textarea + attributes: + label: Expected Behavior + description: Please describe what you expected to happen. + validations: + required: true + +- type: textarea + attributes: + label: Steps to Reproduce + description: Please be as detailed as possible when providing steps to reproduce, failure to provide steps will result in this issue being closed. + validations: + required: true + +- type: input + id: panel-version + attributes: + label: Panel Version + description: Version number of your Panel (latest is not a version) + placeholder: 1.4.0 + validations: + required: true + +- type: input + id: wings-version + attributes: + label: Wings Version + description: Version number of your Wings (latest is not a version) + placeholder: 1.4.2 + validations: + required: true + +- type: input + id: egg-details + attributes: + label: Games and/or Eggs Affected + description: Please include the specific game(s) or egg(s) you are running into this bug with. + placeholder: Minecraft (Paper), Minecraft (Forge) + +- type: input + id: docker-image + attributes: + label: Docker Image + description: The specific Docker image you are using for the game(s) above. + placeholder: ghcr.io/pterodactyl/yolks:java_17 + +- type: textarea + id: panel-logs + attributes: + label: Error Logs + description: | + Run the following command to collect logs on your system. + + Wings: `sudo wings diagnostics` + Panel: `tail -n 150 /var/www/pterodactyl/storage/logs/laravel-$(date +%F).log | nc pteropaste.com 99` + placeholder: "https://pteropaste.com/a1h6z" + render: bash + validations: + required: false + +- type: checkboxes + attributes: + label: Is there an existing issue for this? + description: Please [search here](https://github.com/pterodactyl/panel/issues) to see if an issue already exists for your problem. + options: + - label: I have searched the existing issues before opening this issue. + required: true + - label: I have provided all relevant details, including the specific game and Docker images I am using if this issue is related to running a server. + required: true + - label: I have checked in the Discord server and believe this is a bug with the software, and not a configuration issue with my specific system. + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..fee5ad9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: true +contact_links: + - name: Installation Help + url: https://discord.gg/pterodactyl + about: Please visit our Discord for help with your installation. + - name: General Question + url: https://discord.gg/pterodactyl + about: Please visit our Discord for general questions about Pterodactyl. diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml new file mode 100644 index 0000000..d782e39 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -0,0 +1,32 @@ +name: Feature Request +description: Suggest a new feature or improvement for the software. +labels: [feature request] +body: +- type: checkboxes + attributes: + label: Is there an existing feature request for this? + description: Please [search here](https://github.com/pterodactyl/panel/issues?q=is%3Aissue) to see if someone else has already suggested this. + options: + - label: I have searched the existing issues before opening this feature request. + required: true + +- type: textarea + attributes: + label: Describe the feature you would like to see. + description: "A clear & concise description of the feature you'd like to have added, and what issues it would solve." + validations: + required: true + +- type: textarea + attributes: + label: Describe the solution you'd like. + description: "You must explain how you'd like to see this feature implemented. Technical implementation details are not necessary, rather an idea of how you'd like to see this feature used." + validations: + required: true + +- type: textarea + attributes: + label: Additional context to this request. + description: "Add any other context or screenshots about the feature request." + validations: + required: false diff --git a/.github/docker/README.md b/.github/docker/README.md new file mode 100644 index 0000000..5cf97de --- /dev/null +++ b/.github/docker/README.md @@ -0,0 +1,76 @@ +# Pterodactyl Panel - Docker Image +This is a ready to use docker image for the panel. + +## Requirements +This docker image requires some additional software to function. The software can either be provided in other containers (see the [docker-compose.yml](https://github.com/pterodactyl/panel/blob/develop/docker-compose.example.yml) as an example) or as existing instances. + +A mysql database is required. We recommend the stock [MariaDB Image](https://hub.docker.com/_/mariadb/) image if you prefer to run it in a docker container. As a non-containerized option we recommend mariadb. + +A caching software is required as well. We recommend the stock [Redis Image](https://hub.docker.com/_/redis/) image. You can choose any of the [supported options](#cache-drivers). + +You can provide additional settings using a custom `.env` file or by setting the appropriate environment variables in the docker-compose file. + +## Setup + +Start the docker container and the required dependencies (either provide existing ones or start containers as well, see the [docker-compose.yml](https://github.com/pterodactyl/panel/blob/develop/docker-compose.example.yml) file as an example. + +After the startup is complete you'll need to create a user. +If you are running the docker container without docker-compose, use: +``` +docker exec -it php artisan p:user:make +``` +If you are using docker compose use +``` +docker-compose exec panel php artisan p:user:make +``` + +## Environment Variables +There are multiple environment variables to configure the panel when not providing your own `.env` file, see the following table for details on each available option. + +Note: If your `APP_URL` starts with `https://` you need to provide an `LE_EMAIL` as well so Certificates can be generated. + +| Variable | Description | Required | +| ------------------- | ------------------------------------------------------------------------------ | -------- | +| `APP_URL` | The URL the panel will be reachable with (including protocol) | yes | +| `APP_TIMEZONE` | The timezone to use for the panel | yes | +| `LE_EMAIL` | The email used for letsencrypt certificate generation | yes | +| `DB_HOST` | The host of the mysql instance | yes | +| `DB_PORT` | The port of the mysql instance | yes | +| `DB_DATABASE` | The name of the mysql database | yes | +| `DB_USERNAME` | The mysql user | yes | +| `DB_PASSWORD` | The mysql password for the specified user | yes | +| `CACHE_DRIVER` | The cache driver (see [Cache drivers](#cache-drivers) for detais) | yes | +| `SESSION_DRIVER` | | yes | +| `QUEUE_DRIVER` | | yes | +| `REDIS_HOST` | The hostname or IP address of the redis database | yes | +| `REDIS_PASSWORD` | The password used to secure the redis database | maybe | +| `REDIS_PORT` | The port the redis database is using on the host | maybe | +| `MAIL_DRIVER` | The email driver (see [Mail drivers](#mail-drivers) for details) | yes | +| `MAIL_FROM` | The email that should be used as the sender email | yes | +| `MAIL_HOST` | The host of your mail driver instance | maybe | +| `MAIL_PORT` | The port of your mail driver instance | maybe | +| `MAIL_USERNAME` | The username for your mail driver | maybe | +| `MAIL_PASSWORD` | The password for your mail driver | maybe | + + +### Cache drivers +You can choose between different cache drivers depending on what you prefer. +We recommend redis when using docker as it can be started in a container easily. + +| Driver | Description | Required variables | +| -------- | ------------------------------------ | ------------------------------------------------------ | +| redis | host where redis is running | `REDIS_HOST` | +| redis | port redis is running on | `REDIS_PORT` | +| redis | redis database password | `REDIS_PASSWORD` | + +### Mail drivers +You can choose between different mail drivers according to your needs. +Every driver requires `MAIL_FROM` to be set. + +| Driver | Description | Required variables | +| -------- | ------------------------------------ | ------------------------------------------------------------- | +| mail | uses the installed php mail | | +| mandrill | [Mandrill](http://www.mandrill.com/) | `MAIL_USERNAME` | +| postmark | [Postmark](https://postmarkapp.com/) | `MAIL_USERNAME` | +| mailgun | [Mailgun](https://www.mailgun.com/) | `MAIL_USERNAME`, `MAIL_HOST` | +| smtp | Any SMTP server can be configured | `MAIL_USERNAME`, `MAIL_HOST`, `MAIL_PASSWORD`, `MAIL_PORT` | diff --git a/.github/docker/default.conf b/.github/docker/default.conf new file mode 100644 index 0000000..b6105e5 --- /dev/null +++ b/.github/docker/default.conf @@ -0,0 +1,51 @@ +# If using Ubuntu this file should be placed in: +# /etc/nginx/sites-available/ +# +# If using CentOS this file should be placed in: +# /etc/nginx/conf.d/ +# +server { + listen 80; + server_name _; + + root /app/public; + index index.html index.htm index.php; + charset utf-8; + + location / { + try_files $uri $uri/ /index.php?$query_string; + } + + location = /favicon.ico { access_log off; log_not_found off; } + location = /robots.txt { access_log off; log_not_found off; } + + access_log off; + error_log /var/log/nginx/pterodactyl.app-error.log error; + + # allow larger file uploads and longer script runtimes + client_max_body_size 100m; + client_body_timeout 120s; + + sendfile off; + + location ~ \.php$ { + fastcgi_split_path_info ^(.+\.php)(/.+)$; + # the fastcgi_pass path needs to be changed accordingly when using CentOS + fastcgi_pass 127.0.0.1:9000; + fastcgi_index index.php; + include fastcgi_params; + fastcgi_param PHP_VALUE "upload_max_filesize = 100M \n post_max_size=100M"; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param HTTP_PROXY ""; + fastcgi_intercept_errors off; + fastcgi_buffer_size 16k; + fastcgi_buffers 4 16k; + fastcgi_connect_timeout 300; + fastcgi_send_timeout 300; + fastcgi_read_timeout 300; + } + + location ~ /\.ht { + deny all; + } +} diff --git a/.github/docker/default_ssl.conf b/.github/docker/default_ssl.conf new file mode 100644 index 0000000..9ec5c10 --- /dev/null +++ b/.github/docker/default_ssl.conf @@ -0,0 +1,70 @@ +# If using Ubuntu this file should be placed in: +# /etc/nginx/sites-available/ +# +server { + listen 80; + server_name ; + return 301 https://$server_name$request_uri; +} + +server { + listen 443 ssl http2; + server_name ; + + root /app/public; + index index.php; + + access_log /var/log/nginx/pterodactyl.app-access.log; + error_log /var/log/nginx/pterodactyl.app-error.log error; + + # allow larger file uploads and longer script runtimes + client_max_body_size 100m; + client_body_timeout 120s; + + sendfile off; + + # strengthen ssl security + ssl_certificate /etc/letsencrypt/live//fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live//privkey.pem; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_prefer_server_ciphers on; + ssl_session_cache shared:SSL:10m; + ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:ECDHE-RSA-AES128-GCM-SHA256:AES256+EECDH:DHE-RSA-AES128-GCM-SHA256:AES256+EDH:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4"; + + # See the link below for more SSL information: + # https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html + # + # ssl_dhparam /etc/ssl/certs/dhparam.pem; + + # Add headers to serve security related headers + add_header Strict-Transport-Security "max-age=15768000; preload;"; + add_header X-Content-Type-Options nosniff; + add_header X-XSS-Protection "1; mode=block"; + add_header X-Robots-Tag none; + add_header Content-Security-Policy "frame-ancestors 'self'"; + + location / { + try_files $uri $uri/ /index.php?$query_string; + } + + location ~ \.php$ { + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass 127.0.0.1:9000; + fastcgi_index index.php; + include fastcgi_params; + fastcgi_param PHP_VALUE "upload_max_filesize = 100M \n post_max_size=100M"; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param HTTP_PROXY ""; + fastcgi_intercept_errors off; + fastcgi_buffer_size 16k; + fastcgi_buffers 4 16k; + fastcgi_connect_timeout 300; + fastcgi_send_timeout 300; + fastcgi_read_timeout 300; + include /etc/nginx/fastcgi_params; + } + + location ~ /\.ht { + deny all; + } +} \ No newline at end of file diff --git a/.github/docker/entrypoint.sh b/.github/docker/entrypoint.sh new file mode 100644 index 0000000..9cd4c9b --- /dev/null +++ b/.github/docker/entrypoint.sh @@ -0,0 +1,81 @@ +#!/bin/ash -e +cd /app + +mkdir -p /var/log/panel/logs/ /var/log/supervisord/ /var/log/nginx/ /var/log/php7/ \ + && chmod 777 /var/log/panel/logs/ \ + && ln -s /app/storage/logs/ /var/log/panel/ + +## check for .env file and generate app keys if missing +if [ -f /app/var/.env ]; then + echo "external vars exist." + rm -rf /app/.env + ln -s /app/var/.env /app/ +else + echo "external vars don't exist." + rm -rf /app/.env + touch /app/var/.env + + ## manually generate a key because key generate --force fails + if [ -z $APP_KEY ]; then + echo -e "Generating key." + APP_KEY=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1) + echo -e "Generated app key: $APP_KEY" + echo -e "APP_KEY=$APP_KEY" > /app/var/.env + else + echo -e "APP_KEY exists in environment, using that." + echo -e "APP_KEY=$APP_KEY" > /app/var/.env + fi + + ln -s /app/var/.env /app/ +fi + +echo "Checking if https is required." +if [ -f /etc/nginx/http.d/panel.conf ]; then + echo "Using nginx config already in place." + if [ $LE_EMAIL ]; then + echo "Checking for cert update" + certbot certonly -d $(echo $APP_URL | sed 's~http[s]*://~~g') --standalone -m $LE_EMAIL --agree-tos -n + else + echo "No letsencrypt email is set" + fi +else + echo "Checking if letsencrypt email is set." + if [ -z $LE_EMAIL ]; then + echo "No letsencrypt email is set using http config." + cp .github/docker/default.conf /etc/nginx/http.d/panel.conf + else + echo "writing ssl config" + cp .github/docker/default_ssl.conf /etc/nginx/http.d/panel.conf + echo "updating ssl config for domain" + sed -i "s||$(echo $APP_URL | sed 's~http[s]*://~~g')|g" /etc/nginx/http.d/panel.conf + echo "generating certs" + certbot certonly -d $(echo $APP_URL | sed 's~http[s]*://~~g') --standalone -m $LE_EMAIL --agree-tos -n + fi + echo "Removing the default nginx config" + rm -rf /etc/nginx/http.d/default.conf +fi + +if [[ -z $DB_PORT ]]; then + echo -e "DB_PORT not specified, defaulting to 3306" + DB_PORT=3306 +fi + +## check for DB up before starting the panel +echo "Checking database status." +until nc -z -v -w30 $DB_HOST $DB_PORT +do + echo "Waiting for database connection..." + # wait for 1 seconds before check again + sleep 1 +done + +## make sure the db is set up +echo -e "Migrating and Seeding D.B" +php artisan migrate --seed --force + +## start cronjobs for the queue +echo -e "Starting cron jobs." +crond -L /var/log/crond -l 5 + +echo -e "Starting supervisord." +exec "$@" diff --git a/.github/docker/supervisord.conf b/.github/docker/supervisord.conf new file mode 100644 index 0000000..da6823a --- /dev/null +++ b/.github/docker/supervisord.conf @@ -0,0 +1,39 @@ +[unix_http_server] +file=/tmp/supervisor.sock ; path to your socket file + +[supervisord] +logfile=/var/log/supervisord/supervisord.log ; supervisord log file +logfile_maxbytes=50MB ; maximum size of logfile before rotation +logfile_backups=2 ; number of backed up logfiles +loglevel=error ; info, debug, warn, trace +pidfile=/var/run/supervisord.pid ; pidfile location +nodaemon=false ; run supervisord as a daemon +minfds=1024 ; number of startup file descriptors +minprocs=200 ; number of process descriptors +user=root ; default user +childlogdir=/var/log/supervisord/ ; where child log files will live + +[rpcinterface:supervisor] +supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface + +[supervisorctl] +serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL for a unix socket + +[program:php-fpm] +command=/usr/local/sbin/php-fpm -F +autostart=true +autorestart=true + +[program:queue-worker] +command=/usr/local/bin/php /app/artisan queue:work --queue=high,standard,low --sleep=3 --tries=3 +user=nginx +autostart=true +autorestart=true + +[program:nginx] +command=/usr/sbin/nginx -g 'daemon off;' +autostart=true +autorestart=true +priority=10 +stdout_events_enabled=true +stderr_events_enabled=true \ No newline at end of file diff --git a/.github/docker/www.conf b/.github/docker/www.conf new file mode 100644 index 0000000..c0c1790 --- /dev/null +++ b/.github/docker/www.conf @@ -0,0 +1,16 @@ +[www] + +user = nginx +group = nginx + +listen = 127.0.0.1:9000 +listen.owner = nginx +listen.group = nginx +listen.mode = 0750 + +pm = ondemand +pm.max_children = 9 +pm.process_idle_timeout = 10s +pm.max_requests = 200 + +clear_env = no \ No newline at end of file diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..6874f7f --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,37 @@ +name: Build + +on: + push: + branches: + - "develop" + - "1.0-develop" + pull_request: + branches: + - "develop" + - "1.0-develop" + +jobs: + ui: + name: UI + runs-on: ubuntu-24.04 + permissions: + contents: read + strategy: + fail-fast: false + matrix: + node-version: [16] + steps: + - name: Code Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: yarn + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Build + run: yarn build:production diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..9bfaf70 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,78 @@ +name: Tests + +on: + push: + branches: + - "develop" + - "1.0-develop" + pull_request: + branches: + - "develop" + - "1.0-develop" + +jobs: + tests: + name: Tests + runs-on: ubuntu-24.04 + permissions: + contents: read + strategy: + fail-fast: false + matrix: + php: [8.2, 8.3] + database: + - mariadb:10.5 + - mariadb:10.11 + - mariadb:11.5 + - mysql:8 + - mysql:9 + services: + database: + image: ${{ matrix.database }} + env: + MYSQL_ALLOW_EMPTY_PASSWORD: yes + MYSQL_DATABASE: testing + ports: + - 3306 + steps: + - name: Code Checkout + uses: actions/checkout@v4 + + - name: Get cache directory + id: composer-cache + run: | + echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Cache + uses: actions/cache@v4 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ matrix.php }}-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-composer-${{ matrix.php }}- + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: bcmath, cli, curl, gd, mbstring, mysql, openssl, pdo, tokenizer, xml, zip + tools: composer:v2 + coverage: none + + - name: Setup .env + run: cp .env.ci .env + + - name: Install dependencies + run: composer install --no-interaction --no-progress --no-suggest --prefer-dist + + - name: Unit tests + run: vendor/bin/phpunit --bootstrap vendor/autoload.php tests/Unit + if: ${{ always() }} + env: + DB_HOST: UNIT_NO_DB + + - name: Integration tests + run: vendor/bin/phpunit tests/Integration + env: + DB_PORT: ${{ job.services.database.ports[3306] }} + DB_USERNAME: root diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml new file mode 100644 index 0000000..232b6e0 --- /dev/null +++ b/.github/workflows/docker.yaml @@ -0,0 +1,72 @@ +name: Docker + +on: + push: + branches: + - develop + - 1.0-develop + - release/v1.11.5 + pull_request: + branches: + - develop + - 1.0-develop + release: + types: + - published + +jobs: + push: + name: Push + runs-on: ubuntu-24.04 + if: "!contains(github.ref, 'develop') || (!contains(github.event.head_commit.message, 'skip docker') && !contains(github.event.head_commit.message, 'docker skip'))" + permissions: + contents: read + packages: write + steps: + - name: Code checkout + uses: actions/checkout@v4 + + - name: Docker metadata + id: docker_meta + uses: docker/metadata-action@v5 + with: + images: ghcr.io/nookure/nooktheme + flavor: | + latest=false + tags: | + type=raw,value=latest,enable=${{ github.event_name == 'release' && github.event.action == 'published' && github.event.release.prerelease == false }} + type=ref,event=tag + type=ref,event=branch + + - name: Setup QEMU + uses: docker/setup-qemu-action@v3 + + - name: Setup Docker buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + if: "github.event_name != 'pull_request'" + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.TOKEN }} + + - name: Update version + if: "github.event_name == 'release' && github.event.action == 'published'" + env: + REF: ${{ github.event.release.tag_name }} + run: | + sed -i "s/ 'version' => 'canary',/ 'version' => '${REF:1}',/" config/app.php + + - name: Build and Push + uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile + push: ${{ github.event_name != 'pull_request' }} + platforms: linux/amd64,linux/arm64 + labels: ${{ steps.docker_meta.outputs.labels }} + tags: ${{ steps.docker_meta.outputs.tags }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml new file mode 100644 index 0000000..44571e5 --- /dev/null +++ b/.github/workflows/lint.yaml @@ -0,0 +1,38 @@ +name: Lint + +on: + push: + branches: + - "develop" + - "1.0-develop" + pull_request: + branches: + - "develop" + - "1.0-develop" + +jobs: + lint: + name: Lint + runs-on: ubuntu-24.04 + permissions: + contents: read + steps: + - name: Code Checkout + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.2" + extensions: bcmath, curl, gd, mbstring, mysql, openssl, pdo, tokenizer, xml, zip + tools: composer:v2 + coverage: none + + - name: Setup .env + run: cp .env.ci .env + + - name: Install dependencies + run: composer install --no-interaction --no-progress --no-suggest --prefer-dist + + - name: PHP CS Fixer + run: vendor/bin/php-cs-fixer fix --dry-run --diff diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..fa8f29b --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,72 @@ +name: Release + +on: + push: + tags: + - "v*" + +jobs: + release: + name: Release + runs-on: ubuntu-24.04 + permissions: + contents: write # write is required to create releases and push. + steps: + - name: Code checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 16 + cache: yarn + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Build + run: yarn build:production + + - name: Create release branch and bump version + env: + REF: ${{ github.ref }} + run: | + BRANCH=release/"${REF:10}" + git config --local user.email 'ci@pterodactyl.io' + git config --local user.name 'Pterodactyl CI' + git checkout -b "$BRANCH" + git push -u origin "$BRANCH" + sed -i "s/ 'version' => 'canary',/ 'version' => '${REF:11}',/" config/app.php + git add config/app.php + git commit -m 'ci(release): bump version' + git push + + - name: Create release archive + run: | + rm -rf node_modules tests CODE_OF_CONDUCT.md CONTRIBUTING.md flake.lock flake.nix phpunit.xml shell.nix + tar -czf panel.tar.gz * .editorconfig .env.example .eslintignore .eslintrc.js .gitignore .prettierrc.json + + - name: Extract changelog + env: + REF: ${{ github.ref }} + run: | + sed -n "/^## ${REF:10}/,/^## /{/^## /b;p}" CHANGELOG.md > ./RELEASE_CHANGELOG + + - name: Create checksum and add to changelog + run: | + SUM=`sha256sum panel.tar.gz` + echo -e "\n#### SHA256 Checksum\n\n\`\`\`\n$SUM\n\`\`\`\n" >> ./RELEASE_CHANGELOG + echo "$SUM" > checksum.txt + + - name: Create release + id: create_release + uses: softprops/action-gh-release@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + draft: true + prerelease: ${{ contains(github.ref, 'rc') || contains(github.ref, 'beta') || contains(github.ref, 'alpha') }} + body_path: ./RELEASE_CHANGELOG + files: | + panel.tar.gz + checksum.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..547f318 --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +/vendor +*.DS_Store* +!.env.ci +!.env.example +.env* +.vagrant/* +.vscode/* +storage/framework/* +/.idea +/nbproject +/.direnv + +node_modules +*.log +_ide_helper.php +_ide_helper_models.php +.phpstorm.meta.php +.yarn +public/assets/manifest.json + +# For local development with docker +# Remove if we ever put the Dockerfile in the repo +.dockerignore +docker-compose.yml + +# for image related files +misc +.php-cs-fixer.cache +coverage.xml +resources/lang/locales.js +.phpunit.result.cache + +/public/build +/public/hot +result +docker-compose.yaml +release diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 0000000..9245b7e --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,52 @@ +in(__DIR__) + ->exclude([ + 'vendor', + 'node_modules', + 'storage', + 'bootstrap/cache', + ]) + ->notName(['_ide_helper*']); + +return (new Config()) + ->setRiskyAllowed(true) + ->setFinder($finder) + ->setRules([ + '@Symfony' => true, + '@PSR1' => true, + '@PSR2' => true, + '@PSR12' => true, + 'align_multiline_comment' => ['comment_type' => 'phpdocs_like'], + 'combine_consecutive_unsets' => true, + 'concat_space' => ['spacing' => 'one'], + 'heredoc_to_nowdoc' => true, + 'no_alias_functions' => true, + 'no_unreachable_default_argument_value' => true, + 'no_useless_return' => true, + 'ordered_imports' => [ + 'sort_algorithm' => 'length', + ], + 'phpdoc_align' => [ + 'align' => 'left', + 'tags' => [ + 'param', + 'property', + 'return', + 'throws', + 'type', + 'var', + ], + ], + 'random_api_migration' => true, + 'ternary_to_null_coalescing' => true, + 'yoda_style' => [ + 'equal' => false, + 'identical' => false, + 'less_and_greater' => false, + ], + ]); diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..d000a22 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,9 @@ +{ + "printWidth": 120, + "tabWidth": 4, + "useTabs": false, + "semi": true, + "singleQuote": true, + "jsxSingleQuote": true, + "endOfLine": "lf" +} diff --git a/BUILDING.md b/BUILDING.md new file mode 100644 index 0000000..d8b7033 --- /dev/null +++ b/BUILDING.md @@ -0,0 +1,63 @@ +# Local Development +Pterodactyl is now powered by React, Typescript, and Tailwindcss using webpack at its core to generate compiled assets. +Release versions of Pterodactyl will include pre-compiled, minified, and hashed assets ready-to-go. + +However, if you are interested in running custom themes or making modifications to the React files you'll need a build +system in place to generate these compiled assets. To get your environment setup you'll need at minimum: + +* [Node.js](https://nodejs.org/en/) v14.x.x +* [Yarn](https://classic.yarnpkg.com/lang/en/) v1.x.x +* [Go](https://golang.org/) 1.17.x + +### Install Dependencies +```bash +yarn install +``` + +The command above will download all of the dependencies necessary to get Pterodactyl assets building. After that, its as +simple as running the command below to generate assets while you're developing. Until you've run this command at least +once you'll likely see a 500 error on your Panel about a missing `manifest.json` file. This is generated by the commands +below. + +```bash +# Build the compiled set of assets for development. +yarn run build + +# Build the assets automatically as they are changed. This allows you to refresh +# the page and see the changes immediately. +yarn run watch +``` + +### Hot Module Reloading +For more advanced users, we also support 'Hot Module Reloading', allowing you to quickly see changes you're making +to the Vue template files without having to reload the page you're on. To Get started with this, you just need +to run the command below. + +```bash +PUBLIC_PATH=http://192.168.1.1:8080 yarn run serve --host 192.168.1.1 +``` + +There are two _very important_ parts of this command to take note of and change for your specific environment. The first +is the `--host` flag, which is required and should point to the machine where the `webpack-serve` server will be running. +The second is the `PUBLIC_PATH` environment variable which is the URL pointing to the HMR server and is appended to all of +the asset URLs used in Pterodactyl. + +#### Development Environment +If you're using the [`pterodactyl/development`](https://github.com/pterodactyl/development) environments, which are +highly recommended, you can just run `yarn run serve` to run the HMR server, no additional configuration is necessary. + +### Building for Production +Once you have your files squared away and ready for the live server, you'll be needing to generate compiled, minified, +and hashed assets to push live. To do so, run the command below: + +```bash +yarn run build:production +``` + +This will generate a production JS bundle and associated assets, all located in `public/assets/` which will need to +be uploaded to your server or CDN for clients to use. + +### Running Wings +To run `wings` in development all you need to do is set up the configuration file as normal when adding a new node, and +then you can build and run a local version of Wings by executing `make debug` in the Wings code directory. This must +be run on a Linux VM of some sort, you cannot run this locally on macOS or Windows. diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..3518c4f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,1823 @@ +# Changelog +This file is a running track of new features and fixes to each version of the panel released starting with `v0.4.0`. + +This project follows [Semantic Versioning](http://semver.org) guidelines. + +## v1.11.11 + +### Fixed + +* Fixed CVE-2025-49132 + +## v1.11.10 + +### BREAKING + +* Minimum PHP verion is now 8.2 due to Laravel upgrade! + +### Fixed + +* Update Laravel to address [CVE-2024-52301](https://github.com/advisories/GHSA-gv7v-rgg6-548h) + +## v1.11.9 + +### Fixed + +* Fixed issue with CI not pushing Docker image + +## v1.11.8 + +### Fixed + +* Fixed an issue where a `DELETE` request was used instead of a `POST`, potentially logging user passwords in plain text if they disable 2FA. + +## v1.11.7 + +### Added + +* Java 21 to Minecraft eggs + +### Changed + +* Updated Minecraft EULA link + +### Fixed + +* Fixed backups not ever being marked as completed (#5088) +* Fixed `.7z` files not being detected as a compressed file (#5016) + +## v1.11.6 + +### Changed + +* Better node ownership checks for internal backup endpoints +* Improved validation rules on `docker_image` fields to prevent invalid inputs + +### Fixed + +* Multiple XSS vulnerabilities in the admin area ([GHSA-384w-wffr-x63q](https://github.com/pterodactyl/panel/security/advisories/GHSA-384w-wffr-x63q)) + +## v1.11.5 +### Fixed +* Rust egg using the wrong Docker image, breaking Rust modding frameworks. + +## v1.11.4 +### Added +* Added support for the `server.queryport` option on the Rust egg. +* Added support for the Carbon modding framework to the Rust egg. + +### Changed +* Upgraded to Laravel 10. +* Sensitive data is no longer shown in the CopyOnClick toast notification. + +### Fixed +* Allow SVGs to be edited in the server's file manager. +* Properly validate the request body when creating a backup. +* Fixed issue with schedules running at the wrong time when the panel utilized a timezone with non-hour offsets (such as `Australia/Darwin`). +* Fixes the log directory when running the Panel in a container. +* Fixes the permission name used to check if a user has permission to read files/folders. +* Fixes the ability to unset a server's description through the client API. +* Fixed the MassActionBar on the server's file manager blocking elements below it, preventing them from being interacted with. + +## v1.11.3 +### Changed +* When updating a server's description through the client API, if no value is specified, the description will now remain unchanged. +* When installing the Panel for the first time, the queue driver will now all default to `redis` instead of `sync`. + +### Fixed +* `php artisan p:environment:mail` not correctly setting the right variable for `MAIL_FROM_ADDRESS`. +* Fixed the conflict state rendering on the UI for a server showing `reinstall_failed` as `restoring_backup`. +* Fixed the unknown column `uuid` error when jobs fail, causing them not to get stored correctly. +* Fixed the server task endpoints in the client API not allowing `sequence_id` and `continue_on_failure` to be set. + +## v1.11.2 +### Changed +* Telemetry no longer sends a map of Egg and Nest UUIDs to the number of servers using them. +* Increased the timeout for the decompress files endpoint in the client API from 15 seconds to 15 minutes. + +### Fixed +* Fixed Panel Docker image having a `v` prefix in the version displayed in the admin area. +* Fixed emails using the wrong queue name, causing them to not be sent. +* Fixed the settings keys used for configuring SMTP settings, causing settings to not save properly. +* Fixed the `MAIL_EHLO_DOMAIN` environment variable not being properly backwards compatible with the old `SERVER_NAME` variable. + +## v1.11.1 +### Fixed +* Fixed Panel Docker image showing `canary` as it's version. + +## v1.11.0 +### Changed (since 1.10.4) +* Changed minimum PHP version requirement from `7.4` to `8.0`. +* Upgraded from Laravel 8 to Laravel 9. +* This release requires Wings v1.11.x in order for Server Transfers to work. +* `MB` byte suffixes are now displayed as `MiB` to more accurately reflect the actual value. +* Server re-installation failures are tracked independently of the initial installation process. + +### Fixed (since 1.10.4) +* Node maintenance mode now properly blocks access to servers. +* Fixed the length validation on the Minecraft Forge egg. +* Fixed the password in the JDBC string not being properly URL encoded. +* Fixed an issue where Wings would throw a validation error while attempting to upload activity logs. +* Properly handle a missing `Content-Length` header in the response from the daemon. +* Ensure activity log properties are always returned as an object instead of an empty array. + +### Added (since 1.10.4) +* Added the `server:settings.description` activity log event for when a server description is changed. +* Added the ability to cancel file uploads in the file manager for a server. +* Added a telemetry service to collect anonymous metrics from the panel, this feature is *enabled* by default and can be toggled using the `PTERODACTYL_TELEMETRY_ENABLED` environment variable. + +## v1.11.0-rc.2 +### Changed +* `MB` byte suffixes are now displayed as `MiB` to more accurately reflect the actual value. +* Server re-installation failures are tracked independently of the initial installation process. + +### Fixed +* Properly handle a missing `Content-Length` header in the response from the daemon. +* Ensure activity log properties are always returned as an object instead of an empty array. + +### Added +* Added the `server:settings.description` activity log event for when a server description is changed. +* Added the ability to cancel file uploads in the file manager for a server. +* Added a telemetry service to collect anonymous metrics from the panel, this feature is disabled by default and can be toggled using the `PTERODACTYL_TELEMETRY_ENABLED` environment variable. + +## v1.11.0-rc.1 +### Changed +* Changed minimum PHP version requirement from `7.4` to `8.0`. +* Upgraded from Laravel 8 to Laravel 9. +* This release requires Wings v1.11.x in order for Server Transfers to work. + +### Fixed +* Node maintenance mode now properly blocks access to servers. +* Fixed the length validation on the Minecraft Forge egg. +* Fixed the password in the JDBC string not being properly URL encoded. +* Fixed an issue where Wings would throw a validation error while attempting to upload activity logs. + +## v1.10.4 +### Fixed +* Fixed an issue where subusers could be given permissions that are not actually registered or used. +* Fixed an issue where node FQDNs could not just be IP addresses. + +### Changed +* Change maximum number of API keys per user from `10` to `25`. +* Change byte unit prefix from `B` to `iB` to better reflect our usage of base 2 (multiples of 1024). + +## v1.10.3 +### Fixed +* S3 Backup driver now supports Cloudflare R2. +* Node FQDNs can now be used with AAAA records with no A records present. +* Server transfers can no longer be initiated if the server is being installed, transferred, or restoring a backup. +* Fixed an issue relating to the use of arrays in the `config_files` field with eggs. +* Fixed `oom_disabled` not being mapped in the Application API when creating a new server. + +### Added +* File manager now supports selecting multiple files for upload (when using the upload button). +* Added a configuration option for specifying the S3 storage class for backups. + +### Changed +* Servers will now show the current uptime when the server is starting rather than only showing when the server is marked as online. + +## v1.10.2 +### Fixed +* Fixes a rendering issue with egg descriptions in the admin area +* Fixes the page title on the SSH Keys page + +### Changed +* Additional validation rules will now show a toggle switch rather than an input when editing server variables +* The eggs endpoint will now always return an empty JSON object for the `config_files` field, even if the field is completely empty + +### Added +* Adds a `Force Outgoing IP` option for eggs that can be used to ensure servers making outgoing connections use their allocation IP rather than the node's primary ip +* Adds options to configure sending of email (re)install notifications +* Add an option to configure the part size for backups uploaded to S3 + +## v1.10.1 +### Fixed +* Fixes a surprise `clock()` function that was used for debugging and should not have made it into the release. This was causing activity events to not properly sync between the Panel and Wings. + +## v1.10.0 +### Fixed +* Fixes improper cache key naming on the frontend causing server activity logs to be duplicated across server page views. +* Fixes overflow issues on dialogs when the internal content is too long. +* Fixes spinner overlay on console improperly taking up the entire page making it impossible to use navigation controls. +* Fixes 2FA QR code background being too dark for some phones to properly scan. +* File manager now properly displays an error message if a user attempts to upload a folder rather than files. +* Fixes the "Create Directory" dialog persisting the previously entered value when it is re-opened. + +### Changed +* IP addresses in activity logs are now always displayed to administrators, regardless of if they own the server or not. +* Scroll down indicator on the console has been changed to a down arrow to be clearer. +* Docker builds have been updated to use `PHP 8.1`. +* Recaptcha validation domain is now configurable using the `RECAPTCHA_DOMAIN` environment variable. +* Drag and drop overlay on the file manager has been tweaked to be slightly more consistent with the frontend style and be a little easier to read. + +### Added +* Adds support for the `user_uuid` claim on all generated JWTs which allows Wings to properly identify the user performing each action. +* Adds support for recieving external activity log events from Wings instances (power state, commands, SFTP, and uploads). +* Adds support for tracking failed password-based SFTP logins. +* Server name and description are now passed along to Wings making them available in egg variables for parsing and including. +* Adds support for displaying all active file uploads in the file manager. + +## v1.9.2 +### Fixed +* Fixes rouding in sidebar of CPU usage graph that was causing an excessive number of zeros to be rendered. +* Fixes the Java Version selector modal having the wrong default value selected initially. +* Fixes console rendering in Safari that was causing the console to resize excessively and graphs to overlay content. +* Fixes missing "Starting"/"Stopping" status display in the server uptime block. +* Fixes incorrect formatting of activity log when viewing certain file actions. + +### Changed +* Updated the UI for the two-step authorization setup on accounts to use new Dialog UI and provide better clarity to new users. + +### Added +* Added missing `` tag to template output to avoid entering quirks mode in browsers. +* Added password requirement when enabling TOTP on an account. + +## v1.9.1 +### Fixed +* Fixes missing "Click to Copy" for server address on the console data blocks. +* Fixes data points on the graphs not being properly rounded to two decimal places. +* Returns byte formatting logic to use `1024` as the base value, rather than `1000`. +* Fixes permission error occurring when a server is marked as installing and an admin navigates to the console screen. +* Fixes improper display of install/transfer warning on the server console page. +* Fixes permission matching for the server settings page to correctly allow access when a user has _any_ of the needed permissions. + +### Changed +* Moves the server data blocks to the right-hand side of the console, rather than the left. +* Rather than defaulting graph values at `0` when resetting or refreshing the page, their values are now hidden entirely. +* **[security]** Hides IP addresses from all activity log entries that are not directly associated with the currently signed in user. + +### Added +* Adds the current resource limits for a server next to each data block on the console screen. + +## v1.9.0 +### Added +* Added support for using Tailwind classes inside components using `className={}` rather than having to use `twin.macro` with the `css={}` prop. +* Added HeadlessUI and Heroicons packages. +* Added new `Tooltip.tsx` component to support displaying tooltips within the Panel. +* Adds a new activity log view for both user accounts and individual servers. This builds upon data collected in previous releases. +* Added a new column `api_key_id` to the `activity_logs` table to indicate if the user performed the action while using an API key. +* Adds initial support for language translations on the front-end. The underlying implementation details are working, however work has not yet begun on actually translating all of the strings yet. Expect this to continue in future releases. +* Improved accessibility for navigation icons by adding a tooltip on hover to indicate what each one does. +* Adds logging for API keys that are blocked from performing an API action due to IP address limiting. +* Adds support for `?filter[description]=foo` when querying servers on both the client and application API. + +### Changed +* Updated how release assets are generated to perform more logical bundle splitting. This should help reduce the amount of data users have to download at once in order to render the UI. +* Upgraded From TailwindCSS 2 to 3 — for most people this should have minimal if any impact. +* Chart.js updated from v2 to v3. +* Reduced the number of custom colors in use — by default we now use Tailwind's default color pallet, with the exception of a custom gray scheme. +* **[deprecated]** The use of `neutral` and `primary` have been deprecated in class names, prefer `gray` and `blue` respectively. +* Begins the process of dropping the use of Gravatars for user avatars and replaces them with dynamically generated SVG images. +* Improved front-end route definitions to make it easier for external modifications to inject their routes and components into the codebase without having to modify as many core files. +* Redesigned the server console screen to better display data users might be looking for, and increase the height of the console itself. +* Merged the two network data graphs into a single dual-line graph to better display incoming and outgoing data volumes. +* Updated all byte formatting logic to use `1000` as the divisor rather than `1024` to be more consistent with what users most likely expect. +* Changed the underlying `eslint` rules applied to the front-end codebase to simplify them dramatically. We now utilize `prettier` in combination with some basic default rulesets to make it easier to understand the expected formatting. + +### Fixed +* Fixes a bug causing a 404 error when attempting to delete a database from a server in the admin control panel. +* Fixes console input auto-capitalizing and auto-correcting when entering text on some mobile devices. +* Fixes SES service configuration using a hard-coded `us-east-1` region. +* Fixes a bug causing a 404 error when attempting to delete an SSH key from your account when the SHA256 hash includes a slash. +* Fixes mobile keyboards automatically attempting to capitalize and spellcheck typing on the server console. +* Fixes improper support for IP address CIDR ranges when creating API keys for the client area. +* Fixes a bug preventing additional included details from being returned from the application API when utilizing a client API key as an administrator. + +## v1.8.1 +### Fixed +* Fixes a bug causing mounts to return a 404 error when adding them to a server. +* Fixes a bug causing the Egg Image dropdown to not display properly when creating a new server. +* Fixes a bug causing an error when attemping to create a new server via the API. + +## v1.8.0 +**Important:** this version updates the `version` field on generated Eggs to be `PTDL_v2` due to formatting changes. This +should be completely seamless for most installations as the Panel is able to convert between the two. Custom solutions +using these eggs should be updated to account for the new format. + +This release also changes API key behavior — "client" keys belonging to admin users can now be used to access +the `/api/application` endpoints in their entirety. Existing "application" keys generated in the admin area should +be considered deprecated, but will continue to work. Application keys _will not_ work with the client API. + +### Fixed +* Schedules are no longer run when a server is suspended or marked as installing. +* The remote field when creating a database is no longer limited to an IP address and `%` wildcard — all expected MySQL remote host values are allowed. +* Allocations cannot be deleted from a server by a user if the server is configured with an `allocation_limit` set to `0`. +* The Java Version modal no longer shows a dropdown and update option to users that do not have permission to make those changes. +* The Java Version modal now correctly returns only the images available to the server's selected Egg. +* Fixes leading and trailing spaces being removed from variable values on file manager endpoints, causing errors when trying to perform actions against certain files and folders. + +### Changed +* Forces HTTPS on URLs when the `APP_URL` value is set and includes `https://` within the URL. This addresses proxy misconfiguration issues that would cause URLs to be generated incorrectly. +* Lowers the default timeout values for requests to Wings instances from 10 seconds to 5 seconds. +* Additional permissions (`CREATE TEMPORARY TABLES`, `CREATE VIEW`, `SHOW VIEW`, `EVENT`, and `TRIGGER`) are granted to users when creating new databases for servers. +* development: removed Laravel Debugbar in favor of Clockwork for debugging. +* The 2FA input field when logging in is now correctly identified as `one-time-password` to help browser autofill capabilities. +* Changed API authentication mechanisms to make use of Laravel Sanctum to significantly clean up our internal handling of sessions. +* API keys generated by the system now set a prefix to identify them as Pterodactyl API keys, and if they are client or application keys. This prefix looks like `ptlc_` for client keys, and `ptla_` for application keys. Existing API keys are unaffected by this change. + +### Added +* Added support for PHP 8.1 in addition to PHP 8.0 and 7.4. +* Adds more support for catching potential PID exhaustion errors in different games. +* It is now possible to create a new node on the Panel using an artisan command. +* A new cron cheatsheet has been added which appears when creating a schedule. +* Adds support for filtering the `/api/application/nodes/:id/allocations` endpoint using `?filter[server_id]=0` to only return allocations that are not currently assigned to a server on that node. +* Adds support for naming docker image values in an Egg to improve front-end display capabilities. +* Adds command to return the configuration for a specific node in both YAML and JSON format (`php artisan p:node:configuration`). +* Adds command to return a list of all nodes available on the Panel in both table and JSON format (`php artisan p:node:list`). +* Adds server network (inbound/outbound) usage graphs to the console screen. +* Adds support for configuring CORS on the API by setting the `APP_CORS_ALLOWED_ORIGINS=example.com,dashboard.example.com` environment variable. By default all instances are configured with this set to `*` which allows any origin. +* Adds proper activity logging for the following areas of the Panel: authentication, user account modifications, server modification. This is an initial test implementation before further roll-out in the software. Events are logged into the database but are not currently exposed in the UI — they will be displayed in a future update. + +### Removed +* Removes Google Analytics from the front end code. +* Removes multiple middleware that were previously used for configuring API access and controlling model fetching. This has all been replaced with Laravel Sanctum and standard Laravel API tooling. This should make codebase discovery significantly more simple. +* **DEPRECATED**: The use of `Pterodactyl\Models\AuditLog` is deprecated and all references to this model have been removed from the codebase. In the next major release this model and table will be fully dropped. + +## v1.7.0 +### Fixed +* Fixes typo in message shown to user when deleting a database. +* Fixes formatting of IPv6 addresses when displaying allocations to users. +* Fixes an exception thrown while trying to return error messages from API endpoints that inproperly masked the true underlying error. +* Fixes SSL certificate path generation for Let's Encrypt by ensuring they are always transformed to lowercase. +* Removes duplicate entries when creating a nested folder in the file manager. +* Fixes missing validation of Egg Author email addresses during the setup process that could cause unexpected failures later on. +* Fixes font rendering issues of the console on Firefox due to an outdated version of xterm.js being used. +* Fixes display overlap issues of the two-factor configuration form in a user's settings. +* **[security]** When authenticating using an API key a user session is now only persisted for the duration of the request before being destroyed. + +### Changed +* CPU graph changed to show the maximum amount of CPU available to a server to better match how the memory graph is displayed. + +### Added +* Adds support for `DB_PORT` environment variable in the Docker enterpoint for the Panel image. +* Adds suport for ARM environments in the Docker image. +* Adds a new warning modal for Steam servers shown when an invalid Game Server Login Token (GSL Token) is detected. +* Adds a new warning modal for Steam servers shown when the installation process runs out of available disk space. +* Adds a new warning modal for Minecraft servers shown when a server exceeds the maximum number of child processes. +* Adds support for displaying certain server variable fields as a checkbox when they're detected as using `boolean` or `in:0,1` validation rules. +* Adds support for Pug and Jade in the file editor. +* Adds an entry to the `robots.txt` file to correctly disallow all bot indexing. + + +## v1.6.6 +### Fixed +* **[security]** Fixes a CSRF vulnerability for both the administrative test email endpoint and node auto-deployment token generation endpoint. [GHSA-wwgq-9jhf-qgw6](https://github.com/pterodactyl/panel/security/advisories/GHSA-wwgq-9jhf-qgw6) + +### Changed +* Updates Minecraft eggs to include latest Java 17 yolk by default. + +## v1.6.5 +### Fixed +* Fixes broken application API endpoints due to changes introduced with session management in 1.6.4. + +## v1.6.4 +_This release should not be used, please use `1.6.5`. It has been pulled from our releases._ + +### Fixed +* Fixes a session management bug that would cause a user who signs out of one browser to be unintentionally logged out of other browser sessions when using the client API. + +## v1.6.3 +### Fixed +* **[Security]** Changes logout endpoint to be a POST request with CSRF-token validation to prevent a malicious actor from triggering a user logout. +* Fixes Wings receiving the wrong server suspension state when syncing servers. + +### Added +* Adds additional throttling to login and password reset endpoints. +* Adds server uptime display when viewing a server console. + +## v1.6.2 +### Fixed +* **[Security]** Fixes an authentication bypass vulerability that could allow a malicious actor to login as another user in the Panel without knowing that user's email or password. + +## v1.6.1 +### Fixed +* Fixes server build modifications not being properly persisted to the database when edited. +* Correctly exposes the `oom_disabled` field in the `build` limits block for a server build so that Wings can pick it up. +* +## v1.6.0 +### Fixed +* Fixes array merging logic for server transfers that would cause a 500 error to occur in some scenarios. +* Fixes user password updates not correctly logging the user out and returning a failure message even upon successful update. +* Fixes the count of used backups when browsing a paginated backup list for a server. +* Fixes an error being triggered when API endpoints are called with no `User-Agent` header and an audit log is generated for the action. +* Fixes state management on the frontend not properly resetting the loading indicator when adding subusers to a server. +* Fixes extraneous API calls being made to Wings for the server file listing when not on a file manager screen. + +### Added +* Adds foreign key relationship on the `mount_node`, `mount_server` and `egg_mount` tables. +* Adds environment variable `PER_SCHEDULE_TASK_LIMIT` to allow manual overrides for the number of tasks that can exist on a single schedule. This is currently defaulted to `10`. +* OOM killer can now be configured at the time of server creation. + +### Changed +* Server updates are not dependent on a successful call to Wings occurring — if the API call fails internally the error will be logged but the server update will still be persisted. + +### Removed +* Removed `WingsServerRepository::update()` function — if you were previously using this to modify server elements on Wings please replace calls to it with `::sync()` after updating Wings. + +## v1.5.1 +### Fixed +* Fixes Docker image 404ing instead of being able to access the Panel. +* Fixes Java version feature being only loaded when the `eula` feature is specified. +* Fixes `php artisan p:upgrade` not forcing and seeding while running migrations. +* Fixes spinner overlays overlapping on the server console page. +* Fixes Wings being unable to update backup statuses. + +## v1.5.0 +### Fixed +* Fixes deleting a locked backup that has also been marked as failed to allow deletion rather than returning an error about being locked. +* Fixes server creation process not correctly sending `start_on_completion` to Wings instance. +* Fixes `z-index` on file mass delete modal so it is displayed on top of all elements, rather than hidden under some. +* Supports re-sending requests to the Panel API for backups that are currently marked as failed, allowing a previously failed backup to be marked as successful. +* Minor updates to multiple default eggs for improved error handling and more accurate field-level validation. + +### Updated +* Updates help text for CPU limiting when creating a new server to properly indicate virtual threads are included, rather than only physical threads. +* Updates all of the default eggs shipped with the Panel to reference new [`ghcr.io` yolks repository](https://github.com/pterodactyl/yolks). +* When adding 2FA to an account the key used to generate the token is now displayed to the user allowing them to manually input into their app if necessary. + +### Added +* Adds SSL/TLS options for MySQL and Redis in line with most recent Laravel updates. +* New users created for server MySQL instances will now have the correct permissions for creating foreign keys on tables. +* Adds new automatic popup feature to allow users to quickly update their Minecraft servers to the latest Java® eggs as necessary if unsupported versions are detected. + +### Removed +* Removes legacy `userInteraction` key from eggs which was unused. + +## v1.4.2 +### Fixed +* Fixes logic to disallow creating a backup schedule if the server's backup limit is set to 0. +* Fixes bug preventing a database host from being updated if the linked node is set to "none". +* Fixes files and menus under the "Mass Actions Bar" being unclickable when it is visible. +* Fixes issues with the Teamspeak and Mumble eggs causing installs to fail. +* Fixes automated query to avoid pruning backups that are still running unintentionally. +* Fixes "Delete Server" confirmation modal on the admin screen to actually show up when deleting rather than immediately deleting the server. + +### Added +* Adds support for locking individual server backups to prevent deletion by users or automated backup processes. +* List of files to be deleted is now shown on the delete file confirmation modal. +* Adds support for using `IF` statements in database queries when a database user is created through the Panel. +* Adds support for using a custom mailgun API endpoint rather than only the US based endpoint. +* Adds CPU limit display next to the current CPU usage to match disk and memory usage reporting. +* Adds a "Scroll to Bottom" helper element to the server console when not scrolled to the bottom currently. +* Adds support for querying the API for servers by using the `uuidShort` field rather than only the `uuid` field. + +### Changed +* Updates codebase to use TypeScript 4. +* **[security]**: removes the external dependency for loading QRCode images. They're now generated directly on the frontend using JavaScript. + +## v1.4.1 +### Added +* Adds support for only running a schedule if the server is currently in an online state. +* Adds support for ignoring errors during task execution and continuing on to the next item in the sequence. For example, continuing to a server restart even if sending a command beforehand failed. +* Adds the ability to specify the group to use for file permissions when using the `p:upgrade` command. +* Adds the ability to manually run a schedule even if it is currently disabled. + +## v1.4.0 +### Fixed +* Removes the use of tagging when storing server resource usage in the cache. This addresses errors encountered when using the `file` driver. +* Fixes Wings response handling if Wings returns an error response with a 200-level status code that would improperly be passed back to the client as a successful request. +* Fixes use of JSON specific functions in SQL queries to better support MariaDB users. +* Fixes a migration that could fail on some MySQL/MariaDB setups when trying to encrypt node token values. + +### Changed +* Increases the maximum length allowed for a server name using the Rust egg. +* Updated server resource utilization API call to Wings to use new API response format used by `Wings@1.4.0`. + +## v1.3.2 +### Fixed +* Fixes self-upgrade incorrectly executing the command to un-tar downloaded archives. +* Fixes the checkbox to delete all files when restoring a backup not actually passing that along in the API call. Files will now properly be deleted when restoring if selected. +* Fixes some keybindings not working correctly in the server console on Windows machines. +* Fixes mobile UI incorrectly squishing the Docker image selector on the server settings page. +* Fixes recovery tokens not having a `created_at` value set on them properly when they are created. +* Fixes flawed migration that would not correctly set the month value into schedule crons. +* Fixes incorrect mounting for Docker compose file that would cause error logs to be missing. + +### Changed +* Server resource lookups are now cached on the Panel for 20 seconds at a time to reduce the load from multiple clients requesting the same server's stats. +* Bungeecord egg no longer force-enables the query listener. +* Adds page to the dashboard URL to allow easy loading of a specific pagination page rather than resetting back to the first page when refreshing. +* All application API endpoints now correctly support the `?per_page=N` query parameter to specify how many resources to return at once. + +## v1.3.1 +### Fixed +* Fixes the Rust egg not properly seeding during the upgrade & installation process. +* Fixes backups not being downloadable via the frontend. +* Fixes backup listing showing the wrong number of existing backups based on the current page you're on. + +## v1.3.0 +### Fixed +* Fixes administrator "Other Servers" toggle being persisted wrongly when signing out and signing into a non-administrator account on the server dashboard. +* Fixes composer failing to run properly in local environments where there is no database connection available once configured. +* Fixes SQL exception caused by the Panel attempting to store null values in the database. +* Fixes validation errors caused by improper defaults when trying to edit system settings in the admin area. +* Fixes console overflow when using smaller-than-default font sizes in Firefox. +* Fixes console text input field having a white background when manually building new assets from the release build due to a missing `babel-macros` definition file. +* Fixes database improperly using a signed `smallint` field rather than an unsigned field which restricted SFTP ports to 32767 or less. +* Fixes server console resize handler to no longer encounter an exception at random that breaks the entire UI. +* Fixes unhandled error caused by entering an invalid IP address or FQDN when creating a new node allocation. +* Fixes unhandled error when Wings would fetch a server configuration from the Panel that uses an Egg with invalid JSON data for the configuration fields. +* Fixes email not being sent to a user when their server is done installing. + +### Added +* Adds support for automatically copying SFTP connection details when clicking into the text field. +* Messaging about a node not having any allocations available for deployment has been adjusted to be more understandable by users. +* Adds automated self-upgrade process for Pterodactyl Panel once this version is installed on servers. This allows users to update by using a single command. +* Adds support for specifying a month when creating or modifying a server schedule. +* Adds support for restoring backups (including those in S3 buckets) to a server and optionally deleting all existing files when doing so. +* Adds underlying support for audit logging on servers. Currently this is only used by some internal functionality but will be slowly expanded as time permits to allow more robust logging. +* Adds logic to automatically reset failed server states when Wings is rebooted. This will kick servers out of "installing" and "restoring from backup" states automatically. + +### Changed +* Updated to `Laravel 8` and bumped minimum PHP version from `7.3` to `7.4` with PHP `8.0` being the recommended. +* Server state is now stored in a single `status` column within the database rather than multiple different `tinyint` columns. + +## v1.2.2 +### Fixed +* **[security]** Fixes authentication bypass allowing a user to take control of specific server actions such as executing schedules, rotating database passwords, and viewing or deleting a backup. + +## v1.2.1 +### Fixed +* Fixes URL-encoding of filenames when working in the filemanager to fix issues when moving, renaming, or deleting files. +* Fixes URL-encoding of email addresses when requesting a password reset. + +### Added +* Adds the ability for users to select a base Java Docker image for most Minecraft specific eggs shipped as defaults. + +## v1.2.0 +### Fixed +* Fixes newest backup being deleted when creating a new one using the schedule tasks, rather than the oldest backup. +* Fixes multiple encoding issues when handling file names in the manager. +* Fixes database password not properly being copied to the clipboard when clicked. +* Fixes failed transfers unintentionally locking a server into a failed state and not properly releasing allocations that were reserved. +* Fixes error box on server pages having an oval refresh button rather than a perfect circle. +* Fixes a bunch of errors and usage issues relating to backups especially when uploading to S3-based systems. +* Fixes HMR breaking navigation in development modes on the frontend. + +### Changed +* Updated Paper egg to default to Java 11 as the base docker image. +* Removes the file mode display from the File Manager row listing. +* Updated input UI elements to have thicker borders and more consistent highlighting when active. +* Changed searchbar toggle from `"k"` to `Cmd/Ctrl + "/"` to avoid accidental toggles and be more consistent with other sites. +* Upgrades TailwindCSS to `v2`. + +### Added +* Adds support for eggs to define multiple Docker images that can be selected by users (e.g. Java 8 & 11 images for a single egg). +* Adds support for configuring the default interval for failed backups to be pruned from the system to avoid long running backups being incorrectly cleared. +* Adds server transfer output logging to the server console allowing admins to see how a transfer is progressing directly in the UI. +* Adds client API endpoint to download a file from a remote souce. This functionality is not currently expressed in the UI. + +## v1.1.3 +### Fixed +* Server bulk power actions command will no longer attempt to run commands against installing or suspended servers. +* Fixes the application API throwing an error when attempting to return variables for a server. +* Fixes an error when attempting to install Panel dependencies without specifying an `.env` file due to an unset default timezone. +* Fixes a null value flip in the database migrations. +* Fixes password change endpoint for users allowing a blank value to be provided (even if nothing actually happened). +* Fixes database IP addresses not allowing a `0` in the first octet field. +* Fixes node information being impossible to update if there was a network error during the process. Any errors encountered communicating with Wings are now reported but will not block the actual saving of the changes. +* **[Security]** When 2FA is required on an account the client API endpoints will now properly return an error and the UI will redirect the user to setup 2FA. +* **[Security]** When changing the owner of a server the old owner's JWT is now properly invalidated on Wings. +* Fixes a server error when requesting database information for a server as a subuser and the account is not granted `view_password` permissions. + +### Added +* Adds support for basic backup rotation on a server when creating scheduled backup tasks. +* Makes URLs present in the console clickable. +* Adds `chmod` support to the file manager so that users can manually make modifications to file permissions as they need. + +### Changed +* UI will no longer show a delete button to users when they're editing themselves. +* Updated logic for bulk power actions to no longer run actions against suspended or installing servers. + +## v1.1.2 +### Fixed +* Fixes an exception thrown while trying to validate IP access for the client API. +* Fixes command history scrolling not putting the cursor at the end of the line. +* Fixes file manager rows triggering a 404 when middle-clicked to open in a new tab. + +## v1.1.1 +### Fixed +* Fixes allocation permissions checking on the frontend checking the wrong permission therefore leading to the item never showing up. +* Fixes allocations not updating correctly when added or deleted and switching between pages. + +## v1.1.0 +This release **requires** `Wings@1.1.0` in order to work properly due to breaking internal API changes. + +### Fixed +* Fixes subuser creation/edit modal not submitting correctly when attemping to make modifications. +* Fixes a few remaining issues with multiple egg install scripts. +* Removes the ability for a schedule to have a null name and replaces any existing null names with a randomly generated name. +* Fixes schedules aborting the entire run process if a single schedule encountered an exception. This resolves batches of schedules never running correctly if they occur after a broken schedule. +* Fixes schedules not properly resetting themselves if an exception was encountered during the run. +* Fixes numerous N+1 query run-aways when loading multiple servers via the API. +* Fixes numerous issues with displaying directory and file names in the file manager if they included special characters that could not be decoded properly. +* Fixes CPU pinning not being properly passed along to Wings when updated (this also fixes memory/CPU/disk not passing along correctly as well). +* Fixes spinner not displaying properly when displayed over a modal. + +### Added +* Adds ability for users to generate their own additional server allocations via the frontend if enabled. +* Adds the ability for a user to remove un-needed allocations from their server (as long as it is not the primary allocation). +* Adds support for tracking the last 32 sent console commands for a server. Access the history by using the arrow keys when the command field is active. +* Adds S3 specific environment variables allowing for backups to use any S3 compatiable system, not just AWS. +* Adds support for copying a server allocation address to the clipboard when clicked. +* Adds information about the next schedule run time when viewing an individual schedule. +* Adds link to view a server in the admin control panel to the frontend server view when logged in as a root admin. +* Adds support for egg-specific frontend/backend functionality. This is a beta feature meant for internal features at this time. +* Adds back the EULA warning popup when starting a Minecraft server without an accepted EULA. +* Adds missing descriptions for some user permissions on the frontend UI. + +### Changed +* Adds Save/Invite button to top of subuser edit/creation modal to reduce the need for scrolling. +* Updated language for server transfers and mounts to be less confusing. +* Wings API endpoint for fetching all servers on a node is now properly paginated to reduce system load when returning hundreds or thousands of servers at once. +* Removes unnecessary Wings API calls when adding/editing/deleting mounts. +* Primary allocation for a server is now always returned, even if the subuser does not have permission to view all of the server allocations. +* Google Analytics frontend code is now only loaded when a valid key is provided. + +## v1.0.3 +### Fixed +* Fixes bug causing subusers to not be creatable or editable via the frontend for servers. +* Fixes system timezone not being passed along properly to the MySQL connection causing scheduled tasks to run every minute when the MySQL instance and Panel timezone did not line up. +* Fixes listing servers owned by a user in the admin area to actually list their servers. + +### Changed +* Adds SameSite `lax` attribute for cookies generated by the Panel. +* Adds better filtering for searching servers in the admin area to better key off name, uuid, or owner username/email. + +## v1.0.2 +### Added +* Adds support for searching inside the file editor. +* Adds support for manually executing a schedule regardless of if it is currently queued or not. +* Adds an indicator to the schedule UI to show when a schedule is currently processing. +* Adds support for setting the `backup_limit` of a server via the API. +* **[Security]** Adds login throttling to the 2FA verification endpoint. + +### Fixed +* Fixes subuser page title missing server name. +* Fixes schedule task `sequence_id` not properly being reset when a schedule's task is deleted. +* Fixes misc. UI bugs throughout the frontend when long text overflows its bounds. +* Fixes user deletion command to properly handle email & ID searching. +* Fixes color issues in the terminal causing certain text & background combinations to be illegible. +* Fixes reCAPTCHA not properly resetting on login failure. +* Fixes error messages not properly resetting between login screens. +* Fixes a UI crash when attempting to create or view a directory or file that contained the `%` somewhere in the name. + +### Changed +* Updated the search modal to close itself when the ESC key is pressed. +* Updates the schedule view and editing UI to better display information to users. +* Changed logic powering server searching on the frontend to return more accurate results and return all servers when executing the query as an admin. +* Admin CP link no longer opens in a new tab. +* Mounts will no longer allow a user to mount certain directory combinations. This blocks mounting one server's files into another server, and blocks using the server data directory as a mount destination. +* Cleaned up assorted server build modification code. +* Updates default eggs to have improved install scripts and more consistent container usage. + +## v1.0.1 +### Fixed +* Fixes 500 error when mounting a mount to a server, and other related errors when handling mounts. +* Ensures that `server_transfers` database is deleted if it already exists to avoid unnecessary error. +* Fixes servers getting marked as "not installed" when modifying their startup arguments. +* Fixes filemanager breadcrumbs being set incorrectly when navigating between files and folders. + +### Changed +* Change the requests per minute from 240 to 720 for the client API to avoid unecessarily displaying +"Too Many Requests" errors. +* Added error output to certain commands that will output and terminate the command execution if the database +migrations have not been run correctly for the instance. + +## v1.0.0 +Pterodactyl 1.0 represents the culmination of over two years of work, almost 2,000 commits, endless bug and feature requests, and a dream that +has been in the making since 2013. 🎉 + +Due to the sheer size and timeline of this release I've massively truncated the listing below. There are numerous smaller +bug fixes and changes that would simply be too difficult to keep track of here. Please feel free to browse through the releases +tab for this repository to see more specific changes that have been made. + +### Added +* Adds a new client-facing API allowing a user to control all aspects of their individual servers, or servers +which they have been granted access to as a subuser. +* Adds the ability for backups to be created for a server both manually and via a scheduled task. +* Adds the ability for users to modify their server allocations on the fly and include notes for each allocation. +* Adds the ability for users to generate recovery tokens for 2FA protected logins which can be used in place of +a code should their device be inaccessible. +* Adds support for transfering servers between Nodes via the Panel. +* Adds the ability to assign specific CPU cores to a server (CPU Pinning) process. +* Server owners can now reinstall their assigned server egg automatically with a button on the frontend. + +### Changed +* The entire user frontend has been replaced with a responsive, React backed design implemented using Tailwind CSS. +* Replaces a large amount of complex daemon authentication logic by funneling most API calls through the Panel, and using +JSON Web Tokens where necessary to handle one-time direct authentication with Wings. +* Frontend server listing now includes a toggle to show or hide servers which an administrator has access to, rather +than always showing all servers on the system when logged into an admin account. +* We've replaced Ace Editor on the frontend with a better solution to allow lighter builds and more end-user functionality. +* Server permissions have been overhauled to be both easier to understand in the codebase, and allows plugins to better +hook into the permission system. + +### Removed +* Removes large swaths of code complexity and confusing interface designs that caused a lot of pain to new developers +trying to jump into the codebase. We've simplified this to stick to more established Laravel design standards to make +it easy to parse through the project and make contributions. + +## v0.7.19 (Derelict Dermodactylus) +### Fixed +* **[Security]** Fixes XSS in the admin area's server owner selection. + +## v0.7.18 (Derelict Dermodactylus) +### Fixed +* **[Security]** Re-addressed missed endpoint that would not properly limit a user account to 5 API keys. +* **[Security]** Addresses a Client API vulnerability that would allow a user to list all servers on the system ([`GHSA-6888-7f3w-92jx`](https://github.com/pterodactyl/panel/security/advisories/GHSA-6888-7f3w-92jx)) + +## v0.7.17 (Derelict Dermodactylus) +### Fixed +* Limited accounts to 5 API keys at a time. +* Fixes database passwords not being generated with the proper requirements for some MySQL setups. +* Hostnames that are not FQDNs/IP addresses can now be used for connecting to a MySQL host. + +## v0.7.16 (Derelict Dermodactylus) +### Fixed +* Fixed the /api/application/servers endpoint erroring when including subusers or egg +* Fixed bug in migration files causing failures when using MySQL 8. +* Fixed missing redirect return when an error occurs while modifying database information. +* Fixes bug in login attempt tracking. +* Fixes a bug where certain URL encoded files would not be editable in the file manager. + +### Added +* The application API now includes the egg's name in the egg model's response. +* The /api/application/servers endpoint can now include server's databases and subusers. + +## v0.7.15 (Derelict Dermodactylus) +### Fixed +* Fixes support for PHP 7.3 when running `composer install` commands due to a dependency that needed updating. +* Automatic allocation field when creating a new node (or updating one) should now properly remeber its old +value when showing an error state. +* Mass deleting files now executes properly and doesn't result in a JS console error. +* Scrolling on email settings page now works. +* Database host management will now properly display an error message to the user when there is any type of MySQL related +error encountered during creation or update. +* Two-factor tokens generated when a company name has a space in it will now properly be parsed on iOS authenticator devices. +* Fixed 500 error when trying to request subuser's from a server in the application API. +* Creating a node allocation via the API no longer requires an alias field be passed through in the request. +* Bulk power management for servers via the CLI no longer fails when servers span multiple nodes. + +### Added +* Server listing view now displays the total used disk space for each server. +* Client API endpoint to list all servers now supports an additional `?filter=subuser-of|all|admin|owner` parameter to +return different groupings of servers. The default value is `subuser-of` which will include all of the user's servers +that they are the owner of, as well as all servers they're a subuser of. +* Added back ability to toggle OOM killer status on a per-server basis. +* Added `LOCK TABLES` permission for generated database users. + +### Changed +* Updated Paper egg to not download `server.properties` each time. [parkervcp/eggs#260](https://github.com/parkervcp/eggs/issues/260) +* Insurgency egg now uses the proper dedicated server ID. +* Teamspeak egg updated with improved installation process and grabbing latest versions. +* OOM killer disabled by default on all new servers. +* Passwords generated for MySQL now include special characters and are 24 characters in length. + +## v0.7.14 (Derelict Dermodactylus) +### Fixed +* **[SECURITY]** Fixes an XSS vulnerability when performing certain actions in the file manager. +* **[SECURITY]** Attempting to login as a user who has 2FA enabled will no longer request the 2FA token before validating +that their password is correct. This closes a user existence leak that would expose that an account exists if +it had 2FA enabled. + +### Changed +* Support for setting a node to listen on ports lower than 1024. +* QR code URLs are now generated without the use of an external library to reduce the dependency tree. +* Regenerated database passwords now respect the same settings that were used when initially created. +* Cleaned up 2FA QR code generation to use a more up-to-date library and API. +* Console charts now properly start at 0 and scale based on server configuration. No more crazy spikes that +are due to a change of one unit. + +## v0.7.13 (Derelict Dermodactylus) +### Fixed +* Fixes a bug with the location update API endpoint throwing an error due to an unexected response value. +* Fixes bug where node creation API endpoint was not correctly requiring the `disk_overallocate` key. +* Prevents an exception from being thrown when a database with the same name is created on two different hosts. +* Fixes the redis password not saving correctly when setting up the environment from the command line. +* Fixes a bug with transaction handling in many areas of the application that would cause validation error messages +and other session data to not be persisted properly when using the database as the session driver. +* Fix a bug introduced at some point in the past that causes internal data integrity exceptions to not bubble up to +the user correctly, leading to extraneous and confusing exception messages. +* Fixes a bug causing servers to not be marked as having failed installation in some cases. + +### Changed +* `allocation_limit` for servers now defaults to a null value, and is not required in PATCH/POST requests when adding +a server through the API. +* The `PATCH` endpoint for `/api/applications/servers/{server}/build` now accepts an array called `limits` to match +the response from the server `GET` endpoint. + +### Added +* The server listing for a node is now paginated to 25 servers per page to improve performance on large nodes. + +## v0.7.12 (Derelict Dermodactylus) +### Fixed +* Fixes an issue with the locations API endpoint referencing an invalid namespace. +* Fixes the `store()` function on the locations API not working due to an incorrect return typehint. +* Fixes daemon secrets not being able to be reset on a Node. +* Fixes an issue where files were not editable due to missing URL encoding in the file manager. +* Fixed checking of language changes +* Fixed Spigot egg not building versions other than `latest`. +* Fixed the Forge egg install script. +* Fixes a bug that would ignore the `skip_scripts` setting when creating or editing a server. + +### Updated +* Upgraded core to use Laravel `5.7.14`. +* Updated Simplified Chinese translation pack. + +### Added +* Added support for opening and editing Python files through the web editor. +* Adds Russian translation. + +## v0.7.11 (Derelict Dermodactylus) +### Fixed +* Fixes an issue with certain systems not handling an API folder that was named `API` but referenced as `Api` in the namespace. +* TS3 egg updated to use CLI arguments correctly and have a more minimalistic installation script. +* Terminal was not properly displaying longer lines leading to some visual inconsistency. +* Assorted translation updates. +* Pagination for server listing now properly respects configuration setting. +* Client API now properly respects permissions that are set and allows subusers to access their assigned servers. + +### Changed +* Removed PhraseApp integration from Panel code as it is no longer used. +* SFTP login endpoint now returns the permissions for that user rather than requiring additional queries to get that data. + +### Added +* You can now test your mail settings from the Admin CP without waiting to see if things are working correctly. + +## v0.7.10 (Derelict Dermodactylus) +### Fixed +* Scheduled tasks triggered manually no longer improperly change the `next_run_at` time and do not run twice in a row anymore. +* Changing the maximum web-based file upload size for a node now properly validates and updates. +* Changing configuration values for a node now correctly updates them on the daemon on the first request, rather than requiring a second request to set them. + +### Changed +* Egg and server variable values are no longer limited to 191 characters. Turns out some games require a large number of characters in these fields. + +### Added +* Users can now select their preferred language in their account settings. + +## v0.7.9 (Derelict Dermodactylus) +### Fixed +* Fixes a two-factor authentication bypass present in the password reset process for an account. + +## v0.7.8 (Derelict Dermodactylus) +### Added +* Nodes can now be put into maintenance mode to deny access to servers temporarily. +* Basic statistics about your panel are now available in the Admin CP. +* Added support for using a MySQL socket location for connections rather than a TCP connection. Set a `DB_SOCKET` variable in your `.env` file to use this. + +### Fixed +* Hitting Ctrl+Z when editing a file on the web now works as expected. +* Logo now links to the correct location on all pages. +* Permissions checking to determine if a user can see the task management page now works correctly. +* Fixed `pterodactyl.environment_variables` to be used correctly for global environment variables. The wrong config variable name was being using previously. +* Fixes tokens being sent to users when their account is created to actually work. Implements Laravel's internal token creation mechanisms rather than trying to do it custom. +* Updates some eggs to ensure they have the correct data and will continue working down the road. Fixes autoupdating on some source servers and MC related download links. +* Emails should send properly now when a server is marked as installed to let the owner know it is ready for action. +* Cancelling a file manager operation should cancel correctly across all browsers now. + +### Changed +* Attempting to upload a folder via the web file manager will now display a warning telling the user to use SFTP. +* Changing your account password will now log out all other sessions that currently exist for that user. +* Subusers with no permissions selected can be created. + +## v0.7.7 (Derelict Dermodactylus) +### Fixed +* Fixes an issue with the sidebar logo not working correctly in some browsers due to the CSS being assigned. +* Fixes a bunch of typos through the code base. +* Fixes a bug when attempting to load the dropdown menu for server owner in some cases. +* Fixes an exception thrown when the database connection address was not filled out correctly while adding a database to a server. +* Fixes some mistakes in the German translation of the panel. + +### Added +* Added a new client API endpoint for gathering the utilization stats for servers including disk, cpu, and memory. `GET /api/client/servers//utilization` +* Added validation to variable validation rules to validate that the validation rules are valid because we heard you like validating your validation. +* Added German translations for many previously untranslated parts of the panel. + +### Changed +* Updated core framework from Laravel 5.5 to Laravel 5.6. +* Improved support for Windows based environments. +* Spigot Egg now builds spigot for you rather than requiring a download location be specified. + +## v0.7.6 (Derelict Dermodactylus) +### Fixed +* Fixes a UI error when attempting to change the default Nest and Egg for an existing server. +* Correct permissions check in UI to allow subusers with permission to `view-allocations` the ability to actually see the sidebar link. +* Fixes improper behavior when marking an egg as copying the configuration from another. +* Debug bar is only checked when the app is set to debug mode in the API session handler, rather than when it is in local mode to match the plugin settings. +* Added validation to port allocations to prevent allocation of restricted or invalid ports. +* Fix data integrity exception thrown when attempting to store updated server egg variables. +* Added missing permissions check on 'SFTP Configuration' page to ensure user has permission to access a server's SFTP server before showing a user credentials. + +### Added +* Added ability for end users to change the name of their server through the UI. This option is only open to the server owner or an admin. +* Added giant warning message if you attempt to change an encryption key once one has been set. + +### Changed +* Panel now throws proper 504: Gateway Timeout errors on server listing when daemon is offline. +* Sessions handled through redis now use a separate database (default `1`) to store session database to avoid logging users out when flushing the cache. +* File manager UI improved to be clearer with buttons and cleaner on mobile. +* reCAPTCHA's secret key position swapped with website key in advanced panel settings to be consistent with Google's reCAPTCHA dashboard. +* Changed DisplayException to handle its own logging correctly and check if the previous exception is marked as one that should not be logged. +* Changed 'New Folder' modal in file manager to include a trailing slash. + +## v0.7.5 (Derelict Dermodactylus) +### Fixed +* Fixes application API keys being created as a client API key. +* Search term is now passed through when using paginated result sets. +* Reduces the number of SQL queries executed when rendering the server listing to increase performance. +* Fixes exceptions being thrown for non-existent subuser permissions. +* Fixes exception caused when trying to revoke admin privileges from a user account due to a bad endpoint. + +### Changed +* Databases are now properly paginated when viewing a database host. +* No more loading daemon keys for every server model being loaded, some of us value our databases. +* Changed behavior of the subuser middleware to add a daemon access key if one is missing from the database for some reason. +* Server short-codes are now based on the UUID as they were in previous versions of Pterodactyl. + +## v0.7.4-h1 (Derelict Dermodactylus) +### Fixed +* Being able to create servers is kind of a core aspect of the software, pushing releases late at night is not a great idea. + +## v0.7.4 (Derelict Dermodactylus) +### Fixed +* Fixes a bug when reinstalling a server that would not mark the server as installing, resulting in some UI issues. +* Handle 404 errors from missing models in the application API bindings correctly. +* Fix validation error returned when no environment variables are passed, even if there are no variables required. +* Fix improper permissions on `PATCH /api/servers//startup` endpoint which was preventing editing any start variables. +* Should fix migration issues from 0.6 when there are more than API key in the database. + +### Changed +* Changes order that validation of resource existence occurs in API requests to not try and use a non-existent model when validating data. + +### Added +* Adds back client API for sending commands or power toggles to a server though the Panel API: `/api/client/servers/` +* Added proper transformer for Packs and re-enabled missing includes on server. +* Added support for using Filesystem as a caching driver, although not recommended. +* Added support for user management of server databases. +* **Added bulk power management CLI interface to send start, stop, kill, restart actions to servers across configurable nodes.** + +## v0.7.3 (Derelict Dermodactylus) +### Fixed +* Fixes server creation API endpoint not passing the provided `external_id` to the creation service. +* Fixes a bug causing users to be un-editable on new installations once more than one user exists. +* Fixes default order of buttons in certain parts of the panel that would default to 'Delete' rather than 'Save' when pressing enter. + +### Added +* Adds ability to modify the external ID for a server through the API. + +## v0.7.2 (Derelict Dermodactylus) +### Fixed +* Fixes an exception thrown when trying to access the `/nests/:id/eggs/:id` API endpoint. +* Fixes search on server listing page. +* Schedules with no names are now clickable to allow editing. +* Fixes broken permissions check that would deny access to API keys that did in fact have permission. + +### Added +* Adds ability to include egg variables on an API request. +* Added `external_id` column to servers that allows for easier linking with external services such as WHMCS. +* Added back the sidebar when viewing servers that allows for quick-switching to a different server. +* Added API endpoint to get a server by external ID. + +## v0.7.1 (Derelict Dermodactylus) +### Fixed +* Fixes an exception when no token is entered on the 2-Factor enable/disable page and the form is submitted. +* Fixes an exception when trying to perform actions against a User model due to a validator that could not be cast to a string correctly. +* Allow FQDNs in database host creation UI correctly. +* Fixes database naming scheme using `d###_` rather than `s###_` when creating server databases. +* Fix exception thrown when attempting to update an existing database host. + +### Changed +* Adjusted exception handler behavior to log more stack information for PDO exceptions while not exposing credentials. + +### Added +* Very basic cache busting until asset management can be changed to make use of better systems. + +## v0.7.0 (Derelict Dermodactylus) +### Fixed +* `[rc.2]` — Fixes bad API behavior on `/user` routes. +* `[rc.2]` — Fixes Admin CP user editing resetting a password on users unintentionally. +* `[rc.2]` — Fixes bug with server creation API endpoint that would fail to validate `allocation.default` correctly. +* `[rc.2]` — Fix data integrity exception occurring due to invalid data being passed to server creation service on the API. +* `[rc.2]` — Fix data integrity exception that could occur when an email containing non-username characters was passed. +* `[rc.2]` — Fix data integrity exception occurring when no default value is provided for an egg variable. +* `[rc.2]` — Fixes a bug that would cause non-editable variables on the front-end to throw a validation error. +* `[rc.2]` — Fixes a data integrity exception occurring when saving egg variables with no value. +* Fixes a design bug in the database that prevented the storage of negative numbers, thus preventing a server from being assigned unlimited swap. +* Fixes a bug where the 'Assign New Allocations' box would only show IPs that were present in the current pagination block. +* Unable to change the daemon secret for a server via the Admin CP. +* Using default value in rules when creating a new variable if the rules is empty. +* Fixes a design-flaw in the allocation management part of nodes that would run a MySQL query for each port being allocated. This behavior is now changed to only execute one query to add multiple ports at once. +* Attempting to create a server when no nodes are configured now redirects to the node creation page. +* Fixes missing library issue for teamspeak when used with mariadb. +* Fixes inability to change the default port on front-end when viewing a server. +* Fixes bug preventing deletion of nests that have other nests referencing them as children. +* Fixes console sometimes not loading properly on slow connections + +### Added +* Added ability to search the following API endpoints: list users, list servers, and list locations. +* Add support for finding a user by external ID using `/api/application/users/external/` or by passing it as the search term when listing all users. +* Added a unique key to the servers table to data integrity issues where an allocation would be assigned to more than one server at once. +* Added support for editing an existing schedule. +* Added support for editing symlinked files on the Panel. +* Added new application specific API to Panel with endpoints at `/api/application`. Includes new Admin CP interface for managing keys and an easier permissions system. +* Nest and Egg listings now show the associated ID in order to make API requests easier. +* Added star indicators to user listing in Admin CP to indicate users who are set as a root admin. +* Creating a new node will now requires a SSL connection if the Panel is configured to use SSL as well. +* Socketio error messages due to permissions are now rendered correctly in the UI rather than causing a silent failure. +* File manager now supports mass deletion option for files and folders. +* Support for CS:GO as a default service option selection. +* Support for GMOD as a default service option selection. +* Added test suite for core aspects of the project (Services, Repositories, Commands, etc.) to lessen the chances for bugs to escape into releases. +* New CLI command to disabled 2-Factor Authentication on an account if necessary. +* Ability to delete users and locations via the CLI. +* You can now require 2FA for all users, admins only, or at will using a simple configuration in the Admin CP. +* **Added ability to export and import service options and their associated settings and environment variables via the Admin CP.** +* Default allocation for a server can be changed on the front-end by users. This includes two new subuser permissions as well. +* Significant improvements to environment variable control for servers. Now ships with built-in abilities to define extra variables in the Panel's configuration file, or in-code for those heavily modifying the Panel. +* Quick link to server edit view in ACP on frontend when viewing servers. +* Databases created in the Panel now include `EXECUTE` privilege. + +### Changed +* PHP 7.2 is now the minimum required version for this software. +* Egg variable default values are no longer validated against the ruleset when configuring them. Validation of those rules will only occur when editing or creating a server. +* Changed logger to skip reporting stack-traces on PDO exceptions due to sensitive information being contained within. +* Changed behavior of allocation IP Address/Ports box to automatically store the value entered if a user unfocuses the field without hitting space. +* Changed order in which allocations are displayed to prioritize those with servers attached (in ascending IP & port order) followed by ascending IP & port order where no server is attached. +* Revoking the administrative status for an admin will revoke all authentication tokens currently assigned to their account. +* Updated core framework to Laravel 5.5. This includes many dependency updates. +* Certain AWS specific environment keys were changed, this should have minimal impact on users unless you specifically enabled AWS specific features. The renames are: `AWS_KEY -> AWS_ACCESS_KEY_ID`, `AWS_SECRET -> AWS_SECRET_ACCESS_KEY`, `AWS_REGION -> AWS_DEFAULT_REGION` +* API keys have been changed to only use a single public key passed in a bearer token. All existing keys can continue being used, however only the first 32 characters should be sent. +* Moved Docker image setting to be on the startup management page for a server rather than the details page. This value changes based on the Nest and Egg that are selected. +* Two-Factor authentication tokens are now 32 bytes in length, and are stored encrypted at rest in the database. +* Login page UI has been improved to be more sleek and welcoming to users. +* Changed 2FA login process to be more secure. Previously authentication checking happened on the 2FA post page, now it happens prior and is passed along to the 2FA page to avoid storing any credentials. +* **Services renamed to Nests. Service Options renamed to Eggs.** 🥚 +* Theme colors and login pages updated to give a more unique feel to the project. +* Massive overhaul to the backend code that allows for much easier updating of core functionality as well as support for better testing. This overhaul also reduces complex code logic, and allows for faster response times in the application. +* CLI commands updated to be easier to type, now stored in the `p:` namespace. +* Logout icon is now more universal and not just a power icon. +* Administrative logout notice now uses SWAL rather than a generic javascript popup. +* Server creation page now only asks for a node to deploy to, rather than requiring a location and then a node. +* Database passwords are now hidden by default and will only show if clicked on. In addition, database view in ACP now indicates that passwords must be viewed on the front-end. +* Localhost cannot be used as a connection address in the environment configuration script. `127.0.0.1` is allowed. +* Application locale can now be quickly set using an environment variable `APP_LOCALE` rather than having to edit core files. + +### Removed +* OOM exceptions can no longer be disabled on servers due to a startling number of users that were using it to avoid allocating proper amounts of resources to servers. +* SFTP settings page now only displays connection address and username. Password setting was removed as it is no longer necessary with Daemon changes. + +## v0.7.0-rc.2 (Derelict Dermodactylus) +### Fixed +* `[rc.1]` — Fixes exception thrown when revoking user sessions. +* `[rc.1]` — Fixes exception that would occur when trying to delete allocations from a node. +* `[rc.1]` — Fixes exception thrown when attempting to adjust mail settings as well as a validation error thrown afterwards. +* `[rc.1]` — Fixes bug preventing modification of the default value for an Egg variable. +* `[rc.1]` — Fixed a bug that would occur when attempting to reset the daemon secret for a node. +* `[rc.1]` — Fix exception thrown when attempting to modify an existing database host. +* `[rc.1]` — Fix an auto deployment bug causing a node to be ignored if it had no servers already attached to it. + +### Changed +* Changed logger to skip reporting stack-traces on PDO exceptions due to sensitive information being contained within. + +### Added +* Added support for editing an existing schedule. + +## v0.7.0-rc.1 (Derelict Dermodactylus) +### Fixed +* `[beta.4]` — Fixes some bad search and replace action that happened previously and was throwing errors when validating user permissions. +* `[beta.4]` — Fixes behavior of variable validation to not break the page when no rules are provided. +* `[beta.4]` — Fix bug preventing the editing of files in the file manager. + +### Added +* Added support for editing symlinked files on the Panel. +* Added new application specific API to Panel with endpoints at `/api/application`. Includes new Admin CP interface for managing keys and an easier permissions system. + +## v0.7.0-beta.4 (Derelict Dermodactylus) +### Fixed +* `[beta.3]` — Fixes a bug with the default environment file that was causing an inability to perform a fresh install when running package discovery. +* `[beta.3]` — Fixes an edge case caused by the Laravel 5.5 upgrade that would try to perform an in_array check against a null value. +* `[beta.3]` — Fixes a bug that would cause an error when attempting to create a new user on the Panel. +* `[beta.3]` — Fixes error handling of the settings service provider when no migrations have been run. +* `[beta.3]` — Fixes validation error when trying to use 'None' as the 'Copy Script From' option for an egg script. +* Fixes a design bug in the database that prevented the storage of negative numbers, thus preventing a server from being assigned unlimited swap. +* Fixes a bug where the 'Assign New Allocations' box would only show IPs that were present in the current pagination block. + +### Added +* Nest and Egg listings now show the associated ID in order to make API requests easier. + +### Changed +* Changed behavior of allocation IP Address/Ports box to automatically store the value entered if a user unfocuses the field without hitting space. +* Changed order in which allocations are displayed to prioritize those with servers attached (in ascending IP & port order) followed by ascending IP & port order where no server is attached. + +### Removed +* OOM exceptions can no longer be disabled on servers due to a startling number of users that were using it to avoid allocating proper amounts of resources to servers. + +## v0.7.0-beta.3 (Derelict Dermodactylus) +### Fixed +* `[beta.2]` — Fixes a bug that would cause an endless exception message stream in the console when attempting to setup environment settings in certain instances. +* `[beta.2]` — Fixes a bug causing the dropdown menu for a server's egg to display the wrong selected value. +* `[beta.2]` — Fixes a bug that would throw a red page of death when submitting an invalid egg variable value for a server in the Admin CP. +* `[beta.2]` — Someone found a `@todo` that I never `@todid` and thus database hosts could not be created without being linked to a node. This is fixed... +* `[beta.2]` — Fixes bug that caused incorrect rendering of CPU usage on server graphs due to missing variable. +* `[beta.2]` — Fixes bug causing schedules to be un-deletable. +* `[beta.2]` — Fixes bug that prevented the deletion of nodes due to an allocation deletion cascade issue with the SQL schema. +* `[beta.2]` — Fixes a bug causing eggs not extending other eggs to fail validation. + +### Changed +* Revoking the administrative status for an admin will revoke all authentication tokens currently assigned to their account. +* Updated core framework to Laravel 5.5. This includes many dependency updates. +* Certain AWS specific environment keys were changed, this should have minimal impact on users unless you specifically enabled AWS specific features. The renames are: `AWS_KEY -> AWS_ACCESS_KEY_ID`, `AWS_SECRET -> AWS_SECRET_ACCESS_KEY`, `AWS_REGION -> AWS_DEFAULT_REGION` +* API keys have been changed to only use a single public key passed in a bearer token. All existing keys can continue being used, however only the first 32 characters should be sent. + +### Added +* Added star indicators to user listing in Admin CP to indicate users who are set as a root admin. +* Creating a new node will now requires a SSL connection if the Panel is configured to use SSL as well. + +## v0.7.0-beta.2 (Derelict Dermodactylus) +### Fixed +* `[beta.1]` — Fixes a CORS header issue due to a wrong API endpoint being provided in the administrative node listing. +* `[beta.1]` — Fixes bug that would prevent root admins from accessing servers they were not set as the owner of. +* `[beta.1]` — Fixes wrong URL redirect being provided when creating a subuser. +* `[beta.1]` — Fixes missing check in environment setup that would leave the Hashids salt empty. +* `[beta.1]` — Fixes bug preventing loading of allocations when trying to create a new server. +* `[beta.1]` — Fixes bug causing inability to create new servers on the Panel. +* `[beta.1]` — Fixes bug causing inability to delete an allocation due to misconfigured JS. +* `[beta.1]` — Fixes bug causing inability to set the IP alias for an allocation to an empty value. +* `[beta.1]` — Fixes bug that caused startup changes to not propagate to the server correctly on the first save. +* `[beta.1]` — Fixes bug that prevented subusers from accessing anything over socketio due to a missing permission. + +### Changed +* Moved Docker image setting to be on the startup management page for a server rather than the details page. This value changes based on the Nest and Egg that are selected. +* Two-Factor authentication tokens are now 32 bytes in length, and are stored encrypted at rest in the database. +* Login page UI has been improved to be more sleek and welcoming to users. +* Changed 2FA login process to be more secure. Previously authentication checking happened on the 2FA post page, now it happens prior and is passed along to the 2FA page to avoid storing any credentials. + +### Added +* Socketio error messages due to permissions are now rendered correctly in the UI rather than causing a silent failure. + +## v0.7.0-beta.1 (Derelict Dermodactylus) +### Added +* File manager now supports mass deletion option for files and folders. +* Support for CS:GO as a default service option selection. +* Support for GMOD as a default service option selection. +* Added test suite for core aspects of the project (Services, Repositories, Commands, etc.) to lessen the chances for bugs to escape into releases. +* New CLI command to disabled 2-Factor Authentication on an account if necessary. +* Ability to delete users and locations via the CLI. +* You can now require 2FA for all users, admins only, or at will using a simple configuration in the Admin CP. +* **Added ability to export and import service options and their associated settings and environment variables via the Admin CP.** +* Default allocation for a server can be changed on the front-end by users. This includes two new subuser permissions as well. +* Significant improvements to environment variable control for servers. Now ships with built-in abilities to define extra variables in the Panel's configuration file, or in-code for those heavily modifying the Panel. +* Quick link to server edit view in ACP on frontend when viewing servers. +* Databases created in the Panel now include `EXECUTE` privilege. + +### Changed +* **Services renamed to Nests. Service Options renamed to Eggs.** 🥚 +* Theme colors and login pages updated to give a more unique feel to the project. +* Massive overhaul to the backend code that allows for much easier updating of core functionality as well as support for better testing. This overhaul also reduces complex code logic, and allows for faster response times in the application. +* CLI commands updated to be easier to type, now stored in the `p:` namespace. +* Logout icon is now more universal and not just a power icon. +* Administrative logout notice now uses SWAL rather than a generic javascript popup. +* Server creation page now only asks for a node to deploy to, rather than requiring a location and then a node. +* Database passwords are now hidden by default and will only show if clicked on. In addition, database view in ACP now indicates that passwords must be viewed on the front-end. +* Localhost cannot be used as a connection address in the environment configuration script. `127.0.0.1` is allowed. +* Application locale can now be quickly set using an environment variable `APP_LOCALE` rather than having to edit core files. + +### Fixed +* Unable to change the daemon secret for a server via the Admin CP. +* Using default value in rules when creating a new variable if the rules is empty. +* Fixes a design-flaw in the allocation management part of nodes that would run a MySQL query for each port being allocated. This behavior is now changed to only execute one query to add multiple ports at once. +* Attempting to create a server when no nodes are configured now redirects to the node creation page. +* Fixes missing library issue for teamspeak when used with mariadb. +* Fixes inability to change the default port on front-end when viewing a server. +* Fixes bug preventing deletion of nests that have other nests referencing them as children. +* Fixes console sometimes not loading properly on slow connections + +### Removed +* SFTP settings page now only displays connection address and username. Password setting was removed as it is no longer necessary with Daemon changes. + +## v0.6.4 (Courageous Carniadactylus) +### Fixed +* Fixed the console rendering on page load, I guess people don't like watching it load line-by-line for 10 minutes. Who would have guessed... +* Re-added support for up/down arrows loading previous commands in the console window. + +### Changed +* Panel API for Daemon now responds with a `HTTP/401 Unauthorized` error when unable to locate a node with a given authentication token, rather than a `HTTP/404 Not Found` response. +* Added better colors and styling for the terminal that can be adjusted per-theme. +* Session timeout adjusted to be 7 days by default. + +## v0.6.3 (Courageous Carniadactylus) +### Fixed +* **[Security]** — Addresses an oversight in how the terminal rendered information sent from the server feed which allowed a malicious user to execute arbitrary commands on the game-server process itself by using a specifically crafted in-game command. + +### Changed +* Removed `jquery.terminal` and replaced it with an in-house developed terminal with less potential for security issues. + +## v0.6.2 (Courageous Carniadactylus) +### Fixed +* Fixes a few typos throughout the panel, there are more don't worry. +* Fixes bug when disabling 2FA due to a misnamed route. +* API now returns a 404 error when deleting a user that doesn't exist, rather than saying it was successful. +* Service variables that allow empty input now allow you to empty out the assigned value and set it back to blank. +* Fixes a bug where changing the default allocation for a server would not actually apply that allocation as the default on the daemon. +* Newly created service variables are now backfilled and assigned to existing servers properly. + +### Added +* Added a `Vagrantfile` to the repository to help speed up development and testing for those who don't want to do a full dedicated install. +* Added a confirmation dialog to the logout button for admins to prevent misguided clickers from accidentally logging out when they wanted to switch to Admin or Server views. + +### Changed +* Blocked out the `Reinstall` button for servers that have failed installation to avoid confusion and bugs causing the daemon to break. +* Updated dependencies, listed below. +``` +aws/aws-sdk-php (3.26.5 => 3.29.7) +laravel/framework (v5.4.21 => v5.4.27) +barryvdh/laravel-debugbar (v2.3.2 => v2.4.0) +fideloper/proxy (3.3.0 => 3.3.3) +igaster/laravel-theme (v1.14 => v1.16) +laravel/tinker (v1.0.0 => v1.0.1) +spatie/laravel-fractal (4.0.0 => 4.0.1) +``` + +## v0.6.1 (Courageous Carniadactylus) +### Fixed +* Fixes a bug preventing the use of services that have no variables attached to them. +* Fixes 'Remember Me' checkbox being ignored when using 2FA on an account. +* API now returns a useful error displaying what went wrong rather than an obscure 'An Error was Encountered' message when API issues arise. +* Fixes bug preventing the creation of new files in the file manager due to a missing JS dependency on page load. +* Prevent using a service option tag that contains special characters that are not valid. Now only allows alpha-numeric, no spaces or underscores. +* Fix unhandled exception due to missing `Log` class when using the API and causing an error. + +### Changed +* Renamed session cookies from `laravel_session` to `pterodactyl_session`. +* Sessions are now encrypted before being stored as an additional layer of security. +* It is now possible to clear out a server description and have it be blank, rather than throwing an error about the field being required. + +## v0.6.0 (Courageous Carniadactylus) +### Fixed +* Bug causing error logs to be spammed if someone timed out on an ajax based page. +* Fixes edge case where specific server names could cause daemon errors due to an invalid SFTP username being created by the panel. +* Fixes sessions being removed on browser close, and set sessions to idle for up to 3 hours before being marked as expired. +* Emails sending with 'Pterodactyl Panel' as the from name. Now configurable by using `php artisan pterodactyl:mail` to update. +* Fixes potential bug with invalid CIDR notation (ex: `192.168.1.1/z`) when adding allocations that could cause over 4 million records to be created at once. +* Fixes bug where daemon was unable to register that certain games had fully booted and were ready to play on. +* Fixes bug causing MySQL user accounts to be corrupted when resetting a password via the panel. +* Fixes remote timing attack vulnerability due to hmac comparison in API middleware. +* `[rc.1]` — Server deletion is fixed, caused by removed download table. +* `[rc.1]` — Server status indication on front-end no longer shows `Error` when server is marked as installing or suspended. +* `[rc.1]` — Fixes issues with SteamCMD not registering and installing games properly. + +### Changed +* Admin API and base routes for user management now define the fields that should be passed to repositories rather than passing all fields. +* User model now defines mass assignment fields using `$fillable` rather than `$guarded`. +* 2FA checkpoint on login is now its own page, and not an AJAX based call. Improves security on that front. +* Updated Server model code to be more efficient, as well as make life easier for backend changes and work. +* Reduced the number of database queries being executed when viewing a specific server. This is done by caching the query for up to 15 minutes in memcached. +* User creation emails include more information and are sent by the event listener rather than the repository. +* Account password reset emails now auto-fill the email when clicking the link. +* New theme applied to Admin CP. Many graphical changes were made, some data was moved around and some display data changed. Too much was changed to feasibly log it all in here. Major breaking changes or notable new features will be logged. +* New server creation page now makes significantly less AJAX calls and is much quicker to respond. +* Server and Node view pages wee modified to split tabs into individual pages to make re-themeing and modifications significantly easier, and reduce MySQL query loads on page. +* Most of the backend `UnhandledException` display errors now include a clearer error that directs admins to the program's logs. +* Table seeders for services now can be run during upgrades and will attempt to locate and update, or create new if not found in the database. +* Many structural changes to the database and `Pterodactyl\Models` classes that would flood this changelog if they were all included. All required migrations included to handle database changes. +* Clarified details for database hosts to prevent users entering invalid account details, as well as renamed tables and columns relating to it to keep things clearer. +* Updated all code to be Laravel compliant when using `env()` and moved to using `config()` throughout non `config/*.php` files. +* Subuser permissions are now stored in `Permission::listPermissions()` to make views way cleaner and make adding to views significantly cleaner. +* Attempting to reset a password for an account that does not exist no longer returns an error, rather it displays a success message. Failed resets trigger a `Pterodactyl\Events\Auth\FailedPasswordReset` event that can be caught if needed to perform other actions. +* Servers are no longer queued for deletion due to the general hassle and extra logic required. +* Updated all panel components to run on Laravel v5.4 rather than 5.3 which is EOL. +* Routes are now handled in the `routes/` folder, and use a significantly cleaner syntax. Controller names and methods have been updated as well to be clearer as well as avoid conflicts with PHP reserved keywords. +* API has been completely overhauled to use new permissions system. **Any old API keys will immediately become invalid and fail to operate properly anymore. You will need to generate new keys.** +* Cleaned up dynamic database connection setting to use a single function call from the host model. +* Deleting a server safely now continues even if the daemon reports a `HTTP/404` missing server error (requires `Daemon@0.4.0-beta.2.1`) +* Changed behavior when modifying server allocation information. You can now remove the default allocation assuming you are passing a new allocation at the same time. Reduces the number of steps to change the default allocation for a server. +* Environment setting commands now attempt to auto-quote strings with spaces in them, as well as comment lines that are edited to avoid manual changes being overwritten. +* Version in footer of panel now displays correctly if panel is installed using Git rather than a download from source. +* Mobile views are now more... viewable. Fixes `col-xs-6` usage throughout the Admin CP where it was intended to be `col-md-6`. +* Node Configuration tokens and Download tokens are stored using the cache helpers rather than a database to speed up functions and make use of auto-expiration/deletion functions. +* Old daemon routes using `/remote` have been changed to use `/daemon`, panel changes now reflect this. +* Only display servers that a user is owner of or subuser of in the Admin CP rather than all servers if the user is marked as an admin. +* Panel now sends all non-default allocations as `ALLOC_#__IP` and `ALLOC_#__PORT` to the daemon, as well as the location. + +### Added +* Remote routes for daemon to contact in order to allow Daemon to retrieve updated service configuration files on boot. Centralizes services to the panel rather than to each daemon. +* Basic service pack implementation to allow assignment of modpacks or software to a server to pre-install applications and allow users to update. +* Users can now have a username as well as client name assigned to their account. +* Ability to create a node through the CLI using `pterodactyl:node` as well as locations via `pterodactyl:location`. +* New theme (AdminLTE) for front-end with tweaks to backend files to work properly with it. +* Add support for PhraseApp's in-context editor +* Notifications when a user is added or removed as a subuser for a server. +* New cache policy for ServerPolicy to avoid making 15+ queries per page load when confirming if a user has permission to perform an action. +* Ability to assign multiple allocations at once when creating a new server. +* New `humanReadable` macro on `File` facade that accepts a file path and returns a human readable size. (`File::humanReadable(path, precision)`) +* Added ability to edit database host details after creation on the system. +* Login attempts and password reset requests are now protected by invisible ReCaptcha. This feature can be disabled with a `.env` variable. +* Server listing for individual users is now searchable on the front-end. +* Servers that a user is associated with as a subuser are now displayed in addition to owned servers when listing users in the Admin CP. +* Ability to launch the console in a new window as an individual unit. https://s3.kelp.in/IrTyE.png +* Server listing and view in Admin CP now shows the SFTP username/Docker container name. +* Administrative server view includes link in navigation to go to server console/frontend management. +* Added new scripts for service options that allows installation of software in a privileged Docker container on the node prior to marking a server as installed. +* Added ability to reinstall a server using the currently assigned service and option. +* Added ability to change a server's service and service option, as well as change pack assignments and other management services in that regard. +* Added support for using a proxy such as Cloudflare with a node connection. Previously there was no way to tell the panel to connect over SSL without marking the Daemon as also using SSL. + +### Removed +* Removed all old theme JS and CSS folders to cleanup and avoid confusion in the future. +* Old API calls to `Server::create` will fail due to changed data structure. +* Many old routes were modified to reflect new standards in panel, and many of the controller functions being called were also modified. This shouldn't really impact anyone unless you have been digging into the code and modifying things. +* `Server::getUserDaemonSecret(Server $server)` was removed and replaced with `User::daemonSecret(Server $server)` in order to clean up models. +* `Server::getByUUID()` was replaced with `Server::byUuid()` as well as various other functions through-out the Server model. +* `Server::getHeaders()` was removed and replaced with `Server::getClient()` which returns a Guzzle Client with the correct headers already assigned. + +## v0.6.0-rc.1 +### Fixed +* `[beta.2.1]` — Fixed a bug preventing the deletion of a server. +* It is now possible to modify a server's disk limits after the server is created. +* `[beta.2.1]` — Fixes a bug causing login issues and password reset failures when reCAPTCHA is enabled. +* Fixes remote timing attack vulnerability due to hmac comparison in API middleware. +* `[beta.2.1]` — Fixes bug requiring docker image field to be filled out when adding a service option. +* `[beta.2.1]` — Fixes inability to mark a user as a non-admin once they were assigned the role. + +### Added +* Added new scripts for service options that allows installation of software in a privileged Docker container on the node prior to marking a server as installed. +* Added ability to reinstall a server using the currently assigned service and option. +* Added ability to change a server's service and service option, as well as change pack assignments and other management services in that regard. +* Added support for using a proxy such as Cloudflare with a node connection. Previously there was no way to tell the panel to connect over SSL without marking the Daemon as also using SSL. + +### Changed +* Environment setting commands now attempt to auto-quote strings with spaces in them, as well as comment lines that are edited to avoid manual changes being overwritten. +* Version in footer of panel now displays correctly if panel is installed using Git rather than a download from source. +* Mobile views are now more... viewable. Fixes `col-xs-6` usage throughout the Admin CP where it was intended to be `col-md-6`. +* Node Configuration tokens and Download tokens are stored using the cache helpers rather than a database to speed up functions and make use of auto-expiration/deletion functions. +* Old daemon routes using `/remote` have been changed to use `/daemon`, panel changes now reflect this. +* Only display servers that a user is owner of or subuser of in the Admin CP rather than all servers if the user is marked as an admin. + +## v0.6.0-beta.2.1 +### Fixed +* `[beta.2]` — Suspended servers now show as suspended. +* `[beta.2]` — Corrected the information when a task has not run yet. +* `[beta.2]` — Fixes filemanager 404 when editing a file within a directory. +* `[beta.2]` — Fixes exception in tasks when deleting a server. +* `[beta.2]` — Fixes bug with Terarria and Voice servers reporting a `TypeError: Service is not a constructor` in the daemon due to a missing service configuration. +* `[beta.2]` — Fixes password reset form throwing a MethodNotAllowed error when accessed. +* `[beta.2]` — Fixes invalid password bug when attempting to change account email address. +* `[beta.2]` — New attempt at fixing the issues when rendering files in the browser file editor on certain browsers. +* `[beta.2]` — Fixes broken auto-deploy time checking causing no tokens to work. +* `[beta.2]` — Fixes display of subusers after creation. +* `[beta.2]` — Fixes bug throwing model not found exception when editing an existing subuser. + +### Changed +* Deleting a server safely now continues even if the daemon reports a `HTTP/404` missing server error (requires `Daemon@0.4.0-beta.2.1`) +* Changed behavior when modifying server allocation information. You can now remove the default allocation assuming you are passing a new allocation at the same time. Reduces the number of steps to change the default allocation for a server. + +### Added +* Server listing and view in Admin CP now shows the SFTP username/Docker container name. +* Administrative server view includes link in navigation to go to server console/frontend management. + +## v0.6.0-beta.2 +### Fixed +* `[beta.1]` — Fixes task management system not running correctly. +* `[beta.1]` — Fixes API endpoint for command sending missing the required class definition. +* `[beta.1]` — Fixes panel looking for an old compiled classfile that is no longer used. This was causing errors relating to `missing class DingoAPI` when trying to upgrade the panel. +* `[beta.1]` — Should fix render issues when trying to edit some files via the panel file editor. + +### Added +* Ability to launch the console in a new window as an individual unit. https://s3.kelp.in/IrTyE.png + +## v0.6.0-beta.1 +### Fixed +* `[pre.7]` — Fixes bug with subuser checkbox display. +* `[pre.7]` — Fixes bug with injected JS that was causing `` to be ignored in templates. +* `[pre.7]` — Fixes exception thrown when trying to delete a node due to a misnamed model. +* `[pre.7]` — Fixes username vanishing on failed login attempts. +* `[pre.7]` — Terminal is now fixed to actually output all lines, rather than leaving one hanging in neverland until the browser is resized. + +### Added +* Login attempts and password reset requests are now protected by invisible ReCaptcha. This feature can be disabled with a `.env` variable. +* Server listing for individual users is now searchable on the front-end. +* Servers that a user is associated with as a subuser are now displayed in addition to owned servers when listing users in the Admin CP. + +### Changed +* Subuser permissions are now stored in `Permission::listPermissions()` to make views way cleaner and make adding to views significantly cleaner. +* `[pre.7]` — Sidebar for file manager now is a single link rather than a dropdown. +* Attempting to reset a password for an account that does not exist no longer returns an error, rather it displays a success message. Failed resets trigger a `Pterodactyl\Events\Auth\FailedPasswordReset` event that can be caught if needed to perform other actions. +* Servers are no longer queued for deletion due to the general hassle and extra logic required. +* Updated all panel components to run on Laravel v5.4 rather than 5.3 which is EOL. +* Routes are now handled in the `routes/` folder, and use a significantly cleaner syntax. Controller names and methods have been updated as well to be clearer as well as avoid conflicts with PHP reserved keywords. +* API has been completely overhauled to use new permissions system. **Any old API keys will immediately become invalid and fail to operate properly anymore. You will need to generate new keys.** +* Cleaned up dynamic database connection setting to use a single function call from the host model. +* `[pre.7]` — Corrected a config option for spigot servers to set a boolean value as boolean, and not as a string. + +## v0.6.0-pre.7 +### Fixed +* `[pre.6]` — Addresses misconfigured console queue that was still sending data way to quickly thus causing the console to explode on some devices when large amounts of data were sent. +* `[pre.6]` — Fixes bug in allocation parsing for a node that prevented adding new allocations. +* `[pre.6]` — Fixes typo in migrations that wouldn't save custom regex for non-required variables. +* `[pre.6]` — Fixes auto-deploy checkbox on server creation causing validation error. + +## v0.6.0-pre.6 +### Fixed +* `[pre.5]` — Console based server rebuild tool now actually rebuilds the servers with the correct information. +* `[pre.5]` — Fixes typo and wrong docker container for certain applications. + +### Changed +* Removed all old theme JS and CSS folders to cleanup and avoid confusion in the future. + +### Added +* `[pre.5]` — Added foreign key to `pack_id` to ensure nothing eds up breaking there. + +## v0.6.0-pre.5 +### Changed +* New theme applied to Admin CP. Many graphical changes were made, some data was moved around and some display data changed. Too much was changed to feasibly log it all in here. Major breaking changes or notable new features will be logged. +* New server creation page now makes significantly less AJAX calls and is much quicker to respond. +* Server and Node view pages wee modified to split tabs into individual pages to make re-themeing and modifications significantly easier, and reduce MySQL query loads on page. +* `[pre.4]` — Service and Pack management overhauled to be faster, cleaner, and more extensible in the future. +* Most of the backend `UnhandledException` display errors now include a clearer error that directs admins to the program's logs. +* Table seeders for services now can be run during upgrades and will attempt to locate and update, or create new if not found in the database. +* Many structural changes to the database and `Pterodactyl\Models` classes that would flood this changelog if they were all included. All required migrations included to handle database changes. +* `[pre.4]` — Service pack files are now stored in the database rather than on the host system to make updates easier. +* Clarified details for database hosts to prevent users entering invalid account details, as well as renamed tables and columns relating to it to keep things clearer. +* Updated all code to be Laravel compliant when using `env()` and moved to using `config()` throughout non `config/*.php` files. + +### Fixed +* Fixes potential bug with invalid CIDR notation (ex: `192.168.1.1/z`) when adding allocations that could cause over 4 million records to be created at once. +* `[pre.4]` — Fixes bug preventing server updates from occurring by the system due to undefined `Auth::user()` in the event listener. +* `[pre.4]` — Fixes `Server::byUuid()` caching to actually clear the cache for *all* users, rather than the logged in user by using cache tags. +* `[pre.4]` — Fixes server listing on frontend not displaying a page selector when more than 10 servers exist. +* `[pre.4]` — Fixes non-admin users being unable to create personal API keys. +* Fixes bug where daemon was unable to register that certain games had fully booted and were ready to play on. +* Fixes bug causing MySQL user accounts to be corrupted when resetting a password via the panel. +* `[pre.4]` — Multiple clients refreshing the console no longer clears the console for all parties involved... sorry about that. +* `[pre.4]` — Fixes bug in environment setting script that would not remember defaults and try to re-assign values. + +### Added +* Ability to assign multiple allocations at once when creating a new server. +* New `humanReadable` macro on `File` facade that accepts a file path and returns a human readable size. (`File::humanReadable(path, precision)`) +* Added ability to edit database host details after creation on the system. + +### Deprecated +* Old API calls to `Server::create` will fail due to changed data structure. +* Many old routes were modified to reflect new standards in panel, and many of the controller functions being called were also modified. This shouldn't really impact anyone unless you have been digging into the code and modifying things. + +## v0.6.0-pre.4 +### Fixed +* `[pre.3]` — Fixes bug in cache handler that doesn't cache against the user making the request. Would have allowed for users to access servers not belonging to themselves in production. +* `[pre.3]` — Fixes misnamed MySQL column that was causing the inability to delete certain port ranges from the database. +* `[pre.3]` — Fixes bug preventing rebuilding server containers through the Admin CP. + +### Added +* New cache policy for ServerPolicy to avoid making 15+ queries per page load when confirming if a user has permission to perform an action. + +## v0.6.0-pre.3 +### Fixed +* `[pre.2]` — Fixes bug where servers could not be manually deployed to nodes due to a broken SQL call. +* `[pre.2]` — Fixes inability to edit a server due to owner_id issues. +* `[pre.2]` — Fixes bug when trying to add new subusers. +* Emails sending with 'Pterodactyl Panel' as the from name. Now configurable by using `php artisan pterodactyl:mail` to update. +* `[pre.2]` — Fixes inability to delete accounts due to SQL changes. +* `[pre.2]` — Fixes bug with checking power-permissions that showed the wrong buttons. Also adds check back to sidebar to only show options a user can use. +* `[pre.2]` — Fixes allocation listing on node allocations tab as well as bug preventing deletion of port. +* `[pre.2]` — Fixes bug in services that prevented saving updated settings or creating new services. + +### Changed +* `[pre.2]` — File Manager now displays relevant information on all screen sizes, and includes better button clicking mechanics for dropdown menu. +* Reduced the number of database queries being executed when viewing a specific server. This is done by caching the query for up to 60 minutes in memcached. +* User creation emails include more information and are sent by the event listener rather than the repository. +* Account password reset emails now auto-fill the email when clicking the link. + +### Added +* Notifications when a user is added or removed as a subuser for a server. + +## v0.6.0-pre.2 +### Fixed +* `[pre.1]` — Fixes bug with database seeders that prevented correctly installing the panel. + +### Changed +* `[pre.1]` — Moved around navigation bar on fronted to make it more obvious where logout and admin buttons were, as well as use the right icon for server listing. + +## v0.6.0-pre.1 +### Added +* Remote routes for daemon to contact in order to allow Daemon to retrieve updated service configuration files on boot. Centralizes services to the panel rather than to each daemon. +* Basic service pack implementation to allow assignment of modpacks or software to a server to pre-install applications and allow users to update. +* Users can now have a username as well as client name assigned to their account. +* Ability to create a node through the CLI using `pterodactyl:node` as well as locations via `pterodactyl:location`. +* New theme (AdminLTE) for front-end with tweaks to backend files to work properly with it. +* Add support for PhraseApp's in-context editor + +### Fixed +* Bug causing error logs to be spammed if someone timed out on an ajax based page. +* Fixes edge case where specific server names could cause daemon errors due to an invalid SFTP username being created by the panel. +* Fixes sessions being removed on browser close, and set sessions to idle for up to 3 hours before being marked as expired. + +### Changed +* Admin API and base routes for user management now define the fields that should be passed to repositories rather than passing all fields. +* User model now defines mass assignment fields using `$fillable` rather than `$guarded`. +* 2FA checkpoint on login is now its own page, and not an AJAX based call. Improves security on that front. +* Updated Server model code to be more efficient, as well as make life easier for backend changes and work. + +### Deprecated +* `Server::getUserDaemonSecret(Server $server)` was removed and replaced with `User::daemonSecret(Server $server)` in order to clean up models. +* `Server::getByUUID()` was replaced with `Server::byUuid()` as well as various other functions through-out the Server model. +* `Server::getHeaders()` was removed and replaced with `Server::getClient()` which returns a Guzzle Client with the correct headers already assigned. + +## v0.5.6 (Bodacious Boreopterus) +### Added +* Added the following languages: Estonian `et`, Dutch `nl`, Norwegian `nb` (partial), Romanian `ro`, and Russian `ru`. Interested in helping us translate the panel into more languages, or improving existing translations? Contact us on Discord and let us know. +* Added missing `strings.password` to language file for English. +* Allow listing of users from the API by passing either the user ID or their email. + +### Fixed +* Fixes bug where assigning a variable a default value (or valid value) of `0` would cause the panel to reject the value thinking it did not exist. +* Addresses potential for crash by limiting total ports that can be assigned per-range to 2000. +* Fixes server names requiring at minimum 4 characters. Name can now be 1 to 200 characters long. :pencil2: +* Fixes bug that would allow adding the owner of a server as a subuser for that same server. +* Fixes bug that would allow creating multiple subusers with the same email address. +* Fixes bug where Sponge servers were improperly tagged as a spigot server in the daemon causing issues when booting or modifying configuration files. +* Use transpiled ES6 -> ES5 filemanager code in browsers. +* Fixes service option name displaying the name of a newly added variable after the variable is added and until the page is refreshed. (see #208) + +### Changed +* Filemanager and EULA checking javascript is now written in pure ES6 code rather than as a blade-syntax template. This allows the use of babel to transpile into ES5 as a minified version. + +## v0.5.5 (Bodacious Boreopterus) +### Added +* New API route to return allocations given a server ID. This adds support for a community-driven WHMCS module :rocket: available [here](https://github.com/hammerdawn/Pterodactyl-WHMCS). + +### Fixed +* Fixes subuser display when trying to edit an existing subuser. + +## v0.5.4 (Bodacious Boreopterus) +### Added +* Changing node configuration values now automatically makes a call to the daemon and updates the configuration there. Changing daemon tokens now does not require any intervention, and takes effect immediately. SSL & Port configurations will still require a daemon reboot. +* New button in file manager that triggers the right click menu to enable support on mobile devices and those who cannot right click (blessed be them). +* Support for filtering users when listing all users on the system. +* Container ID and User ID on the daemon are now shown when viewing a server in the panel. + +### Changed +* File uploads now account for a maximum file size that is assigned for the daemon, and gives cleaner errors when that limit is reached. +* File upload limit can now be controlled from the panel. +* Updates regex and default values for some Minecraft services to reflect current technology. + +### Fixed +* Fixes potential for generated password to not meet own validation requirements. +* Fixes some regex checking issues with newer versions of Minecraft. + +## v0.5.3 (Bodacious Boreopterus) +### Fixed +* Fixed an error that occurred when viewing a node listing when no nodes were created yet due to a mis-declared variable. Also fixes a bug that would have all nodes trying to connect to the daemon using the same secret token on the node listing, causing only the last node to display properly. +* Fixes a bug that displayed the panel version rather than the daemon version when viewing a node. +* Fixes a multiplicator being applied to an overallocation field rather than a storage space field when adding a node. + +### Changed +* Added a few new configuration variables for nodes to the default config, as well as a variable that will be used in future versions of the daemon. + +## v0.5.2 (Bodacious Boreopterus) +### Fixed +* Time axis on server graphs is corrected to show the minutes rather than the current month. +* Node deletion now works correctly and deletes allocations as well. +* Fixes a bug that would leave orphaned databases on the system if there was an error during creation. +* Fixes an issue that could occur if a UUID contained `#e#` formatting within it when it comes to creating databases. +* Fixed node status display to account for updated daemon security changes. +* Fixes default language being selected as German (defaults to English now). +* Fixes bug preventing the deletion of database servers. + +### Changed +* Using `node:` when filtering servers now properly filters the servers by node name, rather than looking for the node ID. +* Using `owner:` when filtering servers now properly filters by the owner's email rather than ID. +* Added some quick help buttons to the admin index page for getting support or checking the documentation. +* Panel now displays `Pterodactyl Panel` as the company name if one is not set. + +### Added +* Added basic information about the daemon when viewing a node, including the host OS and version, CPU count, and the daemon version. +* Added version checking for the daemon and panel that alerts admins when daemons or the panel is out of date. +* Added multiplicator support to certain memory and disk fields that allow users to enter `10g` and have it converted to MB automatically. + +## v0.5.1 (Bodacious Boreopterus) +### Fixed +* Fixes a bug that allowed a user to bypass 2FA authentication if using the correct username and password for an account. + +## v0.5.0 (Bodacious Boreopterus) +After nearly a month in the works, version `v0.5.0` is finally here! 🎉 + +### Added +* Foreign keys are now enabled on all tables that the panel makes use of to prevent accidental data deletion when associated with other tables. +* Javascript changes to prevent crashing browsers when large quantities of data are sent over the websocket to the console. Includes a small popover message on the console to alert users that it is being throttled. +* Support for 'ARK: Survival Evolved' servers through the panel. +* Support for filtering servers within Admin CP to narrow down results by name, email, allocation, or defined fields. +* Setup scripts (user, mail, env) now support argument flags for use in containers and other non-terminal environments. +* New API endpoints for individual users to control their servers with at `/api/me/*`. +* Typeahead support for owner email when adding a new server. +* Scheduled command to clear out task log every month (configurable timespan). +* Support for allocating a FQDN as an allocation (panel will convert to IP and assign the FQDN as the alias automatically). +* Refresh files button in file manager to reload file listing without full page refresh. +* Added support for file copying through the file manager. [#127](https://github.com/Pterodactyl/Panel/issues/127) +* Creating new files and folders directly from the right-click dropdown menu in the file manager. +* Support for setting custom `user_id` when using the API to create users. +* Support for creating a new server through the API by passing a user ID rather than an email. +* Passing `?daemon=true` flag to [`/api/servers/:id`](https://pterodactyl.readme.io/v0.5.0/reference#single-server) will return the daemon stats as well as the `daemon_token` if using HTTPS. +* Small check for current node status that shows up to the left of the name when viewing a listing of all nodes. +* Support for creating server without having to assign a node and allocation manually. Simply select the checkbox or pass `auto_deploy=true` to the API to auto-select a node and allocation given a location. +* Support for setting IP Aliases through the panel on the node overview page. Also cleaned up allocation removal. +* Support for renaming files through the panel's file manager. + +### Changed +* Servers are now queued for deletion to allow for cancellation of deletion, as well as run in the background to speed up page loading. +* Switched to new graphing library to make graphs less... broken. +* Rebuild triggers are only sent to the node if there is actually something changed that requires a rebuild. +* Dependencies are now hard-coded into the `composer.json` file to prevent users installing slightly different versions with different features or bugs. +* Server related tasks now use the lowest priority queue to prevent clogging the pipes when there are more important tasks to be run by the panel. +* Dates displayed in the file manager are now more user friendly. +* Creating a user, server, or node now returns `HTTP/1.1 200` and a JSON element with the user/server/node's ID. +* Environment setting script is much more user friendly and does not require an excessive amount of clicking and typing. +* File upload method switched from BinaryJS to Socket.io implementation to fix bugs as well as be a little speedier and allow upload throttling. +* `Server::getbyUUID()` now accepts either the `uuidShort` or full-length `uuid` for server identification. +* API keys are tied to individual users and no longer created through the Admin CP. +* **ALL** API routes previously returning paginated result sets, or result sets nested inside a descriptive block (e.g. `servers:`) have been changed to return a single array of all associated items. Please see the [updated documentation](https://pterodactyl.readme.io/v0.5.0/reference) for how this change might effect your API use. +* API route for [`/api/users/:id`](https://pterodactyl.readme.io/v0.5.0/reference#single-user) now includes an array of all servers the user is set as the owner of. +* Prevent clicking server start button until server is completely off, not just stopping. +* Upon successful creation of a node it will redirect to the allocation tab and display a clearer message to add allocations. +* Trying to add a new node if no location exists redirects user to location management page and alerts them to add a location first. +* `Server\AjaxController@postSetConnection` is now `Server\AjaxController@postSetPrimary` and accepts one post parameter of `allocation` rather than a combined `ip:port` value. +* Port allocations on server view are now cleaner and should make more sense. +* Improved File Manager + * Rewritten Javascript to load, rename, and handle other file actions. + * Uses Ace Editor for editing files rather than a non-formatted textarea + * File actions that were previously icons to the right are now contained in a menu that appears when right-clicking a file or folder. + +### Fixed +* Fixes bug where resetting a user password through the login form would not hold passwords to the same requirements as the rest of the panel (mixed case and at least one numeric character). +* Fixes bug where no error would be displayed when adding a new server with an invalid owner email. +* Fixes a bug that could allow an admin to delete the default allocation for a server causing all sorts of issues. +* Databases assigned to a server are now actually deleted when a server is removed. +* Server overview listing the location short-code as the name of the node. +* Server task manager only sending commands every 5 minutes at the quickest. +* Fixes additional port allocation from removing the wrong row when clicking 'x'. +* Updated Socket.io client file to version `1.5.0` to match the latest release. Correlates with setting hard dependencies in the Daemon. +* Team Fortress named 'Insurgency' in panel in database seeder. ([#96](https://github.com/Pterodactyl/Panel/issues/96), PR by [@MeltedLux](https://github.com/MeltedLux)) +* Server allocation listing display now showing the connection IP unless an alias was assigned. +* Fixed bug where node allocation would appear to be successful but actual encounter an error. Made it cleared how to enter ports. +* Fixes display where an extra space was added to the end of SFTP passwords when they were copied from the panel. [#116](https://github.com/Pterodactyl/Panel/issues/116), thanks [@OrangeJuiced](https://github.com/OrangeJuiced) +* Fixes a bug that prevented viewing database servers if not assigned to a node. +* Checkboxes previously not displayed checkmarks are now fixed. + +### Fixed (bugs from v0.5.0-rc.2) +* Fixes a bug causing password resets to fail for server databases. +* Fixes a bug during installation that would prevent the 'Ark: Survival Evolved' service option from being added to the panel unless it was an update. +* Fixes constant scrolling to bottom of console; console now only scrolls to the bottom on new data. + +### Removed +* Removed active session management table displaying the last location of a session. +* Removed online player listing due to inconsistency in query library and an assortment of query related bugs. This will return in future versions when we get it working correctly. + +## v0.5.0-rc.2 (Bodacious Boreopterus) + +### Fixed +* Fixes a bug that would cause MySQL errors when attempting to install the panel rather than upgrading. + +## v0.5.0-rc.1 (Bodacious Boreopterus) + +### Added +* Foreign keys are now enabled on all tables that the panel makes use of to prevent accidental data deletion when associated with other tables. +* Javascript changes to prevent crashing browsers when large quantities of data are sent over the websocket to the console. Includes a small popover message on the console to alert users that it is being throttled. +* Support for 'ARK: Survival Evolved' servers through the panel. + +### Fixed +* Fixes bug where resetting a user password through the login form would not hold passwords to the same requirements as the rest of the panel (mixed case and at least one numeric character). +* Fixes misnamed environment variable for Bungeecord Servers (`BUNGE_VERSION` -> `BUNGEE_VERSION`). +* Fixes bug where no error would be displayed when adding a new server with an invalid owner email. +* Fixes a bug that could allow an admin to delete the default allocation for a server causing all sorts of issues. +* Databases assigned to a server are now actually deleted when a server is removed. +* Fixes file uploads being improperly throttled. + +### Changed +* Servers are now queued for deletion to allow for cancellation of deletion, as well as run in the background to speed up page loading. +* Switched to new graphing library to make graphs less... broken. +* Rebuild triggers are only sent to the node if there is actually something changed that requires a rebuild. +* Dependencies are now hard-coded into the `composer.json` file to prevent users installing slightly different versions with different features or bugs. +* Server related tasks now use the lowest priority queue to prevent clogging the pipes when there are more important tasks to be run by the panel. +* Decompressing files now shows a pop-over box that does not dismiss until it is complete. +* Dates displayed in the file manager are now more user friendly. + +### Removed +* Removed online player listing due to inconsistency in query library and an assortment of query related bugs. This will return in future versions when we get it working correctly. + +## v0.5.0-pre.3 (Bodacious Boreopterus) + +### Added +* Return node configuration from remote API by using `/api/nodes/{id}/config` endpoint. Only accepts SSL connections. +* Support for filtering servers within Admin CP to narrow down results by name, email, allocation, or defined fields. +* Setup scripts (user, mail, env) now support argument flags for use in containers and other non-terminal environments. +* New API endpoints for individual users to control their servers with at `/api/me/*`. +* Typeahead support for owner email when adding a new server. +* Scheduled command to clear out task log every month (configurable timespan). +* Support for allocating a FQDN as an allocation (panel will convert to IP and assign the FQDN as the alias automatically). +* Refresh files button in file manager to reload file listing without full page refresh. + +### Changed +* Creating a user, server, or node now returns `HTTP/1.1 200` and a JSON element with the user/server/node's ID. +* Environment setting script is much more user friendly and does not require an excessive amount of clicking and typing. +* File upload method switched from BinaryJS to Socket.io implementation to fix bugs as well as be a little speedier and allow upload throttling. +* `Server::getbyUUID()` now accepts either the `uuidShort` or full-length `uuid` for server identification. +* API keys are tied to individual users and no longer created through the Admin CP. + +### Fixed +* Server overview listing the location short-code as the name of the node. +* Server task manager only sending commands every 5 minutes at the quickest. +* Fixes additional port allocation from removing the wrong row when clicking 'x'. + +## v0.5.0-pre.2 (Bodacious Boreopterus) + +### Added +* Added support for file copying through the file manager. [#127](https://github.com/Pterodactyl/Panel/issues/127) +* Creating new files and folders directly from the right-click dropdown menu in the file manager. +* Support for setting custom `user_id` when using the API to create users. +* Support for creating a new server through the API by passing a user ID rather than an email. +* Passing `?daemon=true` flag to [`/api/servers/:id`](https://pterodactyl.readme.io/v0.5.0/reference#single-server) will return the daemon stats as well as the `daemon_token` if using HTTPS. +* Small check for current node status that shows up to the left of the name when viewing a listing of all nodes. + +### Changed +* Support for sub-folders within the `getJavascript()` route for servers. +* **ALL** API routes previously returning paginated result sets, or result sets nested inside a descriptive block (e.g. `servers:`) have been changed to return a single array of all associated items. Please see the [updated documentation](https://pterodactyl.readme.io/v0.5.0/reference) for how this change might effect your API use. +* API route for [`/api/users/:id`](https://pterodactyl.readme.io/v0.5.0/reference#single-user) now includes an array of all servers the user is set as the owner of. + +### Fixed +* File manager would do multiple up-down-up-down loading actions if you escaped renaming a file. Fixed the binding issue. [#122](https://github.com/Pterodactyl/Panel/issues/122) +* File manager actions would not trigger properly if text in a row was used to right-click from. +* File manager rename field would not disappear when pressing the escape key in chrome. [#121](https://github.com/Pterodactyl/Panel/issues/121) +* Fixes bug where server image assigned was not being saved to the database. +* Fixes instances where selecting auto-deploy would not hide the node selection dropdown. +* Fixes bug in auto-deployment that would throw a `ModelNotFoundException` if the location passed was not valid. Not normally an issue in the panel, but caused display issues for the API. +* Updated Socket.io client file to version `1.5.0` to match the latest release. Correlates with setting hard dependencies in the Daemon. + +## v0.5.0-pre.1 (Bodacious Boreopterus) + +### Added +* Support for creating server without having to assign a node and allocation manually. Simply select the checkbox or pass `auto_deploy=true` to the API to auto-select a node and allocation given a location. +* Support for setting IP Aliases through the panel on the node overview page. Also cleaned up allocation removal. +* Support for renaming files through the panel's file manager. + +### Changed +* Prevent clicking server start button until server is completely off, not just stopping. +* Upon successful creation of a node it will redirect to the allocation tab and display a clearer message to add allocations. +* Trying to add a new node if no location exists redirects user to location management page and alerts them to add a location first. +* `Server\AjaxController@postSetConnection` is now `Server\AjaxController@postSetPrimary` and accepts one post parameter of `allocation` rather than a combined `ip:port` value. +* Port allocations on server view are now cleaner and should make more sense. +* Improved File Manager + * Rewritten Javascript to load, rename, and handle other file actions. + * Uses Ace Editor for editing files rather than a non-formatted textarea + * File actions that were previously icons to the right are now contained in a menu that appears when right-clicking a file or folder. + +### Fixed +* Team Fortress named 'Insurgency' in panel in database seeder. ([#96](https://github.com/Pterodactyl/Panel/issues/96), PR by [@MeltedLux](https://github.com/MeltedLux)) +* Server allocation listing display now showing the connection IP unless an alias was assigned. +* Fixed bug where node allocation would appear to be successful but actual encounter an error. Made it cleared how to enter ports. +* Fixes display where an extra space was added to the end of SFTP passwords when they were copied from the panel. [#116](https://github.com/Pterodactyl/Panel/issues/116), thanks [@OrangeJuiced](https://github.com/OrangeJuiced) + +### Removed +* Removed active session management table displaying the last location of a session. + +## v0.4.1 (Articulate Aerotitan) + +### Changed +* Overallocate fields are now auto-filled with a value of `0` + +### Fixed +* Wrong error highlighting of overallocate fields on Node creation ([#90](https://github.com/Pterodactyl/Panel/issues/90), thanks [@schrej](https://github.com/schrej)) +* Server link in navbar directed to 404 link (PR by [@Randomfish132](https://github.com/Randomfish132)) +* Composer fails to finish ([#92](https://github.com/Pterodactyl/Panel/issues/92), PR by [@schrej](https://github.com/schrej), thanks [@parkervcp](https://github.com/parkervcp)) + +## v0.4.0 (Arty Aerodactylus) + +### Added +* Task scheduler supporting customized CRON syntax or dropdown selected options. (currently only support command and power options) +* Adds support for changing per-server database passwords from the panel. +* Allows for use of IP rather than a FQDN if the node is not using SSL +* Adds support for IP Aliases on display pages for users. This makes it possible to use GRE tunnels and still show the user what IP they should be connecting to. +* Adds support for suspending servers +* Adds support for viewing SFTP password within the panel ([#74](https://github.com/Pterodactyl/Panel/issues/74), thanks [@ET-Bent](https://github.com/ET-Bent)) +* Improved API with support for server suspension and build modification. +* Improved service management and setup on first install. +* New terminal that supports ANSI color codes as well as cleaner output. You can also simply type `start` or `boot` to start your server rather than having to use the start button. + +### Fixed +* Fixes password auto-generation on 'Manage Server' page. ([#67](https://github.com/Pterodactyl/Panel/issues/67), thanks [@ET-Bent](https://github.com/ET-Bent)) +* Fixes some overly verbose user output when an error occurs +* Prevent calling daemon until database call has been confirmed when changing default connection. +* Fixes a few display issues relating to subusers and database management. +* Fixes the server name in the header not linking to the server correctly. ([#79](https://github.com/Pterodactyl/Panel/issues/79), thanks [@xX1bumblebee1Xx](https://github.com/xX1bumblebee1Xx)) +* Fixes bug where non-admins could not see command box on servers. ([#83](https://github.com/Pterodactyl/Panel/issues/83), thanks [@xX1bumblebee1Xx](https://github.com/xX1bumblebee1Xx)) +* Fixes bug where files could not be uploaded through the "click and select" system, only through "drag and drop." ([#82](https://github.com/Pterodactyl/Panel/issues/83), thanks [@xX1bumblebee1Xx](https://github.com/xX1bumblebee1Xx)) +* Fixes a bug where new files could not be created through the panel for a server. ([#85](https://github.com/Pterodactyl/Panel/issues/85), thanks [@xX1bumblebee1Xx](https://github.com/xX1bumblebee1Xx)) +* Fixes the exception handler to properly display and log exceptions that might occur rather than leaving a vague error. ([#81](https://github.com/Pterodactyl/Panel/issues/83)) + +### Changed +* Update Laravel to version `5.3` and update dependencies. + +### Deprecated +* Requires Pterodactyl Daemon `v0.2.*` + +### Security +* Fixes listing of server variables for server. Previously a bug made it possible to view settings for all servers, even if the user didn't own that server. ([#69](https://github.com/Pterodactyl/Panel/issues/69)) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..d002649 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at support@pterodactyl.io. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..eb24ef6 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,31 @@ +# Contributing + +Pterodactyl does not accept Pull Requests (PRs) _for new functionality_ from users that are not currently part of the +core project team. It has become overwhelming to try and give the proper time and attention that such complicated PRs +tend to require — and deserve. As a result, it is in the project's best interest to limit the scope of work on +new functionality to work done within the core project team. + +PRs that address existing _bugs_ with a corresponding issue opened in our issue tracker will continue to be accepted +and reviewed. Their scope is often significantly more targeted, and simply improving upon existing and well defined +logic. + +### Responsible Disclosure + +This is a fairly in-depth project and makes use of a lot of parts. We strive to keep everything as secure as possible +and welcome you to take a look at the code provided in this project yourself. We do ask that you be considerate of +others who are using the software and not publicly disclose security issues without contacting us first by email. + +We'll make a deal with you: if you contact us by email, and we fail to respond to you within a week you are welcome to +publicly disclose whatever issue you have found. We understand how frustrating it is when you find something big and +no one will respond to you. This holds us to a standard of providing prompt attention to any issues that arise and +keeping this community safe. + +If you've found what you believe is a security issue please email `matthew@pterodactyl.io`. Please check +[SECURITY.md](/SECURITY.md) for additional details. + +### Contact Us + +You can find us in a couple places online. First and foremost, we're active right here on GitHub. If you encounter a +bug or other problems, open an issue on here for us to take a look at it. We also accept feature requests here as well. + +You can also find us on [Discord](https://discord.gg/pterodactyl). diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3c10ad4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,41 @@ +# Stage 0: +# Build the assets that are needed for the frontend. This build stage is then discarded +# since we won't need NodeJS anymore in the future. This Docker image ships a final production +# level distribution of Pterodactyl. +FROM --platform=$TARGETOS/$TARGETARCH mhart/alpine-node:14 +WORKDIR /app +COPY . ./ +RUN yarn install --frozen-lockfile \ + && yarn run build:production + +# Stage 1: +# Build the actual container with all of the needed PHP dependencies that will run the application. +FROM --platform=$TARGETOS/$TARGETARCH php:8.2-fpm-alpine +WORKDIR /app +COPY . ./ +COPY --from=0 /app/public/assets ./public/assets +RUN apk add --no-cache --update ca-certificates dcron curl git supervisor tar unzip nginx libpng-dev libxml2-dev libzip-dev certbot certbot-nginx \ + && docker-php-ext-configure zip \ + && docker-php-ext-install bcmath gd pdo_mysql zip \ + && curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \ + && cp .env.example .env \ + && mkdir -p bootstrap/cache/ storage/logs storage/framework/sessions storage/framework/views storage/framework/cache \ + && chmod 777 -R bootstrap storage \ + && composer install --no-dev --optimize-autoloader \ + && rm -rf .env bootstrap/cache/*.php \ + && mkdir -p /app/storage/logs/ \ + && chown -R nginx:nginx . + +RUN rm /usr/local/etc/php-fpm.conf \ + && echo "* * * * * /usr/local/bin/php /app/artisan schedule:run >> /dev/null 2>&1" >> /var/spool/cron/crontabs/root \ + && echo "0 23 * * * certbot renew --nginx --quiet" >> /var/spool/cron/crontabs/root \ + && sed -i s/ssl_session_cache/#ssl_session_cache/g /etc/nginx/nginx.conf \ + && mkdir -p /var/run/php /var/run/nginx + +COPY .github/docker/default.conf /etc/nginx/http.d/default.conf +COPY .github/docker/www.conf /usr/local/etc/php-fpm.conf +COPY .github/docker/supervisord.conf /etc/supervisord.conf + +EXPOSE 80 443 +ENTRYPOINT [ "/bin/ash", ".github/docker/entrypoint.sh" ] +CMD [ "supervisord", "-n", "-c", "/etc/supervisord.conf" ] diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..cb0e2a9 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,24 @@ +# The MIT License (MIT) + +``` +Pterodactyl® +Copyright © Dane Everitt and contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` diff --git a/NookLicense.md b/NookLicense.md new file mode 100644 index 0000000..e72bfdd --- /dev/null +++ b/NookLicense.md @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..6326ff4 --- /dev/null +++ b/README.md @@ -0,0 +1,173 @@ +# Nook Theme +NookTheme is a free and open source [Pterodactyl theme](https://pterodactyl.io) designed to be simple, clean, and modern. + +![Image](https://i.imgur.com/AFjHGBr.png) + +
+View Screenshots + +![Image](https://i.imgur.com/CNxF3iT.png) +![Image](https://i.imgur.com/IflRtEX.png) +![Image](https://i.imgur.com/vNLK5jP.png) +![Image](https://i.imgur.com/dnxV2CS.png) +
+ +## Installation + +This will update your panel to the latest version of NookTheme panel is based.
+You can see the version in the current branch name. + +
+Upgrade PHP + +Before proceeding with the installation steps, ensure that your PHP version is upgraded to 8.2 or newer. Follow the instructions below to upgrade PHP: + +1. Update your package list: +```bash +sudo apt update +``` + +2. Install the required dependencies: +```bash +sudo apt install -y software-properties-common +``` + +3. Add the PHP repository: +```bash +sudo add-apt-repository ppa:ondrej/php +``` + +4. Update your package list again: +```bash +sudo apt update +``` + +5. Install PHP 8.3: +```bash +sudo apt install -y php8.3 +``` + +6. Verify the PHP version: +```bash +php -v +``` + +
+ +### Enter Maintenance Mode + +Whenever you are performing an update you should be sure to place your Panel into maintenance mode. This will prevent +users from encountering unexpected errors and ensure everything can be updated before users encounter +potentially new features. + +```bash +cd /var/www/pterodactyl + +php artisan down +``` + +### Download the theme + +The first step in the update process is to download the new panel files from GitHub. The command below will download +the release archive for the most recent version of Pterodactyl, save it in the current directory and will automatically +unpack the archive into your current folder. + +```bash +curl -L https://github.com/Nookure/NookTheme/releases/latest/download/panel.tar.gz | tar -xzv +``` + +Once all of the files are downloaded we need to set the correct permissions on the cache and storage directories to avoid +any webserver related errors. + +```bash +chmod -R 755 storage/* bootstrap/cache +``` + +### Update Dependencies + +After you've downloaded all of the new files you will need to upgrade the core components of the panel. To do this, +simply run the commands below and follow any prompts. + +```bash +composer install --no-dev --optimize-autoloader +``` + +### Clear Compiled Template Cache + +You'll also want to clear the compiled template cache to ensure that new and modified templates show up correctly for +users. + +```bash +php artisan view:clear +php artisan config:clear +``` + +### Database Updates + +You'll also need to update your database schema for the newest version of Pterodactyl. Running the command below +will update the schema and ensure the default eggs we ship are up to date (and add any new ones we might have). Just +remember, _never edit core eggs we ship_! They will be overwritten by this update process. + +```bash +php artisan migrate --seed --force +``` + +### Set Permissions + +The last step is to set the proper owner of the files to be the user that runs your webserver. In most cases this +is `www-data` but can vary from system to system — sometimes being `nginx`, `caddy`, `apache`, or even `nobody`. + +```bash +# If using NGINX or Apache (not on CentOS): +chown -R www-data:www-data /var/www/pterodactyl/* + +# If using NGINX on CentOS: +chown -R nginx:nginx /var/www/pterodactyl/* + +# If using Apache on CentOS +chown -R apache:apache /var/www/pterodactyl/* +``` + +### Restarting Queue Workers + +After _every_ update you should restart the queue worker to ensure that the new code is loaded in and used. + +```bash +php artisan queue:restart +``` + +### Exit Maintenance Mode + +Now that everything has been updated you need to exit maintenance mode so that the Panel can resume accepting +connections. + +```bash +php artisan up +``` + +## Documentation + +* [Panel Documentation](https://pterodactyl.io/panel/1.0/getting_started.html) +* [Wings Documentation](https://pterodactyl.io/wings/1.0/installing.html) +* [Community Guides](https://pterodactyl.io/community/about.html) +* Or, get additional help [via Discord](https://discord.nookure.com/) + +## Star History + + + + + + Star History Chart + + + +## License + +Pterodactyl® Copyright © 2015 - 2023 Dane Everitt and contributors. + +> Nookure is not affiliated with Pterodactyl® Panel or its contributors. + +Pterodactyl code released under the [MIT License](./LICENSE.md). + +NookTheme code edits released under the [GNU GPLv3 License](./NookLicense.md). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..96c0684 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,19 @@ +# Security Policy + +## Supported Versions + +The following versions of Pterodactyl are receiving active support and maintenance. Any security vulnerabilities discovered must be reproducible in supported versions. + +| Panel | Daemon | Supported | +|--------|--------------|--------------------| +| 1.11.x | wings@1.11.x | :white_check_mark: | +| 0.7.x | daemon@0.6.x | :x: | + + +## Reporting a Vulnerability + +Please reach out directly to any project team member on Discord when reporting a security vulnerability, or you can email `matthew@pterodactyl.io`. + +We make every effort to respond as soon as possible, although it may take a day or two for us to sync internally and determine the severity of the report and its impact. Please, _do not_ use a public facing channel or GitHub issues to report sensitive security issues. + +As part of our process, we will create a security advisory for the affected versions and disclose it publicly, usually two to four weeks after a releasing a version that addresses it. diff --git a/app/Console/Commands/Environment/AppSettingsCommand.php b/app/Console/Commands/Environment/AppSettingsCommand.php new file mode 100644 index 0000000..b143719 --- /dev/null +++ b/app/Console/Commands/Environment/AppSettingsCommand.php @@ -0,0 +1,183 @@ + 'Redis (recommended)', + 'memcached' => 'Memcached', + 'file' => 'Filesystem', + ]; + + public const SESSION_DRIVERS = [ + 'redis' => 'Redis (recommended)', + 'memcached' => 'Memcached', + 'database' => 'MySQL Database', + 'file' => 'Filesystem', + 'cookie' => 'Cookie', + ]; + + public const QUEUE_DRIVERS = [ + 'redis' => 'Redis (recommended)', + 'database' => 'MySQL Database', + 'sync' => 'Sync', + ]; + + protected $description = 'Configure basic environment settings for the Panel.'; + + protected $signature = 'p:environment:setup + {--new-salt : Whether or not to generate a new salt for Hashids.} + {--author= : The email that services created on this instance should be linked to.} + {--url= : The URL that this Panel is running on.} + {--timezone= : The timezone to use for Panel times.} + {--cache= : The cache driver backend to use.} + {--session= : The session driver backend to use.} + {--queue= : The queue driver backend to use.} + {--redis-host= : Redis host to use for connections.} + {--redis-pass= : Password used to connect to redis.} + {--redis-port= : Port to connect to redis over.} + {--settings-ui= : Enable or disable the settings UI.} + {--telemetry= : Enable or disable anonymous telemetry.}'; + + protected array $variables = []; + + /** + * AppSettingsCommand constructor. + */ + public function __construct(private Kernel $console) + { + parent::__construct(); + } + + /** + * Handle command execution. + * + * @throws \Pterodactyl\Exceptions\PterodactylException + */ + public function handle(): int + { + if (empty(config('hashids.salt')) || $this->option('new-salt')) { + $this->variables['HASHIDS_SALT'] = str_random(20); + } + + $this->output->comment('Provide the email address that eggs exported by this Panel should be from. This should be a valid email address.'); + $this->variables['APP_SERVICE_AUTHOR'] = $this->option('author') ?? $this->ask( + 'Egg Author Email', + config('pterodactyl.service.author', 'unknown@unknown.com') + ); + + if (!filter_var($this->variables['APP_SERVICE_AUTHOR'], FILTER_VALIDATE_EMAIL)) { + $this->output->error('The service author email provided is invalid.'); + + return 1; + } + + $this->output->comment('The application URL MUST begin with https:// or http:// depending on if you are using SSL or not. If you do not include the scheme your emails and other content will link to the wrong location.'); + $this->variables['APP_URL'] = $this->option('url') ?? $this->ask( + 'Application URL', + config('app.url', 'https://example.com') + ); + + $this->output->comment('The timezone should match one of PHP\'s supported timezones. If you are unsure, please reference https://php.net/manual/en/timezones.php.'); + $this->variables['APP_TIMEZONE'] = $this->option('timezone') ?? $this->anticipate( + 'Application Timezone', + \DateTimeZone::listIdentifiers(), + config('app.timezone') + ); + + $selected = config('cache.default', 'redis'); + $this->variables['CACHE_DRIVER'] = $this->option('cache') ?? $this->choice( + 'Cache Driver', + self::CACHE_DRIVERS, + array_key_exists($selected, self::CACHE_DRIVERS) ? $selected : null + ); + + $selected = config('session.driver', 'redis'); + $this->variables['SESSION_DRIVER'] = $this->option('session') ?? $this->choice( + 'Session Driver', + self::SESSION_DRIVERS, + array_key_exists($selected, self::SESSION_DRIVERS) ? $selected : null + ); + + $selected = config('queue.default', 'redis'); + $this->variables['QUEUE_CONNECTION'] = $this->option('queue') ?? $this->choice( + 'Queue Driver', + self::QUEUE_DRIVERS, + array_key_exists($selected, self::QUEUE_DRIVERS) ? $selected : null + ); + + if (!is_null($this->option('settings-ui'))) { + $this->variables['APP_ENVIRONMENT_ONLY'] = $this->option('settings-ui') == 'true' ? 'false' : 'true'; + } else { + $this->variables['APP_ENVIRONMENT_ONLY'] = $this->confirm('Enable UI based settings editor?', true) ? 'false' : 'true'; + } + + $this->output->comment('Please reference https://pterodactyl.io/panel/1.0/additional_configuration.html#telemetry for more detailed information regarding telemetry data and collection.'); + $this->variables['PTERODACTYL_TELEMETRY_ENABLED'] = $this->option('telemetry') ?? $this->confirm( + 'Enable sending anonymous telemetry data?', + config('pterodactyl.telemetry.enabled', true) + ) ? 'true' : 'false'; + + // Make sure session cookies are set as "secure" when using HTTPS + if (str_starts_with($this->variables['APP_URL'], 'https://')) { + $this->variables['SESSION_SECURE_COOKIE'] = 'true'; + } + + $this->checkForRedis(); + $this->writeToEnvironment($this->variables); + + $this->info($this->console->output()); + + return 0; + } + + /** + * Check if redis is selected, if so, request connection details and verify them. + */ + private function checkForRedis() + { + $items = collect($this->variables)->filter(function ($item) { + return $item === 'redis'; + }); + + // Redis was not selected, no need to continue. + if (count($items) === 0) { + return; + } + + $this->output->note('You\'ve selected the Redis driver for one or more options, please provide valid connection information below. In most cases you can use the defaults provided unless you have modified your setup.'); + $this->variables['REDIS_HOST'] = $this->option('redis-host') ?? $this->ask( + 'Redis Host', + config('database.redis.default.host') + ); + + $askForRedisPassword = true; + if (!empty(config('database.redis.default.password'))) { + $this->variables['REDIS_PASSWORD'] = config('database.redis.default.password'); + $askForRedisPassword = $this->confirm('It seems a password is already defined for Redis, would you like to change it?'); + } + + if ($askForRedisPassword) { + $this->output->comment('By default a Redis server instance has no password as it is running locally and inaccessible to the outside world. If this is the case, simply hit enter without entering a value.'); + $this->variables['REDIS_PASSWORD'] = $this->option('redis-pass') ?? $this->output->askHidden( + 'Redis Password' + ); + } + + if (empty($this->variables['REDIS_PASSWORD'])) { + $this->variables['REDIS_PASSWORD'] = 'null'; + } + + $this->variables['REDIS_PORT'] = $this->option('redis-port') ?? $this->ask( + 'Redis Port', + config('database.redis.default.port') + ); + } +} diff --git a/app/Console/Commands/Environment/DatabaseSettingsCommand.php b/app/Console/Commands/Environment/DatabaseSettingsCommand.php new file mode 100644 index 0000000..fb4a2e2 --- /dev/null +++ b/app/Console/Commands/Environment/DatabaseSettingsCommand.php @@ -0,0 +1,113 @@ +output->note('It is highly recommended to not use "localhost" as your database host as we have seen frequent socket connection issues. If you want to use a local connection you should be using "127.0.0.1".'); + $this->variables['DB_HOST'] = $this->option('host') ?? $this->ask( + 'Database Host', + config('database.connections.mysql.host', '127.0.0.1') + ); + + $this->variables['DB_PORT'] = $this->option('port') ?? $this->ask( + 'Database Port', + config('database.connections.mysql.port', 3306) + ); + + $this->variables['DB_DATABASE'] = $this->option('database') ?? $this->ask( + 'Database Name', + config('database.connections.mysql.database', 'panel') + ); + + $this->output->note('Using the "root" account for MySQL connections is not only highly frowned upon, it is also not allowed by this application. You\'ll need to have created a MySQL user for this software.'); + $this->variables['DB_USERNAME'] = $this->option('username') ?? $this->ask( + 'Database Username', + config('database.connections.mysql.username', 'pterodactyl') + ); + + $askForMySQLPassword = true; + if (!empty(config('database.connections.mysql.password')) && $this->input->isInteractive()) { + $this->variables['DB_PASSWORD'] = config('database.connections.mysql.password'); + $askForMySQLPassword = $this->confirm('It appears you already have a MySQL connection password defined, would you like to change it?'); + } + + if ($askForMySQLPassword) { + $this->variables['DB_PASSWORD'] = $this->option('password') ?? $this->secret('Database Password'); + } + + try { + $this->testMySQLConnection(); + } catch (\PDOException $exception) { + $this->output->error(sprintf('Unable to connect to the MySQL server using the provided credentials. The error returned was "%s".', $exception->getMessage())); + $this->output->error('Your connection credentials have NOT been saved. You will need to provide valid connection information before proceeding.'); + + if ($this->confirm('Go back and try again?')) { + $this->database->disconnect('_pterodactyl_command_test'); + + return $this->handle(); + } + + return 1; + } + + $this->writeToEnvironment($this->variables); + + $this->info($this->console->output()); + + return 0; + } + + /** + * Test that we can connect to the provided MySQL instance and perform a selection. + */ + private function testMySQLConnection() + { + config()->set('database.connections._pterodactyl_command_test', [ + 'driver' => 'mysql', + 'host' => $this->variables['DB_HOST'], + 'port' => $this->variables['DB_PORT'], + 'database' => $this->variables['DB_DATABASE'], + 'username' => $this->variables['DB_USERNAME'], + 'password' => $this->variables['DB_PASSWORD'], + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_unicode_ci', + 'strict' => true, + ]); + + $this->database->connection('_pterodactyl_command_test')->getPdo(); + } +} diff --git a/app/Console/Commands/Environment/EmailSettingsCommand.php b/app/Console/Commands/Environment/EmailSettingsCommand.php new file mode 100644 index 0000000..3a21139 --- /dev/null +++ b/app/Console/Commands/Environment/EmailSettingsCommand.php @@ -0,0 +1,152 @@ +variables['MAIL_DRIVER'] = $this->option('driver') ?? $this->choice( + trans('command/messages.environment.mail.ask_driver'), + [ + 'smtp' => 'SMTP Server', + 'sendmail' => 'sendmail Binary', + 'mailgun' => 'Mailgun Transactional Email', + 'mandrill' => 'Mandrill Transactional Email', + 'postmark' => 'Postmark Transactional Email', + ], + $this->config->get('mail.default', 'smtp') + ); + + $method = 'setup' . studly_case($this->variables['MAIL_DRIVER']) . 'DriverVariables'; + if (method_exists($this, $method)) { + $this->{$method}(); + } + + $this->variables['MAIL_FROM_ADDRESS'] = $this->option('email') ?? $this->ask( + trans('command/messages.environment.mail.ask_mail_from'), + $this->config->get('mail.from.address') + ); + + $this->variables['MAIL_FROM_NAME'] = $this->option('from') ?? $this->ask( + trans('command/messages.environment.mail.ask_mail_name'), + $this->config->get('mail.from.name') + ); + + $this->writeToEnvironment($this->variables); + + $this->line('Updating stored environment configuration file.'); + $this->line(''); + } + + /** + * Handle variables for SMTP driver. + */ + private function setupSmtpDriverVariables() + { + $this->variables['MAIL_HOST'] = $this->option('host') ?? $this->ask( + trans('command/messages.environment.mail.ask_smtp_host'), + $this->config->get('mail.mailers.smtp.host') + ); + + $this->variables['MAIL_PORT'] = $this->option('port') ?? $this->ask( + trans('command/messages.environment.mail.ask_smtp_port'), + $this->config->get('mail.mailers.smtp.port') + ); + + $this->variables['MAIL_USERNAME'] = $this->option('username') ?? $this->ask( + trans('command/messages.environment.mail.ask_smtp_username'), + $this->config->get('mail.mailers.smtp.username') + ); + + $this->variables['MAIL_PASSWORD'] = $this->option('password') ?? $this->secret( + trans('command/messages.environment.mail.ask_smtp_password') + ); + + $this->variables['MAIL_ENCRYPTION'] = $this->option('encryption') ?? $this->choice( + trans('command/messages.environment.mail.ask_encryption'), + ['tls' => 'TLS', 'ssl' => 'SSL', '' => 'None'], + $this->config->get('mail.mailers.smtp.encryption', 'tls') + ); + } + + /** + * Handle variables for mailgun driver. + */ + private function setupMailgunDriverVariables() + { + $this->variables['MAILGUN_DOMAIN'] = $this->option('host') ?? $this->ask( + trans('command/messages.environment.mail.ask_mailgun_domain'), + $this->config->get('services.mailgun.domain') + ); + + $this->variables['MAILGUN_SECRET'] = $this->option('password') ?? $this->ask( + trans('command/messages.environment.mail.ask_mailgun_secret'), + $this->config->get('services.mailgun.secret') + ); + + $this->variables['MAILGUN_ENDPOINT'] = $this->option('endpoint') ?? $this->ask( + trans('command/messages.environment.mail.ask_mailgun_endpoint'), + $this->config->get('services.mailgun.endpoint') + ); + } + + /** + * Handle variables for mandrill driver. + */ + private function setupMandrillDriverVariables() + { + $this->variables['MANDRILL_SECRET'] = $this->option('password') ?? $this->ask( + trans('command/messages.environment.mail.ask_mandrill_secret'), + $this->config->get('services.mandrill.secret') + ); + } + + /** + * Handle variables for postmark driver. + */ + private function setupPostmarkDriverVariables() + { + $this->variables['MAIL_DRIVER'] = 'smtp'; + $this->variables['MAIL_HOST'] = 'smtp.postmarkapp.com'; + $this->variables['MAIL_PORT'] = 587; + $this->variables['MAIL_USERNAME'] = $this->variables['MAIL_PASSWORD'] = $this->option('username') ?? $this->ask( + trans('command/messages.environment.mail.ask_postmark_username'), + $this->config->get('mail.username') + ); + } +} diff --git a/app/Console/Commands/InfoCommand.php b/app/Console/Commands/InfoCommand.php new file mode 100644 index 0000000..25c7744 --- /dev/null +++ b/app/Console/Commands/InfoCommand.php @@ -0,0 +1,81 @@ +output->title('Version Information'); + $this->table([], [ + ['Panel Version', $this->config->get('app.version')], + ['Latest Version', $this->versionService->getPanel()], + ['Up-to-Date', $this->versionService->isLatestPanel() ? 'Yes' : $this->formatText('No', 'bg=red')], + ['Unique Identifier', $this->config->get('pterodactyl.service.author')], + ], 'compact'); + + $this->output->title('Application Configuration'); + $this->table([], [ + ['Environment', $this->formatText($this->config->get('app.env'), $this->config->get('app.env') === 'production' ?: 'bg=red')], + ['Debug Mode', $this->formatText($this->config->get('app.debug') ? 'Yes' : 'No', !$this->config->get('app.debug') ?: 'bg=red')], + ['Installation URL', $this->config->get('app.url')], + ['Installation Directory', base_path()], + ['Timezone', $this->config->get('app.timezone')], + ['Cache Driver', $this->config->get('cache.default')], + ['Queue Driver', $this->config->get('queue.default')], + ['Session Driver', $this->config->get('session.driver')], + ['Filesystem Driver', $this->config->get('filesystems.default')], + ['Default Theme', $this->config->get('themes.active')], + ['Proxies', $this->config->get('trustedproxies.proxies')], + ], 'compact'); + + $this->output->title('Database Configuration'); + $driver = $this->config->get('database.default'); + $this->table([], [ + ['Driver', $driver], + ['Host', $this->config->get("database.connections.$driver.host")], + ['Port', $this->config->get("database.connections.$driver.port")], + ['Database', $this->config->get("database.connections.$driver.database")], + ['Username', $this->config->get("database.connections.$driver.username")], + ], 'compact'); + + // TODO: Update this to handle other mail drivers + $this->output->title('Email Configuration'); + $this->table([], [ + ['Driver', $this->config->get('mail.default')], + ['Host', $this->config->get('mail.mailers.smtp.host')], + ['Port', $this->config->get('mail.mailers.smtp.port')], + ['Username', $this->config->get('mail.mailers.smtp.username')], + ['From Address', $this->config->get('mail.from.address')], + ['From Name', $this->config->get('mail.from.name')], + ['Encryption', $this->config->get('mail.mailers.smtp.encryption')], + ], 'compact'); + } + + /** + * Format output in a Name: Value manner. + */ + private function formatText(string $value, string $opts = ''): string + { + return sprintf('<%s>%s', $opts, $value); + } +} diff --git a/app/Console/Commands/Location/DeleteLocationCommand.php b/app/Console/Commands/Location/DeleteLocationCommand.php new file mode 100644 index 0000000..3193e0c --- /dev/null +++ b/app/Console/Commands/Location/DeleteLocationCommand.php @@ -0,0 +1,55 @@ +locations = $this->locations ?? $this->repository->all(); + $short = $this->option('short') ?? $this->anticipate( + trans('command/messages.location.ask_short'), + $this->locations->pluck('short')->toArray() + ); + + $location = $this->locations->where('short', $short)->first(); + if (is_null($location)) { + $this->error(trans('command/messages.location.no_location_found')); + if ($this->input->isInteractive()) { + $this->handle(); + } + + return; + } + + $this->deletionService->handle($location->id); + $this->line(trans('command/messages.location.deleted')); + } +} diff --git a/app/Console/Commands/Location/MakeLocationCommand.php b/app/Console/Commands/Location/MakeLocationCommand.php new file mode 100644 index 0000000..f09f160 --- /dev/null +++ b/app/Console/Commands/Location/MakeLocationCommand.php @@ -0,0 +1,40 @@ +option('short') ?? $this->ask(trans('command/messages.location.ask_short')); + $long = $this->option('long') ?? $this->ask(trans('command/messages.location.ask_long')); + + $location = $this->creationService->handle(compact('short', 'long')); + $this->line(trans('command/messages.location.created', [ + 'name' => $location->short, + 'id' => $location->id, + ])); + } +} diff --git a/app/Console/Commands/Maintenance/CleanServiceBackupFilesCommand.php b/app/Console/Commands/Maintenance/CleanServiceBackupFilesCommand.php new file mode 100644 index 0000000..f256fe4 --- /dev/null +++ b/app/Console/Commands/Maintenance/CleanServiceBackupFilesCommand.php @@ -0,0 +1,45 @@ +disk = $filesystem->disk(); + } + + /** + * Handle command execution. + */ + public function handle() + { + $files = $this->disk->files('services/.bak'); + + collect($files)->each(function (\SplFileInfo $file) { + $lastModified = Carbon::createFromTimestamp($this->disk->lastModified($file->getPath())); + if ($lastModified->diffInMinutes(Carbon::now()) > self::BACKUP_THRESHOLD_MINUTES) { + $this->disk->delete($file->getPath()); + $this->info(trans('command/messages.maintenance.deleting_service_backup', ['file' => $file->getFilename()])); + } + }); + } +} diff --git a/app/Console/Commands/Maintenance/PruneOrphanedBackupsCommand.php b/app/Console/Commands/Maintenance/PruneOrphanedBackupsCommand.php new file mode 100644 index 0000000..b7a04f8 --- /dev/null +++ b/app/Console/Commands/Maintenance/PruneOrphanedBackupsCommand.php @@ -0,0 +1,49 @@ +option('prune-age') ?? config('backups.prune_age', 360); + if (!$since || !is_digit($since)) { + throw new \InvalidArgumentException('The "--prune-age" argument must be a value greater than 0.'); + } + + $query = $this->backupRepository->getBuilder() + ->whereNull('completed_at') + ->where('created_at', '<=', CarbonImmutable::now()->subMinutes($since)->toDateTimeString()); + + $count = $query->count(); + if (!$count) { + $this->info('There are no orphaned backups to be marked as failed.'); + + return; + } + + $this->warn("Marking $count uncompleted backups that are older than $since minutes as failed."); + + $query->update([ + 'is_successful' => false, + 'completed_at' => CarbonImmutable::now(), + 'updated_at' => CarbonImmutable::now(), + ]); + } +} diff --git a/app/Console/Commands/Node/MakeNodeCommand.php b/app/Console/Commands/Node/MakeNodeCommand.php new file mode 100644 index 0000000..2d2623a --- /dev/null +++ b/app/Console/Commands/Node/MakeNodeCommand.php @@ -0,0 +1,69 @@ +option('name') ?? $this->ask('Enter a short identifier used to distinguish this node from others'); + $data['description'] = $this->option('description') ?? $this->ask('Enter a description to identify the node'); + $data['location_id'] = $this->option('locationId') ?? $this->ask('Enter a valid location id'); + $data['scheme'] = $this->option('scheme') ?? $this->anticipate( + 'Please either enter https for SSL or http for a non-ssl connection', + ['https', 'http'], + 'https' + ); + $data['fqdn'] = $this->option('fqdn') ?? $this->ask('Enter a domain name (e.g node.example.com) to be used for connecting to the daemon. An IP address may only be used if you are not using SSL for this node'); + $data['public'] = $this->option('public') ?? $this->confirm('Should this node be public? As a note, setting a node to private you will be denying the ability to auto-deploy to this node.', true); + $data['behind_proxy'] = $this->option('proxy') ?? $this->confirm('Is your FQDN behind a proxy?'); + $data['maintenance_mode'] = $this->option('maintenance') ?? $this->confirm('Should maintenance mode be enabled?'); + $data['memory'] = $this->option('maxMemory') ?? $this->ask('Enter the maximum amount of memory'); + $data['memory_overallocate'] = $this->option('overallocateMemory') ?? $this->ask('Enter the amount of memory to over allocate by, -1 will disable checking and 0 will prevent creating new servers'); + $data['disk'] = $this->option('maxDisk') ?? $this->ask('Enter the maximum amount of disk space'); + $data['disk_overallocate'] = $this->option('overallocateDisk') ?? $this->ask('Enter the amount of memory to over allocate by, -1 will disable checking and 0 will prevent creating new server'); + $data['upload_size'] = $this->option('uploadSize') ?? $this->ask('Enter the maximum filesize upload', '100'); + $data['daemonListen'] = $this->option('daemonListeningPort') ?? $this->ask('Enter the wings listening port', '8080'); + $data['daemonSFTP'] = $this->option('daemonSFTPPort') ?? $this->ask('Enter the wings SFTP listening port', '2022'); + $data['daemonBase'] = $this->option('daemonBase') ?? $this->ask('Enter the base folder', '/var/lib/pterodactyl/volumes'); + + $node = $this->creationService->handle($data); + $this->line('Successfully created a new node on the location ' . $data['location_id'] . ' with the name ' . $data['name'] . ' and has an id of ' . $node->id . '.'); + } +} diff --git a/app/Console/Commands/Node/NodeConfigurationCommand.php b/app/Console/Commands/Node/NodeConfigurationCommand.php new file mode 100644 index 0000000..9bf0f42 --- /dev/null +++ b/app/Console/Commands/Node/NodeConfigurationCommand.php @@ -0,0 +1,44 @@ +argument('node')) ? 'id' : 'uuid'; + + /** @var \Pterodactyl\Models\Node $node */ + $node = Node::query()->where($column, $this->argument('node'))->firstOr(function () { + $this->error('The selected node does not exist.'); + + exit(1); + }); + + $format = $this->option('format'); + if (!in_array($format, ['yaml', 'yml', 'json'])) { + $this->error('Invalid format specified. Valid options are "yaml" and "json".'); + + return 1; + } + + if ($format === 'json') { + $this->output->write($node->getJsonConfiguration(true)); + } else { + $this->output->write($node->getYamlConfiguration()); + } + + $this->output->newLine(); + + return 0; + } +} diff --git a/app/Console/Commands/Node/NodeListCommand.php b/app/Console/Commands/Node/NodeListCommand.php new file mode 100644 index 0000000..718ddd0 --- /dev/null +++ b/app/Console/Commands/Node/NodeListCommand.php @@ -0,0 +1,34 @@ +with('location')->get()->map(function (Node $node) { + return [ + 'id' => $node->id, + 'uuid' => $node->uuid, + 'name' => $node->name, + 'location' => $node->location->short, + 'host' => $node->getConnectionAddress(), + ]; + }); + + if ($this->option('format') === 'json') { + $this->output->write($nodes->toJson(JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); + } else { + $this->table(['ID', 'UUID', 'Name', 'Location', 'Host'], $nodes->toArray()); + } + + $this->output->newLine(); + + return 0; + } +} diff --git a/app/Console/Commands/Overrides/KeyGenerateCommand.php b/app/Console/Commands/Overrides/KeyGenerateCommand.php new file mode 100644 index 0000000..bc8a4ed --- /dev/null +++ b/app/Console/Commands/Overrides/KeyGenerateCommand.php @@ -0,0 +1,28 @@ +input->isInteractive()) { + $this->output->warning('It appears you have already configured an application encryption key. Continuing with this process with overwrite that key and cause data corruption for any existing encrypted data. DO NOT CONTINUE UNLESS YOU KNOW WHAT YOU ARE DOING.'); + if (!$this->confirm('I understand the consequences of performing this command and accept all responsibility for the loss of encrypted data.')) { + return; + } + + if (!$this->confirm('Are you sure you wish to continue? Changing the application encryption key WILL CAUSE DATA LOSS.')) { + return; + } + } + + parent::handle(); + } +} diff --git a/app/Console/Commands/Overrides/SeedCommand.php b/app/Console/Commands/Overrides/SeedCommand.php new file mode 100644 index 0000000..7b7b5ed --- /dev/null +++ b/app/Console/Commands/Overrides/SeedCommand.php @@ -0,0 +1,26 @@ +hasCompletedMigrations()) { + $this->showMigrationWarning(); + + return 1; + } + + return parent::handle(); + } +} diff --git a/app/Console/Commands/Overrides/UpCommand.php b/app/Console/Commands/Overrides/UpCommand.php new file mode 100644 index 0000000..225634d --- /dev/null +++ b/app/Console/Commands/Overrides/UpCommand.php @@ -0,0 +1,26 @@ +hasCompletedMigrations()) { + $this->showMigrationWarning(); + + return 1; + } + + return parent::handle() ?? 0; + } +} diff --git a/app/Console/Commands/Schedule/ProcessRunnableCommand.php b/app/Console/Commands/Schedule/ProcessRunnableCommand.php new file mode 100644 index 0000000..d3dd134 --- /dev/null +++ b/app/Console/Commands/Schedule/ProcessRunnableCommand.php @@ -0,0 +1,76 @@ +with('tasks') + ->whereRelation('server', fn (Builder $builder) => $builder->whereNull('status')) + ->where('is_active', true) + ->where('is_processing', false) + ->whereRaw('next_run_at <= NOW()') + ->get(); + + if ($schedules->count() < 1) { + $this->line('There are no scheduled tasks for servers that need to be run.'); + + return 0; + } + + $bar = $this->output->createProgressBar(count($schedules)); + foreach ($schedules as $schedule) { + $bar->clear(); + $this->processSchedule($schedule); + $bar->advance(); + $bar->display(); + } + + $this->line(''); + + return 0; + } + + /** + * Processes a given schedule and logs and errors encountered the console output. This should + * never throw an exception out, otherwise you'll end up killing the entire run group causing + * any other schedules to not process correctly. + * + * @see https://github.com/pterodactyl/panel/issues/2609 + */ + protected function processSchedule(Schedule $schedule) + { + if ($schedule->tasks->isEmpty()) { + return; + } + + try { + $this->getLaravel()->make(ProcessScheduleService::class)->handle($schedule); + + $this->line(trans('command/messages.schedule.output_line', [ + 'schedule' => $schedule->name, + 'hash' => $schedule->hashid, + ])); + } catch (\Throwable|\Exception $exception) { + Log::error($exception, ['schedule_id' => $schedule->id]); + + $this->error("An error was encountered while processing Schedule #$schedule->id: " . $exception->getMessage()); + } + } +} diff --git a/app/Console/Commands/Server/BulkPowerActionCommand.php b/app/Console/Commands/Server/BulkPowerActionCommand.php new file mode 100644 index 0000000..4e7ae1a --- /dev/null +++ b/app/Console/Commands/Server/BulkPowerActionCommand.php @@ -0,0 +1,106 @@ +argument('action'); + $nodes = empty($this->option('nodes')) ? [] : explode(',', $this->option('nodes')); + $servers = empty($this->option('servers')) ? [] : explode(',', $this->option('servers')); + + $validator = $this->validator->make([ + 'action' => $action, + 'nodes' => $nodes, + 'servers' => $servers, + ], [ + 'action' => 'string|in:start,stop,kill,restart', + 'nodes' => 'array', + 'nodes.*' => 'integer|min:1', + 'servers' => 'array', + 'servers.*' => 'integer|min:1', + ]); + + if ($validator->fails()) { + foreach ($validator->getMessageBag()->all() as $message) { + $this->output->error($message); + } + + throw new ValidationException($validator); + } + + $count = $this->getQueryBuilder($servers, $nodes)->count(); + if (!$this->confirm(trans('command/messages.server.power.confirm', ['action' => $action, 'count' => $count])) && $this->input->isInteractive()) { + return; + } + + $bar = $this->output->createProgressBar($count); + $powerRepository = $this->powerRepository; + $this->getQueryBuilder($servers, $nodes)->each(function (Server $server) use ($action, $powerRepository, &$bar) { + $bar->clear(); + + try { + $powerRepository->setServer($server)->send($action); + } catch (DaemonConnectionException $exception) { + $this->output->error(trans('command/messages.server.power.action_failed', [ + 'name' => $server->name, + 'id' => $server->id, + 'node' => $server->node->name, + 'message' => $exception->getMessage(), + ])); + } + + $bar->advance(); + $bar->display(); + }); + + $this->line(''); + } + + /** + * Returns the query builder instance that will return the servers that should be affected. + */ + protected function getQueryBuilder(array $servers, array $nodes): Builder + { + $instance = Server::query()->whereNull('status'); + + if (!empty($nodes) && !empty($servers)) { + $instance->whereIn('id', $servers)->orWhereIn('node_id', $nodes); + } elseif (empty($nodes) && !empty($servers)) { + $instance->whereIn('id', $servers); + } elseif (!empty($nodes) && empty($servers)) { + $instance->whereIn('node_id', $nodes); + } + + return $instance->with('node'); + } +} diff --git a/app/Console/Commands/TelemetryCommand.php b/app/Console/Commands/TelemetryCommand.php new file mode 100644 index 0000000..3e1b0c2 --- /dev/null +++ b/app/Console/Commands/TelemetryCommand.php @@ -0,0 +1,34 @@ +output->info('Collecting telemetry data, this may take a while...'); + + VarDumper::dump($this->telemetryCollectionService->collect()); + } +} diff --git a/app/Console/Commands/UpgradeCommand.php b/app/Console/Commands/UpgradeCommand.php new file mode 100644 index 0000000..6d033a5 --- /dev/null +++ b/app/Console/Commands/UpgradeCommand.php @@ -0,0 +1,195 @@ +option('skip-download'); + if (!$skipDownload) { + $this->output->warning('This command does not verify the integrity of downloaded assets. Please ensure that you trust the download source before continuing. If you do not wish to download an archive, please indicate that using the --skip-download flag, or answering "no" to the question below.'); + $this->output->comment('Download Source (set with --url=):'); + $this->line($this->getUrl()); + } + + if (version_compare(PHP_VERSION, '7.4.0') < 0) { + $this->error('Cannot execute self-upgrade process. The minimum required PHP version required is 7.4.0, you have [' . PHP_VERSION . '].'); + } + + $user = 'www-data'; + $group = 'www-data'; + if ($this->input->isInteractive()) { + if (!$skipDownload) { + $skipDownload = !$this->confirm('Would you like to download and unpack the archive files for the latest version?', true); + } + + if (is_null($this->option('user'))) { + $userDetails = posix_getpwuid(fileowner('public')); + $user = $userDetails['name'] ?? 'www-data'; + + if (!$this->confirm("Your webserver user has been detected as [{$user}]: is this correct?", true)) { + $user = $this->anticipate( + 'Please enter the name of the user running your webserver process. This varies from system to system, but is generally "www-data", "nginx", or "apache".', + [ + 'www-data', + 'nginx', + 'apache', + ] + ); + } + } + + if (is_null($this->option('group'))) { + $groupDetails = posix_getgrgid(filegroup('public')); + $group = $groupDetails['name'] ?? 'www-data'; + + if (!$this->confirm("Your webserver group has been detected as [{$group}]: is this correct?", true)) { + $group = $this->anticipate( + 'Please enter the name of the group running your webserver process. Normally this is the same as your user.', + [ + 'www-data', + 'nginx', + 'apache', + ] + ); + } + } + + if (!$this->confirm('Are you sure you want to run the upgrade process for your Panel?')) { + $this->warn('Upgrade process terminated by user.'); + + return; + } + } + + ini_set('output_buffering', '0'); + $bar = $this->output->createProgressBar($skipDownload ? 9 : 10); + $bar->start(); + + if (!$skipDownload) { + $this->withProgress($bar, function () { + $this->line("\$upgrader> curl -L \"{$this->getUrl()}\" | tar -xzv"); + $process = Process::fromShellCommandline("curl -L \"{$this->getUrl()}\" | tar -xzv"); + $process->run(function ($type, $buffer) { + $this->{$type === Process::ERR ? 'error' : 'line'}($buffer); + }); + }); + } + + $this->withProgress($bar, function () { + $this->line('$upgrader> php artisan down'); + $this->call('down'); + }); + + $this->withProgress($bar, function () { + $this->line('$upgrader> chmod -R 755 storage bootstrap/cache'); + $process = new Process(['chmod', '-R', '755', 'storage', 'bootstrap/cache']); + $process->run(function ($type, $buffer) { + $this->{$type === Process::ERR ? 'error' : 'line'}($buffer); + }); + }); + + $this->withProgress($bar, function () { + $command = ['composer', 'install', '--no-ansi']; + if (config('app.env') === 'production' && !config('app.debug')) { + $command[] = '--optimize-autoloader'; + $command[] = '--no-dev'; + } + + $this->line('$upgrader> ' . implode(' ', $command)); + $process = new Process($command); + $process->setTimeout(10 * 60); + $process->run(function ($type, $buffer) { + $this->line($buffer); + }); + }); + + /** @var \Illuminate\Foundation\Application $app */ + $app = require __DIR__ . '/../../../bootstrap/app.php'; + /** @var \Pterodactyl\Console\Kernel $kernel */ + $kernel = $app->make(Kernel::class); + $kernel->bootstrap(); + $this->setLaravel($app); + + $this->withProgress($bar, function () { + $this->line('$upgrader> php artisan view:clear'); + $this->call('view:clear'); + }); + + $this->withProgress($bar, function () { + $this->line('$upgrader> php artisan config:clear'); + $this->call('config:clear'); + }); + + $this->withProgress($bar, function () { + $this->line('$upgrader> php artisan migrate --force --seed'); + $this->call('migrate', ['--force' => true, '--seed' => true]); + }); + + $this->withProgress($bar, function () use ($user, $group) { + $this->line("\$upgrader> chown -R {$user}:{$group} *"); + $process = Process::fromShellCommandline("chown -R {$user}:{$group} *", $this->getLaravel()->basePath()); + $process->setTimeout(10 * 60); + $process->run(function ($type, $buffer) { + $this->{$type === Process::ERR ? 'error' : 'line'}($buffer); + }); + }); + + $this->withProgress($bar, function () { + $this->line('$upgrader> php artisan queue:restart'); + $this->call('queue:restart'); + }); + + $this->withProgress($bar, function () { + $this->line('$upgrader> php artisan up'); + $this->call('up'); + }); + + $this->newLine(2); + $this->info('Panel has been successfully upgraded. Please ensure you also update any Wings instances: https://pterodactyl.io/wings/1.0/upgrading.html'); + } + + protected function withProgress(ProgressBar $bar, \Closure $callback) + { + $bar->clear(); + $callback(); + $bar->advance(); + $bar->display(); + } + + protected function getUrl(): string + { + if ($this->option('url')) { + return $this->option('url'); + } + + return sprintf(self::DEFAULT_URL, $this->option('release') ? 'download/v' . $this->option('release') : 'latest/download'); + } +} diff --git a/app/Console/Commands/User/DeleteUserCommand.php b/app/Console/Commands/User/DeleteUserCommand.php new file mode 100644 index 0000000..2b13b48 --- /dev/null +++ b/app/Console/Commands/User/DeleteUserCommand.php @@ -0,0 +1,71 @@ +option('user') ?? $this->ask(trans('command/messages.user.search_users')); + Assert::notEmpty($search, 'Search term should be an email address, got: %s.'); + + $results = User::query() + ->where('id', 'LIKE', "$search%") + ->orWhere('username', 'LIKE', "$search%") + ->orWhere('email', 'LIKE', "$search%") + ->get(); + + if (count($results) < 1) { + $this->error(trans('command/messages.user.no_users_found')); + if ($this->input->isInteractive()) { + return $this->handle(); + } + + return 1; + } + + if ($this->input->isInteractive()) { + $tableValues = []; + foreach ($results as $user) { + $tableValues[] = [$user->id, $user->email, $user->name]; + } + + $this->table(['User ID', 'Email', 'Name'], $tableValues); + if (!$deleteUser = $this->ask(trans('command/messages.user.select_search_user'))) { + return $this->handle(); + } + } else { + if (count($results) > 1) { + $this->error(trans('command/messages.user.multiple_found')); + + return 1; + } + + $deleteUser = $results->first(); + } + + if ($this->confirm(trans('command/messages.user.confirm_delete')) || !$this->input->isInteractive()) { + $this->deletionService->handle($deleteUser); + $this->info(trans('command/messages.user.deleted')); + } + + return 0; + } +} diff --git a/app/Console/Commands/User/DisableTwoFactorCommand.php b/app/Console/Commands/User/DisableTwoFactorCommand.php new file mode 100644 index 0000000..0522201 --- /dev/null +++ b/app/Console/Commands/User/DisableTwoFactorCommand.php @@ -0,0 +1,43 @@ +input->isInteractive()) { + $this->output->warning(trans('command/messages.user.2fa_help_text')); + } + + $email = $this->option('email') ?? $this->ask(trans('command/messages.user.ask_email')); + $user = $this->repository->setColumns(['id', 'email'])->findFirstWhere([['email', '=', $email]]); + + $this->repository->withoutFreshModel()->update($user->id, [ + 'use_totp' => false, + 'totp_secret' => null, + ]); + $this->info(trans('command/messages.user.2fa_disabled', ['email' => $user->email])); + } +} diff --git a/app/Console/Commands/User/MakeUserCommand.php b/app/Console/Commands/User/MakeUserCommand.php new file mode 100644 index 0000000..635a956 --- /dev/null +++ b/app/Console/Commands/User/MakeUserCommand.php @@ -0,0 +1,51 @@ +option('admin') ?? $this->confirm(trans('command/messages.user.ask_admin')); + $email = $this->option('email') ?? $this->ask(trans('command/messages.user.ask_email')); + $username = $this->option('username') ?? $this->ask(trans('command/messages.user.ask_username')); + $name_first = $this->option('name-first') ?? $this->ask(trans('command/messages.user.ask_name_first')); + $name_last = $this->option('name-last') ?? $this->ask(trans('command/messages.user.ask_name_last')); + + if (is_null($password = $this->option('password')) && !$this->option('no-password')) { + $this->warn(trans('command/messages.user.ask_password_help')); + $this->line(trans('command/messages.user.ask_password_tip')); + $password = $this->secret(trans('command/messages.user.ask_password')); + } + + $user = $this->creationService->handle(compact('email', 'username', 'name_first', 'name_last', 'password', 'root_admin')); + $this->table(['Field', 'Value'], [ + ['UUID', $user->uuid], + ['Email', $user->email], + ['Username', $user->username], + ['Name', $user->name], + ['Admin', $user->root_admin ? 'Yes' : 'No'], + ]); + } +} diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php new file mode 100644 index 0000000..5634329 --- /dev/null +++ b/app/Console/Kernel.php @@ -0,0 +1,76 @@ +load(__DIR__ . '/Commands'); + } + + /** + * Define the application's command schedule. + */ + protected function schedule(Schedule $schedule): void + { + // https://laravel.com/docs/10.x/upgrade#redis-cache-tags + $schedule->command('cache:prune-stale-tags')->hourly(); + + // Execute scheduled commands for servers every minute, as if there was a normal cron running. + $schedule->command(ProcessRunnableCommand::class)->everyMinute()->withoutOverlapping(); + $schedule->command(CleanServiceBackupFilesCommand::class)->daily(); + + if (config('backups.prune_age')) { + // Every 30 minutes, run the backup pruning command so that any abandoned backups can be deleted. + $schedule->command(PruneOrphanedBackupsCommand::class)->everyThirtyMinutes(); + } + + if (config('activity.prune_days')) { + $schedule->command(PruneCommand::class, ['--model' => [ActivityLog::class]])->daily(); + } + + if (config('pterodactyl.telemetry.enabled')) { + $this->registerTelemetry($schedule); + } + } + + /** + * I wonder what this does. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Illuminate\Contracts\Container\BindingResolutionException + */ + private function registerTelemetry(Schedule $schedule): void + { + $settingsRepository = app()->make(SettingsRepository::class); + + $uuid = $settingsRepository->get('app:telemetry:uuid'); + if (is_null($uuid)) { + $uuid = Uuid::uuid4()->toString(); + $settingsRepository->set('app:telemetry:uuid', $uuid); + } + + // Calculate a fixed time to run the data push at, this will be the same time every day. + $time = hexdec(str_replace('-', '', substr($uuid, 27))) % 1440; + $hour = floor($time / 60); + $minute = $time % 60; + + // Run the telemetry collector. + $schedule->call(app()->make(TelemetryCollectionService::class))->description('Collect Telemetry')->dailyAt("$hour:$minute"); + } +} diff --git a/app/Console/RequiresDatabaseMigrations.php b/app/Console/RequiresDatabaseMigrations.php new file mode 100644 index 0000000..2e5ebe6 --- /dev/null +++ b/app/Console/RequiresDatabaseMigrations.php @@ -0,0 +1,55 @@ +getLaravel()->make('migrator'); + + $files = $migrator->getMigrationFiles(database_path('migrations')); + + if (!$migrator->repositoryExists()) { + return false; + } + + if (array_diff(array_keys($files), $migrator->getRepository()->getRan())) { + return false; + } + + return true; + } + + /** + * Throw a massive error into the console to hopefully catch the users attention and get + * them to properly run the migrations rather than ignoring all of the other previous + * errors... + */ + protected function showMigrationWarning(): void + { + $this->getOutput()->writeln(' +| @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | +| | +| Your database has not been properly migrated! | +| | +| @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | + +You must run the following command to finish migrating your database: + + php artisan migrate --step --force + +You will not be able to use Pterodactyl Panel as expected without fixing your +database state by running the command above. +'); + + $this->getOutput()->error('You must correct the error above before continuing.'); + } +} diff --git a/app/Contracts/Core/ReceivesEvents.php b/app/Contracts/Core/ReceivesEvents.php new file mode 100644 index 0000000..dbbad44 --- /dev/null +++ b/app/Contracts/Core/ReceivesEvents.php @@ -0,0 +1,13 @@ +model->event === $event; + } + + public function actor(): ?Model + { + return $this->isSystem() ? null : $this->model->actor; + } + + public function isServerEvent(): bool + { + return Str::startsWith($this->model->event, 'server:'); + } + + public function isSystem(): bool + { + return is_null($this->model->actor_id); + } +} diff --git a/app/Events/Auth/DirectLogin.php b/app/Events/Auth/DirectLogin.php new file mode 100644 index 0000000..99df4e1 --- /dev/null +++ b/app/Events/Auth/DirectLogin.php @@ -0,0 +1,13 @@ +level; + } + + public function getStatusCode(): int + { + return Response::HTTP_BAD_REQUEST; + } + + public function getHeaders(): array + { + return []; + } + + /** + * Render the exception to the user by adding a flashed message to the session + * and then redirecting them back to the page that they came from. If the + * request originated from an API hit, return the error in JSONAPI spec format. + */ + public function render(Request $request): JsonResponse|RedirectResponse + { + if ($request->expectsJson()) { + return response()->json(Handler::toArray($this), $this->getStatusCode(), $this->getHeaders()); + } + + app(AlertsMessageBag::class)->danger($this->getMessage())->flash(); + + return redirect()->back()->withInput(); + } + + /** + * Log the exception to the logs using the defined error level only if the previous + * exception is set. + * + * @throws \Throwable + */ + public function report() + { + if (!$this->getPrevious() instanceof \Exception || !Handler::isReportable($this->getPrevious())) { + return null; + } + + try { + $logger = Container::getInstance()->make(LoggerInterface::class); + } catch (Exception) { + throw $this->getPrevious(); + } + + return $logger->{$this->getErrorLevel()}($this->getPrevious()); + } +} diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php new file mode 100644 index 0000000..80816b4 --- /dev/null +++ b/app/Exceptions/Handler.php @@ -0,0 +1,283 @@ + 401, + AuthorizationException::class => 403, + ValidationException::class => 422, + ]; + + /** + * A list of the inputs that are never flashed for validation exceptions. + */ + protected $dontFlash = [ + 'token', + 'secret', + 'password', + 'password_confirmation', + ]; + + /** + * Registers the exception handling callbacks for the application. This + * will capture specific exception types that we do not want to include + * the detailed stack traces for since they could reveal credentials to + * whoever can read the logs. + * + * @noinspection PhpUnusedLocalVariableInspection + */ + public function register(): void + { + if (config('app.exceptions.report_all', false)) { + $this->dontReport = []; + } + + $this->reportable(function (\PDOException $ex) { + $ex = $this->generateCleanedExceptionStack($ex); + }); + + $this->reportable(function (TransportException $ex) { + $ex = $this->generateCleanedExceptionStack($ex); + }); + } + + private function generateCleanedExceptionStack(\Throwable $exception): string + { + $cleanedStack = ''; + foreach ($exception->getTrace() as $index => $item) { + $cleanedStack .= sprintf( + "#%d %s(%d): %s%s%s\n", + $index, + Arr::get($item, 'file'), + Arr::get($item, 'line'), + Arr::get($item, 'class'), + Arr::get($item, 'type'), + Arr::get($item, 'function') + ); + } + + $message = sprintf( + '%s: %s in %s:%d', + class_basename($exception), + $exception->getMessage(), + $exception->getFile(), + $exception->getLine() + ); + + return $message . "\nStack trace:\n" . trim($cleanedStack); + } + + /** + * Render an exception into an HTTP response. + * + * @param \Illuminate\Http\Request $request + * + * @throws \Throwable + */ + public function render($request, \Throwable $e): Response + { + $connections = $this->container->make(Connection::class); + + // If we are currently wrapped up inside a transaction, we will roll all the way + // back to the beginning. This needs to happen, otherwise session data does not + // get properly persisted. + // + // This is kind of a hack, and ideally things like this should be handled as + // much as possible at the code level, but there are a lot of spots that do a + // ton of actions and were written before this bug discovery was made. + // + // @see https://github.com/pterodactyl/panel/pull/1468 + if ($connections->transactionLevel()) { + $connections->rollBack(0); + } + + return parent::render($request, $e); + } + + /** + * Transform a validation exception into a consistent format to be returned for + * calls to the API. + * + * @param \Illuminate\Http\Request $request + */ + public function invalidJson($request, ValidationException $exception): JsonResponse + { + $codes = Collection::make($exception->validator->failed())->mapWithKeys(function ($reasons, $field) { + $cleaned = []; + foreach ($reasons as $reason => $attrs) { + $cleaned[] = Str::snake($reason); + } + + return [str_replace('.', '_', $field) => $cleaned]; + })->toArray(); + + $errors = Collection::make($exception->errors())->map(function ($errors, $field) use ($codes, $exception) { + $response = []; + foreach ($errors as $key => $error) { + $meta = [ + 'source_field' => $field, + 'rule' => str_replace(self::PTERODACTYL_RULE_STRING, 'p_', Arr::get( + $codes, + str_replace('.', '_', $field) . '.' . $key + )), + ]; + + $converted = $this->convertExceptionToArray($exception)['errors'][0]; + $converted['detail'] = $error; + $converted['meta'] = array_merge($converted['meta'] ?? [], $meta); + + $response[] = $converted; + } + + return $response; + })->flatMap(function ($errors) { + return $errors; + })->toArray(); + + return response()->json(['errors' => $errors], $exception->status); + } + + /** + * Return the exception as a JSONAPI representation for use on API requests. + */ + protected function convertExceptionToArray(\Throwable $e, array $override = []): array + { + $match = self::$exceptionResponseCodes[get_class($e)] ?? null; + + $error = [ + 'code' => class_basename($e), + 'status' => method_exists($e, 'getStatusCode') + ? strval($e->getStatusCode()) + : strval($match ?? '500'), + 'detail' => $e instanceof HttpExceptionInterface || !is_null($match) + ? $e->getMessage() + : 'An unexpected error was encountered while processing this request, please try again.', + ]; + + if ($e instanceof ModelNotFoundException || $e->getPrevious() instanceof ModelNotFoundException) { + // Show a nicer error message compared to the standard "No query results for model" + // response that is normally returned. If we are in debug mode this will get overwritten + // with a more specific error message to help narrow down things. + $error['detail'] = 'The requested resource could not be found on the server.'; + } + + if (config('app.debug')) { + $error = array_merge($error, [ + 'detail' => $e->getMessage(), + 'source' => [ + 'line' => $e->getLine(), + 'file' => str_replace(Application::getInstance()->basePath(), '', $e->getFile()), + ], + 'meta' => [ + 'trace' => Collection::make($e->getTrace()) + ->map(fn ($trace) => Arr::except($trace, ['args'])) + ->all(), + 'previous' => Collection::make($this->extractPrevious($e)) + ->map(fn ($exception) => $e->getTrace()) + ->map(fn ($trace) => Arr::except($trace, ['args'])) + ->all(), + ], + ]); + } + + return ['errors' => [array_merge($error, $override)]]; + } + + /** + * Return an array of exceptions that should not be reported. + */ + public static function isReportable(\Exception $exception): bool + { + return (new static(Container::getInstance()))->shouldReport($exception); + } + + /** + * Convert an authentication exception into an unauthenticated response. + * + * @param \Illuminate\Http\Request $request + */ + protected function unauthenticated($request, AuthenticationException $exception): JsonResponse|RedirectResponse + { + if ($request->expectsJson()) { + return new JsonResponse($this->convertExceptionToArray($exception), JsonResponse::HTTP_UNAUTHORIZED); + } + + return redirect()->guest('/auth/login'); + } + + /** + * Extracts all the previous exceptions that lead to the one passed into this + * function being thrown. + * + * @return \Throwable[] + */ + protected function extractPrevious(\Throwable $e): array + { + $previous = []; + while ($value = $e->getPrevious()) { + if (!$value instanceof \Throwable) { + break; + } + $previous[] = $value; + $e = $value; + } + + return $previous; + } + + /** + * Helper method to allow reaching into the handler to convert an exception + * into the expected array response type. + */ + public static function toArray(\Throwable $e): array + { + return (new self(app()))->convertExceptionToArray($e); + } +} diff --git a/app/Exceptions/Http/Base/InvalidPasswordProvidedException.php b/app/Exceptions/Http/Base/InvalidPasswordProvidedException.php new file mode 100644 index 0000000..bdcb8ec --- /dev/null +++ b/app/Exceptions/Http/Base/InvalidPasswordProvidedException.php @@ -0,0 +1,9 @@ +getResponse() : null; + $this->requestId = $response?->getHeaderLine('X-Request-Id'); + + if ($useStatusCode) { + $this->statusCode = is_null($response) ? $this->statusCode : $response->getStatusCode(); + // There are rare conditions where wings encounters a panic condition and crashes the + // request being made after content has already been sent over the wire. In these cases + // you can end up with a "successful" response code that is actual an error. + // + // Handle those better here since we shouldn't ever end up in this exception state and + // be returning a 2XX level response. + if ($this->statusCode < 400) { + $this->statusCode = Response::HTTP_BAD_GATEWAY; + } + } + + if (is_null($response)) { + $message = 'Could not establish a connection to the machine running this server. Please try again.'; + } else { + $message = sprintf('There was an error while communicating with the machine running this server. This error has been logged, please try again. (code: %s) (request_id: %s)', $response->getStatusCode(), $this->requestId ?? ''); + } + + // Attempt to pull the actual error message off the response and return that if it is not + // a 500 level error. + if ($this->statusCode < 500 && !is_null($response)) { + $body = json_decode($response->getBody()->__toString(), true); + $message = sprintf('An error occurred on the remote host: %s. (request id: %s)', $body['error'] ?? $message, $this->requestId ?? ''); + } + + $level = $this->statusCode >= 500 && $this->statusCode !== 504 + ? DisplayException::LEVEL_ERROR + : DisplayException::LEVEL_WARNING; + + parent::__construct($message, $previous, $level); + } + + /** + * Override the default reporting method for DisplayException by just logging immediately + * here and including the specific X-Request-Id header that was returned by the call. + */ + public function report() + { + Log::{$this->getErrorLevel()}($this->getPrevious(), [ + 'request_id' => $this->requestId, + ]); + } + + /** + * Return the HTTP status code for this exception. + */ + public function getStatusCode(): int + { + return $this->statusCode; + } + + public function getRequestId(): ?string + { + return $this->requestId; + } +} diff --git a/app/Exceptions/Http/HttpForbiddenException.php b/app/Exceptions/Http/HttpForbiddenException.php new file mode 100644 index 0000000..1488265 --- /dev/null +++ b/app/Exceptions/Http/HttpForbiddenException.php @@ -0,0 +1,17 @@ +isSuspended()) { + $message = 'This server is currently suspended and the functionality requested is unavailable.'; + } elseif ($server->node->isUnderMaintenance()) { + $message = 'The node of this server is currently under maintenance and the functionality requested is unavailable.'; + } elseif (!$server->isInstalled()) { + $message = 'This server has not yet completed its installation process, please try again later.'; + } elseif ($server->status === Server::STATUS_RESTORING_BACKUP) { + $message = 'This server is currently restoring from a backup, please try again later.'; + } elseif (!is_null($server->transfer)) { + $message = 'This server is currently being transferred to a new machine, please try again later.'; + } + + parent::__construct($message, $previous); + } +} diff --git a/app/Exceptions/Http/TwoFactorAuthRequiredException.php b/app/Exceptions/Http/TwoFactorAuthRequiredException.php new file mode 100644 index 0000000..95f300e --- /dev/null +++ b/app/Exceptions/Http/TwoFactorAuthRequiredException.php @@ -0,0 +1,18 @@ +getKey(), + $validator->errors()->toJson() + ); + + parent::__construct($message); + } + + /** + * Return the validator message bag. + */ + public function getMessageBag(): MessageBag + { + return $this->validator->errors(); + } + + /** + * Return the status code for this request. + */ + public function getStatusCode(): int + { + return 500; + } + + public function getHeaders(): array + { + return []; + } + + public function getValidator(): Validator + { + return $this->validator; + } + + public function getModel(): Model + { + return $this->model; + } +} diff --git a/app/Exceptions/PterodactylException.php b/app/Exceptions/PterodactylException.php new file mode 100644 index 0000000..451ae92 --- /dev/null +++ b/app/Exceptions/PterodactylException.php @@ -0,0 +1,7 @@ + $port])); + } +} diff --git a/app/Exceptions/Service/Allocation/NoAutoAllocationSpaceAvailableException.php b/app/Exceptions/Service/Allocation/NoAutoAllocationSpaceAvailableException.php new file mode 100644 index 0000000..8e3c9b0 --- /dev/null +++ b/app/Exceptions/Service/Allocation/NoAutoAllocationSpaceAvailableException.php @@ -0,0 +1,18 @@ + 'https://github.com/pterodactyl/panel/blob/develop/package.json', + ]; + } +} diff --git a/app/Exceptions/Transformer/InvalidTransformerLevelException.php b/app/Exceptions/Transformer/InvalidTransformerLevelException.php new file mode 100644 index 0000000..3d4c242 --- /dev/null +++ b/app/Exceptions/Transformer/InvalidTransformerLevelException.php @@ -0,0 +1,9 @@ +config = $app->make(ConfigRepository::class); + } + + /** + * Returns a backup adapter instance. + */ + public function adapter(string $name = null): FilesystemAdapter + { + return $this->get($name ?: $this->getDefaultAdapter()); + } + + /** + * Set the given backup adapter instance. + */ + public function set(string $name, FilesystemAdapter $disk): self + { + $this->adapters[$name] = $disk; + + return $this; + } + + /** + * Gets a backup adapter. + */ + protected function get(string $name): FilesystemAdapter + { + return $this->adapters[$name] = $this->resolve($name); + } + + /** + * Resolve the given backup disk. + */ + protected function resolve(string $name): FilesystemAdapter + { + $config = $this->getConfig($name); + + if (empty($config['adapter'])) { + throw new \InvalidArgumentException("Backup disk [$name] does not have a configured adapter."); + } + + $adapter = $config['adapter']; + + if (isset($this->customCreators[$name])) { + return $this->callCustomCreator($config); + } + + $adapterMethod = 'create' . Str::studly($adapter) . 'Adapter'; + if (method_exists($this, $adapterMethod)) { + $instance = $this->{$adapterMethod}($config); + + Assert::isInstanceOf($instance, FilesystemAdapter::class); + + return $instance; + } + + throw new \InvalidArgumentException("Adapter [$adapter] is not supported."); + } + + /** + * Calls a custom creator for a given adapter type. + */ + protected function callCustomCreator(array $config): mixed + { + return $this->customCreators[$config['adapter']]($this->app, $config); + } + + /** + * Creates a new Wings adapter. + */ + public function createWingsAdapter(array $config): FilesystemAdapter + { + return new InMemoryFilesystemAdapter(); + } + + /** + * Creates a new S3 adapter. + */ + public function createS3Adapter(array $config): FilesystemAdapter + { + $config['version'] = 'latest'; + + if (!empty($config['key']) && !empty($config['secret'])) { + $config['credentials'] = Arr::only($config, ['key', 'secret', 'token']); + } + + $client = new S3Client($config); + + return new S3Filesystem($client, $config['bucket'], $config['prefix'] ?? '', $config['options'] ?? []); + } + + /** + * Returns the configuration associated with a given backup type. + */ + protected function getConfig(string $name): array + { + return $this->config->get("backups.disks.$name") ?: []; + } + + /** + * Get the default backup driver name. + */ + public function getDefaultAdapter(): string + { + return $this->config->get('backups.default'); + } + + /** + * Set the default session driver name. + */ + public function setDefaultAdapter(string $name): void + { + $this->config->set('backups.default', $name); + } + + /** + * Unset the given adapter instances. + * + * @param string|string[] $adapter + */ + public function forget(array|string $adapter): self + { + foreach ((array) $adapter as $adapterName) { + unset($this->adapters[$adapterName]); + } + + return $this; + } + + /** + * Register a custom adapter creator closure. + */ + public function extend(string $adapter, \Closure $callback): self + { + $this->customCreators[$adapter] = $callback; + + return $this; + } +} diff --git a/app/Extensions/DynamicDatabaseConnection.php b/app/Extensions/DynamicDatabaseConnection.php new file mode 100644 index 0000000..5adbd17 --- /dev/null +++ b/app/Extensions/DynamicDatabaseConnection.php @@ -0,0 +1,48 @@ +repository->find($host); + } + + $this->config->set('database.connections.' . $connection, [ + 'driver' => self::DB_DRIVER, + 'host' => $host->host, + 'port' => $host->port, + 'database' => $database, + 'username' => $host->username, + 'password' => $this->encrypter->decrypt($host->password), + 'charset' => self::DB_CHARSET, + 'collation' => self::DB_COLLATION, + ]); + } +} diff --git a/app/Extensions/Facades/Theme.php b/app/Extensions/Facades/Theme.php new file mode 100644 index 0000000..55ebc10 --- /dev/null +++ b/app/Extensions/Facades/Theme.php @@ -0,0 +1,13 @@ +client; + } + + public function getBucket(): string + { + return $this->bucket; + } +} diff --git a/app/Extensions/Hashids.php b/app/Extensions/Hashids.php new file mode 100644 index 0000000..1a6bfdb --- /dev/null +++ b/app/Extensions/Hashids.php @@ -0,0 +1,22 @@ +decode($encoded); + if (!is_array($result)) { + return $default; + } + + return array_first($result, null, $default); + } +} diff --git a/app/Extensions/Illuminate/Database/Eloquent/Builder.php b/app/Extensions/Illuminate/Database/Eloquent/Builder.php new file mode 100644 index 0000000..6ce63c2 --- /dev/null +++ b/app/Extensions/Illuminate/Database/Eloquent/Builder.php @@ -0,0 +1,16 @@ +accessToken = $accessToken; + $this->plainTextToken = $plainTextToken; + } +} diff --git a/app/Extensions/Lcobucci/JWT/Encoding/TimestampDates.php b/app/Extensions/Lcobucci/JWT/Encoding/TimestampDates.php new file mode 100644 index 0000000..e47a3fd --- /dev/null +++ b/app/Extensions/Lcobucci/JWT/Encoding/TimestampDates.php @@ -0,0 +1,29 @@ +getTimestamp(); + } + + return $claims; + } +} diff --git a/app/Extensions/League/Fractal/Serializers/PterodactylSerializer.php b/app/Extensions/League/Fractal/Serializers/PterodactylSerializer.php new file mode 100644 index 0000000..5b53a5a --- /dev/null +++ b/app/Extensions/League/Fractal/Serializers/PterodactylSerializer.php @@ -0,0 +1,58 @@ + $resourceKey, + 'attributes' => $data, + ]; + } + + /** + * Serialize a collection. + */ + public function collection(?string $resourceKey, array $data): array + { + $response = []; + foreach ($data as $datum) { + $response[] = $this->item($resourceKey, $datum); + } + + return [ + 'object' => 'list', + 'data' => $response, + ]; + } + + /** + * Serialize a null resource. + */ + public function null(): ?array + { + return [ + 'object' => 'null_resource', + 'attributes' => null, + ]; + } + + /** + * Merge the included resources with the parent resource being serialized. + */ + public function mergeIncludes(array $transformedData, array $includedData): array + { + foreach ($includedData as $key => $datum) { + $transformedData['relationships'][$key] = $datum; + } + + return $transformedData; + } +} diff --git a/app/Extensions/Spatie/Fractalistic/Fractal.php b/app/Extensions/Spatie/Fractalistic/Fractal.php new file mode 100644 index 0000000..0c65d6e --- /dev/null +++ b/app/Extensions/Spatie/Fractalistic/Fractal.php @@ -0,0 +1,45 @@ +serializer)) { + $this->serializer = new PterodactylSerializer(); + } + + // Automatically set the paginator on the response object if the + // data being provided implements a paginator. + if (is_null($this->paginator) && $this->data instanceof LengthAwarePaginator) { + $this->paginator = new IlluminatePaginatorAdapter($this->data); + } + + // If the resource name is not set attempt to pull it off the transformer + // itself and set it automatically. + if ( + is_null($this->resourceName) + && $this->transformer instanceof TransformerAbstract + && method_exists($this->transformer, 'getResourceName') + ) { + $this->resourceName = $this->transformer->getResourceName(); + } + + return parent::createData(); + } +} diff --git a/app/Extensions/Themes/Theme.php b/app/Extensions/Themes/Theme.php new file mode 100644 index 0000000..fd65583 --- /dev/null +++ b/app/Extensions/Themes/Theme.php @@ -0,0 +1,21 @@ +' . PHP_EOL, $this->getUrl($path)); + } + + public function css($path): string + { + return sprintf('' . PHP_EOL, $this->getUrl($path)); + } + + protected function getUrl($path): string + { + return '/themes/pterodactyl/' . ltrim($path, '/'); + } +} diff --git a/app/Facades/Activity.php b/app/Facades/Activity.php new file mode 100644 index 0000000..5640210 --- /dev/null +++ b/app/Facades/Activity.php @@ -0,0 +1,14 @@ +getTimezone()->toOffsetName(); + } +} diff --git a/app/Helpers/Utilities.php b/app/Helpers/Utilities.php new file mode 100644 index 0000000..0c468e0 --- /dev/null +++ b/app/Helpers/Utilities.php @@ -0,0 +1,57 @@ +getNextRunDate()); + } + + public static function checked(string $name, mixed $default): string + { + $errors = session('errors'); + + if (isset($errors) && $errors instanceof ViewErrorBag && $errors->any()) { + return old($name) ? 'checked' : ''; + } + + return ($default) ? 'checked' : ''; + } +} diff --git a/app/Http/Controllers/Admin/ApiController.php b/app/Http/Controllers/Admin/ApiController.php new file mode 100644 index 0000000..02ad6e5 --- /dev/null +++ b/app/Http/Controllers/Admin/ApiController.php @@ -0,0 +1,87 @@ +view->make('admin.api.index', [ + 'keys' => $this->repository->getApplicationKeys($request->user()), + ]); + } + + /** + * Render view allowing an admin to create a new application API key. + * + * @throws \ReflectionException + */ + public function create(): View + { + $resources = AdminAcl::getResourceList(); + sort($resources); + + return $this->view->make('admin.api.new', [ + 'resources' => $resources, + 'permissions' => [ + 'r' => AdminAcl::READ, + 'rw' => AdminAcl::READ | AdminAcl::WRITE, + 'n' => AdminAcl::NONE, + ], + ]); + } + + /** + * Store the new key and redirect the user back to the application key listing. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + */ + public function store(StoreApplicationApiKeyRequest $request): RedirectResponse + { + $this->keyCreationService->setKeyType(ApiKey::TYPE_APPLICATION)->handle([ + 'memo' => $request->input('memo'), + 'user_id' => $request->user()->id, + ], $request->getKeyPermissions()); + + $this->alert->success('A new application API key has been generated for your account.')->flash(); + + return redirect()->route('admin.api.index'); + } + + /** + * Delete an application API key from the database. + */ + public function delete(Request $request, string $identifier): Response + { + $this->repository->deleteApplicationKey($request->user(), $identifier); + + return response('', 204); + } +} diff --git a/app/Http/Controllers/Admin/BaseController.php b/app/Http/Controllers/Admin/BaseController.php new file mode 100644 index 0000000..53f53ce --- /dev/null +++ b/app/Http/Controllers/Admin/BaseController.php @@ -0,0 +1,26 @@ +view->make('admin.index', ['version' => $this->version]); + } +} diff --git a/app/Http/Controllers/Admin/DatabaseController.php b/app/Http/Controllers/Admin/DatabaseController.php new file mode 100644 index 0000000..864fb03 --- /dev/null +++ b/app/Http/Controllers/Admin/DatabaseController.php @@ -0,0 +1,129 @@ +view->make('admin.databases.index', [ + 'locations' => $this->locationRepository->getAllWithNodes(), + 'hosts' => $this->repository->getWithViewDetails(), + ]); + } + + /** + * Display database host to user. + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function view(int $host): View + { + return $this->view->make('admin.databases.view', [ + 'locations' => $this->locationRepository->getAllWithNodes(), + 'host' => $this->repository->find($host), + 'databases' => $this->databaseRepository->getDatabasesForHost($host), + ]); + } + + /** + * Handle request to create a new database host. + * + * @throws \Throwable + */ + public function create(DatabaseHostFormRequest $request): RedirectResponse + { + try { + $host = $this->creationService->handle($request->normalize()); + } catch (\Exception $exception) { + if ($exception instanceof \PDOException || $exception->getPrevious() instanceof \PDOException) { + $this->alert->danger( + sprintf('There was an error while trying to connect to the host or while executing a query: "%s"', $exception->getMessage()) + )->flash(); + + return redirect()->route('admin.databases')->withInput($request->validated()); + } else { + throw $exception; + } + } + + $this->alert->success('Successfully created a new database host on the system.')->flash(); + + return redirect()->route('admin.databases.view', $host->id); + } + + /** + * Handle updating database host. + * + * @throws \Throwable + */ + public function update(DatabaseHostFormRequest $request, DatabaseHost $host): RedirectResponse + { + $redirect = redirect()->route('admin.databases.view', $host->id); + + try { + $this->updateService->handle($host->id, $request->normalize()); + $this->alert->success('Database host was updated successfully.')->flash(); + } catch (\Exception $exception) { + // Catch any SQL related exceptions and display them back to the user, otherwise just + // throw the exception like normal and move on with it. + if ($exception instanceof \PDOException || $exception->getPrevious() instanceof \PDOException) { + $this->alert->danger( + sprintf('There was an error while trying to connect to the host or while executing a query: "%s"', $exception->getMessage()) + )->flash(); + + return $redirect->withInput($request->normalize()); + } else { + throw $exception; + } + } + + return $redirect; + } + + /** + * Handle request to delete a database host. + * + * @throws \Pterodactyl\Exceptions\Service\HasActiveServersException + */ + public function delete(int $host): RedirectResponse + { + $this->deletionService->handle($host); + $this->alert->success('The requested database host has been deleted from the system.')->flash(); + + return redirect()->route('admin.databases'); + } +} diff --git a/app/Http/Controllers/Admin/LocationController.php b/app/Http/Controllers/Admin/LocationController.php new file mode 100644 index 0000000..ea01cba --- /dev/null +++ b/app/Http/Controllers/Admin/LocationController.php @@ -0,0 +1,103 @@ +view->make('admin.locations.index', [ + 'locations' => $this->repository->getAllWithDetails(), + ]); + } + + /** + * Return the location view page. + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function view(int $id): View + { + return $this->view->make('admin.locations.view', [ + 'location' => $this->repository->getWithNodes($id), + ]); + } + + /** + * Handle request to create new location. + * + * @throws \Throwable + */ + public function create(LocationFormRequest $request): RedirectResponse + { + $location = $this->creationService->handle($request->normalize()); + $this->alert->success('Location was created successfully.')->flash(); + + return redirect()->route('admin.locations.view', $location->id); + } + + /** + * Handle request to update or delete location. + * + * @throws \Throwable + */ + public function update(LocationFormRequest $request, Location $location): RedirectResponse + { + if ($request->input('action') === 'delete') { + return $this->delete($location); + } + + $this->updateService->handle($location->id, $request->normalize()); + $this->alert->success('Location was updated successfully.')->flash(); + + return redirect()->route('admin.locations.view', $location->id); + } + + /** + * Delete a location from the system. + * + * @throws \Exception + * @throws \Pterodactyl\Exceptions\DisplayException + */ + public function delete(Location $location): RedirectResponse + { + try { + $this->deletionService->handle($location->id); + + return redirect()->route('admin.locations'); + } catch (DisplayException $ex) { + $this->alert->danger($ex->getMessage())->flash(); + } + + return redirect()->route('admin.locations.view', $location->id); + } +} diff --git a/app/Http/Controllers/Admin/MountController.php b/app/Http/Controllers/Admin/MountController.php new file mode 100644 index 0000000..097ad66 --- /dev/null +++ b/app/Http/Controllers/Admin/MountController.php @@ -0,0 +1,165 @@ +view->make('admin.mounts.index', [ + 'mounts' => $this->repository->getAllWithDetails(), + ]); + } + + /** + * Return the mount view page. + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function view(string $id): View + { + $nests = Nest::query()->with('eggs')->get(); + $locations = Location::query()->with('nodes')->get(); + + return $this->view->make('admin.mounts.view', [ + 'mount' => $this->repository->getWithRelations($id), + 'nests' => $nests, + 'locations' => $locations, + ]); + } + + /** + * Handle request to create new mount. + * + * @throws \Throwable + */ + public function create(MountFormRequest $request): RedirectResponse + { + $model = (new Mount())->fill($request->validated()); + $model->forceFill(['uuid' => Uuid::uuid4()->toString()]); + + $model->saveOrFail(); + $mount = $model->fresh(); + + $this->alert->success('Mount was created successfully.')->flash(); + + return redirect()->route('admin.mounts.view', $mount->id); + } + + /** + * Handle request to update or delete location. + * + * @throws \Throwable + */ + public function update(MountFormRequest $request, Mount $mount): RedirectResponse + { + if ($request->input('action') === 'delete') { + return $this->delete($mount); + } + + $mount->forceFill($request->validated())->save(); + + $this->alert->success('Mount was updated successfully.')->flash(); + + return redirect()->route('admin.mounts.view', $mount->id); + } + + /** + * Delete a location from the system. + * + * @throws \Exception + */ + public function delete(Mount $mount): RedirectResponse + { + $mount->delete(); + + return redirect()->route('admin.mounts'); + } + + /** + * Adds eggs to the mount's many-to-many relation. + */ + public function addEggs(Request $request, Mount $mount): RedirectResponse + { + $validatedData = $request->validate([ + 'eggs' => 'required|exists:eggs,id', + ]); + + $eggs = $validatedData['eggs'] ?? []; + if (count($eggs) > 0) { + $mount->eggs()->attach($eggs); + } + + $this->alert->success('Mount was updated successfully.')->flash(); + + return redirect()->route('admin.mounts.view', $mount->id); + } + + /** + * Adds nodes to the mount's many-to-many relation. + */ + public function addNodes(Request $request, Mount $mount): RedirectResponse + { + $data = $request->validate(['nodes' => 'required|exists:nodes,id']); + + $nodes = $data['nodes'] ?? []; + if (count($nodes) > 0) { + $mount->nodes()->attach($nodes); + } + + $this->alert->success('Mount was updated successfully.')->flash(); + + return redirect()->route('admin.mounts.view', $mount->id); + } + + /** + * Deletes an egg from the mount's many-to-many relation. + */ + public function deleteEgg(Mount $mount, int $egg_id): Response + { + $mount->eggs()->detach($egg_id); + + return response('', 204); + } + + /** + * Deletes a node from the mount's many-to-many relation. + */ + public function deleteNode(Mount $mount, int $node_id): Response + { + $mount->nodes()->detach($node_id); + + return response('', 204); + } +} diff --git a/app/Http/Controllers/Admin/Nests/EggController.php b/app/Http/Controllers/Admin/Nests/EggController.php new file mode 100644 index 0000000..e71e48e --- /dev/null +++ b/app/Http/Controllers/Admin/Nests/EggController.php @@ -0,0 +1,128 @@ +nestRepository->getWithEggs(); + \JavaScript::put(['nests' => $nests->keyBy('id')]); + + return $this->view->make('admin.eggs.new', ['nests' => $nests]); + } + + /** + * Handle request to store a new Egg. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Service\Egg\NoParentConfigurationFoundException + */ + public function store(EggFormRequest $request): RedirectResponse + { + $data = $request->validated(); + $data['docker_images'] = $this->normalizeDockerImages($data['docker_images'] ?? null); + + $egg = $this->creationService->handle($data); + $this->alert->success(trans('admin/nests.eggs.notices.egg_created'))->flash(); + + return redirect()->route('admin.nests.egg.view', $egg->id); + } + + /** + * Handle request to view a single Egg. + */ + public function view(Egg $egg): View + { + return $this->view->make('admin.eggs.view', [ + 'egg' => $egg, + 'images' => array_map( + fn ($key, $value) => $key === $value ? $value : "$key|$value", + array_keys($egg->docker_images), + $egg->docker_images, + ), + ]); + } + + /** + * Handle request to update an Egg. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Pterodactyl\Exceptions\Service\Egg\NoParentConfigurationFoundException + */ + public function update(EggFormRequest $request, Egg $egg): RedirectResponse + { + $data = $request->validated(); + $data['docker_images'] = $this->normalizeDockerImages($data['docker_images'] ?? null); + + $this->updateService->handle($egg, $data); + $this->alert->success(trans('admin/nests.eggs.notices.updated'))->flash(); + + return redirect()->route('admin.nests.egg.view', $egg->id); + } + + /** + * Handle request to destroy an egg. + * + * @throws \Pterodactyl\Exceptions\Service\Egg\HasChildrenException + * @throws \Pterodactyl\Exceptions\Service\HasActiveServersException + */ + public function destroy(Egg $egg): RedirectResponse + { + $this->deletionService->handle($egg->id); + $this->alert->success(trans('admin/nests.eggs.notices.deleted'))->flash(); + + return redirect()->route('admin.nests.view', $egg->nest_id); + } + + /** + * Normalizes a string of docker image data into the expected egg format. + */ + protected function normalizeDockerImages(string $input = null): array + { + $data = array_map(fn ($value) => trim($value), explode("\n", $input ?? '')); + + $images = []; + // Iterate over the image data provided and convert it into a name => image + // pairing that is used to improve the display on the front-end. + foreach ($data as $value) { + $parts = explode('|', $value, 2); + $images[$parts[0]] = empty($parts[1]) ? $parts[0] : $parts[1]; + } + + return $images; + } +} diff --git a/app/Http/Controllers/Admin/Nests/EggScriptController.php b/app/Http/Controllers/Admin/Nests/EggScriptController.php new file mode 100644 index 0000000..4f997e5 --- /dev/null +++ b/app/Http/Controllers/Admin/Nests/EggScriptController.php @@ -0,0 +1,65 @@ +repository->getWithCopyAttributes($egg); + $copy = $this->repository->findWhere([ + ['copy_script_from', '=', null], + ['nest_id', '=', $egg->nest_id], + ['id', '!=', $egg], + ]); + + $rely = $this->repository->findWhere([ + ['copy_script_from', '=', $egg->id], + ]); + + return $this->view->make('admin.eggs.scripts', [ + 'copyFromOptions' => $copy, + 'relyOnScript' => $rely, + 'egg' => $egg, + ]); + } + + /** + * Handle a request to update the installation script for an Egg. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Pterodactyl\Exceptions\Service\Egg\InvalidCopyFromException + */ + public function update(EggScriptFormRequest $request, Egg $egg): RedirectResponse + { + $this->installScriptService->handle($egg, $request->normalize()); + $this->alert->success(trans('admin/nests.eggs.notices.script_updated'))->flash(); + + return redirect()->route('admin.nests.egg.scripts', $egg); + } +} diff --git a/app/Http/Controllers/Admin/Nests/EggShareController.php b/app/Http/Controllers/Admin/Nests/EggShareController.php new file mode 100644 index 0000000..b2c704e --- /dev/null +++ b/app/Http/Controllers/Admin/Nests/EggShareController.php @@ -0,0 +1,74 @@ +name)), '-'); + + return response($this->exporterService->handle($egg->id), 200, [ + 'Content-Transfer-Encoding' => 'binary', + 'Content-Description' => 'File Transfer', + 'Content-Disposition' => 'attachment; filename=egg-' . $filename . '.json', + 'Content-Type' => 'application/json', + ]); + } + + /** + * Import a new service option using an XML file. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Pterodactyl\Exceptions\Service\Egg\BadJsonFormatException + * @throws \Pterodactyl\Exceptions\Service\InvalidFileUploadException + */ + public function import(EggImportFormRequest $request): RedirectResponse + { + $egg = $this->importerService->handle($request->file('import_file'), $request->input('import_to_nest')); + $this->alert->success(trans('admin/nests.eggs.notices.imported'))->flash(); + + return redirect()->route('admin.nests.egg.view', ['egg' => $egg->id]); + } + + /** + * Update an existing Egg using a new imported file. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Pterodactyl\Exceptions\Service\Egg\BadJsonFormatException + * @throws \Pterodactyl\Exceptions\Service\InvalidFileUploadException + */ + public function update(EggImportFormRequest $request, Egg $egg): RedirectResponse + { + $this->updateImporterService->handle($egg, $request->file('import_file')); + $this->alert->success(trans('admin/nests.eggs.notices.updated_via_import'))->flash(); + + return redirect()->route('admin.nests.egg.view', ['egg' => $egg]); + } +} diff --git a/app/Http/Controllers/Admin/Nests/EggVariableController.php b/app/Http/Controllers/Admin/Nests/EggVariableController.php new file mode 100644 index 0000000..2b84e8b --- /dev/null +++ b/app/Http/Controllers/Admin/Nests/EggVariableController.php @@ -0,0 +1,90 @@ +repository->getWithVariables($egg); + + return $this->view->make('admin.eggs.variables', ['egg' => $egg]); + } + + /** + * Handle a request to create a new Egg variable. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Service\Egg\Variable\BadValidationRuleException + * @throws \Pterodactyl\Exceptions\Service\Egg\Variable\ReservedVariableNameException + */ + public function store(EggVariableFormRequest $request, Egg $egg): RedirectResponse + { + $this->creationService->handle($egg->id, $request->normalize()); + $this->alert->success(trans('admin/nests.variables.notices.variable_created'))->flash(); + + return redirect()->route('admin.nests.egg.variables', $egg->id); + } + + /** + * Handle a request to update an existing Egg variable. + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Pterodactyl\Exceptions\Service\Egg\Variable\ReservedVariableNameException + */ + public function update(EggVariableFormRequest $request, Egg $egg, EggVariable $variable): RedirectResponse + { + $this->updateService->handle($variable, $request->normalize()); + $this->alert->success(trans('admin/nests.variables.notices.variable_updated', [ + 'variable' => htmlspecialchars($variable->name), + ]))->flash(); + + return redirect()->route('admin.nests.egg.variables', $egg->id); + } + + /** + * Handle a request to delete an existing Egg variable from the Panel. + */ + public function destroy(int $egg, EggVariable $variable): RedirectResponse + { + $this->variableRepository->delete($variable->id); + $this->alert->success(trans('admin/nests.variables.notices.variable_deleted', [ + 'variable' => htmlspecialchars($variable->name), + ]))->flash(); + + return redirect()->route('admin.nests.egg.variables', $egg); + } +} diff --git a/app/Http/Controllers/Admin/Nests/NestController.php b/app/Http/Controllers/Admin/Nests/NestController.php new file mode 100644 index 0000000..311a5df --- /dev/null +++ b/app/Http/Controllers/Admin/Nests/NestController.php @@ -0,0 +1,102 @@ +view->make('admin.nests.index', [ + 'nests' => $this->repository->getWithCounts(), + ]); + } + + /** + * Render nest creation page. + */ + public function create(): View + { + return $this->view->make('admin.nests.new'); + } + + /** + * Handle the storage of a new nest. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + */ + public function store(StoreNestFormRequest $request): RedirectResponse + { + $nest = $this->nestCreationService->handle($request->normalize()); + $this->alert->success(trans('admin/nests.notices.created', ['name' => htmlspecialchars($nest->name)]))->flash(); + + return redirect()->route('admin.nests.view', $nest->id); + } + + /** + * Return details about a nest including all the eggs and servers per egg. + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function view(int $nest): View + { + return $this->view->make('admin.nests.view', [ + 'nest' => $this->repository->getWithEggServers($nest), + ]); + } + + /** + * Handle request to update a nest. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function update(StoreNestFormRequest $request, int $nest): RedirectResponse + { + $this->nestUpdateService->handle($nest, $request->normalize()); + $this->alert->success(trans('admin/nests.notices.updated'))->flash(); + + return redirect()->route('admin.nests.view', $nest); + } + + /** + * Handle request to delete a nest. + * + * @throws \Pterodactyl\Exceptions\Service\HasActiveServersException + */ + public function destroy(int $nest): RedirectResponse + { + $this->nestDeletionService->handle($nest); + $this->alert->success(trans('admin/nests.notices.deleted'))->flash(); + + return redirect()->route('admin.nests'); + } +} diff --git a/app/Http/Controllers/Admin/NodeAutoDeployController.php b/app/Http/Controllers/Admin/NodeAutoDeployController.php new file mode 100644 index 0000000..ac0684a --- /dev/null +++ b/app/Http/Controllers/Admin/NodeAutoDeployController.php @@ -0,0 +1,62 @@ +repository->getApplicationKeys($request->user()) + ->filter(function (ApiKey $key) { + foreach ($key->getAttributes() as $permission => $value) { + if ($permission === 'r_nodes' && $value === 1) { + return true; + } + } + + return false; + }) + ->first(); + + // We couldn't find a key that exists for this user with only permission for + // reading nodes. Go ahead and create it now. + if (!$key) { + $key = $this->keyCreationService->setKeyType(ApiKey::TYPE_APPLICATION)->handle([ + 'user_id' => $request->user()->id, + 'memo' => 'Automatically generated node deployment key.', + 'allowed_ips' => [], + ], ['r_nodes' => 1]); + } + + return new JsonResponse([ + 'node' => $node->id, + 'token' => $key->identifier . $this->encrypter->decrypt($key->token), + ]); + } +} diff --git a/app/Http/Controllers/Admin/Nodes/NodeController.php b/app/Http/Controllers/Admin/Nodes/NodeController.php new file mode 100644 index 0000000..d80df6c --- /dev/null +++ b/app/Http/Controllers/Admin/Nodes/NodeController.php @@ -0,0 +1,35 @@ +with('location')->withCount('servers') + ) + ->allowedFilters(['uuid', 'name']) + ->allowedSorts(['id']) + ->paginate(25); + + return $this->view->make('admin.nodes.index', ['nodes' => $nodes]); + } +} diff --git a/app/Http/Controllers/Admin/Nodes/NodeViewController.php b/app/Http/Controllers/Admin/Nodes/NodeViewController.php new file mode 100644 index 0000000..6738903 --- /dev/null +++ b/app/Http/Controllers/Admin/Nodes/NodeViewController.php @@ -0,0 +1,102 @@ +repository->loadLocationAndServerCount($node); + + return $this->view->make('admin.nodes.view.index', [ + 'node' => $node, + 'stats' => $this->repository->getUsageStats($node), + 'version' => $this->versionService, + ]); + } + + /** + * Returns the settings page for a specific node. + */ + public function settings(Request $request, Node $node): View + { + return $this->view->make('admin.nodes.view.settings', [ + 'node' => $node, + 'locations' => $this->locationRepository->all(), + ]); + } + + /** + * Return the node configuration page for a specific node. + */ + public function configuration(Request $request, Node $node): View + { + return $this->view->make('admin.nodes.view.configuration', compact('node')); + } + + /** + * Return the node allocation management page. + */ + public function allocations(Request $request, Node $node): View + { + $node = $this->repository->loadNodeAllocations($node); + + $this->plainInject(['node' => Collection::wrap($node)->only(['id'])]); + + return $this->view->make('admin.nodes.view.allocation', [ + 'node' => $node, + 'allocations' => Allocation::query()->where('node_id', $node->id) + ->groupBy('ip') + ->orderByRaw('INET_ATON(ip) ASC') + ->get(['ip']), + ]); + } + + /** + * Return a listing of servers that exist for this specific node. + */ + public function servers(Request $request, Node $node): View + { + $this->plainInject([ + 'node' => Collection::wrap($node->makeVisible(['daemon_token_id', 'daemon_token'])) + ->only(['scheme', 'fqdn', 'daemonListen', 'daemon_token_id', 'daemon_token']), + ]); + + return $this->view->make('admin.nodes.view.servers', [ + 'node' => $node, + 'servers' => $this->serverRepository->loadAllServersForNode($node->id, 25), + ]); + } +} diff --git a/app/Http/Controllers/Admin/Nodes/SystemInformationController.php b/app/Http/Controllers/Admin/Nodes/SystemInformationController.php new file mode 100644 index 0000000..875b1af --- /dev/null +++ b/app/Http/Controllers/Admin/Nodes/SystemInformationController.php @@ -0,0 +1,40 @@ +repository->setNode($node)->getSystemInformation(); + + return new JsonResponse([ + 'version' => $data['version'] ?? '', + 'system' => [ + 'type' => Str::title($data['os'] ?? 'Unknown'), + 'arch' => $data['architecture'] ?? '--', + 'release' => $data['kernel_version'] ?? '--', + 'cpus' => $data['cpu_count'] ?? 0, + ], + ]); + } +} diff --git a/app/Http/Controllers/Admin/NodesController.php b/app/Http/Controllers/Admin/NodesController.php new file mode 100644 index 0000000..71094a5 --- /dev/null +++ b/app/Http/Controllers/Admin/NodesController.php @@ -0,0 +1,183 @@ +locationRepository->all(); + if (count($locations) < 1) { + $this->alert->warning(trans('admin/node.notices.location_required'))->flash(); + + return redirect()->route('admin.locations'); + } + + return $this->view->make('admin.nodes.new', ['locations' => $locations]); + } + + /** + * Post controller to create a new node on the system. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + */ + public function store(NodeFormRequest $request): RedirectResponse + { + $node = $this->creationService->handle($request->normalize()); + $this->alert->info(trans('admin/node.notices.node_created'))->flash(); + + return redirect()->route('admin.nodes.view.allocation', $node->id); + } + + /** + * Updates settings for a node. + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function updateSettings(NodeFormRequest $request, Node $node): RedirectResponse + { + $this->updateService->handle($node, $request->normalize(), $request->input('reset_secret') === 'on'); + $this->alert->success(trans('admin/node.notices.node_updated'))->flash(); + + return redirect()->route('admin.nodes.view.settings', $node->id)->withInput(); + } + + /** + * Removes a single allocation from a node. + * + * @throws \Pterodactyl\Exceptions\Service\Allocation\ServerUsingAllocationException + */ + public function allocationRemoveSingle(int $node, Allocation $allocation): Response + { + $this->allocationDeletionService->handle($allocation); + + return response('', 204); + } + + /** + * Removes multiple individual allocations from a node. + * + * @throws \Pterodactyl\Exceptions\Service\Allocation\ServerUsingAllocationException + */ + public function allocationRemoveMultiple(Request $request, int $node): Response + { + $allocations = $request->input('allocations'); + foreach ($allocations as $rawAllocation) { + $allocation = new Allocation(); + $allocation->id = $rawAllocation['id']; + $this->allocationRemoveSingle($node, $allocation); + } + + return response('', 204); + } + + /** + * Remove all allocations for a specific IP at once on a node. + */ + public function allocationRemoveBlock(Request $request, int $node): RedirectResponse + { + $this->allocationRepository->deleteWhere([ + ['node_id', '=', $node], + ['server_id', '=', null], + ['ip', '=', $request->input('ip')], + ]); + + $this->alert->success(trans('admin/node.notices.unallocated_deleted', ['ip' => htmlspecialchars($request->input('ip'))])) + ->flash(); + + return redirect()->route('admin.nodes.view.allocation', $node); + } + + /** + * Sets an alias for a specific allocation on a node. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function allocationSetAlias(AllocationAliasFormRequest $request): \Symfony\Component\HttpFoundation\Response + { + $this->allocationRepository->update($request->input('allocation_id'), [ + 'ip_alias' => (empty($request->input('alias'))) ? null : $request->input('alias'), + ]); + + return response('', 204); + } + + /** + * Creates new allocations on a node. + * + * @throws \Pterodactyl\Exceptions\Service\Allocation\CidrOutOfRangeException + * @throws \Pterodactyl\Exceptions\Service\Allocation\InvalidPortMappingException + * @throws \Pterodactyl\Exceptions\Service\Allocation\PortOutOfRangeException + * @throws \Pterodactyl\Exceptions\Service\Allocation\TooManyPortsInRangeException + */ + public function createAllocation(AllocationFormRequest $request, Node $node): RedirectResponse + { + $this->assignmentService->handle($node, $request->normalize()); + $this->alert->success(trans('admin/node.notices.allocations_added'))->flash(); + + return redirect()->route('admin.nodes.view.allocation', $node->id); + } + + /** + * Deletes a node from the system. + * + * @throws \Pterodactyl\Exceptions\DisplayException + */ + public function delete(int|Node $node): RedirectResponse + { + $this->deletionService->handle($node); + $this->alert->success(trans('admin/node.notices.node_deleted'))->flash(); + + return redirect()->route('admin.nodes'); + } +} diff --git a/app/Http/Controllers/Admin/Servers/CreateServerController.php b/app/Http/Controllers/Admin/Servers/CreateServerController.php new file mode 100644 index 0000000..c0fee8c --- /dev/null +++ b/app/Http/Controllers/Admin/Servers/CreateServerController.php @@ -0,0 +1,85 @@ +alert->warning(trans('admin/server.alerts.node_required'))->flash(); + + return redirect()->route('admin.nodes'); + } + + $nests = $this->nestRepository->getWithEggs(); + + \JavaScript::put([ + 'nodeData' => $this->nodeRepository->getNodesForServerCreation(), + 'nests' => $nests->map(function ($item) { + return array_merge($item->toArray(), [ + 'eggs' => $item->eggs->keyBy('id')->toArray(), + ]); + })->keyBy('id'), + ]); + + return $this->view->make('admin.servers.new', [ + 'locations' => Location::all(), + 'nests' => $nests, + ]); + } + + /** + * Create a new server on the remote system. + * + * @throws \Illuminate\Validation\ValidationException + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableAllocationException + * @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableNodeException + * @throws \Throwable + */ + public function store(ServerFormRequest $request): RedirectResponse + { + $data = $request->except(['_token']); + if (!empty($data['custom_image'])) { + $data['image'] = $data['custom_image']; + unset($data['custom_image']); + } + + $server = $this->creationService->handle($data); + + $this->alert->success(trans('admin/server.alerts.server_created'))->flash(); + + return new RedirectResponse('/admin/servers/view/' . $server->id); + } +} diff --git a/app/Http/Controllers/Admin/Servers/ServerController.php b/app/Http/Controllers/Admin/Servers/ServerController.php new file mode 100644 index 0000000..430c3f2 --- /dev/null +++ b/app/Http/Controllers/Admin/Servers/ServerController.php @@ -0,0 +1,38 @@ +with('node', 'user', 'allocation')) + ->allowedFilters([ + AllowedFilter::exact('owner_id'), + AllowedFilter::custom('*', new AdminServerFilter()), + ]) + ->paginate(config()->get('pterodactyl.paginate.admin.servers')); + + return $this->view->make('admin.servers.index', ['servers' => $servers]); + } +} diff --git a/app/Http/Controllers/Admin/Servers/ServerTransferController.php b/app/Http/Controllers/Admin/Servers/ServerTransferController.php new file mode 100644 index 0000000..8941ce1 --- /dev/null +++ b/app/Http/Controllers/Admin/Servers/ServerTransferController.php @@ -0,0 +1,117 @@ +validate([ + 'node_id' => 'required|exists:nodes,id', + 'allocation_id' => 'required|bail|unique:servers|exists:allocations,id', + 'allocation_additional' => 'nullable', + ]); + + $node_id = $validatedData['node_id']; + $allocation_id = intval($validatedData['allocation_id']); + $additional_allocations = array_map('intval', $validatedData['allocation_additional'] ?? []); + + // Check if the node is viable for the transfer. + $node = $this->nodeRepository->getNodeWithResourceUsage($node_id); + if (!$node->isViable($server->memory, $server->disk)) { + $this->alert->danger(trans('admin/server.alerts.transfer_not_viable'))->flash(); + + return redirect()->route('admin.servers.view.manage', $server->id); + } + + $server->validateTransferState(); + + $this->connection->transaction(function () use ($server, $node_id, $allocation_id, $additional_allocations) { + // Create a new ServerTransfer entry. + $transfer = new ServerTransfer(); + + $transfer->server_id = $server->id; + $transfer->old_node = $server->node_id; + $transfer->new_node = $node_id; + $transfer->old_allocation = $server->allocation_id; + $transfer->new_allocation = $allocation_id; + $transfer->old_additional_allocations = $server->allocations->where('id', '!=', $server->allocation_id)->pluck('id'); + $transfer->new_additional_allocations = $additional_allocations; + + $transfer->save(); + + // Add the allocations to the server, so they cannot be automatically assigned while the transfer is in progress. + $this->assignAllocationsToServer($server, $node_id, $allocation_id, $additional_allocations); + + // Generate a token for the destination node that the source node can use to authenticate with. + $token = $this->nodeJWTService + ->setExpiresAt(CarbonImmutable::now()->addMinutes(15)) + ->setSubject($server->uuid) + ->handle($transfer->newNode, $server->uuid, 'sha256'); + + // Notify the source node of the pending outgoing transfer. + $this->daemonTransferRepository->setServer($server)->notify($transfer->newNode, $token); + + return $transfer; + }); + + $this->alert->success(trans('admin/server.alerts.transfer_started'))->flash(); + + return redirect()->route('admin.servers.view.manage', $server->id); + } + + /** + * Assigns the specified allocations to the specified server. + */ + private function assignAllocationsToServer(Server $server, int $node_id, int $allocation_id, array $additional_allocations) + { + $allocations = $additional_allocations; + $allocations[] = $allocation_id; + + $unassigned = $this->allocationRepository->getUnassignedAllocationIds($node_id); + + $updateIds = []; + foreach ($allocations as $allocation) { + if (!in_array($allocation, $unassigned)) { + continue; + } + + $updateIds[] = $allocation; + } + + if (!empty($updateIds)) { + $this->allocationRepository->updateWhereIn('id', $updateIds, ['server_id' => $server->id]); + } + } +} diff --git a/app/Http/Controllers/Admin/Servers/ServerViewController.php b/app/Http/Controllers/Admin/Servers/ServerViewController.php new file mode 100644 index 0000000..58d8b7d --- /dev/null +++ b/app/Http/Controllers/Admin/Servers/ServerViewController.php @@ -0,0 +1,154 @@ +view->make('admin.servers.view.index', compact('server')); + } + + /** + * Returns the server details page. + */ + public function details(Request $request, Server $server): View + { + return $this->view->make('admin.servers.view.details', compact('server')); + } + + /** + * Returns a view of server build settings. + */ + public function build(Request $request, Server $server): View + { + $allocations = $server->node->allocations->toBase(); + + return $this->view->make('admin.servers.view.build', [ + 'server' => $server, + 'assigned' => $allocations->where('server_id', $server->id)->sortBy('port')->sortBy('ip'), + 'unassigned' => $allocations->where('server_id', null)->sortBy('port')->sortBy('ip'), + ]); + } + + /** + * Returns the server startup management page. + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function startup(Request $request, Server $server): View + { + $nests = $this->nestRepository->getWithEggs(); + $variables = $this->environmentService->handle($server); + + $this->plainInject([ + 'server' => $server, + 'server_variables' => $variables, + 'nests' => $nests->map(function (Nest $item) { + return array_merge($item->toArray(), [ + 'eggs' => $item->eggs->keyBy('id')->toArray(), + ]); + })->keyBy('id'), + ]); + + return $this->view->make('admin.servers.view.startup', compact('server', 'nests')); + } + + /** + * Returns all the databases that exist for the server. + */ + public function database(Request $request, Server $server): View + { + return $this->view->make('admin.servers.view.database', [ + 'hosts' => $this->databaseHostRepository->all(), + 'server' => $server, + ]); + } + + /** + * Returns all the mounts that exist for the server. + */ + public function mounts(Request $request, Server $server): View + { + $server->load('mounts'); + + return $this->view->make('admin.servers.view.mounts', [ + 'mounts' => $this->mountRepository->getMountListForServer($server), + 'server' => $server, + ]); + } + + /** + * Returns the base server management page, or an exception if the server + * is in a state that cannot be recovered from. + * + * @throws \Pterodactyl\Exceptions\DisplayException + */ + public function manage(Request $request, Server $server): View + { + if ($server->status === Server::STATUS_INSTALL_FAILED) { + throw new DisplayException('This server is in a failed install state and cannot be recovered. Please delete and re-create the server.'); + } + + // Check if the panel doesn't have at least 2 nodes configured. + $nodes = $this->nodeRepository->all(); + $canTransfer = false; + if (count($nodes) >= 2) { + $canTransfer = true; + } + + \JavaScript::put([ + 'nodeData' => $this->nodeRepository->getNodesForServerCreation(), + ]); + + return $this->view->make('admin.servers.view.manage', [ + 'server' => $server, + 'locations' => $this->locationRepository->all(), + 'canTransfer' => $canTransfer, + ]); + } + + /** + * Returns the server deletion page. + */ + public function delete(Request $request, Server $server): View + { + return $this->view->make('admin.servers.view.delete', compact('server')); + } +} diff --git a/app/Http/Controllers/Admin/ServersController.php b/app/Http/Controllers/Admin/ServersController.php new file mode 100644 index 0000000..3d2c55a --- /dev/null +++ b/app/Http/Controllers/Admin/ServersController.php @@ -0,0 +1,273 @@ +detailsModificationService->handle($server, $request->only([ + 'owner_id', 'external_id', 'name', 'description', + ])); + + $this->alert->success(trans('admin/server.alerts.details_updated'))->flash(); + + return redirect()->route('admin.servers.view.details', $server->id); + } + + /** + * Toggles the installation status for a server. + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function toggleInstall(Server $server): RedirectResponse + { + if ($server->status === Server::STATUS_INSTALL_FAILED) { + throw new DisplayException(trans('admin/server.exceptions.marked_as_failed')); + } + + $this->repository->update($server->id, [ + 'status' => $server->isInstalled() ? Server::STATUS_INSTALLING : null, + ], true, true); + + $this->alert->success(trans('admin/server.alerts.install_toggled'))->flash(); + + return redirect()->route('admin.servers.view.manage', $server->id); + } + + /** + * Reinstalls the server with the currently assigned service. + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function reinstallServer(Server $server): RedirectResponse + { + $this->reinstallService->handle($server); + $this->alert->success(trans('admin/server.alerts.server_reinstalled'))->flash(); + + return redirect()->route('admin.servers.view.manage', $server->id); + } + + /** + * Manage the suspension status for a server. + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function manageSuspension(Request $request, Server $server): RedirectResponse + { + $this->suspensionService->toggle($server, $request->input('action')); + $this->alert->success(trans('admin/server.alerts.suspension_toggled', [ + 'status' => $request->input('action') . 'ed', + ]))->flash(); + + return redirect()->route('admin.servers.view.manage', $server->id); + } + + /** + * Update the build configuration for a server. + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Illuminate\Validation\ValidationException + */ + public function updateBuild(Request $request, Server $server): RedirectResponse + { + try { + $this->buildModificationService->handle($server, $request->only([ + 'allocation_id', 'add_allocations', 'remove_allocations', + 'memory', 'swap', 'io', 'cpu', 'threads', 'disk', + 'database_limit', 'allocation_limit', 'backup_limit', 'oom_disabled', + ])); + } catch (DataValidationException $exception) { + throw new ValidationException($exception->getValidator()); + } + + $this->alert->success(trans('admin/server.alerts.build_updated'))->flash(); + + return redirect()->route('admin.servers.view.build', $server->id); + } + + /** + * Start the server deletion process. + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Throwable + */ + public function delete(Request $request, Server $server): RedirectResponse + { + $this->deletionService->withForce($request->filled('force_delete'))->handle($server); + $this->alert->success(trans('admin/server.alerts.server_deleted'))->flash(); + + return redirect()->route('admin.servers'); + } + + /** + * Update the startup command as well as variables. + * + * @throws \Illuminate\Validation\ValidationException + */ + public function saveStartup(Request $request, Server $server): RedirectResponse + { + $data = $request->except('_token'); + if (!empty($data['custom_docker_image'])) { + $data['docker_image'] = $data['custom_docker_image']; + unset($data['custom_docker_image']); + } + + try { + $this->startupModificationService + ->setUserLevel(User::USER_LEVEL_ADMIN) + ->handle($server, $data); + } catch (DataValidationException $exception) { + throw new ValidationException($exception->getValidator()); + } + + $this->alert->success(trans('admin/server.alerts.startup_changed'))->flash(); + + return redirect()->route('admin.servers.view.startup', $server->id); + } + + /** + * Creates a new database assigned to a specific server. + * + * @throws \Throwable + */ + public function newDatabase(StoreServerDatabaseRequest $request, Server $server): RedirectResponse + { + $this->databaseManagementService->create($server, [ + 'database' => DatabaseManagementService::generateUniqueDatabaseName($request->input('database'), $server->id), + 'remote' => $request->input('remote'), + 'database_host_id' => $request->input('database_host_id'), + 'max_connections' => $request->input('max_connections'), + ]); + + return redirect()->route('admin.servers.view.database', $server->id)->withInput(); + } + + /** + * Resets the database password for a specific database on this server. + * + * @throws \Throwable + */ + public function resetDatabasePassword(Request $request, Server $server): Response + { + /** @var \Pterodactyl\Models\Database $database */ + $database = $server->databases()->findOrFail($request->input('database')); + + $this->databasePasswordService->handle($database); + + return response('', 204); + } + + /** + * Deletes a database from a server. + * + * @throws \Exception + */ + public function deleteDatabase(Server $server, Database $database): Response + { + $this->databaseManagementService->delete($database); + + return response('', 204); + } + + /** + * Add a mount to a server. + * + * @throws \Throwable + */ + public function addMount(Request $request, Server $server): RedirectResponse + { + $mountServer = (new MountServer())->forceFill([ + 'mount_id' => $request->input('mount_id'), + 'server_id' => $server->id, + ]); + + $mountServer->saveOrFail(); + + $this->alert->success('Mount was added successfully.')->flash(); + + return redirect()->route('admin.servers.view.mounts', $server->id); + } + + /** + * Remove a mount from a server. + */ + public function deleteMount(Server $server, Mount $mount): RedirectResponse + { + MountServer::where('mount_id', $mount->id)->where('server_id', $server->id)->delete(); + + $this->alert->success('Mount was removed successfully.')->flash(); + + return redirect()->route('admin.servers.view.mounts', $server->id); + } +} diff --git a/app/Http/Controllers/Admin/Settings/AdvancedController.php b/app/Http/Controllers/Admin/Settings/AdvancedController.php new file mode 100644 index 0000000..bf68832 --- /dev/null +++ b/app/Http/Controllers/Admin/Settings/AdvancedController.php @@ -0,0 +1,62 @@ +config->get('recaptcha._shipped_secret_key') === $this->config->get('recaptcha.secret_key') + || $this->config->get('recaptcha._shipped_website_key') === $this->config->get('recaptcha.website_key') + ) { + $showRecaptchaWarning = true; + } + + return $this->view->make('admin.settings.advanced', [ + 'showRecaptchaWarning' => $showRecaptchaWarning, + ]); + } + + /** + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function update(AdvancedSettingsFormRequest $request): RedirectResponse + { + foreach ($request->normalize() as $key => $value) { + $this->settings->set('settings::' . $key, $value); + } + + $this->kernel->call('queue:restart'); + $this->alert->success('Advanced settings have been updated successfully and the queue worker was restarted to apply these changes.')->flash(); + + return redirect()->route('admin.settings.advanced'); + } +} diff --git a/app/Http/Controllers/Admin/Settings/IndexController.php b/app/Http/Controllers/Admin/Settings/IndexController.php new file mode 100644 index 0000000..eabede9 --- /dev/null +++ b/app/Http/Controllers/Admin/Settings/IndexController.php @@ -0,0 +1,60 @@ +view->make('admin.settings.index', [ + 'version' => $this->versionService, + 'languages' => $this->getAvailableLanguages(true), + ]); + } + + /** + * Handle settings update. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function update(BaseSettingsFormRequest $request): RedirectResponse + { + foreach ($request->normalize() as $key => $value) { + $this->settings->set('settings::' . $key, $value); + } + + $this->kernel->call('queue:restart'); + $this->alert->success('Panel settings have been updated successfully and the queue worker was restarted to apply these changes.')->flash(); + + return redirect()->route('admin.settings'); + } +} diff --git a/app/Http/Controllers/Admin/Settings/MailController.php b/app/Http/Controllers/Admin/Settings/MailController.php new file mode 100644 index 0000000..ab81ec0 --- /dev/null +++ b/app/Http/Controllers/Admin/Settings/MailController.php @@ -0,0 +1,90 @@ +view->make('admin.settings.mail', [ + 'disabled' => $this->config->get('mail.default') !== 'smtp', + ]); + } + + /** + * Handle request to update SMTP mail settings. + * + * @throws DisplayException + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function update(MailSettingsFormRequest $request): Response + { + if ($this->config->get('mail.default') !== 'smtp') { + throw new DisplayException('This feature is only available if SMTP is the selected email driver for the Panel.'); + } + + $values = $request->normalize(); + if (array_get($values, 'mail:mailers:smtp:password') === '!e') { + $values['mail:mailers:smtp:password'] = ''; + } + + foreach ($values as $key => $value) { + if (in_array($key, SettingsServiceProvider::getEncryptedKeys()) && !empty($value)) { + $value = $this->encrypter->encrypt($value); + } + + $this->settings->set('settings::' . $key, $value); + } + + $this->kernel->call('queue:restart'); + + return response('', 204); + } + + /** + * Submit a request to send a test mail message. + */ + public function test(Request $request): Response + { + try { + Notification::route('mail', $request->user()->email) + ->notify(new MailTested($request->user())); + } catch (\Exception $exception) { + return response($exception->getMessage(), 500); + } + + return response('', 204); + } +} diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php new file mode 100644 index 0000000..1d6db65 --- /dev/null +++ b/app/Http/Controllers/Admin/UserController.php @@ -0,0 +1,153 @@ +select('users.*') + ->selectRaw('COUNT(DISTINCT(subusers.id)) as subuser_of_count') + ->selectRaw('COUNT(DISTINCT(servers.id)) as servers_count') + ->leftJoin('subusers', 'subusers.user_id', '=', 'users.id') + ->leftJoin('servers', 'servers.owner_id', '=', 'users.id') + ->groupBy('users.id') + ) + ->allowedFilters(['username', 'email', 'uuid']) + ->allowedSorts(['id', 'uuid']) + ->paginate(50); + + return $this->view->make('admin.users.index', ['users' => $users]); + } + + /** + * Display new user page. + */ + public function create(): View + { + return $this->view->make('admin.users.new', [ + 'languages' => $this->getAvailableLanguages(true), + ]); + } + + /** + * Display user view page. + */ + public function view(User $user): View + { + return $this->view->make('admin.users.view', [ + 'user' => $user, + 'languages' => $this->getAvailableLanguages(true), + ]); + } + + /** + * Delete a user from the system. + * + * @throws \Exception + * @throws \Pterodactyl\Exceptions\DisplayException + */ + public function delete(Request $request, User $user): RedirectResponse + { + if ($request->user()->id === $user->id) { + throw new DisplayException($this->translator->get('admin/user.exceptions.user_has_servers')); + } + + $this->deletionService->handle($user); + + return redirect()->route('admin.users'); + } + + /** + * Create a user. + * + * @throws \Exception + * @throws \Throwable + */ + public function store(NewUserFormRequest $request): RedirectResponse + { + $user = $this->creationService->handle($request->normalize()); + $this->alert->success($this->translator->get('admin/user.notices.account_created'))->flash(); + + return redirect()->route('admin.users.view', $user->id); + } + + /** + * Update a user on the system. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function update(UserFormRequest $request, User $user): RedirectResponse + { + $this->updateService + ->setUserLevel(User::USER_LEVEL_ADMIN) + ->handle($user, $request->normalize()); + + $this->alert->success(trans('admin/user.notices.account_updated'))->flash(); + + return redirect()->route('admin.users.view', $user->id); + } + + /** + * Get a JSON response of users on the system. + */ + public function json(Request $request): Model|Collection + { + $users = QueryBuilder::for(User::query())->allowedFilters(['email'])->paginate(25); + + // Handle single user requests. + if ($request->query('user_id')) { + $user = User::query()->findOrFail($request->input('user_id')); + $user->md5 = md5(strtolower($user->email)); + + return $user; + } + + return $users->map(function ($item) { + $item->md5 = md5(strtolower($item->email)); + + return $item; + }); + } +} diff --git a/app/Http/Controllers/Api/Application/ApplicationApiController.php b/app/Http/Controllers/Api/Application/ApplicationApiController.php new file mode 100644 index 0000000..dfc2cad --- /dev/null +++ b/app/Http/Controllers/Api/Application/ApplicationApiController.php @@ -0,0 +1,74 @@ +call([$this, 'loadDependencies']); + + // Parse all the includes to use on this request. + $input = $this->request->input('include', []); + $input = is_array($input) ? $input : explode(',', $input); + + $includes = (new Collection($input))->map(function ($value) { + return trim($value); + })->filter()->toArray(); + + $this->fractal->parseIncludes($includes); + $this->fractal->limitRecursion(2); + } + + /** + * Perform dependency injection of certain classes needed for core functionality + * without littering the constructors of classes that extend this abstract. + */ + public function loadDependencies(Fractal $fractal, Request $request) + { + $this->fractal = $fractal; + $this->request = $request; + } + + /** + * Return an instance of an application transformer. + * + * @template T of \Pterodactyl\Transformers\Api\Application\BaseTransformer + * + * @param class-string $abstract + * + * @return T + * + * @noinspection PhpDocSignatureInspection + */ + public function getTransformer(string $abstract) + { + Assert::subclassOf($abstract, BaseTransformer::class); + + return $abstract::fromRequest($this->request); + } + + /** + * Return an HTTP/204 response for the API. + */ + protected function returnNoContent(): Response + { + return new Response('', Response::HTTP_NO_CONTENT); + } +} diff --git a/app/Http/Controllers/Api/Application/Locations/LocationController.php b/app/Http/Controllers/Api/Application/Locations/LocationController.php new file mode 100644 index 0000000..b95a077 --- /dev/null +++ b/app/Http/Controllers/Api/Application/Locations/LocationController.php @@ -0,0 +1,104 @@ +allowedFilters(['short', 'long']) + ->allowedSorts(['id']) + ->paginate($request->query('per_page') ?? 50); + + return $this->fractal->collection($locations) + ->transformWith($this->getTransformer(LocationTransformer::class)) + ->toArray(); + } + + /** + * Return a single location. + */ + public function view(GetLocationRequest $request, Location $location): array + { + return $this->fractal->item($location) + ->transformWith($this->getTransformer(LocationTransformer::class)) + ->toArray(); + } + + /** + * Store a new location on the Panel and return an HTTP/201 response code with the + * new location attached. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + */ + public function store(StoreLocationRequest $request): JsonResponse + { + $location = $this->creationService->handle($request->validated()); + + return $this->fractal->item($location) + ->transformWith($this->getTransformer(LocationTransformer::class)) + ->addMeta([ + 'resource' => route('api.application.locations.view', [ + 'location' => $location->id, + ]), + ]) + ->respond(201); + } + + /** + * Update a location on the Panel and return the updated record to the user. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function update(UpdateLocationRequest $request, Location $location): array + { + $location = $this->updateService->handle($location, $request->validated()); + + return $this->fractal->item($location) + ->transformWith($this->getTransformer(LocationTransformer::class)) + ->toArray(); + } + + /** + * Delete a location from the Panel. + * + * @throws \Pterodactyl\Exceptions\Service\Location\HasActiveNodesException + */ + public function delete(DeleteLocationRequest $request, Location $location): Response + { + $this->deletionService->handle($location); + + return response('', 204); + } +} diff --git a/app/Http/Controllers/Api/Application/Nests/EggController.php b/app/Http/Controllers/Api/Application/Nests/EggController.php new file mode 100644 index 0000000..83c3f77 --- /dev/null +++ b/app/Http/Controllers/Api/Application/Nests/EggController.php @@ -0,0 +1,33 @@ +fractal->collection($nest->eggs) + ->transformWith($this->getTransformer(EggTransformer::class)) + ->toArray(); + } + + /** + * Return a single egg that exists on the specified nest. + */ + public function view(GetEggRequest $request, Nest $nest, Egg $egg): array + { + return $this->fractal->item($egg) + ->transformWith($this->getTransformer(EggTransformer::class)) + ->toArray(); + } +} diff --git a/app/Http/Controllers/Api/Application/Nests/NestController.php b/app/Http/Controllers/Api/Application/Nests/NestController.php new file mode 100644 index 0000000..f0044f5 --- /dev/null +++ b/app/Http/Controllers/Api/Application/Nests/NestController.php @@ -0,0 +1,42 @@ +repository->paginated($request->query('per_page') ?? 50); + + return $this->fractal->collection($nests) + ->transformWith($this->getTransformer(NestTransformer::class)) + ->toArray(); + } + + /** + * Return information about a single Nest model. + */ + public function view(GetNestsRequest $request, Nest $nest): array + { + return $this->fractal->item($nest) + ->transformWith($this->getTransformer(NestTransformer::class)) + ->toArray(); + } +} diff --git a/app/Http/Controllers/Api/Application/Nodes/AllocationController.php b/app/Http/Controllers/Api/Application/Nodes/AllocationController.php new file mode 100644 index 0000000..7740494 --- /dev/null +++ b/app/Http/Controllers/Api/Application/Nodes/AllocationController.php @@ -0,0 +1,83 @@ +allocations()) + ->allowedFilters([ + AllowedFilter::exact('ip'), + AllowedFilter::exact('port'), + 'ip_alias', + AllowedFilter::callback('server_id', function (Builder $builder, $value) { + if (empty($value) || is_bool($value) || !ctype_digit((string) $value)) { + return $builder->whereNull('server_id'); + } + + return $builder->where('server_id', $value); + }), + ]) + ->paginate($request->query('per_page') ?? 50); + + return $this->fractal->collection($allocations) + ->transformWith($this->getTransformer(AllocationTransformer::class)) + ->toArray(); + } + + /** + * Store new allocations for a given node. + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\Service\Allocation\CidrOutOfRangeException + * @throws \Pterodactyl\Exceptions\Service\Allocation\InvalidPortMappingException + * @throws \Pterodactyl\Exceptions\Service\Allocation\PortOutOfRangeException + * @throws \Pterodactyl\Exceptions\Service\Allocation\TooManyPortsInRangeException + */ + public function store(StoreAllocationRequest $request, Node $node): JsonResponse + { + $this->assignmentService->handle($node, $request->validated()); + + return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); + } + + /** + * Delete a specific allocation from the Panel. + * + * @throws \Pterodactyl\Exceptions\Service\Allocation\ServerUsingAllocationException + */ + public function delete(DeleteAllocationRequest $request, Node $node, Allocation $allocation): JsonResponse + { + $this->deletionService->handle($allocation); + + return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); + } +} diff --git a/app/Http/Controllers/Api/Application/Nodes/NodeConfigurationController.php b/app/Http/Controllers/Api/Application/Nodes/NodeConfigurationController.php new file mode 100644 index 0000000..2d812a8 --- /dev/null +++ b/app/Http/Controllers/Api/Application/Nodes/NodeConfigurationController.php @@ -0,0 +1,21 @@ +getConfiguration()); + } +} diff --git a/app/Http/Controllers/Api/Application/Nodes/NodeController.php b/app/Http/Controllers/Api/Application/Nodes/NodeController.php new file mode 100644 index 0000000..e0e3575 --- /dev/null +++ b/app/Http/Controllers/Api/Application/Nodes/NodeController.php @@ -0,0 +1,107 @@ +allowedFilters(['uuid', 'name', 'fqdn', 'daemon_token_id']) + ->allowedSorts(['id', 'uuid', 'memory', 'disk']) + ->paginate($request->query('per_page') ?? 50); + + return $this->fractal->collection($nodes) + ->transformWith($this->getTransformer(NodeTransformer::class)) + ->toArray(); + } + + /** + * Return data for a single instance of a node. + */ + public function view(GetNodeRequest $request, Node $node): array + { + return $this->fractal->item($node) + ->transformWith($this->getTransformer(NodeTransformer::class)) + ->toArray(); + } + + /** + * Create a new node on the Panel. Returns the created node and an HTTP/201 + * status response on success. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + */ + public function store(StoreNodeRequest $request): JsonResponse + { + $node = $this->creationService->handle($request->validated()); + + return $this->fractal->item($node) + ->transformWith($this->getTransformer(NodeTransformer::class)) + ->addMeta([ + 'resource' => route('api.application.nodes.view', [ + 'node' => $node->id, + ]), + ]) + ->respond(201); + } + + /** + * Update an existing node on the Panel. + * + * @throws \Throwable + */ + public function update(UpdateNodeRequest $request, Node $node): array + { + $node = $this->updateService->handle( + $node, + $request->validated(), + $request->input('reset_secret') === true + ); + + return $this->fractal->item($node) + ->transformWith($this->getTransformer(NodeTransformer::class)) + ->toArray(); + } + + /** + * Deletes a given node from the Panel as long as there are no servers + * currently attached to it. + * + * @throws \Pterodactyl\Exceptions\Service\HasActiveServersException + */ + public function delete(DeleteNodeRequest $request, Node $node): JsonResponse + { + $this->deletionService->handle($node); + + return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); + } +} diff --git a/app/Http/Controllers/Api/Application/Nodes/NodeDeploymentController.php b/app/Http/Controllers/Api/Application/Nodes/NodeDeploymentController.php new file mode 100644 index 0000000..4b49fa9 --- /dev/null +++ b/app/Http/Controllers/Api/Application/Nodes/NodeDeploymentController.php @@ -0,0 +1,39 @@ +validated(); + $nodes = $this->viableNodesService->setLocations($data['location_ids'] ?? []) + ->setMemory($data['memory']) + ->setDisk($data['disk']) + ->handle($request->query('per_page'), $request->query('page')); + + return $this->fractal->collection($nodes) + ->transformWith($this->getTransformer(NodeTransformer::class)) + ->toArray(); + } +} diff --git a/app/Http/Controllers/Api/Application/Servers/DatabaseController.php b/app/Http/Controllers/Api/Application/Servers/DatabaseController.php new file mode 100644 index 0000000..1717c5d --- /dev/null +++ b/app/Http/Controllers/Api/Application/Servers/DatabaseController.php @@ -0,0 +1,94 @@ +fractal->collection($server->databases) + ->transformWith($this->getTransformer(ServerDatabaseTransformer::class)) + ->toArray(); + } + + /** + * Return a single server database. + */ + public function view(GetServerDatabaseRequest $request, Server $server, Database $database): array + { + return $this->fractal->item($database) + ->transformWith($this->getTransformer(ServerDatabaseTransformer::class)) + ->toArray(); + } + + /** + * Reset the password for a specific server database. + * + * @throws \Throwable + */ + public function resetPassword(ServerDatabaseWriteRequest $request, Server $server, Database $database): JsonResponse + { + $this->databasePasswordService->handle($database); + + return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); + } + + /** + * Create a new database on the Panel for a given server. + * + * @throws \Throwable + */ + public function store(StoreServerDatabaseRequest $request, Server $server): JsonResponse + { + $database = $this->databaseManagementService->create($server, array_merge($request->validated(), [ + 'database' => $request->databaseName(), + ])); + + return $this->fractal->item($database) + ->transformWith($this->getTransformer(ServerDatabaseTransformer::class)) + ->addMeta([ + 'resource' => route('api.application.servers.databases.view', [ + 'server' => $server->id, + 'database' => $database->id, + ]), + ]) + ->respond(Response::HTTP_CREATED); + } + + /** + * Handle a request to delete a specific server database from the Panel. + */ + public function delete(ServerDatabaseWriteRequest $request, Server $server, Database $database): Response + { + $this->databaseManagementService->delete($database); + + return response('', 204); + } +} diff --git a/app/Http/Controllers/Api/Application/Servers/ExternalServerController.php b/app/Http/Controllers/Api/Application/Servers/ExternalServerController.php new file mode 100644 index 0000000..869472f --- /dev/null +++ b/app/Http/Controllers/Api/Application/Servers/ExternalServerController.php @@ -0,0 +1,23 @@ +where('external_id', $external_id)->firstOrFail(); + + return $this->fractal->item($server) + ->transformWith($this->getTransformer(ServerTransformer::class)) + ->toArray(); + } +} diff --git a/app/Http/Controllers/Api/Application/Servers/ServerController.php b/app/Http/Controllers/Api/Application/Servers/ServerController.php new file mode 100644 index 0000000..2eb6916 --- /dev/null +++ b/app/Http/Controllers/Api/Application/Servers/ServerController.php @@ -0,0 +1,86 @@ +allowedFilters(['uuid', 'uuidShort', 'name', 'description', 'image', 'external_id']) + ->allowedSorts(['id', 'uuid']) + ->paginate($request->query('per_page') ?? 50); + + return $this->fractal->collection($servers) + ->transformWith($this->getTransformer(ServerTransformer::class)) + ->toArray(); + } + + /** + * Create a new server on the system. + * + * @throws \Throwable + * @throws \Illuminate\Validation\ValidationException + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableAllocationException + * @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableNodeException + */ + public function store(StoreServerRequest $request): JsonResponse + { + $server = $this->creationService->handle($request->validated(), $request->getDeploymentObject()); + + return $this->fractal->item($server) + ->transformWith($this->getTransformer(ServerTransformer::class)) + ->respond(201); + } + + /** + * Show a single server transformed for the application API. + */ + public function view(GetServerRequest $request, Server $server): array + { + return $this->fractal->item($server) + ->transformWith($this->getTransformer(ServerTransformer::class)) + ->toArray(); + } + + /** + * Deletes a server. + * + * @throws \Pterodactyl\Exceptions\DisplayException + */ + public function delete(ServerWriteRequest $request, Server $server, string $force = ''): Response + { + $this->deletionService->withForce($force === 'force')->handle($server); + + return $this->returnNoContent(); + } +} diff --git a/app/Http/Controllers/Api/Application/Servers/ServerDetailsController.php b/app/Http/Controllers/Api/Application/Servers/ServerDetailsController.php new file mode 100644 index 0000000..ae5f543 --- /dev/null +++ b/app/Http/Controllers/Api/Application/Servers/ServerDetailsController.php @@ -0,0 +1,59 @@ +detailsModificationService->returnUpdatedModel()->handle( + $server, + $request->validated() + ); + + return $this->fractal->item($updated) + ->transformWith($this->getTransformer(ServerTransformer::class)) + ->toArray(); + } + + /** + * Update the build details for a specific server. + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function build(UpdateServerBuildConfigurationRequest $request, Server $server): array + { + $server = $this->buildModificationService->handle($server, $request->validated()); + + return $this->fractal->item($server) + ->transformWith($this->getTransformer(ServerTransformer::class)) + ->toArray(); + } +} diff --git a/app/Http/Controllers/Api/Application/Servers/ServerManagementController.php b/app/Http/Controllers/Api/Application/Servers/ServerManagementController.php new file mode 100644 index 0000000..d4dcaa2 --- /dev/null +++ b/app/Http/Controllers/Api/Application/Servers/ServerManagementController.php @@ -0,0 +1,61 @@ +suspensionService->toggle($server); + + return $this->returnNoContent(); + } + + /** + * Unsuspend a server on the Panel. + * + * @throws \Throwable + */ + public function unsuspend(ServerWriteRequest $request, Server $server): Response + { + $this->suspensionService->toggle($server, SuspensionService::ACTION_UNSUSPEND); + + return $this->returnNoContent(); + } + + /** + * Mark a server as needing to be reinstalled. + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function reinstall(ServerWriteRequest $request, Server $server): Response + { + $this->reinstallServerService->handle($server); + + return $this->returnNoContent(); + } +} diff --git a/app/Http/Controllers/Api/Application/Servers/StartupController.php b/app/Http/Controllers/Api/Application/Servers/StartupController.php new file mode 100644 index 0000000..6c2da07 --- /dev/null +++ b/app/Http/Controllers/Api/Application/Servers/StartupController.php @@ -0,0 +1,40 @@ +modificationService + ->setUserLevel(User::USER_LEVEL_ADMIN) + ->handle($server, $request->validated()); + + return $this->fractal->item($server) + ->transformWith($this->getTransformer(ServerTransformer::class)) + ->toArray(); + } +} diff --git a/app/Http/Controllers/Api/Application/Users/ExternalUserController.php b/app/Http/Controllers/Api/Application/Users/ExternalUserController.php new file mode 100644 index 0000000..2a8f4f0 --- /dev/null +++ b/app/Http/Controllers/Api/Application/Users/ExternalUserController.php @@ -0,0 +1,23 @@ +where('external_id', $external_id)->firstOrFail(); + + return $this->fractal->item($user) + ->transformWith($this->getTransformer(UserTransformer::class)) + ->toArray(); + } +} diff --git a/app/Http/Controllers/Api/Application/Users/UserController.php b/app/Http/Controllers/Api/Application/Users/UserController.php new file mode 100644 index 0000000..0ddae27 --- /dev/null +++ b/app/Http/Controllers/Api/Application/Users/UserController.php @@ -0,0 +1,114 @@ +allowedFilters(['email', 'uuid', 'username', 'external_id']) + ->allowedSorts(['id', 'uuid']) + ->paginate($request->query('per_page') ?? 50); + + return $this->fractal->collection($users) + ->transformWith($this->getTransformer(UserTransformer::class)) + ->toArray(); + } + + /** + * Handle a request to view a single user. Includes any relations that + * were defined in the request. + */ + public function view(GetUsersRequest $request, User $user): array + { + return $this->fractal->item($user) + ->transformWith($this->getTransformer(UserTransformer::class)) + ->toArray(); + } + + /** + * Update an existing user on the system and return the response. Returns the + * updated user model response on success. Supports handling of token revocation + * errors when switching a user from an admin to a normal user. + * + * Revocation errors are returned under the 'revocation_errors' key in the response + * meta. If there are no errors this is an empty array. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function update(UpdateUserRequest $request, User $user): array + { + $this->updateService->setUserLevel(User::USER_LEVEL_ADMIN); + $user = $this->updateService->handle($user, $request->validated()); + + $response = $this->fractal->item($user) + ->transformWith($this->getTransformer(UserTransformer::class)); + + return $response->toArray(); + } + + /** + * Store a new user on the system. Returns the created user and an HTTP/201 + * header on successful creation. + * + * @throws \Exception + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + */ + public function store(StoreUserRequest $request): JsonResponse + { + $user = $this->creationService->handle($request->validated()); + + return $this->fractal->item($user) + ->transformWith($this->getTransformer(UserTransformer::class)) + ->addMeta([ + 'resource' => route('api.application.users.view', [ + 'user' => $user->id, + ]), + ]) + ->respond(201); + } + + /** + * Handle a request to delete a user from the Panel. Returns a HTTP/204 response + * on successful deletion. + * + * @throws \Pterodactyl\Exceptions\DisplayException + */ + public function delete(DeleteUserRequest $request, User $user): JsonResponse + { + $this->deletionService->handle($user); + + return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); + } +} diff --git a/app/Http/Controllers/Api/Client/AccountController.php b/app/Http/Controllers/Api/Client/AccountController.php new file mode 100644 index 0000000..cbe8b03 --- /dev/null +++ b/app/Http/Controllers/Api/Client/AccountController.php @@ -0,0 +1,75 @@ +fractal->item($request->user()) + ->transformWith($this->getTransformer(AccountTransformer::class)) + ->toArray(); + } + + /** + * Update the authenticated user's email address. + */ + public function updateEmail(UpdateEmailRequest $request): JsonResponse + { + $original = $request->user()->email; + $this->updateService->handle($request->user(), $request->validated()); + + if ($original !== $request->input('email')) { + Activity::event('user:account.email-changed') + ->property(['old' => $original, 'new' => $request->input('email')]) + ->log(); + } + + return new JsonResponse([], Response::HTTP_NO_CONTENT); + } + + /** + * Update the authenticated user's password. All existing sessions will be logged + * out immediately. + * + * @throws \Throwable + */ + public function updatePassword(UpdatePasswordRequest $request): JsonResponse + { + $user = $this->updateService->handle($request->user(), $request->validated()); + + $guard = $this->manager->guard(); + // If you do not update the user in the session you'll end up working with a + // cached copy of the user that does not include the updated password. Do this + // to correctly store the new user details in the guard and allow the logout + // other devices functionality to work. + $guard->setUser($user); + + // This method doesn't exist in the stateless Sanctum world. + if (method_exists($guard, 'logoutOtherDevices')) { + $guard->logoutOtherDevices($request->input('password')); + } + + Activity::event('user:account.password-changed')->log(); + + return new JsonResponse([], Response::HTTP_NO_CONTENT); + } +} diff --git a/app/Http/Controllers/Api/Client/ActivityLogController.php b/app/Http/Controllers/Api/Client/ActivityLogController.php new file mode 100644 index 0000000..dbbd06c --- /dev/null +++ b/app/Http/Controllers/Api/Client/ActivityLogController.php @@ -0,0 +1,30 @@ +user()->activity()) + ->with('actor') + ->allowedFilters([AllowedFilter::partial('event')]) + ->allowedSorts(['timestamp']) + ->whereNotIn('activity_logs.event', ActivityLog::DISABLED_EVENTS) + ->paginate(min($request->query('per_page', 25), 100)) + ->appends($request->query()); + + return $this->fractal->collection($activity) + ->transformWith($this->getTransformer(ActivityLogTransformer::class)) + ->toArray(); + } +} diff --git a/app/Http/Controllers/Api/Client/ApiKeyController.php b/app/Http/Controllers/Api/Client/ApiKeyController.php new file mode 100644 index 0000000..ac00a4d --- /dev/null +++ b/app/Http/Controllers/Api/Client/ApiKeyController.php @@ -0,0 +1,71 @@ +fractal->collection($request->user()->apiKeys) + ->transformWith($this->getTransformer(ApiKeyTransformer::class)) + ->toArray(); + } + + /** + * Store a new API key for a user's account. + * + * @throws \Pterodactyl\Exceptions\DisplayException + */ + public function store(StoreApiKeyRequest $request): array + { + if ($request->user()->apiKeys->count() >= 25) { + throw new DisplayException('You have reached the account limit for number of API keys.'); + } + + $token = $request->user()->createToken( + $request->input('description'), + $request->input('allowed_ips') + ); + + Activity::event('user:api-key.create') + ->subject($token->accessToken) + ->property('identifier', $token->accessToken->identifier) + ->log(); + + return $this->fractal->item($token->accessToken) + ->transformWith($this->getTransformer(ApiKeyTransformer::class)) + ->addMeta(['secret_token' => $token->plainTextToken]) + ->toArray(); + } + + /** + * Deletes a given API key. + */ + public function delete(ClientApiRequest $request, string $identifier): JsonResponse + { + /** @var \Pterodactyl\Models\ApiKey $key */ + $key = $request->user()->apiKeys() + ->where('key_type', ApiKey::TYPE_ACCOUNT) + ->where('identifier', $identifier) + ->firstOrFail(); + + Activity::event('user:api-key.delete') + ->property('identifier', $key->identifier) + ->log(); + + $key->delete(); + + return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); + } +} diff --git a/app/Http/Controllers/Api/Client/ClientApiController.php b/app/Http/Controllers/Api/Client/ClientApiController.php new file mode 100644 index 0000000..a468d76 --- /dev/null +++ b/app/Http/Controllers/Api/Client/ClientApiController.php @@ -0,0 +1,56 @@ +parseIncludes(), function ($datum) use ($transformer) { + return in_array($datum, $transformer->getAvailableIncludes()); + }); + + return array_merge($filtered, $merge); + } + + /** + * Returns the parsed includes for this request. + */ + protected function parseIncludes(): array + { + $includes = $this->request->query('include') ?? []; + + if (!is_string($includes)) { + return $includes; + } + + return array_map(function ($item) { + return trim($item); + }, explode(',', $includes)); + } + + /** + * Return an instance of an application transformer. + * + * @template T of \Pterodactyl\Transformers\Api\Client\BaseClientTransformer + * + * @param class-string $abstract + * + * @return T + * + * @noinspection PhpDocSignatureInspection + */ + public function getTransformer(string $abstract) + { + Assert::subclassOf($abstract, BaseClientTransformer::class); + + return $abstract::fromRequest($this->request); + } +} diff --git a/app/Http/Controllers/Api/Client/ClientController.php b/app/Http/Controllers/Api/Client/ClientController.php new file mode 100644 index 0000000..9afb726 --- /dev/null +++ b/app/Http/Controllers/Api/Client/ClientController.php @@ -0,0 +1,81 @@ +user(); + $transformer = $this->getTransformer(ServerTransformer::class); + + // Start the query builder and ensure we eager load any requested relationships from the request. + $builder = QueryBuilder::for( + Server::query()->with($this->getIncludesForTransformer($transformer, ['node'])) + )->allowedFilters([ + 'uuid', + 'name', + 'description', + 'external_id', + AllowedFilter::custom('*', new MultiFieldServerFilter()), + ]); + + $type = $request->input('type'); + // Either return all the servers the user has access to because they are an admin `?type=admin` or + // just return all the servers the user has access to because they are the owner or a subuser of the + // server. If ?type=admin-all is passed all servers on the system will be returned to the user, rather + // than only servers they can see because they are an admin. + if (in_array($type, ['admin', 'admin-all'])) { + // If they aren't an admin but want all the admin servers don't fail the request, just + // make it a query that will never return any results back. + if (!$user->root_admin) { + $builder->whereRaw('1 = 2'); + } else { + $builder = $type === 'admin-all' + ? $builder + : $builder->whereNotIn('servers.id', $user->accessibleServers()->pluck('id')->all()); + } + } elseif ($type === 'owner') { + $builder = $builder->where('servers.owner_id', $user->id); + } else { + $builder = $builder->whereIn('servers.id', $user->accessibleServers()->pluck('id')->all()); + } + + $servers = $builder->paginate(min($request->query('per_page', 50), 100))->appends($request->query()); + + return $this->fractal->transformWith($transformer)->collection($servers)->toArray(); + } + + /** + * Returns all the subuser permissions available on the system. + */ + public function permissions(): array + { + return [ + 'object' => 'system_permissions', + 'attributes' => [ + 'permissions' => Permission::permissions(), + ], + ]; + } +} diff --git a/app/Http/Controllers/Api/Client/SSHKeyController.php b/app/Http/Controllers/Api/Client/SSHKeyController.php new file mode 100644 index 0000000..aa0f0c6 --- /dev/null +++ b/app/Http/Controllers/Api/Client/SSHKeyController.php @@ -0,0 +1,67 @@ +fractal->collection($request->user()->sshKeys) + ->transformWith($this->getTransformer(UserSSHKeyTransformer::class)) + ->toArray(); + } + + /** + * Stores a new SSH key for the authenticated user's account. + */ + public function store(StoreSSHKeyRequest $request): array + { + $model = $request->user()->sshKeys()->create([ + 'name' => $request->input('name'), + 'public_key' => $request->getPublicKey(), + 'fingerprint' => $request->getKeyFingerprint(), + ]); + + Activity::event('user:ssh-key.create') + ->subject($model) + ->property('fingerprint', $request->getKeyFingerprint()) + ->log(); + + return $this->fractal->item($model) + ->transformWith($this->getTransformer(UserSSHKeyTransformer::class)) + ->toArray(); + } + + /** + * Deletes an SSH key from the user's account. + */ + public function delete(ClientApiRequest $request): JsonResponse + { + $this->validate($request, ['fingerprint' => ['required', 'string']]); + + $key = $request->user()->sshKeys() + ->where('fingerprint', $request->input('fingerprint')) + ->first(); + + if (!is_null($key)) { + $key->delete(); + + Activity::event('user:ssh-key.delete') + ->subject($key) + ->property('fingerprint', $key->fingerprint) + ->log(); + } + + return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); + } +} diff --git a/app/Http/Controllers/Api/Client/Servers/ActivityLogController.php b/app/Http/Controllers/Api/Client/Servers/ActivityLogController.php new file mode 100644 index 0000000..f569c41 --- /dev/null +++ b/app/Http/Controllers/Api/Client/Servers/ActivityLogController.php @@ -0,0 +1,54 @@ +authorize(Permission::ACTION_ACTIVITY_READ, $server); + + $activity = QueryBuilder::for($server->activity()) + ->with('actor') + ->allowedSorts(['timestamp']) + ->allowedFilters([AllowedFilter::partial('event')]) + ->whereNotIn('activity_logs.event', ActivityLog::DISABLED_EVENTS) + ->when(config('activity.hide_admin_activity'), function (Builder $builder) use ($server) { + // We could do this with a query and a lot of joins, but that gets pretty + // painful so for now we'll execute a simpler query. + $subusers = $server->subusers()->pluck('user_id')->merge($server->owner_id); + + $builder->select('activity_logs.*') + ->leftJoin('users', function (JoinClause $join) { + $join->on('users.id', 'activity_logs.actor_id') + ->where('activity_logs.actor_type', (new User())->getMorphClass()); + }) + ->where(function (Builder $builder) use ($subusers) { + $builder->whereNull('users.id') + ->orWhere('users.root_admin', 0) + ->orWhereIn('users.id', $subusers); + }); + }) + ->paginate(min($request->query('per_page', 25), 100)) + ->appends($request->query()); + + return $this->fractal->collection($activity) + ->transformWith($this->getTransformer(ActivityLogTransformer::class)) + ->toArray(); + } +} diff --git a/app/Http/Controllers/Api/Client/Servers/BackupController.php b/app/Http/Controllers/Api/Client/Servers/BackupController.php new file mode 100644 index 0000000..11907c5 --- /dev/null +++ b/app/Http/Controllers/Api/Client/Servers/BackupController.php @@ -0,0 +1,224 @@ +user()->can(Permission::ACTION_BACKUP_READ, $server)) { + throw new AuthorizationException(); + } + + $limit = min($request->query('per_page') ?? 20, 50); + + return $this->fractal->collection($server->backups()->paginate($limit)) + ->transformWith($this->getTransformer(BackupTransformer::class)) + ->addMeta([ + 'backup_count' => $this->repository->getNonFailedBackups($server)->count(), + ]) + ->toArray(); + } + + /** + * Starts the backup process for a server. + * + * @throws \Spatie\Fractalistic\Exceptions\InvalidTransformation + * @throws \Spatie\Fractalistic\Exceptions\NoTransformerSpecified + * @throws \Throwable + */ + public function store(StoreBackupRequest $request, Server $server): array + { + $action = $this->initiateBackupService + ->setIgnoredFiles(explode(PHP_EOL, $request->input('ignored') ?? '')); + + // Only set the lock status if the user even has permission to delete backups, + // otherwise ignore this status. This gets a little funky since it isn't clear + // how best to allow a user to create a backup that is locked without also preventing + // them from just filling up a server with backups that can never be deleted? + if ($request->user()->can(Permission::ACTION_BACKUP_DELETE, $server)) { + $action->setIsLocked((bool) $request->input('is_locked')); + } + + $backup = $action->handle($server, $request->input('name')); + + Activity::event('server:backup.start') + ->subject($backup) + ->property(['name' => $backup->name, 'locked' => (bool) $request->input('is_locked')]) + ->log(); + + return $this->fractal->item($backup) + ->transformWith($this->getTransformer(BackupTransformer::class)) + ->toArray(); + } + + /** + * Toggles the lock status of a given backup for a server. + * + * @throws \Throwable + * @throws \Illuminate\Auth\Access\AuthorizationException + */ + public function toggleLock(Request $request, Server $server, Backup $backup): array + { + if (!$request->user()->can(Permission::ACTION_BACKUP_DELETE, $server)) { + throw new AuthorizationException(); + } + + $action = $backup->is_locked ? 'server:backup.unlock' : 'server:backup.lock'; + + $backup->update(['is_locked' => !$backup->is_locked]); + + Activity::event($action)->subject($backup)->property('name', $backup->name)->log(); + + return $this->fractal->item($backup) + ->transformWith($this->getTransformer(BackupTransformer::class)) + ->toArray(); + } + + /** + * Returns information about a single backup. + * + * @throws \Illuminate\Auth\Access\AuthorizationException + */ + public function view(Request $request, Server $server, Backup $backup): array + { + if (!$request->user()->can(Permission::ACTION_BACKUP_READ, $server)) { + throw new AuthorizationException(); + } + + return $this->fractal->item($backup) + ->transformWith($this->getTransformer(BackupTransformer::class)) + ->toArray(); + } + + /** + * Deletes a backup from the panel as well as the remote source where it is currently + * being stored. + * + * @throws \Throwable + */ + public function delete(Request $request, Server $server, Backup $backup): JsonResponse + { + if (!$request->user()->can(Permission::ACTION_BACKUP_DELETE, $server)) { + throw new AuthorizationException(); + } + + $this->deleteBackupService->handle($backup); + + Activity::event('server:backup.delete') + ->subject($backup) + ->property(['name' => $backup->name, 'failed' => !$backup->is_successful]) + ->log(); + + return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); + } + + /** + * Download the backup for a given server instance. For daemon local files, the file + * will be streamed back through the Panel. For AWS S3 files, a signed URL will be generated + * which the user is redirected to. + * + * @throws \Throwable + * @throws \Illuminate\Auth\Access\AuthorizationException + */ + public function download(Request $request, Server $server, Backup $backup): JsonResponse + { + if (!$request->user()->can(Permission::ACTION_BACKUP_DOWNLOAD, $server)) { + throw new AuthorizationException(); + } + + if ($backup->disk !== Backup::ADAPTER_AWS_S3 && $backup->disk !== Backup::ADAPTER_WINGS) { + throw new BadRequestHttpException('The backup requested references an unknown disk driver type and cannot be downloaded.'); + } + + $url = $this->downloadLinkService->handle($backup, $request->user()); + + Activity::event('server:backup.download')->subject($backup)->property('name', $backup->name)->log(); + + return new JsonResponse([ + 'object' => 'signed_url', + 'attributes' => ['url' => $url], + ]); + } + + /** + * Handles restoring a backup by making a request to the Wings instance telling it + * to begin the process of finding (or downloading) the backup and unpacking it + * over the server files. + * + * If the "truncate" flag is passed through in this request then all the + * files that currently exist on the server will be deleted before restoring. + * Otherwise, the archive will simply be unpacked over the existing files. + * + * @throws \Throwable + */ + public function restore(RestoreBackupRequest $request, Server $server, Backup $backup): JsonResponse + { + // Cannot restore a backup unless a server is fully installed and not currently + // processing a different backup restoration request. + if (!is_null($server->status)) { + throw new BadRequestHttpException('This server is not currently in a state that allows for a backup to be restored.'); + } + + if (!$backup->is_successful && is_null($backup->completed_at)) { + throw new BadRequestHttpException('This backup cannot be restored at this time: not completed or failed.'); + } + + $log = Activity::event('server:backup.restore') + ->subject($backup) + ->property(['name' => $backup->name, 'truncate' => $request->input('truncate')]); + + $log->transaction(function () use ($backup, $server, $request) { + // If the backup is for an S3 file we need to generate a unique Download link for + // it that will allow Wings to actually access the file. + if ($backup->disk === Backup::ADAPTER_AWS_S3) { + $url = $this->downloadLinkService->handle($backup, $request->user()); + } + + // Update the status right away for the server so that we know not to allow certain + // actions against it via the Panel API. + $server->update(['status' => Server::STATUS_RESTORING_BACKUP]); + + $this->daemonRepository->setServer($server)->restore($backup, $url ?? null, $request->input('truncate')); + }); + + return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); + } +} diff --git a/app/Http/Controllers/Api/Client/Servers/CommandController.php b/app/Http/Controllers/Api/Client/Servers/CommandController.php new file mode 100644 index 0000000..6cc50de --- /dev/null +++ b/app/Http/Controllers/Api/Client/Servers/CommandController.php @@ -0,0 +1,54 @@ +repository->setServer($server)->send($request->input('command')); + } catch (DaemonConnectionException $exception) { + $previous = $exception->getPrevious(); + + if ($previous instanceof BadResponseException) { + if ( + $previous->getResponse() instanceof ResponseInterface + && $previous->getResponse()->getStatusCode() === Response::HTTP_BAD_GATEWAY + ) { + throw new HttpException(Response::HTTP_BAD_GATEWAY, 'Server must be online in order to send commands.', $exception); + } + } + + throw $exception; + } + + Activity::event('server:console.command')->property('command', $request->input('command'))->log(); + + return $this->returnNoContent(); + } +} diff --git a/app/Http/Controllers/Api/Client/Servers/DatabaseController.php b/app/Http/Controllers/Api/Client/Servers/DatabaseController.php new file mode 100644 index 0000000..728c67e --- /dev/null +++ b/app/Http/Controllers/Api/Client/Servers/DatabaseController.php @@ -0,0 +1,102 @@ +fractal->collection($server->databases) + ->transformWith($this->getTransformer(DatabaseTransformer::class)) + ->toArray(); + } + + /** + * Create a new database for the given server and return it. + * + * @throws \Throwable + * @throws \Pterodactyl\Exceptions\Service\Database\TooManyDatabasesException + * @throws \Pterodactyl\Exceptions\Service\Database\DatabaseClientFeatureNotEnabledException + */ + public function store(StoreDatabaseRequest $request, Server $server): array + { + $database = $this->deployDatabaseService->handle($server, $request->validated()); + + Activity::event('server:database.create') + ->subject($database) + ->property('name', $database->database) + ->log(); + + return $this->fractal->item($database) + ->parseIncludes(['password']) + ->transformWith($this->getTransformer(DatabaseTransformer::class)) + ->toArray(); + } + + /** + * Rotates the password for the given server model and returns a fresh instance to + * the caller. + * + * @throws \Throwable + */ + public function rotatePassword(RotatePasswordRequest $request, Server $server, Database $database): array + { + $this->passwordService->handle($database); + $database->refresh(); + + Activity::event('server:database.rotate-password') + ->subject($database) + ->property('name', $database->database) + ->log(); + + return $this->fractal->item($database) + ->parseIncludes(['password']) + ->transformWith($this->getTransformer(DatabaseTransformer::class)) + ->toArray(); + } + + /** + * Removes a database from the server. + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function delete(DeleteDatabaseRequest $request, Server $server, Database $database): Response + { + $this->managementService->delete($database); + + Activity::event('server:database.delete') + ->subject($database) + ->property('name', $database->database) + ->log(); + + return new Response('', Response::HTTP_NO_CONTENT); + } +} diff --git a/app/Http/Controllers/Api/Client/Servers/FileController.php b/app/Http/Controllers/Api/Client/Servers/FileController.php new file mode 100644 index 0000000..23e6718 --- /dev/null +++ b/app/Http/Controllers/Api/Client/Servers/FileController.php @@ -0,0 +1,265 @@ +fileRepository + ->setServer($server) + ->getDirectory($request->get('directory') ?? '/'); + + return $this->fractal->collection($contents) + ->transformWith($this->getTransformer(FileObjectTransformer::class)) + ->toArray(); + } + + /** + * Return the contents of a specified file for the user. + * + * @throws \Throwable + */ + public function contents(GetFileContentsRequest $request, Server $server): Response + { + $response = $this->fileRepository->setServer($server)->getContent( + $request->get('file'), + config('pterodactyl.files.max_edit_size') + ); + + Activity::event('server:file.read')->property('file', $request->get('file'))->log(); + + return new Response($response, Response::HTTP_OK, ['Content-Type' => 'text/plain']); + } + + /** + * Generates a one-time token with a link that the user can use to + * download a given file. + * + * @throws \Throwable + */ + public function download(GetFileContentsRequest $request, Server $server): array + { + $token = $this->jwtService + ->setExpiresAt(CarbonImmutable::now()->addMinutes(15)) + ->setUser($request->user()) + ->setClaims([ + 'file_path' => rawurldecode($request->get('file')), + 'server_uuid' => $server->uuid, + ]) + ->handle($server->node, $request->user()->id . $server->uuid); + + Activity::event('server:file.download')->property('file', $request->get('file'))->log(); + + return [ + 'object' => 'signed_url', + 'attributes' => [ + 'url' => sprintf( + '%s/download/file?token=%s', + $server->node->getConnectionAddress(), + $token->toString() + ), + ], + ]; + } + + /** + * Writes the contents of the specified file to the server. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function write(WriteFileContentRequest $request, Server $server): JsonResponse + { + $this->fileRepository->setServer($server)->putContent($request->get('file'), $request->getContent()); + + Activity::event('server:file.write')->property('file', $request->get('file'))->log(); + + return new JsonResponse([], Response::HTTP_NO_CONTENT); + } + + /** + * Creates a new folder on the server. + * + * @throws \Throwable + */ + public function create(CreateFolderRequest $request, Server $server): JsonResponse + { + $this->fileRepository + ->setServer($server) + ->createDirectory($request->input('name'), $request->input('root', '/')); + + Activity::event('server:file.create-directory') + ->property('name', $request->input('name')) + ->property('directory', $request->input('root')) + ->log(); + + return new JsonResponse([], Response::HTTP_NO_CONTENT); + } + + /** + * Renames a file on the remote machine. + * + * @throws \Throwable + */ + public function rename(RenameFileRequest $request, Server $server): JsonResponse + { + $this->fileRepository + ->setServer($server) + ->renameFiles($request->input('root'), $request->input('files')); + + Activity::event('server:file.rename') + ->property('directory', $request->input('root')) + ->property('files', $request->input('files')) + ->log(); + + return new JsonResponse([], Response::HTTP_NO_CONTENT); + } + + /** + * Copies a file on the server. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function copy(CopyFileRequest $request, Server $server): JsonResponse + { + $this->fileRepository + ->setServer($server) + ->copyFile($request->input('location')); + + Activity::event('server:file.copy')->property('file', $request->input('location'))->log(); + + return new JsonResponse([], Response::HTTP_NO_CONTENT); + } + + /** + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function compress(CompressFilesRequest $request, Server $server): array + { + $file = $this->fileRepository->setServer($server)->compressFiles( + $request->input('root'), + $request->input('files') + ); + + Activity::event('server:file.compress') + ->property('directory', $request->input('root')) + ->property('files', $request->input('files')) + ->log(); + + return $this->fractal->item($file) + ->transformWith($this->getTransformer(FileObjectTransformer::class)) + ->toArray(); + } + + /** + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function decompress(DecompressFilesRequest $request, Server $server): JsonResponse + { + set_time_limit(300); + + $this->fileRepository->setServer($server)->decompressFile( + $request->input('root'), + $request->input('file') + ); + + Activity::event('server:file.decompress') + ->property('directory', $request->input('root')) + ->property('files', $request->input('file')) + ->log(); + + return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); + } + + /** + * Deletes files or folders for the server in the given root directory. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function delete(DeleteFileRequest $request, Server $server): JsonResponse + { + $this->fileRepository->setServer($server)->deleteFiles( + $request->input('root'), + $request->input('files') + ); + + Activity::event('server:file.delete') + ->property('directory', $request->input('root')) + ->property('files', $request->input('files')) + ->log(); + + return new JsonResponse([], Response::HTTP_NO_CONTENT); + } + + /** + * Updates file permissions for file(s) in the given root directory. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function chmod(ChmodFilesRequest $request, Server $server): JsonResponse + { + $this->fileRepository->setServer($server)->chmodFiles( + $request->input('root'), + $request->input('files') + ); + + return new JsonResponse([], Response::HTTP_NO_CONTENT); + } + + /** + * Requests that a file be downloaded from a remote location by Wings. + * + * @throws \Throwable + */ + public function pull(PullFileRequest $request, Server $server): JsonResponse + { + $this->fileRepository->setServer($server)->pull( + $request->input('url'), + $request->input('directory'), + $request->safe(['filename', 'use_header', 'foreground']) + ); + + Activity::event('server:file.pull') + ->property('directory', $request->input('directory')) + ->property('url', $request->input('url')) + ->log(); + + return new JsonResponse([], Response::HTTP_NO_CONTENT); + } +} diff --git a/app/Http/Controllers/Api/Client/Servers/FileUploadController.php b/app/Http/Controllers/Api/Client/Servers/FileUploadController.php new file mode 100644 index 0000000..98fcd58 --- /dev/null +++ b/app/Http/Controllers/Api/Client/Servers/FileUploadController.php @@ -0,0 +1,54 @@ + 'signed_url', + 'attributes' => [ + 'url' => $this->getUploadUrl($server, $request->user()), + ], + ]); + } + + /** + * Returns an url where files can be uploaded to. + */ + protected function getUploadUrl(Server $server, User $user): string + { + $token = $this->jwtService + ->setExpiresAt(CarbonImmutable::now()->addMinutes(15)) + ->setUser($user) + ->setClaims(['server_uuid' => $server->uuid]) + ->handle($server->node, $user->id . $server->uuid); + + return sprintf( + '%s/upload/file?token=%s', + $server->node->getConnectionAddress(), + $token->toString() + ); + } +} diff --git a/app/Http/Controllers/Api/Client/Servers/NetworkAllocationController.php b/app/Http/Controllers/Api/Client/Servers/NetworkAllocationController.php new file mode 100644 index 0000000..4e3c5f9 --- /dev/null +++ b/app/Http/Controllers/Api/Client/Servers/NetworkAllocationController.php @@ -0,0 +1,140 @@ +fractal->collection($server->allocations) + ->transformWith($this->getTransformer(AllocationTransformer::class)) + ->toArray(); + } + + /** + * Set the primary allocation for a server. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function update(UpdateAllocationRequest $request, Server $server, Allocation $allocation): array + { + $original = $allocation->notes; + + $allocation->forceFill(['notes' => $request->input('notes')])->save(); + + if ($original !== $allocation->notes) { + Activity::event('server:allocation.notes') + ->subject($allocation) + ->property(['allocation' => $allocation->toString(), 'old' => $original, 'new' => $allocation->notes]) + ->log(); + } + + return $this->fractal->item($allocation) + ->transformWith($this->getTransformer(AllocationTransformer::class)) + ->toArray(); + } + + /** + * Set the primary allocation for a server. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function setPrimary(SetPrimaryAllocationRequest $request, Server $server, Allocation $allocation): array + { + $this->serverRepository->update($server->id, ['allocation_id' => $allocation->id]); + + Activity::event('server:allocation.primary') + ->subject($allocation) + ->property('allocation', $allocation->toString()) + ->log(); + + return $this->fractal->item($allocation) + ->transformWith($this->getTransformer(AllocationTransformer::class)) + ->toArray(); + } + + /** + * Set the notes for the allocation for a server. + *s. + * + * @throws \Pterodactyl\Exceptions\DisplayException + */ + public function store(NewAllocationRequest $request, Server $server): array + { + if ($server->allocations()->count() >= $server->allocation_limit) { + throw new DisplayException('Cannot assign additional allocations to this server: limit has been reached.'); + } + + $allocation = $this->assignableAllocationService->handle($server); + + Activity::event('server:allocation.create') + ->subject($allocation) + ->property('allocation', $allocation->toString()) + ->log(); + + return $this->fractal->item($allocation) + ->transformWith($this->getTransformer(AllocationTransformer::class)) + ->toArray(); + } + + /** + * Delete an allocation from a server. + * + * @throws \Pterodactyl\Exceptions\DisplayException + */ + public function delete(DeleteAllocationRequest $request, Server $server, Allocation $allocation): JsonResponse + { + // Don't allow the deletion of allocations if the server does not have an + // allocation limit set. + if (empty($server->allocation_limit)) { + throw new DisplayException('You cannot delete allocations for this server: no allocation limit is set.'); + } + + if ($allocation->id === $server->allocation_id) { + throw new DisplayException('You cannot delete the primary allocation for this server.'); + } + + Allocation::query()->where('id', $allocation->id)->update([ + 'notes' => null, + 'server_id' => null, + ]); + + Activity::event('server:allocation.delete') + ->subject($allocation) + ->property('allocation', $allocation->toString()) + ->log(); + + return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); + } +} diff --git a/app/Http/Controllers/Api/Client/Servers/PowerController.php b/app/Http/Controllers/Api/Client/Servers/PowerController.php new file mode 100644 index 0000000..ca05757 --- /dev/null +++ b/app/Http/Controllers/Api/Client/Servers/PowerController.php @@ -0,0 +1,35 @@ +repository->setServer($server)->send( + $request->input('signal') + ); + + Activity::event(strtolower("server:power.{$request->input('signal')}"))->log(); + + return $this->returnNoContent(); + } +} diff --git a/app/Http/Controllers/Api/Client/Servers/ResourceUtilizationController.php b/app/Http/Controllers/Api/Client/Servers/ResourceUtilizationController.php new file mode 100644 index 0000000..dcaf481 --- /dev/null +++ b/app/Http/Controllers/Api/Client/Servers/ResourceUtilizationController.php @@ -0,0 +1,41 @@ +uuid"; + $stats = $this->cache->remember($key, Carbon::now()->addSeconds(20), function () use ($server) { + return $this->repository->setServer($server)->getDetails(); + }); + + return $this->fractal->item($stats) + ->transformWith($this->getTransformer(StatsTransformer::class)) + ->toArray(); + } +} diff --git a/app/Http/Controllers/Api/Client/Servers/ScheduleController.php b/app/Http/Controllers/Api/Client/Servers/ScheduleController.php new file mode 100644 index 0000000..b955fb1 --- /dev/null +++ b/app/Http/Controllers/Api/Client/Servers/ScheduleController.php @@ -0,0 +1,184 @@ +schedules->loadMissing('tasks'); + + return $this->fractal->collection($schedules) + ->transformWith($this->getTransformer(ScheduleTransformer::class)) + ->toArray(); + } + + /** + * Store a new schedule for a server. + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + */ + public function store(StoreScheduleRequest $request, Server $server): array + { + /** @var \Pterodactyl\Models\Schedule $model */ + $model = $this->repository->create([ + 'server_id' => $server->id, + 'name' => $request->input('name'), + 'cron_day_of_week' => $request->input('day_of_week'), + 'cron_month' => $request->input('month'), + 'cron_day_of_month' => $request->input('day_of_month'), + 'cron_hour' => $request->input('hour'), + 'cron_minute' => $request->input('minute'), + 'is_active' => (bool) $request->input('is_active'), + 'only_when_online' => (bool) $request->input('only_when_online'), + 'next_run_at' => $this->getNextRunAt($request), + ]); + + Activity::event('server:schedule.create') + ->subject($model) + ->property('name', $model->name) + ->log(); + + return $this->fractal->item($model) + ->transformWith($this->getTransformer(ScheduleTransformer::class)) + ->toArray(); + } + + /** + * Returns a specific schedule for the server. + */ + public function view(ViewScheduleRequest $request, Server $server, Schedule $schedule): array + { + if ($schedule->server_id !== $server->id) { + throw new NotFoundHttpException(); + } + + $schedule->loadMissing('tasks'); + + return $this->fractal->item($schedule) + ->transformWith($this->getTransformer(ScheduleTransformer::class)) + ->toArray(); + } + + /** + * Updates a given schedule with the new data provided. + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function update(UpdateScheduleRequest $request, Server $server, Schedule $schedule): array + { + $active = (bool) $request->input('is_active'); + + $data = [ + 'name' => $request->input('name'), + 'cron_day_of_week' => $request->input('day_of_week'), + 'cron_month' => $request->input('month'), + 'cron_day_of_month' => $request->input('day_of_month'), + 'cron_hour' => $request->input('hour'), + 'cron_minute' => $request->input('minute'), + 'is_active' => $active, + 'only_when_online' => (bool) $request->input('only_when_online'), + 'next_run_at' => $this->getNextRunAt($request), + ]; + + // Toggle the processing state of the scheduled task when it is enabled or disabled so that an + // invalid state can be reset without manual database intervention. + // + // @see https://github.com/pterodactyl/panel/issues/2425 + if ($schedule->is_active !== $active) { + $data['is_processing'] = false; + } + + $this->repository->update($schedule->id, $data); + + Activity::event('server:schedule.update') + ->subject($schedule) + ->property(['name' => $schedule->name, 'active' => $active]) + ->log(); + + return $this->fractal->item($schedule->refresh()) + ->transformWith($this->getTransformer(ScheduleTransformer::class)) + ->toArray(); + } + + /** + * Executes a given schedule immediately rather than waiting on it's normally scheduled time + * to pass. This does not care about the schedule state. + * + * @throws \Throwable + */ + public function execute(TriggerScheduleRequest $request, Server $server, Schedule $schedule): JsonResponse + { + $this->service->handle($schedule, true); + + Activity::event('server:schedule.execute')->subject($schedule)->property('name', $schedule->name)->log(); + + return new JsonResponse([], JsonResponse::HTTP_ACCEPTED); + } + + /** + * Deletes a schedule and it's associated tasks. + */ + public function delete(DeleteScheduleRequest $request, Server $server, Schedule $schedule): JsonResponse + { + $this->repository->delete($schedule->id); + + Activity::event('server:schedule.delete')->subject($schedule)->property('name', $schedule->name)->log(); + + return new JsonResponse([], Response::HTTP_NO_CONTENT); + } + + /** + * Get the next run timestamp based on the cron data provided. + * + * @throws \Pterodactyl\Exceptions\DisplayException + */ + protected function getNextRunAt(Request $request): Carbon + { + try { + return Utilities::getScheduleNextRunDate( + $request->input('minute'), + $request->input('hour'), + $request->input('day_of_month'), + $request->input('month'), + $request->input('day_of_week') + ); + } catch (\Exception $exception) { + throw new DisplayException('The cron data provided does not evaluate to a valid expression.'); + } + } +} diff --git a/app/Http/Controllers/Api/Client/Servers/ScheduleTaskController.php b/app/Http/Controllers/Api/Client/Servers/ScheduleTaskController.php new file mode 100644 index 0000000..e2e4389 --- /dev/null +++ b/app/Http/Controllers/Api/Client/Servers/ScheduleTaskController.php @@ -0,0 +1,175 @@ +tasks()->count() >= $limit) { + throw new ServiceLimitExceededException("Schedules may not have more than $limit tasks associated with them. Creating this task would put this schedule over the limit."); + } + + if ($server->backup_limit === 0 && $request->action === 'backup') { + throw new HttpForbiddenException("A backup task cannot be created when the server's backup limit is set to 0."); + } + + /** @var \Pterodactyl\Models\Task|null $lastTask */ + $lastTask = $schedule->tasks()->orderByDesc('sequence_id')->first(); + + /** @var \Pterodactyl\Models\Task $task */ + $task = $this->connection->transaction(function () use ($request, $schedule, $lastTask) { + $sequenceId = ($lastTask->sequence_id ?? 0) + 1; + $requestSequenceId = $request->integer('sequence_id', $sequenceId); + + // Ensure that the sequence id is at least 1. + if ($requestSequenceId < 1) { + $requestSequenceId = 1; + } + + // If the sequence id from the request is greater than or equal to the next available + // sequence id, we don't need to do anything special. Otherwise, we need to update + // the sequence id of all tasks that are greater than or equal to the request sequence + // id to be one greater than the current value. + if ($requestSequenceId < $sequenceId) { + $schedule->tasks() + ->where('sequence_id', '>=', $requestSequenceId) + ->increment('sequence_id'); + $sequenceId = $requestSequenceId; + } + + return $this->repository->create([ + 'schedule_id' => $schedule->id, + 'sequence_id' => $sequenceId, + 'action' => $request->input('action'), + 'payload' => $request->input('payload') ?? '', + 'time_offset' => $request->input('time_offset'), + 'continue_on_failure' => $request->boolean('continue_on_failure'), + ]); + }); + + Activity::event('server:task.create') + ->subject($schedule, $task) + ->property(['name' => $schedule->name, 'action' => $task->action, 'payload' => $task->payload]) + ->log(); + + return $this->fractal->item($task) + ->transformWith($this->getTransformer(TaskTransformer::class)) + ->toArray(); + } + + /** + * Updates a given task for a server. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function update(StoreTaskRequest $request, Server $server, Schedule $schedule, Task $task): array + { + if ($schedule->id !== $task->schedule_id || $server->id !== $schedule->server_id) { + throw new NotFoundHttpException(); + } + + if ($server->backup_limit === 0 && $request->action === 'backup') { + throw new HttpForbiddenException("A backup task cannot be created when the server's backup limit is set to 0."); + } + + $this->connection->transaction(function () use ($request, $schedule, $task) { + $sequenceId = $request->integer('sequence_id', $task->sequence_id); + // Ensure that the sequence id is at least 1. + if ($sequenceId < 1) { + $sequenceId = 1; + } + + // Shift all other tasks in the schedule up or down to make room for the new task. + if ($sequenceId < $task->sequence_id) { + $schedule->tasks() + ->where('sequence_id', '>=', $sequenceId) + ->where('sequence_id', '<', $task->sequence_id) + ->increment('sequence_id'); + } elseif ($sequenceId > $task->sequence_id) { + $schedule->tasks() + ->where('sequence_id', '>', $task->sequence_id) + ->where('sequence_id', '<=', $sequenceId) + ->decrement('sequence_id'); + } + + $this->repository->update($task->id, [ + 'sequence_id' => $sequenceId, + 'action' => $request->input('action'), + 'payload' => $request->input('payload') ?? '', + 'time_offset' => $request->input('time_offset'), + 'continue_on_failure' => $request->boolean('continue_on_failure'), + ]); + }); + + Activity::event('server:task.update') + ->subject($schedule, $task) + ->property(['name' => $schedule->name, 'action' => $task->action, 'payload' => $task->payload]) + ->log(); + + return $this->fractal->item($task->refresh()) + ->transformWith($this->getTransformer(TaskTransformer::class)) + ->toArray(); + } + + /** + * Delete a given task for a schedule. If there are subsequent tasks stored in the database + * for this schedule their sequence IDs are decremented properly. + * + * @throws \Exception + */ + public function delete(ClientApiRequest $request, Server $server, Schedule $schedule, Task $task): JsonResponse + { + if ($task->schedule_id !== $schedule->id || $schedule->server_id !== $server->id) { + throw new NotFoundHttpException(); + } + + if (!$request->user()->can(Permission::ACTION_SCHEDULE_UPDATE, $server)) { + throw new HttpForbiddenException('You do not have permission to perform this action.'); + } + + $schedule->tasks() + ->where('sequence_id', '>', $task->sequence_id) + ->decrement('sequence_id'); + $task->delete(); + + Activity::event('server:task.delete')->subject($schedule, $task)->property('name', $schedule->name)->log(); + + return new JsonResponse(null, Response::HTTP_NO_CONTENT); + } +} diff --git a/app/Http/Controllers/Api/Client/Servers/ServerController.php b/app/Http/Controllers/Api/Client/Servers/ServerController.php new file mode 100644 index 0000000..63eb9b9 --- /dev/null +++ b/app/Http/Controllers/Api/Client/Servers/ServerController.php @@ -0,0 +1,35 @@ +fractal->item($server) + ->transformWith($this->getTransformer(ServerTransformer::class)) + ->addMeta([ + 'is_server_owner' => $request->user()->id === $server->owner_id, + 'user_permissions' => $this->permissionsService->handle($server, $request->user()), + ]) + ->toArray(); + } +} diff --git a/app/Http/Controllers/Api/Client/Servers/SettingsController.php b/app/Http/Controllers/Api/Client/Servers/SettingsController.php new file mode 100644 index 0000000..b939f16 --- /dev/null +++ b/app/Http/Controllers/Api/Client/Servers/SettingsController.php @@ -0,0 +1,95 @@ +input('name'); + $description = $request->has('description') ? (string) $request->input('description') : $server->description; + $this->repository->update($server->id, [ + 'name' => $name, + 'description' => $description, + ]); + + if ($server->name !== $name) { + Activity::event('server:settings.rename') + ->property(['old' => $server->name, 'new' => $name]) + ->log(); + } + + if ($server->description !== $description) { + Activity::event('server:settings.description') + ->property(['old' => $server->description, 'new' => $description]) + ->log(); + } + + return new JsonResponse([], Response::HTTP_NO_CONTENT); + } + + /** + * Reinstalls the server on the daemon. + * + * @throws \Throwable + */ + public function reinstall(ReinstallServerRequest $request, Server $server): JsonResponse + { + $this->reinstallServerService->handle($server); + + Activity::event('server:reinstall')->log(); + + return new JsonResponse([], Response::HTTP_ACCEPTED); + } + + /** + * Changes the Docker image in use by the server. + * + * @throws \Throwable + */ + public function dockerImage(SetDockerImageRequest $request, Server $server): JsonResponse + { + if (!in_array($server->image, array_values($server->egg->docker_images))) { + throw new BadRequestHttpException('This server\'s Docker image has been manually set by an administrator and cannot be updated.'); + } + + $original = $server->image; + $server->forceFill(['image' => $request->input('docker_image')])->saveOrFail(); + + if ($original !== $server->image) { + Activity::event('server:startup.image') + ->property(['old' => $original, 'new' => $request->input('docker_image')]) + ->log(); + } + + return new JsonResponse([], Response::HTTP_NO_CONTENT); + } +} diff --git a/app/Http/Controllers/Api/Client/Servers/StartupController.php b/app/Http/Controllers/Api/Client/Servers/StartupController.php new file mode 100644 index 0000000..9548be2 --- /dev/null +++ b/app/Http/Controllers/Api/Client/Servers/StartupController.php @@ -0,0 +1,99 @@ +startupCommandService->handle($server); + + return $this->fractal->collection( + $server->variables()->where('user_viewable', true)->get() + ) + ->transformWith($this->getTransformer(EggVariableTransformer::class)) + ->addMeta([ + 'startup_command' => $startup, + 'docker_images' => $server->egg->docker_images, + 'raw_startup_command' => $server->startup, + ]) + ->toArray(); + } + + /** + * Updates a single variable for a server. + * + * @throws \Illuminate\Validation\ValidationException + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function update(UpdateStartupVariableRequest $request, Server $server): array + { + /** @var \Pterodactyl\Models\EggVariable $variable */ + $variable = $server->variables()->where('env_variable', $request->input('key'))->first(); + $original = $variable->server_value; + + if (is_null($variable) || !$variable->user_viewable) { + throw new BadRequestHttpException('The environment variable you are trying to edit does not exist.'); + } elseif (!$variable->user_editable) { + throw new BadRequestHttpException('The environment variable you are trying to edit is read-only.'); + } + + // Revalidate the variable value using the egg variable specific validation rules for it. + $this->validate($request, ['value' => $variable->rules]); + + $this->repository->updateOrCreate([ + 'server_id' => $server->id, + 'variable_id' => $variable->id, + ], [ + 'variable_value' => $request->input('value') ?? '', + ]); + + $variable = $variable->refresh(); + $variable->server_value = $request->input('value'); + + $startup = $this->startupCommandService->handle($server); + + if ($variable->env_variable !== $request->input('value')) { + Activity::event('server:startup.edit') + ->subject($variable) + ->property([ + 'variable' => $variable->env_variable, + 'old' => $original, + 'new' => $request->input('value'), + ]) + ->log(); + } + + return $this->fractal->item($variable) + ->transformWith($this->getTransformer(EggVariableTransformer::class)) + ->addMeta([ + 'startup_command' => $startup, + 'raw_startup_command' => $server->startup, + ]) + ->toArray(); + } +} diff --git a/app/Http/Controllers/Api/Client/Servers/SubuserController.php b/app/Http/Controllers/Api/Client/Servers/SubuserController.php new file mode 100644 index 0000000..2c403c6 --- /dev/null +++ b/app/Http/Controllers/Api/Client/Servers/SubuserController.php @@ -0,0 +1,185 @@ +fractal->collection($server->subusers) + ->transformWith($this->getTransformer(SubuserTransformer::class)) + ->toArray(); + } + + /** + * Returns a single subuser associated with this server instance. + */ + public function view(GetSubuserRequest $request): array + { + $subuser = $request->attributes->get('subuser'); + + return $this->fractal->item($subuser) + ->transformWith($this->getTransformer(SubuserTransformer::class)) + ->toArray(); + } + + /** + * Create a new subuser for the given server. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Service\Subuser\ServerSubuserExistsException + * @throws \Pterodactyl\Exceptions\Service\Subuser\UserIsServerOwnerException + * @throws \Throwable + */ + public function store(StoreSubuserRequest $request, Server $server): array + { + $response = $this->creationService->handle( + $server, + $request->input('email'), + $this->getDefaultPermissions($request) + ); + + Activity::event('server:subuser.create') + ->subject($response->user) + ->property(['email' => $request->input('email'), 'permissions' => $this->getDefaultPermissions($request)]) + ->log(); + + return $this->fractal->item($response) + ->transformWith($this->getTransformer(SubuserTransformer::class)) + ->toArray(); + } + + /** + * Update a given subuser in the system for the server. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function update(UpdateSubuserRequest $request, Server $server): array + { + /** @var \Pterodactyl\Models\Subuser $subuser */ + $subuser = $request->attributes->get('subuser'); + + $permissions = $this->getDefaultPermissions($request); + $current = $subuser->permissions; + + sort($permissions); + sort($current); + + $log = Activity::event('server:subuser.update') + ->subject($subuser->user) + ->property([ + 'email' => $subuser->user->email, + 'old' => $current, + 'new' => $permissions, + 'revoked' => true, + ]); + + // Only update the database and hit up the Wings instance to invalidate JTI's if the permissions + // have actually changed for the user. + if ($permissions !== $current) { + $log->transaction(function ($instance) use ($request, $subuser, $server) { + $this->repository->update($subuser->id, [ + 'permissions' => $this->getDefaultPermissions($request), + ]); + + try { + $this->serverRepository->setServer($server)->revokeUserJTI($subuser->user_id); + } catch (DaemonConnectionException $exception) { + // Don't block this request if we can't connect to the Wings instance. Chances are it is + // offline and the token will be invalid once Wings boots back. + Log::warning($exception, ['user_id' => $subuser->user_id, 'server_id' => $server->id]); + + $instance->property('revoked', false); + } + }); + } + + $log->reset(); + + return $this->fractal->item($subuser->refresh()) + ->transformWith($this->getTransformer(SubuserTransformer::class)) + ->toArray(); + } + + /** + * Removes a subusers from a server's assignment. + */ + public function delete(DeleteSubuserRequest $request, Server $server): JsonResponse + { + /** @var \Pterodactyl\Models\Subuser $subuser */ + $subuser = $request->attributes->get('subuser'); + + $log = Activity::event('server:subuser.delete') + ->subject($subuser->user) + ->property('email', $subuser->user->email) + ->property('revoked', true); + + $log->transaction(function ($instance) use ($server, $subuser) { + $subuser->delete(); + + try { + $this->serverRepository->setServer($server)->revokeUserJTI($subuser->user_id); + } catch (DaemonConnectionException $exception) { + // Don't block this request if we can't connect to the Wings instance. + Log::warning($exception, ['user_id' => $subuser->user_id, 'server_id' => $server->id]); + + $instance->property('revoked', false); + } + }); + + return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); + } + + /** + * Returns the default permissions for subusers and parses out any permissions + * that were passed that do not also exist in the internally tracked list of + * permissions. + */ + protected function getDefaultPermissions(Request $request): array + { + $allowed = Permission::permissions() + ->map(function ($value, $prefix) { + return array_map(function ($value) use ($prefix) { + return "$prefix.$value"; + }, array_keys($value['keys'])); + }) + ->flatten() + ->all(); + + $cleaned = array_intersect($request->input('permissions') ?? [], $allowed); + + return array_unique(array_merge($cleaned, [Permission::ACTION_WEBSOCKET_CONNECT])); + } +} diff --git a/app/Http/Controllers/Api/Client/Servers/WebsocketController.php b/app/Http/Controllers/Api/Client/Servers/WebsocketController.php new file mode 100644 index 0000000..59b6f75 --- /dev/null +++ b/app/Http/Controllers/Api/Client/Servers/WebsocketController.php @@ -0,0 +1,73 @@ +user(); + if ($user->cannot(Permission::ACTION_WEBSOCKET_CONNECT, $server)) { + throw new HttpForbiddenException('You do not have permission to connect to this server\'s websocket.'); + } + + $permissions = $this->permissionsService->handle($server, $user); + + $node = $server->node; + if (!is_null($server->transfer)) { + // Check if the user has permissions to receive transfer logs. + if (!in_array('admin.websocket.transfer', $permissions)) { + throw new HttpForbiddenException('You do not have permission to view server transfer logs.'); + } + + // Redirect the websocket request to the new node if the server has been archived. + if ($server->transfer->archived) { + $node = $server->transfer->newNode; + } + } + + $token = $this->jwtService + ->setExpiresAt(CarbonImmutable::now()->addMinutes(10)) + ->setUser($request->user()) + ->setClaims([ + 'server_uuid' => $server->uuid, + 'permissions' => $permissions, + ]) + ->handle($node, $user->id . $server->uuid); + + $socket = str_replace(['https://', 'http://'], ['wss://', 'ws://'], $node->getConnectionAddress()); + + return new JsonResponse([ + 'data' => [ + 'token' => $token->toString(), + 'socket' => $socket . sprintf('/api/servers/%s/ws', $server->uuid), + ], + ]); + } +} diff --git a/app/Http/Controllers/Api/Client/TwoFactorController.php b/app/Http/Controllers/Api/Client/TwoFactorController.php new file mode 100644 index 0000000..8cfa09b --- /dev/null +++ b/app/Http/Controllers/Api/Client/TwoFactorController.php @@ -0,0 +1,101 @@ +user()->use_totp) { + throw new BadRequestHttpException('Two-factor authentication is already enabled on this account.'); + } + + return new JsonResponse([ + 'data' => $this->setupService->handle($request->user()), + ]); + } + + /** + * Updates a user's account to have two-factor enabled. + * + * @throws \Throwable + * @throws \Illuminate\Validation\ValidationException + */ + public function store(Request $request): JsonResponse + { + $validator = $this->validation->make($request->all(), [ + 'code' => ['required', 'string', 'size:6'], + 'password' => ['required', 'string'], + ]); + + $data = $validator->validate(); + if (!password_verify($data['password'], $request->user()->password)) { + throw new BadRequestHttpException('The password provided was not valid.'); + } + + $tokens = $this->toggleTwoFactorService->handle($request->user(), $data['code'], true); + + Activity::event('user:two-factor.create')->log(); + + return new JsonResponse([ + 'object' => 'recovery_tokens', + 'attributes' => [ + 'tokens' => $tokens, + ], + ]); + } + + /** + * Disables two-factor authentication on an account if the password provided + * is valid. + * + * @throws \Throwable + */ + public function delete(Request $request): JsonResponse + { + if (!password_verify($request->input('password') ?? '', $request->user()->password)) { + throw new BadRequestHttpException('The password provided was not valid.'); + } + + /** @var \Pterodactyl\Models\User $user */ + $user = $request->user(); + + $user->update([ + 'totp_authenticated_at' => Carbon::now(), + 'use_totp' => false, + ]); + + Activity::event('user:two-factor.delete')->log(); + + return new JsonResponse([], Response::HTTP_NO_CONTENT); + } +} diff --git a/app/Http/Controllers/Api/Remote/ActivityProcessingController.php b/app/Http/Controllers/Api/Remote/ActivityProcessingController.php new file mode 100644 index 0000000..b9d12c2 --- /dev/null +++ b/app/Http/Controllers/Api/Remote/ActivityProcessingController.php @@ -0,0 +1,90 @@ +getTimezone(); + + /** @var \Pterodactyl\Models\Node $node */ + $node = $request->attributes->get('node'); + + $servers = $node->servers()->whereIn('uuid', $request->servers())->get()->keyBy('uuid'); + $users = User::query()->whereIn('uuid', $request->users())->get()->keyBy('uuid'); + + $logs = []; + foreach ($request->input('data') as $datum) { + /** @var \Pterodactyl\Models\Server|null $server */ + $server = $servers->get($datum['server']); + if (is_null($server) || !Str::startsWith($datum['event'], 'server:')) { + continue; + } + + try { + $when = Carbon::createFromFormat( + \DateTimeInterface::RFC3339, + preg_replace('/(\.\d+)Z$/', 'Z', $datum['timestamp']), + 'UTC' + ); + } catch (\Exception $exception) { + Log::warning($exception, ['timestamp' => $datum['timestamp']]); + + // If we cannot parse the value for some reason don't blow up this request, just go ahead + // and log the event with the current time, and set the metadata value to have the original + // timestamp that was provided. + $when = Carbon::now(); + $datum['metadata'] = array_merge($datum['metadata'] ?? [], ['original_timestamp' => $datum['timestamp']]); + } + + $log = [ + 'ip' => empty($datum['ip']) ? '127.0.0.1' : $datum['ip'], + 'event' => $datum['event'], + 'properties' => json_encode($datum['metadata'] ?? []), + // We have to change the time to the current timezone due to the way Laravel is handling + // the date casting internally. If we just leave it in UTC it ends up getting double-cast + // and the time is way off. + 'timestamp' => $when->setTimezone($tz), + ]; + + if ($user = $users->get($datum['user'])) { + $log['actor_id'] = $user->id; + $log['actor_type'] = $user->getMorphClass(); + } + + if (!isset($logs[$datum['server']])) { + $logs[$datum['server']] = []; + } + + $logs[$datum['server']][] = $log; + } + + foreach ($logs as $key => $data) { + Assert::isInstanceOf($server = $servers->get($key), Server::class); + + $batch = []; + foreach ($data as $datum) { + $id = ActivityLog::insertGetId($datum); + $batch[] = [ + 'activity_log_id' => $id, + 'subject_id' => $server->id, + 'subject_type' => $server->getMorphClass(), + ]; + } + + ActivityLogSubject::insert($batch); + } + } +} diff --git a/app/Http/Controllers/Api/Remote/Backups/BackupRemoteUploadController.php b/app/Http/Controllers/Api/Remote/Backups/BackupRemoteUploadController.php new file mode 100644 index 0000000..15fe8d9 --- /dev/null +++ b/app/Http/Controllers/Api/Remote/Backups/BackupRemoteUploadController.php @@ -0,0 +1,137 @@ +attributes->get('node'); + + // Get the size query parameter. + $size = (int) $request->query('size'); + if (empty($size)) { + throw new BadRequestHttpException('A non-empty "size" query parameter must be provided.'); + } + + /** @var \Pterodactyl\Models\Backup $model */ + $model = Backup::query() + ->where('uuid', $backup) + ->firstOrFail(); + + // Check that the backup is "owned" by the node making the request. This avoids other nodes + // from messing with backups that they don't own. + /** @var \Pterodactyl\Models\Server $server */ + $server = $model->server; + if ($server->node_id !== $node->id) { + throw new HttpForbiddenException('You do not have permission to access that backup.'); + } + + // Prevent backups that have already been completed from trying to + // be uploaded again. + if (!is_null($model->completed_at)) { + throw new ConflictHttpException('This backup is already in a completed state.'); + } + + // Ensure we are using the S3 adapter. + $adapter = $this->backupManager->adapter(); + if (!$adapter instanceof S3Filesystem) { + throw new BadRequestHttpException('The configured backup adapter is not an S3 compatible adapter.'); + } + + // The path where backup will be uploaded to + $path = sprintf('%s/%s.tar.gz', $model->server->uuid, $model->uuid); + + // Get the S3 client + $client = $adapter->getClient(); + $expires = CarbonImmutable::now()->addMinutes(config('backups.presigned_url_lifespan', 60)); + + // Params for generating the presigned urls + $params = [ + 'Bucket' => $adapter->getBucket(), + 'Key' => $path, + 'ContentType' => 'application/x-gzip', + ]; + + $storageClass = config('backups.disks.s3.storage_class'); + if (!is_null($storageClass)) { + $params['StorageClass'] = $storageClass; + } + + // Execute the CreateMultipartUpload request + $result = $client->execute($client->getCommand('CreateMultipartUpload', $params)); + + // Get the UploadId from the CreateMultipartUpload request, this is needed to create + // the other presigned urls. + $params['UploadId'] = $result->get('UploadId'); + + // Retrieve configured part size + $maxPartSize = $this->getConfiguredMaxPartSize(); + + // Create as many UploadPart presigned urls as needed + $parts = []; + for ($i = 0; $i < ($size / $maxPartSize); ++$i) { + $parts[] = $client->createPresignedRequest( + $client->getCommand('UploadPart', array_merge($params, ['PartNumber' => $i + 1])), + $expires + )->getUri()->__toString(); + } + + // Set the upload_id on the backup in the database. + $model->update(['upload_id' => $params['UploadId']]); + + return new JsonResponse([ + 'parts' => $parts, + 'part_size' => $maxPartSize, + ]); + } + + /** + * Get the configured maximum size of a single part in the multipart upload. + * + * The function tries to retrieve a configured value from the configuration. + * If no value is specified, a fallback value will be used. + * + * Note if the received config cannot be converted to int (0), is zero or is negative, + * the fallback value will be used too. + * + * The fallback value is {@see BackupRemoteUploadController::DEFAULT_MAX_PART_SIZE}. + */ + private function getConfiguredMaxPartSize(): int + { + $maxPartSize = (int) config('backups.max_part_size', self::DEFAULT_MAX_PART_SIZE); + if ($maxPartSize <= 0) { + $maxPartSize = self::DEFAULT_MAX_PART_SIZE; + } + + return $maxPartSize; + } +} diff --git a/app/Http/Controllers/Api/Remote/Backups/BackupStatusController.php b/app/Http/Controllers/Api/Remote/Backups/BackupStatusController.php new file mode 100644 index 0000000..7b30e07 --- /dev/null +++ b/app/Http/Controllers/Api/Remote/Backups/BackupStatusController.php @@ -0,0 +1,160 @@ +attributes->get('node'); + + /** @var \Pterodactyl\Models\Backup $model */ + $model = Backup::query() + ->where('uuid', $backup) + ->firstOrFail(); + + // Check that the backup is "owned" by the node making the request. This avoids other nodes + // from messing with backups that they don't own. + /** @var \Pterodactyl\Models\Server $server */ + $server = $model->server; + if ($server->node_id !== $node->id) { + throw new HttpForbiddenException('You do not have permission to access that backup.'); + } + + if ($model->is_successful) { + throw new BadRequestHttpException('Cannot update the status of a backup that is already marked as completed.'); + } + + $action = $request->boolean('successful') ? 'server:backup.complete' : 'server:backup.fail'; + $log = Activity::event($action)->subject($model, $model->server)->property('name', $model->name); + + $log->transaction(function () use ($model, $request) { + $successful = $request->boolean('successful'); + + $model->fill([ + 'is_successful' => $successful, + // Change the lock state to unlocked if this was a failed backup so that it can be + // deleted easily. Also does not make sense to have a locked backup on the system + // that is failed. + 'is_locked' => $successful ? $model->is_locked : false, + 'checksum' => $successful ? ($request->input('checksum_type') . ':' . $request->input('checksum')) : null, + 'bytes' => $successful ? $request->input('size') : 0, + 'completed_at' => CarbonImmutable::now(), + ])->save(); + + // Check if we are using the s3 backup adapter. If so, make sure we mark the backup as + // being completed in S3 correctly. + $adapter = $this->backupManager->adapter(); + if ($adapter instanceof S3Filesystem) { + $this->completeMultipartUpload($model, $adapter, $successful, $request->input('parts')); + } + }); + + return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); + } + + /** + * Handles toggling the restoration status of a server. The server status field should be + * set back to null, even if the restoration failed. This is not an unsolvable state for + * the server, and the user can keep trying to restore, or just use the reinstall button. + * + * The only thing the successful field does is update the entry value for the audit logs + * table tracking for this restoration. + * + * @throws \Throwable + */ + public function restore(Request $request, string $backup): JsonResponse + { + /** @var \Pterodactyl\Models\Backup $model */ + $model = Backup::query()->where('uuid', $backup)->firstOrFail(); + + $model->server->update(['status' => null]); + + Activity::event($request->boolean('successful') ? 'server:backup.restore-complete' : 'server.backup.restore-failed') + ->subject($model, $model->server) + ->property('name', $model->name) + ->log(); + + return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); + } + + /** + * Marks a multipart upload in a given S3-compatible instance as failed or successful for + * the given backup. + * + * @throws \Exception + * @throws \Pterodactyl\Exceptions\DisplayException + */ + protected function completeMultipartUpload(Backup $backup, S3Filesystem $adapter, bool $successful, ?array $parts): void + { + // This should never really happen, but if it does don't let us fall victim to Amazon's + // wildly fun error messaging. Just stop the process right here. + if (empty($backup->upload_id)) { + // A failed backup doesn't need to error here, this can happen if the backup encounters + // an error before we even start the upload. AWS gives you tooling to clear these failed + // multipart uploads as needed too. + if (!$successful) { + return; + } + + throw new DisplayException('Cannot complete backup request: no upload_id present on model.'); + } + + $params = [ + 'Bucket' => $adapter->getBucket(), + 'Key' => sprintf('%s/%s.tar.gz', $backup->server->uuid, $backup->uuid), + 'UploadId' => $backup->upload_id, + ]; + + $client = $adapter->getClient(); + if (!$successful) { + $client->execute($client->getCommand('AbortMultipartUpload', $params)); + + return; + } + + // Otherwise send a CompleteMultipartUpload request. + $params['MultipartUpload'] = [ + 'Parts' => [], + ]; + + if (is_null($parts)) { + $params['MultipartUpload']['Parts'] = $client->execute($client->getCommand('ListParts', $params))['Parts']; + } else { + foreach ($parts as $part) { + $params['MultipartUpload']['Parts'][] = [ + 'ETag' => $part['etag'], + 'PartNumber' => $part['part_number'], + ]; + } + } + + $client->execute($client->getCommand('CompleteMultipartUpload', $params)); + } +} diff --git a/app/Http/Controllers/Api/Remote/EggInstallController.php b/app/Http/Controllers/Api/Remote/EggInstallController.php new file mode 100644 index 0000000..31df7a9 --- /dev/null +++ b/app/Http/Controllers/Api/Remote/EggInstallController.php @@ -0,0 +1,51 @@ +attributes->get('node'); + + /** @var \Pterodactyl\Models\Server $server */ + $server = $this->repository->findFirstWhere([ + ['uuid', '=', $uuid], + ['node_id', '=', $node->id], + ]); + + $this->repository->loadEggRelations($server); + $egg = $server->getRelation('egg'); + + return response()->json([ + 'scripts' => [ + 'install' => !$egg->copy_script_install ? null : str_replace(["\r\n", "\n", "\r"], "\n", $egg->copy_script_install), + 'privileged' => $egg->script_is_privileged, + ], + 'config' => [ + 'container' => $egg->copy_script_container, + 'entry' => $egg->copy_script_entry, + ], + 'env' => $this->environment->handle($server), + ]); + } +} diff --git a/app/Http/Controllers/Api/Remote/Servers/ServerDetailsController.php b/app/Http/Controllers/Api/Remote/Servers/ServerDetailsController.php new file mode 100644 index 0000000..8ce88e8 --- /dev/null +++ b/app/Http/Controllers/Api/Remote/Servers/ServerDetailsController.php @@ -0,0 +1,118 @@ +repository->getByUuid($uuid); + + return new JsonResponse([ + 'settings' => $this->configurationStructureService->handle($server), + 'process_configuration' => $this->eggConfigurationService->handle($server), + ]); + } + + /** + * Lists all servers with their configurations that are assigned to the requesting node. + */ + public function list(Request $request): ServerConfigurationCollection + { + /** @var \Pterodactyl\Models\Node $node */ + $node = $request->attributes->get('node'); + + // Avoid run-away N+1 SQL queries by preloading the relationships that are used + // within each of the services called below. + $servers = Server::query()->with('allocations', 'egg', 'mounts', 'variables', 'location') + ->where('node_id', $node->id) + // If you don't cast this to a string you'll end up with a stringified per_page returned in + // the metadata, and then Wings will panic crash as a result. + ->paginate((int) $request->input('per_page', 50)); + + return new ServerConfigurationCollection($servers); + } + + /** + * Resets the state of all servers on the node to be normal. This is triggered + * when Wings restarts and is useful for ensuring that any servers on the node + * do not get incorrectly stuck in installing/restoring from backup states since + * a Wings reboot would completely stop those processes. + * + * @throws \Throwable + */ + public function resetState(Request $request): JsonResponse + { + $node = $request->attributes->get('node'); + + // Get all the servers that are currently marked as restoring from a backup + // on this node that do not have a failed backup tracked in the audit logs table + // as well. + // + // For each of those servers we'll track a new audit log entry to mark them as + // failed and then update them all to be in a valid state. + $servers = Server::query() + ->with([ + 'activity' => fn ($builder) => $builder + ->where('activity_logs.event', 'server:backup.restore-started') + ->latest('timestamp'), + ]) + ->where('node_id', $node->id) + ->where('status', Server::STATUS_RESTORING_BACKUP) + ->get(); + + $this->connection->transaction(function () use ($node, $servers) { + /** @var \Pterodactyl\Models\Server $server */ + foreach ($servers as $server) { + /** @var \Pterodactyl\Models\ActivityLog|null $activity */ + $activity = $server->activity->first(); + if (!is_null($activity)) { + if ($subject = $activity->subjects->where('subject_type', 'backup')->first()) { + // Just create a new audit entry for this event and update the server state + // so that power actions, file management, and backups can resume as normal. + Activity::event('server:backup.restore-failed') + ->subject($server, $subject->subject) + ->property('name', $subject->subject->name) + ->log(); + } + } + } + + // Update any server marked as installing or restoring as being in a normal state + // at this point in the process. + Server::query()->where('node_id', $node->id) + ->whereIn('status', [Server::STATUS_INSTALLING, Server::STATUS_RESTORING_BACKUP]) + ->update(['status' => null]); + }); + + return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); + } +} diff --git a/app/Http/Controllers/Api/Remote/Servers/ServerInstallController.php b/app/Http/Controllers/Api/Remote/Servers/ServerInstallController.php new file mode 100644 index 0000000..8fcfbe0 --- /dev/null +++ b/app/Http/Controllers/Api/Remote/Servers/ServerInstallController.php @@ -0,0 +1,80 @@ +repository->getByUuid($uuid); + $egg = $server->egg; + + return new JsonResponse([ + 'container_image' => $egg->copy_script_container, + 'entrypoint' => $egg->copy_script_entry, + 'script' => $egg->copy_script_install, + ]); + } + + /** + * Updates the installation state of a server. + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + */ + public function store(InstallationDataRequest $request, string $uuid): JsonResponse + { + $server = $this->repository->getByUuid($uuid); + $status = null; + + // Make sure the type of failure is accurate + if (!$request->boolean('successful')) { + $status = Server::STATUS_INSTALL_FAILED; + + if ($request->boolean('reinstall')) { + $status = Server::STATUS_REINSTALL_FAILED; + } + } + + // Keep the server suspended if it's already suspended + if ($server->status === Server::STATUS_SUSPENDED) { + $status = Server::STATUS_SUSPENDED; + } + + $this->repository->update($server->id, ['status' => $status, 'installed_at' => CarbonImmutable::now()], true, true); + + // If the server successfully installed, fire installed event. + // This logic allows individually disabling install and reinstall notifications separately. + $isInitialInstall = is_null($server->installed_at); + if ($isInitialInstall && config()->get('pterodactyl.email.send_install_notification', true)) { + $this->eventDispatcher->dispatch(new ServerInstalled($server)); + } elseif (!$isInitialInstall && config()->get('pterodactyl.email.send_reinstall_notification', true)) { + $this->eventDispatcher->dispatch(new ServerInstalled($server)); + } + + return new JsonResponse([], Response::HTTP_NO_CONTENT); + } +} diff --git a/app/Http/Controllers/Api/Remote/Servers/ServerTransferController.php b/app/Http/Controllers/Api/Remote/Servers/ServerTransferController.php new file mode 100644 index 0000000..c14ddea --- /dev/null +++ b/app/Http/Controllers/Api/Remote/Servers/ServerTransferController.php @@ -0,0 +1,107 @@ +repository->getByUuid($uuid); + $transfer = $server->transfer; + if (is_null($transfer)) { + throw new ConflictHttpException('Server is not being transferred.'); + } + + return $this->processFailedTransfer($transfer); + } + + /** + * The daemon notifies us about a transfer success. + * + * @throws \Throwable + */ + public function success(string $uuid): JsonResponse + { + $server = $this->repository->getByUuid($uuid); + $transfer = $server->transfer; + if (is_null($transfer)) { + throw new ConflictHttpException('Server is not being transferred.'); + } + + /** @var \Pterodactyl\Models\Server $server */ + $server = $this->connection->transaction(function () use ($server, $transfer) { + $allocations = array_merge([$transfer->old_allocation], $transfer->old_additional_allocations); + + // Remove the old allocations for the server and re-assign the server to the new + // primary allocation and node. + Allocation::query()->whereIn('id', $allocations)->update(['server_id' => null]); + $server->update([ + 'allocation_id' => $transfer->new_allocation, + 'node_id' => $transfer->new_node, + ]); + + $server = $server->fresh(); + $server->transfer->update(['successful' => true]); + + return $server; + }); + + // Delete the server from the old node making sure to point it to the old node so + // that we do not delete it from the new node the server was transferred to. + try { + $this->daemonServerRepository + ->setServer($server) + ->setNode($transfer->oldNode) + ->delete(); + } catch (DaemonConnectionException $exception) { + Log::warning($exception, ['transfer_id' => $server->transfer->id]); + } + + return new JsonResponse([], Response::HTTP_NO_CONTENT); + } + + /** + * Release all the reserved allocations for this transfer and mark it as failed in + * the database. + * + * @throws \Throwable + */ + protected function processFailedTransfer(ServerTransfer $transfer): JsonResponse + { + $this->connection->transaction(function () use (&$transfer) { + $transfer->forceFill(['successful' => false])->saveOrFail(); + + $allocations = array_merge([$transfer->new_allocation], $transfer->new_additional_allocations); + Allocation::query()->whereIn('id', $allocations)->update(['server_id' => null]); + }); + + return new JsonResponse([], Response::HTTP_NO_CONTENT); + } +} diff --git a/app/Http/Controllers/Api/Remote/SftpAuthenticationController.php b/app/Http/Controllers/Api/Remote/SftpAuthenticationController.php new file mode 100644 index 0000000..a360d1d --- /dev/null +++ b/app/Http/Controllers/Api/Remote/SftpAuthenticationController.php @@ -0,0 +1,165 @@ +parseUsername($request->input('username')); + if (empty($connection['server'])) { + throw new BadRequestHttpException('No valid server identifier was included in the request.'); + } + + if ($this->hasTooManyLoginAttempts($request)) { + $seconds = $this->limiter()->availableIn($this->throttleKey($request)); + + throw new TooManyRequestsHttpException($seconds, "Too many login attempts for this account, please try again in $seconds seconds."); + } + + $user = $this->getUser($request, $connection['username']); + $server = $this->getServer($request, $connection['server']); + + if ($request->input('type') !== 'public_key') { + if (!password_verify($request->input('password'), $user->password)) { + Activity::event('auth:sftp.fail')->property('method', 'password')->subject($user)->log(); + + $this->reject($request); + } + } else { + $key = null; + try { + $key = PublicKeyLoader::loadPublicKey(trim($request->input('password'))); + } catch (NoKeyLoadedException) { + // do nothing + } + + if (!$key || !$user->sshKeys()->where('fingerprint', $key->getFingerprint('sha256'))->exists()) { + // We don't log here because of the way the SFTP system works. This endpoint + // will get hit for every key the user provides, which could be 4 or 5. That is + // a lot of unnecessary log noise. + // + // For now, we'll only log failures due to a bad password as those are not likely + // to occur more than once in a session for the user, and are more likely to be of + // value to the end user. + $this->reject($request, is_null($key)); + } + } + + $this->validateSftpAccess($user, $server); + + return new JsonResponse([ + 'user' => $user->uuid, + 'server' => $server->uuid, + 'permissions' => $this->permissions->handle($server, $user), + ]); + } + + /** + * Finds the server being requested and ensures that it belongs to the node this + * request stems from. + */ + protected function getServer(Request $request, string $uuid): Server + { + return Server::query() + ->where(fn ($builder) => $builder->where('uuid', $uuid)->orWhere('uuidShort', $uuid)) + ->where('node_id', $request->attributes->get('node')->id) + ->firstOr(function () use ($request) { + $this->reject($request); + }); + } + + /** + * Finds a user with the given username or increments the login attempts. + */ + protected function getUser(Request $request, string $username): User + { + return User::query()->where('username', $username)->firstOr(function () use ($request) { + $this->reject($request); + }); + } + + /** + * Parses the username provided to the request. + * + * @return array{"username": string, "server": string} + */ + protected function parseUsername(string $value): array + { + // Reverse the string to avoid issues with usernames that contain periods. + $parts = explode('.', strrev($value), 2); + + // Unreverse the strings after parsing them apart. + return [ + 'username' => strrev(array_get($parts, 1)), + 'server' => strrev(array_get($parts, 0)), + ]; + } + + /** + * Rejects the request and increments the login attempts. + */ + protected function reject(Request $request, bool $increment = true): void + { + if ($increment) { + $this->incrementLoginAttempts($request); + } + + throw new HttpForbiddenException('Authorization credentials were not correct, please try again.'); + } + + /** + * Validates that a user should have permission to use SFTP for the given server. + */ + protected function validateSftpAccess(User $user, Server $server): void + { + if (!$user->root_admin && $server->owner_id !== $user->id) { + $permissions = $this->permissions->handle($server, $user); + + if (!in_array(Permission::ACTION_FILE_SFTP, $permissions)) { + Activity::event('server:sftp.denied')->actor($user)->subject($server)->log(); + + throw new HttpForbiddenException('You do not have permission to access SFTP for this server.'); + } + } + + $server->validateCurrentState(); + } + + /** + * Get the throttle key for the given request. + */ + protected function throttleKey(Request $request): string + { + $username = explode('.', strrev($request->input('username', ''))); + + return strtolower(strrev($username[0] ?? '') . '|' . $request->ip()); + } +} diff --git a/app/Http/Controllers/Auth/AbstractLoginController.php b/app/Http/Controllers/Auth/AbstractLoginController.php new file mode 100644 index 0000000..f07282f --- /dev/null +++ b/app/Http/Controllers/Auth/AbstractLoginController.php @@ -0,0 +1,106 @@ +lockoutTime = config('auth.lockout.time'); + $this->maxLoginAttempts = config('auth.lockout.attempts'); + $this->auth = Container::getInstance()->make(AuthManager::class); + } + + /** + * Get the failed login response instance. + * + * @throws \Pterodactyl\Exceptions\DisplayException + */ + protected function sendFailedLoginResponse(Request $request, Authenticatable $user = null, string $message = null) + { + $this->incrementLoginAttempts($request); + $this->fireFailedLoginEvent($user, [ + $this->getField($request->input('user')) => $request->input('user'), + ]); + + if ($request->route()->named('auth.login-checkpoint')) { + throw new DisplayException($message ?? trans('auth.two_factor.checkpoint_failed')); + } + + throw new DisplayException(trans('auth.failed')); + } + + /** + * Send the response after the user was authenticated. + */ + protected function sendLoginResponse(User $user, Request $request): JsonResponse + { + $request->session()->remove('auth_confirmation_token'); + $request->session()->regenerate(); + + $this->clearLoginAttempts($request); + + $this->auth->guard()->login($user, true); + + Event::dispatch(new DirectLogin($user, true)); + + return new JsonResponse([ + 'data' => [ + 'complete' => true, + 'intended' => $this->redirectPath(), + 'user' => $user->toVueObject(), + ], + ]); + } + + /** + * Determine if the user is logging in using an email or username. + */ + protected function getField(string $input = null): string + { + return ($input && str_contains($input, '@')) ? 'email' : 'username'; + } + + /** + * Fire a failed login event. + */ + protected function fireFailedLoginEvent(Authenticatable $user = null, array $credentials = []) + { + Event::dispatch(new Failed('auth', $user, $credentials)); + } +} diff --git a/app/Http/Controllers/Auth/ForgotPasswordController.php b/app/Http/Controllers/Auth/ForgotPasswordController.php new file mode 100644 index 0000000..d67b0de --- /dev/null +++ b/app/Http/Controllers/Auth/ForgotPasswordController.php @@ -0,0 +1,40 @@ +ip(), $request->input('email'))); + + return $this->sendResetLinkResponse($request, Password::RESET_LINK_SENT); + } + + /** + * Get the response for a successful password reset link. + * + * @param string $response + */ + protected function sendResetLinkResponse(Request $request, $response): JsonResponse + { + return response()->json([ + 'status' => trans($response), + ]); + } +} diff --git a/app/Http/Controllers/Auth/LoginCheckpointController.php b/app/Http/Controllers/Auth/LoginCheckpointController.php new file mode 100644 index 0000000..af05c55 --- /dev/null +++ b/app/Http/Controllers/Auth/LoginCheckpointController.php @@ -0,0 +1,131 @@ +hasTooManyLoginAttempts($request)) { + $this->sendLockoutResponse($request); + } + + $details = $request->session()->get('auth_confirmation_token'); + if (!$this->hasValidSessionData($details)) { + $this->sendFailedLoginResponse($request, null, self::TOKEN_EXPIRED_MESSAGE); + } + + if (!hash_equals($request->input('confirmation_token') ?? '', $details['token_value'])) { + $this->sendFailedLoginResponse($request); + } + + try { + /** @var \Pterodactyl\Models\User $user */ + $user = User::query()->findOrFail($details['user_id']); + } catch (ModelNotFoundException) { + $this->sendFailedLoginResponse($request, null, self::TOKEN_EXPIRED_MESSAGE); + } + + // Recovery tokens go through a slightly different pathway for usage. + if (!is_null($recoveryToken = $request->input('recovery_token'))) { + if ($this->isValidRecoveryToken($user, $recoveryToken)) { + Event::dispatch(new ProvidedAuthenticationToken($user, true)); + + return $this->sendLoginResponse($user, $request); + } + } else { + $decrypted = $this->encrypter->decrypt($user->totp_secret); + + if ($this->google2FA->verifyKey($decrypted, (string) $request->input('authentication_code') ?? '', config('pterodactyl.auth.2fa.window'))) { + Event::dispatch(new ProvidedAuthenticationToken($user)); + + return $this->sendLoginResponse($user, $request); + } + } + + $this->sendFailedLoginResponse($request, $user, !empty($recoveryToken) ? 'The recovery token provided is not valid.' : null); + } + + /** + * Determines if a given recovery token is valid for the user account. If we find a matching token + * it will be deleted from the database. + * + * @throws \Exception + */ + protected function isValidRecoveryToken(User $user, string $value): bool + { + foreach ($user->recoveryTokens as $token) { + if (password_verify($value, $token->token)) { + $token->delete(); + + return true; + } + } + + return false; + } + + /** + * Determines if the data provided from the session is valid or not. This + * will return false if the data is invalid, or if more time has passed than + * was configured when the session was written. + */ + protected function hasValidSessionData(array $data): bool + { + $validator = $this->validation->make($data, [ + 'user_id' => 'required|integer|min:1', + 'token_value' => 'required|string', + 'expires_at' => 'required', + ]); + + if ($validator->fails()) { + return false; + } + + if (!$data['expires_at'] instanceof CarbonInterface) { + return false; + } + + if ($data['expires_at']->isBefore(CarbonImmutable::now())) { + return false; + } + + return true; + } +} diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php new file mode 100644 index 0000000..2dbb34e --- /dev/null +++ b/app/Http/Controllers/Auth/LoginController.php @@ -0,0 +1,84 @@ +view->make('templates/auth.core'); + } + + /** + * Handle a login request to the application. + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Illuminate\Validation\ValidationException + */ + public function login(Request $request): JsonResponse + { + if ($this->hasTooManyLoginAttempts($request)) { + $this->fireLockoutEvent($request); + $this->sendLockoutResponse($request); + } + + try { + $username = $request->input('user'); + + /** @var \Pterodactyl\Models\User $user */ + $user = User::query()->where($this->getField($username), $username)->firstOrFail(); + } catch (ModelNotFoundException) { + $this->sendFailedLoginResponse($request); + } + + // Ensure that the account is using a valid username and password before trying to + // continue. Previously this was handled in the 2FA checkpoint, however that has + // a flaw in which you can discover if an account exists simply by seeing if you + // can proceed to the next step in the login process. + if (!password_verify($request->input('password'), $user->password)) { + $this->sendFailedLoginResponse($request, $user); + } + + if (!$user->use_totp) { + return $this->sendLoginResponse($user, $request); + } + + Activity::event('auth:checkpoint')->withRequestMetadata()->subject($user)->log(); + + $request->session()->put('auth_confirmation_token', [ + 'user_id' => $user->id, + 'token_value' => $token = Str::random(64), + 'expires_at' => CarbonImmutable::now()->addMinutes(5), + ]); + + return new JsonResponse([ + 'data' => [ + 'complete' => false, + 'confirmation_token' => $token, + ], + ]); + } +} diff --git a/app/Http/Controllers/Auth/ResetPasswordController.php b/app/Http/Controllers/Auth/ResetPasswordController.php new file mode 100644 index 0000000..3325a1e --- /dev/null +++ b/app/Http/Controllers/Auth/ResetPasswordController.php @@ -0,0 +1,105 @@ +broker()->reset( + $this->credentials($request), + function ($user, $password) { + $this->resetPassword($user, $password); + } + ); + + // If the password was successfully reset, we will redirect the user back to + // the application's home authenticated view. If there is an error we can + // redirect them back to where they came from with their error message. + if ($response === Password::PASSWORD_RESET) { + return $this->sendResetResponse(); + } + + throw new DisplayException(trans($response)); + } + + /** + * Reset the given user's password. If the user has two-factor authentication enabled on their + * account do not automatically log them in. In those cases, send the user back to the login + * form with a note telling them their password was changed and to log back in. + * + * @param \Illuminate\Contracts\Auth\CanResetPassword|\Pterodactyl\Models\User $user + * @param string $password + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + protected function resetPassword($user, $password) + { + $user = $this->userRepository->update($user->id, [ + 'password' => $this->hasher->make($password), + $user->getRememberTokenName() => Str::random(60), + ]); + + $this->dispatcher->dispatch(new PasswordReset($user)); + + // If the user is not using 2FA log them in, otherwise skip this step and force a + // fresh login where they'll be prompted to enter a token. + if (!$user->use_totp) { + $this->guard()->login($user); + } + + $this->hasTwoFactor = $user->use_totp; + } + + /** + * Send a successful password reset response back to the callee. + */ + protected function sendResetResponse(): JsonResponse + { + return response()->json([ + 'success' => true, + 'redirect_to' => $this->redirectTo, + 'send_to_login' => $this->hasTwoFactor, + ]); + } +} diff --git a/app/Http/Controllers/Base/IndexController.php b/app/Http/Controllers/Base/IndexController.php new file mode 100644 index 0000000..fecaa91 --- /dev/null +++ b/app/Http/Controllers/Base/IndexController.php @@ -0,0 +1,28 @@ +view->make('templates/base.core'); + } +} diff --git a/app/Http/Controllers/Base/LocaleController.php b/app/Http/Controllers/Base/LocaleController.php new file mode 100644 index 0000000..16a3100 --- /dev/null +++ b/app/Http/Controllers/Base/LocaleController.php @@ -0,0 +1,64 @@ +loader = $translator->getLoader(); + } + + /** + * Returns translation data given a specific locale and namespace. + */ + public function __invoke(LocaleRequest $request): JsonResponse + { + $locale = $request->input('locale'); + $namespace = $request->input('namespace'); + $response[$locale][$namespace] = $this->i18n($this->loader->load($locale, $namespace)); + + return new JsonResponse($response, 200, [ + // Cache this in the browser for an hour, and allow the browser to use a stale + // cache for up to a day after it was created while it fetches an updated set + // of translation keys. + 'Cache-Control' => 'public, max-age=3600, stale-while-revalidate=86400', + 'ETag' => md5(json_encode($response, JSON_THROW_ON_ERROR)), + ]); + } + + /** + * Convert standard Laravel translation keys that look like ":foo" + * into key structures that are supported by the front-end i18n + * library, like "{{foo}}". + */ + protected function i18n(array $data): array + { + foreach ($data as $key => $value) { + if (is_array($value)) { + $data[$key] = $this->i18n($value); + } else { + // Find a Laravel style translation replacement in the string and replace it with + // one that the front-end is able to use. This won't always be present, especially + // for complex strings or things where we'd never have a backend component anyways. + // + // For example: + // "Hello :name, the :notifications.0.title notification needs :count actions :foo.0.bar." + // + // Becomes: + // "Hello {{name}}, the {{notifications.0.title}} notification needs {{count}} actions {{foo.0.bar}}." + $data[$key] = preg_replace('/:([\w.-]+\w)([^\w:]?|$)/m', '{{$1}}$2', $value); + } + } + + return $data; + } +} diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php new file mode 100644 index 0000000..ee7af2f --- /dev/null +++ b/app/Http/Controllers/Controller.php @@ -0,0 +1,15 @@ + [ + EncryptCookies::class, + AddQueuedCookiesToResponse::class, + StartSession::class, + ShareErrorsFromSession::class, + VerifyCsrfToken::class, + SubstituteBindings::class, + LanguageMiddleware::class, + ], + 'api' => [ + EnsureStatefulRequests::class, + 'auth:sanctum', + IsValidJson::class, + TrackAPIKey::class, + RequireTwoFactorAuthentication::class, + AuthenticateIPAccess::class, + ], + 'application-api' => [ + SubstituteBindings::class, + AuthenticateApplicationUser::class, + ], + 'client-api' => [ + SubstituteClientBindings::class, + RequireClientApiKey::class, + ], + 'daemon' => [ + SubstituteBindings::class, + DaemonAuthenticate::class, + ], + ]; + + /** + * The application's route middleware. + */ + protected $middlewareAliases = [ + 'auth' => Authenticate::class, + 'auth.basic' => AuthenticateWithBasicAuth::class, + 'auth.session' => AuthenticateSession::class, + 'guest' => RedirectIfAuthenticated::class, + 'csrf' => VerifyCsrfToken::class, + 'throttle' => ThrottleRequests::class, + 'can' => Authorize::class, + 'bindings' => SubstituteBindings::class, + 'recaptcha' => VerifyReCaptcha::class, + 'node.maintenance' => MaintenanceMiddleware::class, + ]; +} diff --git a/app/Http/Middleware/Activity/AccountSubject.php b/app/Http/Middleware/Activity/AccountSubject.php new file mode 100644 index 0000000..fba1ea0 --- /dev/null +++ b/app/Http/Middleware/Activity/AccountSubject.php @@ -0,0 +1,21 @@ +user()); + LogTarget::setSubject($request->user()); + + return $next($request); + } +} diff --git a/app/Http/Middleware/Activity/ServerSubject.php b/app/Http/Middleware/Activity/ServerSubject.php new file mode 100644 index 0000000..c4dd160 --- /dev/null +++ b/app/Http/Middleware/Activity/ServerSubject.php @@ -0,0 +1,29 @@ +route()->parameter('server'); + if ($server instanceof Server) { + LogTarget::setActor($request->user()); + LogTarget::setSubject($server); + } + + return $next($request); + } +} diff --git a/app/Http/Middleware/Activity/TrackAPIKey.php b/app/Http/Middleware/Activity/TrackAPIKey.php new file mode 100644 index 0000000..78aa705 --- /dev/null +++ b/app/Http/Middleware/Activity/TrackAPIKey.php @@ -0,0 +1,27 @@ +user()) { + $token = $request->user()->currentAccessToken(); + + LogTarget::setApiKeyId($token instanceof ApiKey ? $token->id : null); + } + + return $next($request); + } +} diff --git a/app/Http/Middleware/Admin/Servers/ServerInstalled.php b/app/Http/Middleware/Admin/Servers/ServerInstalled.php new file mode 100644 index 0000000..ae99b89 --- /dev/null +++ b/app/Http/Middleware/Admin/Servers/ServerInstalled.php @@ -0,0 +1,31 @@ +route()->parameter('server'); + + if (!$server instanceof Server) { + throw new NotFoundHttpException('No server resource was located in the request parameters.'); + } + + if (!$server->isInstalled()) { + throw new HttpException(Response::HTTP_FORBIDDEN, 'Access to this resource is not allowed due to the current installation state.'); + } + + return $next($request); + } +} diff --git a/app/Http/Middleware/AdminAuthenticate.php b/app/Http/Middleware/AdminAuthenticate.php new file mode 100644 index 0000000..4c7f48b --- /dev/null +++ b/app/Http/Middleware/AdminAuthenticate.php @@ -0,0 +1,23 @@ +user() || !$request->user()->root_admin) { + throw new AccessDeniedHttpException(); + } + + return $next($request); + } +} diff --git a/app/Http/Middleware/Api/Application/AuthenticateApplicationUser.php b/app/Http/Middleware/Api/Application/AuthenticateApplicationUser.php new file mode 100644 index 0000000..e6f83b4 --- /dev/null +++ b/app/Http/Middleware/Api/Application/AuthenticateApplicationUser.php @@ -0,0 +1,24 @@ +user(); + if (!$user || !$user->root_admin) { + throw new AccessDeniedHttpException('This account does not have permission to access the API.'); + } + + return $next($request); + } +} diff --git a/app/Http/Middleware/Api/AuthenticateIPAccess.php b/app/Http/Middleware/Api/AuthenticateIPAccess.php new file mode 100644 index 0000000..b8a9937 --- /dev/null +++ b/app/Http/Middleware/Api/AuthenticateIPAccess.php @@ -0,0 +1,48 @@ +user()->currentAccessToken(); + + // If this is a stateful request just push the request through to the next + // middleware in the stack, there is nothing we need to explicitly check. If + // this is a valid API Key, but there is no allowed IP restriction, also pass + // the request through. + if ($token instanceof TransientToken || empty($token->allowed_ips)) { + return $next($request); + } + + $find = new IP($request->ip()); + foreach ($token->allowed_ips as $ip) { + if (Range::parse($ip)->contains($find)) { + return $next($request); + } + } + + Activity::event('auth:ip-blocked') + ->actor($request->user()) + ->subject($request->user(), $token) + ->property('identifier', $token->identifier) + ->log(); + + throw new AccessDeniedHttpException('This IP address (' . $request->ip() . ') does not have permission to access the API using these credentials.'); + } +} diff --git a/app/Http/Middleware/Api/Client/RequireClientApiKey.php b/app/Http/Middleware/Api/Client/RequireClientApiKey.php new file mode 100644 index 0000000..1ddd0f1 --- /dev/null +++ b/app/Http/Middleware/Api/Client/RequireClientApiKey.php @@ -0,0 +1,25 @@ +user()->currentAccessToken(); + + if ($token instanceof ApiKey && $token->key_type === ApiKey::TYPE_APPLICATION) { + throw new AccessDeniedHttpException('You are attempting to use an application API key on an endpoint that requires a client API key.'); + } + + return $next($request); + } +} diff --git a/app/Http/Middleware/Api/Client/Server/AuthenticateServerAccess.php b/app/Http/Middleware/Api/Client/Server/AuthenticateServerAccess.php new file mode 100644 index 0000000..f60b696 --- /dev/null +++ b/app/Http/Middleware/Api/Client/Server/AuthenticateServerAccess.php @@ -0,0 +1,68 @@ +user(); + $server = $request->route()->parameter('server'); + + if (!$server instanceof Server) { + throw new NotFoundHttpException(trans('exceptions.api.resource_not_found')); + } + + // At the very least, ensure that the user trying to make this request is the + // server owner, a subuser, or a root admin. We'll leave it up to the controllers + // to authenticate more detailed permissions if needed. + if ($user->id !== $server->owner_id && !$user->root_admin) { + // Check for subuser status. + if (!$server->subusers->contains('user_id', $user->id)) { + throw new NotFoundHttpException(trans('exceptions.api.resource_not_found')); + } + } + + try { + $server->validateCurrentState(); + } catch (ServerStateConflictException $exception) { + // Still allow users to get information about their server if it is installing or + // being transferred. + if (!$request->routeIs('api:client:server.view')) { + if (($server->isSuspended() || $server->node->isUnderMaintenance()) && !$request->routeIs('api:client:server.resources')) { + throw $exception; + } + if (!$user->root_admin || !$request->routeIs($this->except)) { + throw $exception; + } + } + } + + $request->attributes->set('server', $server); + + return $next($request); + } +} diff --git a/app/Http/Middleware/Api/Client/Server/ResourceBelongsToServer.php b/app/Http/Middleware/Api/Client/Server/ResourceBelongsToServer.php new file mode 100644 index 0000000..1157ddc --- /dev/null +++ b/app/Http/Middleware/Api/Client/Server/ResourceBelongsToServer.php @@ -0,0 +1,86 @@ +route()->parameters(); + if (is_null($params) || !$params['server'] instanceof Server) { + throw new \InvalidArgumentException('This middleware cannot be used in a context that is missing a server in the parameters.'); + } + + /** @var \Pterodactyl\Models\Server $server */ + $server = $request->route()->parameter('server'); + $exception = new NotFoundHttpException('The requested resource was not found for this server.'); + foreach ($params as $key => $model) { + // Specifically skip the server, we're just trying to see if all of the + // other resources are assigned to this server. Also skip anything that + // is not currently a Model instance since those will just end up being + // a 404 down the road. + if ($key === 'server' || !$model instanceof Model) { + continue; + } + + switch (get_class($model)) { + // All of these models use "server_id" as the field key for the server + // they are assigned to, so the logic is identical for them all. + case Allocation::class: + case Backup::class: + case Database::class: + case Schedule::class: + case Subuser::class: + if ($model->server_id !== $server->id) { + throw $exception; + } + break; + // Regular users are a special case here as we need to make sure they're + // currently assigned as a subuser on the server. + case User::class: + $subuser = $server->subusers()->where('user_id', $model->id)->first(); + if (is_null($subuser)) { + throw $exception; + } + // This is a special case to avoid an additional query being triggered + // in the underlying logic. + $request->attributes->set('subuser', $subuser); + break; + // Tasks are special since they're (currently) the only item in the API + // that requires something in addition to the server in order to be accessed. + case Task::class: + $schedule = $request->route()->parameter('schedule'); + if ($model->schedule_id !== $schedule->id || $schedule->server_id !== $server->id) { + throw $exception; + } + break; + default: + // Don't return a 404 here since we want to make sure no one relies + // on this middleware in a context in which it will not work. Fail safe. + throw new \InvalidArgumentException('There is no handler configured for a resource of this type: ' . get_class($model)); + } + } + + return $next($request); + } +} diff --git a/app/Http/Middleware/Api/Client/SubstituteClientBindings.php b/app/Http/Middleware/Api/Client/SubstituteClientBindings.php new file mode 100644 index 0000000..af68146 --- /dev/null +++ b/app/Http/Middleware/Api/Client/SubstituteClientBindings.php @@ -0,0 +1,33 @@ +router->bind('server', function ($value) { + return Server::query()->where(strlen($value) === 8 ? 'uuidShort' : 'uuid', $value)->firstOrFail(); + }); + + $this->router->bind('user', function ($value, $route) { + /** @var \Pterodactyl\Models\Subuser $match */ + $match = $route->parameter('server') + ->subusers() + ->whereRelation('user', 'uuid', '=', $value) + ->firstOrFail(); + + return $match->user; + }); + + return parent::handle($request, $next); + } +} diff --git a/app/Http/Middleware/Api/Daemon/DaemonAuthenticate.php b/app/Http/Middleware/Api/Daemon/DaemonAuthenticate.php new file mode 100644 index 0000000..6e5ae2a --- /dev/null +++ b/app/Http/Middleware/Api/Daemon/DaemonAuthenticate.php @@ -0,0 +1,67 @@ +route()->getName(), $this->except)) { + return $next($request); + } + + if (is_null($bearer = $request->bearerToken())) { + throw new HttpException(401, 'Access to this endpoint must include an Authorization header.', null, ['WWW-Authenticate' => 'Bearer']); + } + + $parts = explode('.', $bearer); + // Ensure that all of the correct parts are provided in the header. + if (count($parts) !== 2 || empty($parts[0]) || empty($parts[1])) { + throw new BadRequestHttpException('The Authorization header provided was not in a valid format.'); + } + + try { + /** @var \Pterodactyl\Models\Node $node */ + $node = $this->repository->findFirstWhere([ + 'daemon_token_id' => $parts[0], + ]); + + if (hash_equals((string) $this->encrypter->decrypt($node->daemon_token), $parts[1])) { + $request->attributes->set('node', $node); + + return $next($request); + } + } catch (RecordNotFoundException $exception) { + // Do nothing, we don't want to expose a node not existing at all. + } + + throw new AccessDeniedHttpException('You are not authorized to access this resource.'); + } +} diff --git a/app/Http/Middleware/Api/IsValidJson.php b/app/Http/Middleware/Api/IsValidJson.php new file mode 100644 index 0000000..95101a1 --- /dev/null +++ b/app/Http/Middleware/Api/IsValidJson.php @@ -0,0 +1,27 @@ +isJson() && !empty($request->getContent())) { + try { + json_decode($request->getContent(), true, 512, JSON_THROW_ON_ERROR); + } catch (\JsonException $exception) { + throw new BadRequestHttpException('The JSON data passed in the request appears to be malformed: ' . $exception->getMessage()); + } + } + + return $next($request); + } +} diff --git a/app/Http/Middleware/EncryptCookies.php b/app/Http/Middleware/EncryptCookies.php new file mode 100644 index 0000000..1ac425a --- /dev/null +++ b/app/Http/Middleware/EncryptCookies.php @@ -0,0 +1,13 @@ +hasCookie(config('session.cookie')); + } +} diff --git a/app/Http/Middleware/LanguageMiddleware.php b/app/Http/Middleware/LanguageMiddleware.php new file mode 100644 index 0000000..e98ad28 --- /dev/null +++ b/app/Http/Middleware/LanguageMiddleware.php @@ -0,0 +1,26 @@ +app->setLocale($request->user()->language ?? config('app.locale', 'en')); + + return $next($request); + } +} diff --git a/app/Http/Middleware/MaintenanceMiddleware.php b/app/Http/Middleware/MaintenanceMiddleware.php new file mode 100644 index 0000000..61247b4 --- /dev/null +++ b/app/Http/Middleware/MaintenanceMiddleware.php @@ -0,0 +1,32 @@ +attributes->get('server'); + $node = $server->getRelation('node'); + + if ($node->maintenance_mode) { + return $this->response->view('errors.maintenance'); + } + + return $next($request); + } +} diff --git a/app/Http/Middleware/RedirectIfAuthenticated.php b/app/Http/Middleware/RedirectIfAuthenticated.php new file mode 100644 index 0000000..ad3eecd --- /dev/null +++ b/app/Http/Middleware/RedirectIfAuthenticated.php @@ -0,0 +1,28 @@ +authManager->guard($guard)->check()) { + return redirect()->route('index'); + } + + return $next($request); + } +} diff --git a/app/Http/Middleware/RequireTwoFactorAuthentication.php b/app/Http/Middleware/RequireTwoFactorAuthentication.php new file mode 100644 index 0000000..090e146 --- /dev/null +++ b/app/Http/Middleware/RequireTwoFactorAuthentication.php @@ -0,0 +1,67 @@ +user(); + $uri = rtrim($request->getRequestUri(), '/') . '/'; + $current = $request->route()->getName(); + + if (!$user || Str::startsWith($uri, ['/auth/']) || Str::startsWith($current, ['auth.', 'account.'])) { + return $next($request); + } + + $level = (int) config('pterodactyl.auth.2fa_required'); + // If this setting is not configured, or the user is already using 2FA then we can just + // send them right through, nothing else needs to be checked. + // + // If the level is set as admin and the user is not an admin, pass them through as well. + if ($level === self::LEVEL_NONE || $user->use_totp) { + return $next($request); + } elseif ($level === self::LEVEL_ADMIN && !$user->root_admin) { + return $next($request); + } + + // For API calls return an exception which gets rendered nicely in the API response. + if ($request->isJson() || Str::startsWith($uri, '/api/')) { + throw new TwoFactorAuthRequiredException(); + } + + $this->alert->danger(trans('auth.2fa_must_be_enabled'))->flash(); + + return redirect()->to($this->redirectRoute); + } +} diff --git a/app/Http/Middleware/TrimStrings.php b/app/Http/Middleware/TrimStrings.php new file mode 100644 index 0000000..af5382e --- /dev/null +++ b/app/Http/Middleware/TrimStrings.php @@ -0,0 +1,16 @@ +config->get('recaptcha.enabled')) { + return $next($request); + } + + if ($request->filled('g-recaptcha-response')) { + $client = new Client(); + $res = $client->post($this->config->get('recaptcha.domain'), [ + 'form_params' => [ + 'secret' => $this->config->get('recaptcha.secret_key'), + 'response' => $request->input('g-recaptcha-response'), + ], + ]); + + if ($res->getStatusCode() === 200) { + $result = json_decode($res->getBody()); + + if ($result->success && (!$this->config->get('recaptcha.verify_domain') || $this->isResponseVerified($result, $request))) { + return $next($request); + } + } + } + + $this->dispatcher->dispatch( + new FailedCaptcha( + $request->ip(), + !empty($result) ? ($result->hostname ?? null) : null + ) + ); + + throw new HttpException(Response::HTTP_BAD_REQUEST, 'Failed to validate reCAPTCHA data.'); + } + + /** + * Determine if the response from the recaptcha servers was valid. + */ + private function isResponseVerified(\stdClass $result, Request $request): bool + { + if (!$this->config->get('recaptcha.verify_domain')) { + return false; + } + + $url = parse_url($request->url()); + + return $result->hostname === array_get($url, 'host'); + } +} diff --git a/app/Http/Requests/Admin/AdminFormRequest.php b/app/Http/Requests/Admin/AdminFormRequest.php new file mode 100644 index 0000000..8597e1d --- /dev/null +++ b/app/Http/Requests/Admin/AdminFormRequest.php @@ -0,0 +1,35 @@ +user())) { + return false; + } + + return (bool) $this->user()->root_admin; + } + + /** + * Return only the fields that we are interested in from the request. + * This will include empty fields as a null value. + */ + public function normalize(array $only = null): array + { + return $this->only($only ?? array_keys($this->rules())); + } +} diff --git a/app/Http/Requests/Admin/Api/StoreApplicationApiKeyRequest.php b/app/Http/Requests/Admin/Api/StoreApplicationApiKeyRequest.php new file mode 100644 index 0000000..f451239 --- /dev/null +++ b/app/Http/Requests/Admin/Api/StoreApplicationApiKeyRequest.php @@ -0,0 +1,37 @@ +mapWithKeys(function ($resource) use ($modelRules) { + return [AdminAcl::COLUMN_IDENTIFIER . $resource => $modelRules['r_' . $resource]]; + })->merge(['memo' => $modelRules['memo']])->toArray(); + } + + public function attributes(): array + { + return [ + 'memo' => 'Description', + ]; + } + + public function getKeyPermissions(): array + { + return collect($this->validated())->filter(function ($value, $key) { + return substr($key, 0, strlen(AdminAcl::COLUMN_IDENTIFIER)) === AdminAcl::COLUMN_IDENTIFIER; + })->toArray(); + } +} diff --git a/app/Http/Requests/Admin/BaseFormRequest.php b/app/Http/Requests/Admin/BaseFormRequest.php new file mode 100644 index 0000000..cd2c78e --- /dev/null +++ b/app/Http/Requests/Admin/BaseFormRequest.php @@ -0,0 +1,13 @@ + 'required|between:1,256', + ]; + } +} diff --git a/app/Http/Requests/Admin/DatabaseHostFormRequest.php b/app/Http/Requests/Admin/DatabaseHostFormRequest.php new file mode 100644 index 0000000..2e58147 --- /dev/null +++ b/app/Http/Requests/Admin/DatabaseHostFormRequest.php @@ -0,0 +1,30 @@ +method() !== 'POST') { + return DatabaseHost::getRulesForUpdate($this->route()->parameter('host')); + } + + return DatabaseHost::getRules(); + } + + /** + * Modify submitted data before it is passed off to the validator. + */ + protected function getValidatorInstance(): Validator + { + if (!$this->filled('node_id')) { + $this->merge(['node_id' => null]); + } + + return parent::getValidatorInstance(); + } +} diff --git a/app/Http/Requests/Admin/Egg/EggFormRequest.php b/app/Http/Requests/Admin/Egg/EggFormRequest.php new file mode 100644 index 0000000..0a88f5a --- /dev/null +++ b/app/Http/Requests/Admin/Egg/EggFormRequest.php @@ -0,0 +1,47 @@ + 'required|string|max:191', + 'description' => 'nullable|string', + 'docker_images' => ['required', 'string', 'regex:/^[\w#\.\/\- ]*\|*[\w\.\/\-:@ ]*$/im'], + 'force_outgoing_ip' => 'sometimes|boolean', + 'file_denylist' => 'array', + 'startup' => 'required|string', + 'config_from' => 'sometimes|bail|nullable|numeric', + 'config_stop' => 'required_without:config_from|nullable|string|max:191', + 'config_startup' => 'required_without:config_from|nullable|json', + 'config_logs' => 'required_without:config_from|nullable|json', + 'config_files' => 'required_without:config_from|nullable|json', + ]; + + if ($this->method() === 'POST') { + $rules['nest_id'] = 'required|numeric|exists:nests,id'; + } + + return $rules; + } + + public function withValidator($validator) + { + $validator->sometimes('config_from', 'exists:eggs,id', function () { + return (int) $this->input('config_from') !== 0; + }); + } + + public function validated($key = null, $default = null): array + { + $data = parent::validated(); + + return array_merge($data, [ + 'force_outgoing_ip' => array_get($data, 'force_outgoing_ip', false), + ]); + } +} diff --git a/app/Http/Requests/Admin/Egg/EggImportFormRequest.php b/app/Http/Requests/Admin/Egg/EggImportFormRequest.php new file mode 100644 index 0000000..7718480 --- /dev/null +++ b/app/Http/Requests/Admin/Egg/EggImportFormRequest.php @@ -0,0 +1,21 @@ + 'bail|required|file|max:1000|mimetypes:application/json,text/plain', + ]; + + if ($this->method() !== 'PUT') { + $rules['import_to_nest'] = 'bail|required|integer|exists:nests,id'; + } + + return $rules; + } +} diff --git a/app/Http/Requests/Admin/Egg/EggScriptFormRequest.php b/app/Http/Requests/Admin/Egg/EggScriptFormRequest.php new file mode 100644 index 0000000..b93a63c --- /dev/null +++ b/app/Http/Requests/Admin/Egg/EggScriptFormRequest.php @@ -0,0 +1,22 @@ + 'sometimes|nullable|string', + 'script_is_privileged' => 'sometimes|required|boolean', + 'script_entry' => 'sometimes|required|string', + 'script_container' => 'sometimes|required|string', + 'copy_script_from' => 'sometimes|nullable|numeric', + ]; + } +} diff --git a/app/Http/Requests/Admin/Egg/EggVariableFormRequest.php b/app/Http/Requests/Admin/Egg/EggVariableFormRequest.php new file mode 100644 index 0000000..d232254 --- /dev/null +++ b/app/Http/Requests/Admin/Egg/EggVariableFormRequest.php @@ -0,0 +1,24 @@ + 'required|string|min:1|max:191', + 'description' => 'sometimes|nullable|string', + 'env_variable' => 'required|regex:/^[\w]{1,191}$/|notIn:' . EggVariable::RESERVED_ENV_NAMES, + 'options' => 'sometimes|required|array', + 'rules' => 'bail|required|string', + 'default_value' => 'present', + ]; + } +} diff --git a/app/Http/Requests/Admin/LocationFormRequest.php b/app/Http/Requests/Admin/LocationFormRequest.php new file mode 100644 index 0000000..b10e304 --- /dev/null +++ b/app/Http/Requests/Admin/LocationFormRequest.php @@ -0,0 +1,20 @@ +method() === 'PATCH') { + return Location::getRulesForUpdate($this->route()->parameter('location')->id); + } + + return Location::getRules(); + } +} diff --git a/app/Http/Requests/Admin/MountFormRequest.php b/app/Http/Requests/Admin/MountFormRequest.php new file mode 100644 index 0000000..074ea4a --- /dev/null +++ b/app/Http/Requests/Admin/MountFormRequest.php @@ -0,0 +1,20 @@ +method() === 'PATCH') { + return Mount::getRulesForUpdate($this->route()->parameter('mount')->id); + } + + return Mount::getRules(); + } +} diff --git a/app/Http/Requests/Admin/Nest/StoreNestFormRequest.php b/app/Http/Requests/Admin/Nest/StoreNestFormRequest.php new file mode 100644 index 0000000..81505de --- /dev/null +++ b/app/Http/Requests/Admin/Nest/StoreNestFormRequest.php @@ -0,0 +1,16 @@ + 'required|string|min:1|max:191|regex:/^[\w\- ]+$/', + 'description' => 'string|nullable', + ]; + } +} diff --git a/app/Http/Requests/Admin/NewUserFormRequest.php b/app/Http/Requests/Admin/NewUserFormRequest.php new file mode 100644 index 0000000..d8c1993 --- /dev/null +++ b/app/Http/Requests/Admin/NewUserFormRequest.php @@ -0,0 +1,28 @@ +only([ + 'email', + 'username', + 'name_first', + 'name_last', + 'password', + 'language', + 'root_admin', + ])->toArray(); + } +} diff --git a/app/Http/Requests/Admin/Node/AllocationAliasFormRequest.php b/app/Http/Requests/Admin/Node/AllocationAliasFormRequest.php new file mode 100644 index 0000000..f388203 --- /dev/null +++ b/app/Http/Requests/Admin/Node/AllocationAliasFormRequest.php @@ -0,0 +1,16 @@ + 'present|nullable|string', + 'allocation_id' => 'required|numeric|exists:allocations,id', + ]; + } +} diff --git a/app/Http/Requests/Admin/Node/AllocationFormRequest.php b/app/Http/Requests/Admin/Node/AllocationFormRequest.php new file mode 100644 index 0000000..641b8d0 --- /dev/null +++ b/app/Http/Requests/Admin/Node/AllocationFormRequest.php @@ -0,0 +1,17 @@ + 'required|string', + 'allocation_alias' => 'sometimes|nullable|string|max:191', + 'allocation_ports' => 'required|array', + ]; + } +} diff --git a/app/Http/Requests/Admin/Node/NodeFormRequest.php b/app/Http/Requests/Admin/Node/NodeFormRequest.php new file mode 100644 index 0000000..f95189f --- /dev/null +++ b/app/Http/Requests/Admin/Node/NodeFormRequest.php @@ -0,0 +1,25 @@ +method() === 'PATCH') { + return Node::getRulesForUpdate($this->route()->parameter('node')); + } + + $data = Node::getRules(); + $data['fqdn'][] = Fqdn::make('scheme'); + + return $data; + } +} diff --git a/app/Http/Requests/Admin/ServerFormRequest.php b/app/Http/Requests/Admin/ServerFormRequest.php new file mode 100644 index 0000000..bf461dc --- /dev/null +++ b/app/Http/Requests/Admin/ServerFormRequest.php @@ -0,0 +1,58 @@ +after(function ($validator) { + $validator->sometimes('node_id', 'required|numeric|bail|exists:nodes,id', function ($input) { + return !$input->auto_deploy; + }); + + $validator->sometimes('allocation_id', [ + 'required', + 'numeric', + 'bail', + Rule::exists('allocations', 'id')->where(function ($query) { + $query->where('node_id', $this->input('node_id')); + $query->whereNull('server_id'); + }), + ], function ($input) { + return !$input->auto_deploy; + }); + + $validator->sometimes('allocation_additional.*', [ + 'sometimes', + 'required', + 'numeric', + Rule::exists('allocations', 'id')->where(function ($query) { + $query->where('node_id', $this->input('node_id')); + $query->whereNull('server_id'); + }), + ], function ($input) { + return !$input->auto_deploy; + }); + }); + } +} diff --git a/app/Http/Requests/Admin/Servers/Databases/StoreServerDatabaseRequest.php b/app/Http/Requests/Admin/Servers/Databases/StoreServerDatabaseRequest.php new file mode 100644 index 0000000..144755d --- /dev/null +++ b/app/Http/Requests/Admin/Servers/Databases/StoreServerDatabaseRequest.php @@ -0,0 +1,31 @@ + [ + 'required', + 'string', + 'min:1', + 'max:24', + Rule::unique('databases')->where(function (Builder $query) { + $query->where('database_host_id', $this->input('database_host_id') ?? 0); + }), + ], + 'max_connections' => 'nullable', + 'remote' => 'required|string|regex:/^[0-9%.]{1,15}$/', + 'database_host_id' => 'required|integer|exists:database_hosts,id', + ]; + } +} diff --git a/app/Http/Requests/Admin/Settings/AdvancedSettingsFormRequest.php b/app/Http/Requests/Admin/Settings/AdvancedSettingsFormRequest.php new file mode 100644 index 0000000..17608d9 --- /dev/null +++ b/app/Http/Requests/Admin/Settings/AdvancedSettingsFormRequest.php @@ -0,0 +1,50 @@ + 'required|in:true,false', + 'recaptcha:secret_key' => 'required|string|max:191', + 'recaptcha:website_key' => 'required|string|max:191', + 'pterodactyl:guzzle:timeout' => 'required|integer|between:1,60', + 'pterodactyl:guzzle:connect_timeout' => 'required|integer|between:1,60', + 'pterodactyl:client_features:allocations:enabled' => 'required|in:true,false', + 'pterodactyl:client_features:allocations:range_start' => [ + 'nullable', + 'required_if:pterodactyl:client_features:allocations:enabled,true', + 'integer', + 'between:1024,65535', + ], + 'pterodactyl:client_features:allocations:range_end' => [ + 'nullable', + 'required_if:pterodactyl:client_features:allocations:enabled,true', + 'integer', + 'between:1024,65535', + 'gt:pterodactyl:client_features:allocations:range_start', + ], + ]; + } + + public function attributes(): array + { + return [ + 'recaptcha:enabled' => 'reCAPTCHA Enabled', + 'recaptcha:secret_key' => 'reCAPTCHA Secret Key', + 'recaptcha:website_key' => 'reCAPTCHA Website Key', + 'pterodactyl:guzzle:timeout' => 'HTTP Request Timeout', + 'pterodactyl:guzzle:connect_timeout' => 'HTTP Connection Timeout', + 'pterodactyl:client_features:allocations:enabled' => 'Auto Create Allocations Enabled', + 'pterodactyl:client_features:allocations:range_start' => 'Starting Port', + 'pterodactyl:client_features:allocations:range_end' => 'Ending Port', + ]; + } +} diff --git a/app/Http/Requests/Admin/Settings/BaseSettingsFormRequest.php b/app/Http/Requests/Admin/Settings/BaseSettingsFormRequest.php new file mode 100644 index 0000000..8a24dd0 --- /dev/null +++ b/app/Http/Requests/Admin/Settings/BaseSettingsFormRequest.php @@ -0,0 +1,30 @@ + 'required|string|max:191', + 'pterodactyl:auth:2fa_required' => 'required|integer|in:0,1,2', + 'app:locale' => ['required', 'string', Rule::in(array_keys($this->getAvailableLanguages()))], + ]; + } + + public function attributes(): array + { + return [ + 'app:name' => 'Company Name', + 'pterodactyl:auth:2fa_required' => 'Require 2-Factor Authentication', + 'app:locale' => 'Default Language', + ]; + } +} diff --git a/app/Http/Requests/Admin/Settings/MailSettingsFormRequest.php b/app/Http/Requests/Admin/Settings/MailSettingsFormRequest.php new file mode 100644 index 0000000..3f014e9 --- /dev/null +++ b/app/Http/Requests/Admin/Settings/MailSettingsFormRequest.php @@ -0,0 +1,40 @@ + 'required|string', + 'mail:mailers:smtp:port' => 'required|integer|between:1,65535', + 'mail:mailers:smtp:encryption' => ['present', Rule::in([null, 'tls', 'ssl'])], + 'mail:mailers:smtp:username' => 'nullable|string|max:191', + 'mail:mailers:smtp:password' => 'nullable|string|max:191', + 'mail:from:address' => 'required|string|email', + 'mail:from:name' => 'nullable|string|max:191', + ]; + } + + /** + * Override the default normalization function for this type of request + * as we need to accept empty values on the keys. + */ + public function normalize(array $only = null): array + { + $keys = array_flip(array_keys($this->rules())); + + if (empty($this->input('mail:mailers:smtp:password'))) { + unset($keys['mail:mailers:smtp:password']); + } + + return $this->only(array_flip($keys)); + } +} diff --git a/app/Http/Requests/Admin/UserFormRequest.php b/app/Http/Requests/Admin/UserFormRequest.php new file mode 100644 index 0000000..ae5b5f3 --- /dev/null +++ b/app/Http/Requests/Admin/UserFormRequest.php @@ -0,0 +1,28 @@ +route()->parameter('user')) + )->only([ + 'email', + 'username', + 'name_first', + 'name_last', + 'password', + 'language', + 'root_admin', + ])->toArray(); + } +} diff --git a/app/Http/Requests/Api/Application/Allocations/DeleteAllocationRequest.php b/app/Http/Requests/Api/Application/Allocations/DeleteAllocationRequest.php new file mode 100644 index 0000000..6529a9a --- /dev/null +++ b/app/Http/Requests/Api/Application/Allocations/DeleteAllocationRequest.php @@ -0,0 +1,13 @@ + 'required|string', + 'alias' => 'sometimes|nullable|string|max:191', + 'ports' => 'required|array', + 'ports.*' => 'string', + ]; + } + + public function validated($key = null, $default = null): array + { + $data = parent::validated(); + + return [ + 'allocation_ip' => $data['ip'], + 'allocation_ports' => $data['ports'], + 'allocation_alias' => $data['alias'] ?? null, + ]; + } +} diff --git a/app/Http/Requests/Api/Application/ApplicationApiRequest.php b/app/Http/Requests/Api/Application/ApplicationApiRequest.php new file mode 100644 index 0000000..2e0ed13 --- /dev/null +++ b/app/Http/Requests/Api/Application/ApplicationApiRequest.php @@ -0,0 +1,93 @@ +resource)) { + throw new PterodactylException('An ACL resource must be defined on API requests.'); + } + + $token = $this->user()->currentAccessToken(); + if ($token instanceof TransientToken) { + return true; + } + + if ($token->key_type === ApiKey::TYPE_ACCOUNT) { + return true; + } + + return AdminAcl::check($token, $this->resource, $this->permission); + } + + /** + * Default set of rules to apply to API requests. + */ + public function rules(): array + { + return []; + } + + /** + * Helper method allowing a developer to easily hook into this logic without having + * to remember what the method name is called or where to use it. By default this is + * a no-op. + */ + public function withValidator(Validator $validator): void + { + // do nothing + } + + /** + * Returns the named route parameter and asserts that it is a real model that + * exists in the database. + * + * @template T of \Illuminate\Database\Eloquent\Model + * + * @param class-string $expect + * + * @return T + * + * @noinspection PhpDocSignatureInspection + */ + public function parameter(string $key, string $expect) + { + $value = $this->route()->parameter($key); + + Assert::isInstanceOf($value, $expect); + Assert::isInstanceOf($value, Model::class); + Assert::true($value->exists); + + /* @var T $value */ + return $value; + } +} diff --git a/app/Http/Requests/Api/Application/Locations/DeleteLocationRequest.php b/app/Http/Requests/Api/Application/Locations/DeleteLocationRequest.php new file mode 100644 index 0000000..3c41e11 --- /dev/null +++ b/app/Http/Requests/Api/Application/Locations/DeleteLocationRequest.php @@ -0,0 +1,13 @@ +only([ + 'long', + 'short', + ])->toArray(); + } + + /** + * Rename fields to be more clear in error messages. + */ + public function attributes(): array + { + return [ + 'long' => 'Location Description', + 'short' => 'Location Identifier', + ]; + } +} diff --git a/app/Http/Requests/Api/Application/Locations/UpdateLocationRequest.php b/app/Http/Requests/Api/Application/Locations/UpdateLocationRequest.php new file mode 100644 index 0000000..ce42e6f --- /dev/null +++ b/app/Http/Requests/Api/Application/Locations/UpdateLocationRequest.php @@ -0,0 +1,21 @@ +route()->parameter('location')->id; + + return collect(Location::getRulesForUpdate($locationId))->only([ + 'short', + 'long', + ])->toArray(); + } +} diff --git a/app/Http/Requests/Api/Application/Nests/Eggs/GetEggRequest.php b/app/Http/Requests/Api/Application/Nests/Eggs/GetEggRequest.php new file mode 100644 index 0000000..7c6eb54 --- /dev/null +++ b/app/Http/Requests/Api/Application/Nests/Eggs/GetEggRequest.php @@ -0,0 +1,13 @@ + 'integer', + 'memory' => 'required|integer|min:0', + 'disk' => 'required|integer|min:0', + 'location_ids' => 'array', + 'location_ids.*' => 'integer', + ]; + } +} diff --git a/app/Http/Requests/Api/Application/Nodes/GetNodeRequest.php b/app/Http/Requests/Api/Application/Nodes/GetNodeRequest.php new file mode 100644 index 0000000..6d231bc --- /dev/null +++ b/app/Http/Requests/Api/Application/Nodes/GetNodeRequest.php @@ -0,0 +1,7 @@ +only([ + 'public', + 'name', + 'location_id', + 'fqdn', + 'scheme', + 'behind_proxy', + 'maintenance_mode', + 'memory', + 'memory_overallocate', + 'disk', + 'disk_overallocate', + 'upload_size', + 'daemonListen', + 'daemonSFTP', + 'daemonBase', + ])->mapWithKeys(function ($value, $key) { + $key = ($key === 'daemonSFTP') ? 'daemonSftp' : $key; + + return [snake_case($key) => $value]; + })->toArray(); + } + + /** + * Fields to rename for clarity in the API response. + */ + public function attributes(): array + { + return [ + 'daemon_base' => 'Daemon Base Path', + 'upload_size' => 'File Upload Size Limit', + 'location_id' => 'Location', + 'public' => 'Node Visibility', + ]; + } + + /** + * Change the formatting of some data keys in the validated response data + * to match what the application expects in the services. + */ + public function validated($key = null, $default = null): array + { + $response = parent::validated(); + $response['daemonListen'] = $response['daemon_listen']; + $response['daemonSFTP'] = $response['daemon_sftp']; + $response['daemonBase'] = $response['daemon_base'] ?? (new Node())->getAttribute('daemonBase'); + + unset($response['daemon_base'], $response['daemon_listen'], $response['daemon_sftp']); + + return $response; + } +} diff --git a/app/Http/Requests/Api/Application/Nodes/UpdateNodeRequest.php b/app/Http/Requests/Api/Application/Nodes/UpdateNodeRequest.php new file mode 100644 index 0000000..7133bd0 --- /dev/null +++ b/app/Http/Requests/Api/Application/Nodes/UpdateNodeRequest.php @@ -0,0 +1,19 @@ +route()->parameter('node')->id; + + return parent::rules(Node::getRulesForUpdate($node)); + } +} diff --git a/app/Http/Requests/Api/Application/Servers/Databases/GetServerDatabaseRequest.php b/app/Http/Requests/Api/Application/Servers/Databases/GetServerDatabaseRequest.php new file mode 100644 index 0000000..01df4af --- /dev/null +++ b/app/Http/Requests/Api/Application/Servers/Databases/GetServerDatabaseRequest.php @@ -0,0 +1,13 @@ +route()->parameter('server'); + + return [ + 'database' => [ + 'required', + 'alpha_dash', + 'min:1', + 'max:48', + Rule::unique('databases')->where(function (Builder $query) use ($server) { + $query->where('server_id', $server->id)->where('database', $this->databaseName()); + }), + ], + 'remote' => 'required|string|regex:/^[0-9%.]{1,15}$/', + 'host' => 'required|integer|exists:database_hosts,id', + ]; + } + + /** + * Return data formatted in the correct format for the service to consume. + */ + public function validated($key = null, $default = null): array + { + return [ + 'database' => $this->input('database'), + 'remote' => $this->input('remote'), + 'database_host_id' => $this->input('host'), + ]; + } + + /** + * Format error messages in a more understandable format for API output. + */ + public function attributes(): array + { + return [ + 'host' => 'Database Host Server ID', + 'remote' => 'Remote Connection String', + 'database' => 'Database Name', + ]; + } + + /** + * Returns the database name in the expected format. + */ + public function databaseName(): string + { + $server = $this->route()->parameter('server'); + + Assert::isInstanceOf($server, Server::class); + + return DatabaseManagementService::generateUniqueDatabaseName($this->input('database'), $server->id); + } +} diff --git a/app/Http/Requests/Api/Application/Servers/GetExternalServerRequest.php b/app/Http/Requests/Api/Application/Servers/GetExternalServerRequest.php new file mode 100644 index 0000000..50c9dab --- /dev/null +++ b/app/Http/Requests/Api/Application/Servers/GetExternalServerRequest.php @@ -0,0 +1,13 @@ + 'string|max:100', + ]; + } +} diff --git a/app/Http/Requests/Api/Application/Servers/ServerWriteRequest.php b/app/Http/Requests/Api/Application/Servers/ServerWriteRequest.php new file mode 100644 index 0000000..df2d76c --- /dev/null +++ b/app/Http/Requests/Api/Application/Servers/ServerWriteRequest.php @@ -0,0 +1,13 @@ + $rules['external_id'], + 'name' => $rules['name'], + 'description' => array_merge(['nullable'], $rules['description']), + 'user' => $rules['owner_id'], + 'egg' => $rules['egg_id'], + 'docker_image' => $rules['image'], + 'startup' => $rules['startup'], + 'environment' => 'present|array', + 'skip_scripts' => 'sometimes|boolean', + 'oom_disabled' => 'sometimes|boolean', + + // Resource limitations + 'limits' => 'required|array', + 'limits.memory' => $rules['memory'], + 'limits.swap' => $rules['swap'], + 'limits.disk' => $rules['disk'], + 'limits.io' => $rules['io'], + 'limits.threads' => $rules['threads'], + 'limits.cpu' => $rules['cpu'], + + // Application Resource Limits + 'feature_limits' => 'required|array', + 'feature_limits.databases' => $rules['database_limit'], + 'feature_limits.allocations' => $rules['allocation_limit'], + 'feature_limits.backups' => $rules['backup_limit'], + + // Placeholders for rules added in withValidator() function. + 'allocation.default' => '', + 'allocation.additional.*' => '', + + // Automatic deployment rules + 'deploy' => 'sometimes|required|array', + 'deploy.locations' => 'array', + 'deploy.locations.*' => 'integer|min:1', + 'deploy.dedicated_ip' => 'required_with:deploy,boolean', + 'deploy.port_range' => 'array', + 'deploy.port_range.*' => 'string', + + 'start_on_completion' => 'sometimes|boolean', + ]; + } + + /** + * Normalize the data into a format that can be consumed by the service. + */ + public function validated($key = null, $default = null): array + { + $data = parent::validated(); + + return [ + 'external_id' => array_get($data, 'external_id'), + 'name' => array_get($data, 'name'), + 'description' => array_get($data, 'description'), + 'owner_id' => array_get($data, 'user'), + 'egg_id' => array_get($data, 'egg'), + 'image' => array_get($data, 'docker_image'), + 'startup' => array_get($data, 'startup'), + 'environment' => array_get($data, 'environment'), + 'memory' => array_get($data, 'limits.memory'), + 'swap' => array_get($data, 'limits.swap'), + 'disk' => array_get($data, 'limits.disk'), + 'io' => array_get($data, 'limits.io'), + 'cpu' => array_get($data, 'limits.cpu'), + 'threads' => array_get($data, 'limits.threads'), + 'skip_scripts' => array_get($data, 'skip_scripts', false), + 'allocation_id' => array_get($data, 'allocation.default'), + 'allocation_additional' => array_get($data, 'allocation.additional'), + 'start_on_completion' => array_get($data, 'start_on_completion', false), + 'database_limit' => array_get($data, 'feature_limits.databases'), + 'allocation_limit' => array_get($data, 'feature_limits.allocations'), + 'backup_limit' => array_get($data, 'feature_limits.backups'), + 'oom_disabled' => array_get($data, 'oom_disabled'), + ]; + } + + /* + * Run validation after the rules above have been applied. + * + * @param \Illuminate\Validation\Validator $validator + */ + public function withValidator(Validator $validator): void + { + $validator->sometimes('allocation.default', [ + 'required', 'integer', 'bail', + Rule::exists('allocations', 'id')->where(function ($query) { + $query->whereNull('server_id'); + }), + ], function ($input) { + return !$input->deploy; + }); + + $validator->sometimes('allocation.additional.*', [ + 'integer', + Rule::exists('allocations', 'id')->where(function ($query) { + $query->whereNull('server_id'); + }), + ], function ($input) { + return !$input->deploy; + }); + + $validator->sometimes('deploy.locations', 'present', function ($input) { + return $input->deploy; + }); + + $validator->sometimes('deploy.port_range', 'present', function ($input) { + return $input->deploy; + }); + } + + /** + * Return a deployment object that can be passed to the server creation service. + */ + public function getDeploymentObject(): ?DeploymentObject + { + if (is_null($this->input('deploy'))) { + return null; + } + + $object = new DeploymentObject(); + $object->setDedicated($this->input('deploy.dedicated_ip', false)); + $object->setLocations($this->input('deploy.locations', [])); + $object->setPorts($this->input('deploy.port_range', [])); + + return $object; + } +} diff --git a/app/Http/Requests/Api/Application/Servers/UpdateServerBuildConfigurationRequest.php b/app/Http/Requests/Api/Application/Servers/UpdateServerBuildConfigurationRequest.php new file mode 100644 index 0000000..f1c977f --- /dev/null +++ b/app/Http/Requests/Api/Application/Servers/UpdateServerBuildConfigurationRequest.php @@ -0,0 +1,113 @@ +parameter('server', Server::class)); + + return [ + 'allocation' => $rules['allocation_id'], + 'oom_disabled' => $rules['oom_disabled'], + + 'limits' => 'sometimes|array', + 'limits.memory' => $this->requiredToOptional('memory', $rules['memory'], true), + 'limits.swap' => $this->requiredToOptional('swap', $rules['swap'], true), + 'limits.io' => $this->requiredToOptional('io', $rules['io'], true), + 'limits.cpu' => $this->requiredToOptional('cpu', $rules['cpu'], true), + 'limits.threads' => $this->requiredToOptional('threads', $rules['threads'], true), + 'limits.disk' => $this->requiredToOptional('disk', $rules['disk'], true), + + // Legacy rules to maintain backwards compatable API support without requiring + // a major version bump. + // + // @see https://github.com/pterodactyl/panel/issues/1500 + 'memory' => $this->requiredToOptional('memory', $rules['memory']), + 'swap' => $this->requiredToOptional('swap', $rules['swap']), + 'io' => $this->requiredToOptional('io', $rules['io']), + 'cpu' => $this->requiredToOptional('cpu', $rules['cpu']), + 'threads' => $this->requiredToOptional('threads', $rules['threads']), + 'disk' => $this->requiredToOptional('disk', $rules['disk']), + + 'add_allocations' => 'bail|array', + 'add_allocations.*' => 'integer', + 'remove_allocations' => 'bail|array', + 'remove_allocations.*' => 'integer', + + 'feature_limits' => 'required|array', + 'feature_limits.databases' => $rules['database_limit'], + 'feature_limits.allocations' => $rules['allocation_limit'], + 'feature_limits.backups' => $rules['backup_limit'], + ]; + } + + /** + * Convert the allocation field into the expected format for the service handler. + */ + public function validated($key = null, $default = null): array + { + $data = parent::validated(); + + $data['allocation_id'] = $data['allocation']; + $data['database_limit'] = $data['feature_limits']['databases'] ?? null; + $data['allocation_limit'] = $data['feature_limits']['allocations'] ?? null; + $data['backup_limit'] = $data['feature_limits']['backups'] ?? null; + unset($data['allocation'], $data['feature_limits']); + + // Adjust the limits field to match what is expected by the model. + if (!empty($data['limits'])) { + foreach ($data['limits'] as $key => $value) { + $data[$key] = $value; + } + + unset($data['limits']); + } + + return $data; + } + + /** + * Custom attributes to use in error message responses. + */ + public function attributes(): array + { + return [ + 'add_allocations' => 'allocations to add', + 'remove_allocations' => 'allocations to remove', + 'add_allocations.*' => 'allocation to add', + 'remove_allocations.*' => 'allocation to remove', + 'feature_limits.databases' => 'Database Limit', + 'feature_limits.allocations' => 'Allocation Limit', + 'feature_limits.backups' => 'Backup Limit', + ]; + } + + /** + * Converts existing rules for certain limits into a format that maintains backwards + * compatability with the old API endpoint while also supporting a more correct API + * call. + * + * @see https://github.com/pterodactyl/panel/issues/1500 + */ + protected function requiredToOptional(string $field, array $rules, bool $limits = false): array + { + if (!in_array('required', $rules)) { + return $rules; + } + + return (new Collection($rules)) + ->filter(function ($value) { + return $value !== 'required'; + }) + ->prepend($limits ? 'required_with:limits' : 'required_without:limits') + ->toArray(); + } +} diff --git a/app/Http/Requests/Api/Application/Servers/UpdateServerDetailsRequest.php b/app/Http/Requests/Api/Application/Servers/UpdateServerDetailsRequest.php new file mode 100644 index 0000000..aecc1cf --- /dev/null +++ b/app/Http/Requests/Api/Application/Servers/UpdateServerDetailsRequest.php @@ -0,0 +1,49 @@ +parameter('server', Server::class)); + + return [ + 'external_id' => $rules['external_id'], + 'name' => $rules['name'], + 'user' => $rules['owner_id'], + 'description' => array_merge(['nullable'], $rules['description']), + ]; + } + + /** + * Convert the posted data into the correct format that is expected + * by the application. + */ + public function validated($key = null, $default = null): array + { + return [ + 'external_id' => $this->input('external_id'), + 'name' => $this->input('name'), + 'owner_id' => $this->input('user'), + 'description' => $this->input('description'), + ]; + } + + /** + * Rename some attributes in error messages to clarify the field + * being discussed. + */ + public function attributes(): array + { + return [ + 'user' => 'User ID', + 'name' => 'Server Name', + ]; + } +} diff --git a/app/Http/Requests/Api/Application/Servers/UpdateServerStartupRequest.php b/app/Http/Requests/Api/Application/Servers/UpdateServerStartupRequest.php new file mode 100644 index 0000000..985b10a --- /dev/null +++ b/app/Http/Requests/Api/Application/Servers/UpdateServerStartupRequest.php @@ -0,0 +1,43 @@ +parameter('server', Server::class)); + + return [ + 'startup' => $data['startup'], + 'environment' => 'present|array', + 'egg' => $data['egg_id'], + 'image' => $data['image'], + 'skip_scripts' => 'present|boolean', + ]; + } + + /** + * Return the validated data in a format that is expected by the service. + */ + public function validated($key = null, $default = null): array + { + $data = parent::validated(); + + return collect($data)->only(['startup', 'environment', 'skip_scripts'])->merge([ + 'egg_id' => array_get($data, 'egg'), + 'docker_image' => array_get($data, 'image'), + ])->toArray(); + } +} diff --git a/app/Http/Requests/Api/Application/Users/DeleteUserRequest.php b/app/Http/Requests/Api/Application/Users/DeleteUserRequest.php new file mode 100644 index 0000000..5e840a1 --- /dev/null +++ b/app/Http/Requests/Api/Application/Users/DeleteUserRequest.php @@ -0,0 +1,13 @@ +only([ + 'external_id', + 'email', + 'username', + 'password', + 'language', + 'root_admin', + ])->toArray(); + + $response['first_name'] = $rules['name_first']; + $response['last_name'] = $rules['name_last']; + + return $response; + } + + public function validated($key = null, $default = null): array + { + $data = parent::validated(); + + $data['name_first'] = $data['first_name']; + $data['name_last'] = $data['last_name']; + + unset($data['first_name'], $data['last_name']); + + return $data; + } + + /** + * Rename some fields to be more user friendly. + */ + public function attributes(): array + { + return [ + 'external_id' => 'Third Party Identifier', + 'name_first' => 'First Name', + 'name_last' => 'Last Name', + 'root_admin' => 'Root Administrator Status', + ]; + } +} diff --git a/app/Http/Requests/Api/Application/Users/UpdateUserRequest.php b/app/Http/Requests/Api/Application/Users/UpdateUserRequest.php new file mode 100644 index 0000000..fa2e129 --- /dev/null +++ b/app/Http/Requests/Api/Application/Users/UpdateUserRequest.php @@ -0,0 +1,18 @@ +parameter('user', User::class)->id; + + return parent::rules(User::getRulesForUpdate($userId)); + } +} diff --git a/app/Http/Requests/Api/Client/Account/StoreApiKeyRequest.php b/app/Http/Requests/Api/Client/Account/StoreApiKeyRequest.php new file mode 100644 index 0000000..efa1cc3 --- /dev/null +++ b/app/Http/Requests/Api/Client/Account/StoreApiKeyRequest.php @@ -0,0 +1,47 @@ + $rules['memo'], + 'allowed_ips' => [...$rules['allowed_ips'], 'max:50'], + 'allowed_ips.*' => 'string', + ]; + } + + /** + * Check that each of the values entered is actually valid. + */ + public function withValidator(Validator $validator): void + { + $validator->after(function (Validator $validator) { + if (!is_array($ips = $this->input('allowed_ips'))) { + return; + } + + foreach ($ips as $index => $ip) { + $valid = false; + try { + $valid = Range::parse($ip)->valid(); + } catch (\Exception $exception) { + if ($exception->getMessage() !== 'Invalid IP address format') { + throw $exception; + } + } finally { + $validator->errors()->addIf(!$valid, "allowed_ips.{$index}", '"' . $ip . '" is not a valid IP address or CIDR range.'); + } + } + }); + } +} diff --git a/app/Http/Requests/Api/Client/Account/StoreSSHKeyRequest.php b/app/Http/Requests/Api/Client/Account/StoreSSHKeyRequest.php new file mode 100644 index 0000000..3c8cf89 --- /dev/null +++ b/app/Http/Requests/Api/Client/Account/StoreSSHKeyRequest.php @@ -0,0 +1,78 @@ + UserSSHKey::getRulesForField('name'), + 'public_key' => UserSSHKey::getRulesForField('public_key'), + ]; + } + + /** + * Check to see if this SSH key has already been added to the user's account + * and if so return an error. + */ + public function withValidator(Validator $validator): void + { + $validator->after(function () { + try { + $this->key = PublicKeyLoader::loadPublicKey($this->input('public_key')); + } catch (NoKeyLoadedException $exception) { + $this->validator->errors()->add('public_key', 'The public key provided is not valid.'); + + return; + } + + if ($this->key instanceof DSA) { + $this->validator->errors()->add('public_key', 'DSA keys are not supported.'); + } + + if ($this->key instanceof RSA && $this->key->getLength() < 2048) { + $this->validator->errors()->add('public_key', 'RSA keys must be at least 2048 bytes in length.'); + } + + $fingerprint = $this->key->getFingerprint('sha256'); + if ($this->user()->sshKeys()->where('fingerprint', $fingerprint)->exists()) { + $this->validator->errors()->add('public_key', 'The public key provided already exists on your account.'); + } + }); + } + + /** + * Returns the public key but formatted in a consistent manner. + */ + public function getPublicKey(): string + { + return $this->key->toString('PKCS8'); + } + + /** + * Returns the SHA256 fingerprint of the key provided. + */ + public function getKeyFingerprint(): string + { + if (!$this->key) { + throw new \Exception('The public key was not properly loaded for this request.'); + } + + return $this->key->getFingerprint('sha256'); + } +} diff --git a/app/Http/Requests/Api/Client/Account/UpdateEmailRequest.php b/app/Http/Requests/Api/Client/Account/UpdateEmailRequest.php new file mode 100644 index 0000000..6287ba5 --- /dev/null +++ b/app/Http/Requests/Api/Client/Account/UpdateEmailRequest.php @@ -0,0 +1,38 @@ +make(Hasher::class); + + // Verify password matches when changing password or email. + if (!$hasher->check($this->input('password'), $this->user()->password)) { + throw new InvalidPasswordProvidedException(trans('validation.internal.invalid_password')); + } + + return true; + } + + public function rules(): array + { + $rules = User::getRulesForUpdate($this->user()); + + return ['email' => $rules['email']]; + } +} diff --git a/app/Http/Requests/Api/Client/Account/UpdatePasswordRequest.php b/app/Http/Requests/Api/Client/Account/UpdatePasswordRequest.php new file mode 100644 index 0000000..de8215b --- /dev/null +++ b/app/Http/Requests/Api/Client/Account/UpdatePasswordRequest.php @@ -0,0 +1,37 @@ +make(Hasher::class); + + // Verify password matches when changing password or email. + if (!$hasher->check($this->input('current_password'), $this->user()->password)) { + throw new InvalidPasswordProvidedException(trans('validation.internal.invalid_password')); + } + + return true; + } + + public function rules(): array + { + return [ + 'password' => ['required', 'string', 'confirmed', 'min:8'], + ]; + } +} diff --git a/app/Http/Requests/Api/Client/ClientApiRequest.php b/app/Http/Requests/Api/Client/ClientApiRequest.php new file mode 100644 index 0000000..5ae1680 --- /dev/null +++ b/app/Http/Requests/Api/Client/ClientApiRequest.php @@ -0,0 +1,33 @@ +route()->parameter('server'); + + if ($server instanceof Server) { + return $this->user()->can($this->permission(), $server); + } + + // If there is no server available on the reqest, trigger a failure since + // we expect there to be one at this point. + return false; + } + + return true; + } +} diff --git a/app/Http/Requests/Api/Client/GetServersRequest.php b/app/Http/Requests/Api/Client/GetServersRequest.php new file mode 100644 index 0000000..28b4c2d --- /dev/null +++ b/app/Http/Requests/Api/Client/GetServersRequest.php @@ -0,0 +1,11 @@ + 'required|boolean']; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Backups/StoreBackupRequest.php b/app/Http/Requests/Api/Client/Servers/Backups/StoreBackupRequest.php new file mode 100644 index 0000000..2871c03 --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Backups/StoreBackupRequest.php @@ -0,0 +1,23 @@ + 'nullable|string|max:191', + 'is_locked' => 'nullable|boolean', + 'ignored' => 'nullable|string', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Databases/DeleteDatabaseRequest.php b/app/Http/Requests/Api/Client/Servers/Databases/DeleteDatabaseRequest.php new file mode 100644 index 0000000..eb2cbc5 --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Databases/DeleteDatabaseRequest.php @@ -0,0 +1,15 @@ +route()->parameter('server'); + + Assert::isInstanceOf($server, Server::class); + + return [ + 'database' => [ + 'required', + 'alpha_dash', + 'min:3', + 'max:48', + // Yes, I am aware that you could have the same database name across two unique hosts. However, + // I don't really care about that for this validation. We just want to make sure it is unique to + // the server itself. No need for complexity. + Rule::unique('databases')->where(function (Builder $query) use ($server) { + $query->where('server_id', $server->id) + ->where('database', DatabaseManagementService::generateUniqueDatabaseName($this->input('database'), $server->id)); + }), + ], + 'remote' => Database::getRulesForField('remote'), + ]; + } + + public function messages(): array + { + return [ + 'database.unique' => 'The database name you have selected is already in use by this server.', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Files/ChmodFilesRequest.php b/app/Http/Requests/Api/Client/Servers/Files/ChmodFilesRequest.php new file mode 100644 index 0000000..bf94cb6 --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Files/ChmodFilesRequest.php @@ -0,0 +1,25 @@ + 'required|nullable|string', + 'files' => 'required|array', + 'files.*.file' => 'required|string', + 'files.*.mode' => 'required|numeric', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Files/CompressFilesRequest.php b/app/Http/Requests/Api/Client/Servers/Files/CompressFilesRequest.php new file mode 100644 index 0000000..3d6e96a --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Files/CompressFilesRequest.php @@ -0,0 +1,26 @@ + 'sometimes|nullable|string', + 'files' => 'required|array', + 'files.*' => 'string', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Files/CopyFileRequest.php b/app/Http/Requests/Api/Client/Servers/Files/CopyFileRequest.php new file mode 100644 index 0000000..686c23a --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Files/CopyFileRequest.php @@ -0,0 +1,22 @@ + 'required|string', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Files/CreateFolderRequest.php b/app/Http/Requests/Api/Client/Servers/Files/CreateFolderRequest.php new file mode 100644 index 0000000..e465557 --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Files/CreateFolderRequest.php @@ -0,0 +1,25 @@ + 'sometimes|nullable|string', + 'name' => 'required|string', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Files/DecompressFilesRequest.php b/app/Http/Requests/Api/Client/Servers/Files/DecompressFilesRequest.php new file mode 100644 index 0000000..6e24682 --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Files/DecompressFilesRequest.php @@ -0,0 +1,27 @@ + 'sometimes|nullable|string', + 'file' => 'required|string', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Files/DeleteFileRequest.php b/app/Http/Requests/Api/Client/Servers/Files/DeleteFileRequest.php new file mode 100644 index 0000000..10db328 --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Files/DeleteFileRequest.php @@ -0,0 +1,24 @@ + 'required|nullable|string', + 'files' => 'required|array', + 'files.*' => 'string', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Files/DownloadFileRequest.php b/app/Http/Requests/Api/Client/Servers/Files/DownloadFileRequest.php new file mode 100644 index 0000000..c588c9b --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Files/DownloadFileRequest.php @@ -0,0 +1,18 @@ +user()->can('file.read', $this->parameter('server', Server::class)); + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Files/GetFileContentsRequest.php b/app/Http/Requests/Api/Client/Servers/Files/GetFileContentsRequest.php new file mode 100644 index 0000000..ea6ed7b --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Files/GetFileContentsRequest.php @@ -0,0 +1,27 @@ + 'required|string', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Files/ListFilesRequest.php b/app/Http/Requests/Api/Client/Servers/Files/ListFilesRequest.php new file mode 100644 index 0000000..2544314 --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Files/ListFilesRequest.php @@ -0,0 +1,25 @@ + 'sometimes|nullable|string', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Files/PullFileRequest.php b/app/Http/Requests/Api/Client/Servers/Files/PullFileRequest.php new file mode 100644 index 0000000..5f76482 --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Files/PullFileRequest.php @@ -0,0 +1,26 @@ + 'required|string|url', + 'directory' => 'nullable|string', + 'filename' => 'nullable|string', + 'use_header' => 'boolean', + 'foreground' => 'boolean', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Files/RenameFileRequest.php b/app/Http/Requests/Api/Client/Servers/Files/RenameFileRequest.php new file mode 100644 index 0000000..61c4a0c --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Files/RenameFileRequest.php @@ -0,0 +1,30 @@ + 'required|nullable|string', + 'files' => 'required|array', + 'files.*' => 'array', + 'files.*.to' => 'required|string', + 'files.*.from' => 'required|string', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Files/UploadFileRequest.php b/app/Http/Requests/Api/Client/Servers/Files/UploadFileRequest.php new file mode 100644 index 0000000..a591fdf --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Files/UploadFileRequest.php @@ -0,0 +1,14 @@ + 'required|string', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/GetServerRequest.php b/app/Http/Requests/Api/Client/Servers/GetServerRequest.php new file mode 100644 index 0000000..3798d77 --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/GetServerRequest.php @@ -0,0 +1,18 @@ + array_merge($rules['notes'], ['present']), + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Schedules/DeleteScheduleRequest.php b/app/Http/Requests/Api/Client/Servers/Schedules/DeleteScheduleRequest.php new file mode 100644 index 0000000..19d9722 --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Schedules/DeleteScheduleRequest.php @@ -0,0 +1,13 @@ + $rules['name'], + 'is_active' => array_merge(['filled'], $rules['is_active']), + 'minute' => $rules['cron_minute'], + 'hour' => $rules['cron_hour'], + 'day_of_month' => $rules['cron_day_of_month'], + 'day_of_week' => $rules['cron_day_of_week'], + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Schedules/StoreTaskRequest.php b/app/Http/Requests/Api/Client/Servers/Schedules/StoreTaskRequest.php new file mode 100644 index 0000000..5ceb7c8 --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Schedules/StoreTaskRequest.php @@ -0,0 +1,29 @@ + 'required|in:command,power,backup', + 'payload' => 'required_unless:action,backup|string|nullable', + 'time_offset' => 'required|numeric|min:0|max:900', + 'sequence_id' => 'sometimes|required|numeric|min:1', + 'continue_on_failure' => 'sometimes|required|boolean', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Schedules/TriggerScheduleRequest.php b/app/Http/Requests/Api/Client/Servers/Schedules/TriggerScheduleRequest.php new file mode 100644 index 0000000..216274a --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Schedules/TriggerScheduleRequest.php @@ -0,0 +1,19 @@ +route()->parameter('server'); + $schedule = $this->route()->parameter('schedule'); + + // If the schedule does not belong to this server throw a 404 error. Also throw an + // error if the task being requested does not belong to the associated schedule. + if ($server instanceof Server && $schedule instanceof Schedule) { + $task = $this->route()->parameter('task'); + + if ($schedule->server_id !== $server->id || ($task instanceof Task && $task->schedule_id !== $schedule->id)) { + throw new NotFoundHttpException('The requested resource does not exist on the system.'); + } + } + + return true; + } + + public function permission(): string + { + return Permission::ACTION_SCHEDULE_READ; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/SendCommandRequest.php b/app/Http/Requests/Api/Client/Servers/SendCommandRequest.php new file mode 100644 index 0000000..171cd19 --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/SendCommandRequest.php @@ -0,0 +1,27 @@ + 'required|string|min:1', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/SendPowerRequest.php b/app/Http/Requests/Api/Client/Servers/SendPowerRequest.php new file mode 100644 index 0000000..e071388 --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/SendPowerRequest.php @@ -0,0 +1,37 @@ +input('signal')) { + case 'start': + return Permission::ACTION_CONTROL_START; + case 'stop': + case 'kill': + return Permission::ACTION_CONTROL_STOP; + case 'restart': + return Permission::ACTION_CONTROL_RESTART; + } + + return '__invalid'; + } + + /** + * Rules to validate this request against. + */ + public function rules(): array + { + return [ + 'signal' => 'required|string|in:start,stop,restart,kill', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Settings/ReinstallServerRequest.php b/app/Http/Requests/Api/Client/Servers/Settings/ReinstallServerRequest.php new file mode 100644 index 0000000..63f01db --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Settings/ReinstallServerRequest.php @@ -0,0 +1,14 @@ + Server::getRules()['name'], + 'description' => 'string|nullable', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Settings/SetDockerImageRequest.php b/app/Http/Requests/Api/Client/Servers/Settings/SetDockerImageRequest.php new file mode 100644 index 0000000..231fec8 --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Settings/SetDockerImageRequest.php @@ -0,0 +1,30 @@ +route()->parameter('server'); + + Assert::isInstanceOf($server, Server::class); + + return [ + 'docker_image' => ['required', 'string', 'max:191', 'regex:/^[\w#\.\/\- ]*\|*[\w\.\/\-:@ ]*$/', Rule::in(array_values($server->egg->docker_images))], + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Startup/GetStartupRequest.php b/app/Http/Requests/Api/Client/Servers/Startup/GetStartupRequest.php new file mode 100644 index 0000000..fee92bc --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Startup/GetStartupRequest.php @@ -0,0 +1,14 @@ + 'required|string', + 'value' => 'present', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Subusers/DeleteSubuserRequest.php b/app/Http/Requests/Api/Client/Servers/Subusers/DeleteSubuserRequest.php new file mode 100644 index 0000000..eabd84e --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Subusers/DeleteSubuserRequest.php @@ -0,0 +1,13 @@ + 'required|email|between:1,191', + 'permissions' => 'required|array', + 'permissions.*' => 'string', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Subusers/SubuserRequest.php b/app/Http/Requests/Api/Client/Servers/Subusers/SubuserRequest.php new file mode 100644 index 0000000..7c4fab9 --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Subusers/SubuserRequest.php @@ -0,0 +1,74 @@ +route()->parameter('user'); + // Don't allow a user to edit themselves on the server. + if ($user instanceof User) { + if ($user->uuid === $this->user()->uuid) { + return false; + } + } + + // If this is a POST request, validate that the user can even assign the permissions they + // have selected to assign. + if ($this->method() === Request::METHOD_POST && $this->has('permissions')) { + $this->validatePermissionsCanBeAssigned( + $this->input('permissions') ?? [] + ); + } + + return true; + } + + /** + * Validates that the permissions we are trying to assign can actually be assigned + * by the user making the request. + * + * @throws \Illuminate\Contracts\Container\BindingResolutionException + */ + protected function validatePermissionsCanBeAssigned(array $permissions) + { + $user = $this->user(); + /** @var \Pterodactyl\Models\Server $server */ + $server = $this->route()->parameter('server'); + + // If we are a root admin or the server owner, no need to perform these checks. + if ($user->root_admin || $user->id === $server->owner_id) { + return; + } + + // Otherwise, get the current subuser's permission set, and ensure that the + // permissions they are trying to assign are not _more_ than the ones they + // already have. + /** @var \Pterodactyl\Models\Subuser|null $subuser */ + /** @var \Pterodactyl\Services\Servers\GetUserPermissionsService $service */ + $service = $this->container->make(GetUserPermissionsService::class); + + if (count(array_diff($permissions, $service->handle($server, $user))) > 0) { + throw new HttpForbiddenException('Cannot assign permissions to a subuser that your account does not actively possess.'); + } + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Subusers/UpdateSubuserRequest.php b/app/Http/Requests/Api/Client/Servers/Subusers/UpdateSubuserRequest.php new file mode 100644 index 0000000..bd8929a --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Subusers/UpdateSubuserRequest.php @@ -0,0 +1,21 @@ + 'required|array', + 'permissions.*' => 'string', + ]; + } +} diff --git a/app/Http/Requests/Api/Remote/ActivityEventRequest.php b/app/Http/Requests/Api/Remote/ActivityEventRequest.php new file mode 100644 index 0000000..43f527d --- /dev/null +++ b/app/Http/Requests/Api/Remote/ActivityEventRequest.php @@ -0,0 +1,50 @@ + ['required', 'array'], + 'data.*' => ['array'], + 'data.*.user' => ['sometimes', 'nullable', 'uuid'], + 'data.*.server' => ['required', 'uuid'], + 'data.*.event' => ['required', 'string'], + 'data.*.metadata' => ['present', 'nullable', 'array'], + 'data.*.ip' => ['sometimes', 'nullable', 'ip'], + 'data.*.timestamp' => ['required', 'string'], + ]; + } + + /** + * Returns all the unique server UUIDs that were received in this request. + */ + public function servers(): array + { + return Collection::make($this->input('data'))->pluck('server')->unique()->toArray(); + } + + /** + * Returns all the unique user UUIDs that were submitted in this request. + */ + public function users(): array + { + return Collection::make($this->input('data')) + ->filter(function ($value) { + return !empty($value['user']); + }) + ->pluck('user') + ->unique() + ->toArray(); + } +} diff --git a/app/Http/Requests/Api/Remote/AuthenticateWebsocketDetailsRequest.php b/app/Http/Requests/Api/Remote/AuthenticateWebsocketDetailsRequest.php new file mode 100644 index 0000000..1bae01d --- /dev/null +++ b/app/Http/Requests/Api/Remote/AuthenticateWebsocketDetailsRequest.php @@ -0,0 +1,20 @@ + 'required|string', + ]; + } +} diff --git a/app/Http/Requests/Api/Remote/InstallationDataRequest.php b/app/Http/Requests/Api/Remote/InstallationDataRequest.php new file mode 100644 index 0000000..13b3e77 --- /dev/null +++ b/app/Http/Requests/Api/Remote/InstallationDataRequest.php @@ -0,0 +1,21 @@ + 'present|boolean', + 'reinstall' => 'sometimes|boolean', + ]; + } +} diff --git a/app/Http/Requests/Api/Remote/ReportBackupCompleteRequest.php b/app/Http/Requests/Api/Remote/ReportBackupCompleteRequest.php new file mode 100644 index 0000000..d0dd309 --- /dev/null +++ b/app/Http/Requests/Api/Remote/ReportBackupCompleteRequest.php @@ -0,0 +1,21 @@ + 'required|boolean', + 'checksum' => 'nullable|string|required_if:successful,true', + 'checksum_type' => 'nullable|string|required_if:successful,true', + 'size' => 'nullable|numeric|required_if:successful,true', + 'parts' => 'nullable|array', + 'parts.*.etag' => 'required|string', + 'parts.*.part_number' => 'required|numeric', + ]; + } +} diff --git a/app/Http/Requests/Api/Remote/SftpAuthenticationFormRequest.php b/app/Http/Requests/Api/Remote/SftpAuthenticationFormRequest.php new file mode 100644 index 0000000..964c279 --- /dev/null +++ b/app/Http/Requests/Api/Remote/SftpAuthenticationFormRequest.php @@ -0,0 +1,39 @@ + ['nullable', 'in:password,public_key'], + 'username' => ['required', 'string'], + 'password' => ['required', 'string'], + ]; + } + + /** + * Return only the fields that we are interested in from the request. + * This will include empty fields as a null value. + */ + public function normalize(): array + { + return $this->only( + array_keys($this->rules()) + ); + } +} diff --git a/app/Http/Requests/Auth/LoginCheckpointRequest.php b/app/Http/Requests/Auth/LoginCheckpointRequest.php new file mode 100644 index 0000000..3c9392a --- /dev/null +++ b/app/Http/Requests/Auth/LoginCheckpointRequest.php @@ -0,0 +1,41 @@ + 'required|string', + 'authentication_code' => [ + 'nullable', + 'numeric', + Rule::requiredIf(function () { + return empty($this->input('recovery_token')); + }), + ], + 'recovery_token' => [ + 'nullable', + 'string', + Rule::requiredIf(function () { + return empty($this->input('authentication_code')); + }), + ], + ]; + } +} diff --git a/app/Http/Requests/Auth/LoginRequest.php b/app/Http/Requests/Auth/LoginRequest.php new file mode 100644 index 0000000..d088469 --- /dev/null +++ b/app/Http/Requests/Auth/LoginRequest.php @@ -0,0 +1,21 @@ + 'required|string|min:1', + 'password' => 'required|string', + ]; + } +} diff --git a/app/Http/Requests/Auth/ResetPasswordRequest.php b/app/Http/Requests/Auth/ResetPasswordRequest.php new file mode 100644 index 0000000..e5bb329 --- /dev/null +++ b/app/Http/Requests/Auth/ResetPasswordRequest.php @@ -0,0 +1,22 @@ + 'required|string', + 'email' => 'required|email', + 'password' => 'required|string|confirmed|min:8', + ]; + } +} diff --git a/app/Http/Requests/Base/LocaleRequest.php b/app/Http/Requests/Base/LocaleRequest.php new file mode 100644 index 0000000..0a2cc03 --- /dev/null +++ b/app/Http/Requests/Base/LocaleRequest.php @@ -0,0 +1,16 @@ + ['required', 'string', 'regex:/^[a-z][a-z]$/'], + 'namespace' => ['required', 'string', 'regex:/^[a-z]{1,191}$/'], + ]; + } +} diff --git a/app/Http/Requests/FrontendUserFormRequest.php b/app/Http/Requests/FrontendUserFormRequest.php new file mode 100644 index 0000000..66d13d8 --- /dev/null +++ b/app/Http/Requests/FrontendUserFormRequest.php @@ -0,0 +1,29 @@ +user()); + } + + /** + * Return only the fields that we are interested in from the request. + * This will include empty fields as a null value. + */ + public function normalize(): array + { + return $this->only( + array_keys($this->rules()) + ); + } +} diff --git a/app/Http/Resources/Wings/ServerConfigurationCollection.php b/app/Http/Resources/Wings/ServerConfigurationCollection.php new file mode 100644 index 0000000..84fc2f8 --- /dev/null +++ b/app/Http/Resources/Wings/ServerConfigurationCollection.php @@ -0,0 +1,32 @@ +make(EggConfigurationService::class); + $configuration = Container::getInstance()->make(ServerConfigurationStructureService::class); + + return $this->collection->map(function (Server $server) use ($configuration, $egg) { + return [ + 'uuid' => $server->uuid, + 'settings' => $configuration->handle($server), + 'process_configuration' => $egg->handle($server), + ]; + })->toArray(); + } +} diff --git a/app/Http/ViewComposers/AssetComposer.php b/app/Http/ViewComposers/AssetComposer.php new file mode 100644 index 0000000..d42f8a8 --- /dev/null +++ b/app/Http/ViewComposers/AssetComposer.php @@ -0,0 +1,32 @@ +with('asset', $this->assetHashService); + $view->with('siteConfiguration', [ + 'name' => config('app.name') ?? 'Pterodactyl', + 'locale' => config('app.locale') ?? 'en', + 'recaptcha' => [ + 'enabled' => config('recaptcha.enabled', false), + 'siteKey' => config('recaptcha.website_key') ?? '', + ], + ]); + } +} diff --git a/app/Jobs/Job.php b/app/Jobs/Job.php new file mode 100644 index 0000000..0eb64bc --- /dev/null +++ b/app/Jobs/Job.php @@ -0,0 +1,21 @@ +queue = 'standard'; + } + + /** + * Run the job and send actions to the daemon running the server. + * + * @throws \Throwable + */ + public function handle( + DaemonCommandRepository $commandRepository, + InitiateBackupService $backupService, + DaemonPowerRepository $powerRepository + ) { + // Do not process a task that is not set to active, unless it's been manually triggered. + if (!$this->task->schedule->is_active && !$this->manualRun) { + $this->markTaskNotQueued(); + $this->markScheduleComplete(); + + return; + } + + $server = $this->task->server; + // If we made it to this point and the server status is not null it means the + // server was likely suspended or marked as reinstalling after the schedule + // was queued up. Just end the task right now — this should be a very rare + // condition. + if (!is_null($server->status)) { + $this->failed(); + + return; + } + + // Perform the provided task against the daemon. + try { + switch ($this->task->action) { + case Task::ACTION_POWER: + $powerRepository->setServer($server)->send($this->task->payload); + break; + case Task::ACTION_COMMAND: + $commandRepository->setServer($server)->send($this->task->payload); + break; + case Task::ACTION_BACKUP: + $backupService->setIgnoredFiles(explode(PHP_EOL, $this->task->payload))->handle($server, null, true); + break; + default: + throw new \InvalidArgumentException('Invalid task action provided: ' . $this->task->action); + } + } catch (\Exception $exception) { + // If this isn't a DaemonConnectionException on a task that allows for failures + // throw the exception back up the chain so that the task is stopped. + if (!($this->task->continue_on_failure && $exception instanceof DaemonConnectionException)) { + throw $exception; + } + } + + $this->markTaskNotQueued(); + $this->queueNextTask(); + } + + /** + * Handle a failure while sending the action to the daemon or otherwise processing the job. + */ + public function failed(\Exception $exception = null) + { + $this->markTaskNotQueued(); + $this->markScheduleComplete(); + } + + /** + * Get the next task in the schedule and queue it for running after the defined period of wait time. + */ + private function queueNextTask() + { + /** @var \Pterodactyl\Models\Task|null $nextTask */ + $nextTask = Task::query()->where('schedule_id', $this->task->schedule_id) + ->orderBy('sequence_id', 'asc') + ->where('sequence_id', '>', $this->task->sequence_id) + ->first(); + + if (is_null($nextTask)) { + $this->markScheduleComplete(); + + return; + } + + $nextTask->update(['is_queued' => true]); + + $this->dispatch((new self($nextTask, $this->manualRun))->delay($nextTask->time_offset)); + } + + /** + * Marks the parent schedule as being complete. + */ + private function markScheduleComplete() + { + $this->task->schedule()->update([ + 'is_processing' => false, + 'last_run_at' => CarbonImmutable::now()->toDateTimeString(), + ]); + } + + /** + * Mark a specific task as no longer being queued. + */ + private function markTaskNotQueued() + { + $this->task->update(['is_queued' => false]); + } +} diff --git a/app/Listeners/Auth/AuthenticationListener.php b/app/Listeners/Auth/AuthenticationListener.php new file mode 100644 index 0000000..ec25b19 --- /dev/null +++ b/app/Listeners/Auth/AuthenticationListener.php @@ -0,0 +1,38 @@ +user) { + $activity = $activity->subject($event->user); + } + + if ($event instanceof Failed) { + foreach ($event->credentials as $key => $value) { + $activity = $activity->property($key, $value); + } + } + + $activity->event($event instanceof Failed ? 'auth:fail' : 'auth:success')->log(); + } + + public function subscribe(Dispatcher $events): void + { + $events->listen(Failed::class, self::class); + $events->listen(DirectLogin::class, self::class); + } +} diff --git a/app/Listeners/Auth/PasswordResetListener.php b/app/Listeners/Auth/PasswordResetListener.php new file mode 100644 index 0000000..27ed19c --- /dev/null +++ b/app/Listeners/Auth/PasswordResetListener.php @@ -0,0 +1,25 @@ +request = $request; + } + + public function handle(PasswordReset $event): void + { + Activity::event('event:password-reset') + ->withRequestMetadata() + ->subject($event->user) + ->log(); + } +} diff --git a/app/Listeners/Auth/TwoFactorListener.php b/app/Listeners/Auth/TwoFactorListener.php new file mode 100644 index 0000000..91d9208 --- /dev/null +++ b/app/Listeners/Auth/TwoFactorListener.php @@ -0,0 +1,17 @@ +recovery ? 'auth:recovery-token' : 'auth:token') + ->withRequestMetadata() + ->subject($event->user) + ->log(); + } +} diff --git a/app/Models/APILog.php b/app/Models/APILog.php new file mode 100644 index 0000000..673f94e --- /dev/null +++ b/app/Models/APILog.php @@ -0,0 +1,30 @@ + 'boolean', + ]; +} diff --git a/app/Models/ActivityLog.php b/app/Models/ActivityLog.php new file mode 100644 index 0000000..e8f8a4e --- /dev/null +++ b/app/Models/ActivityLog.php @@ -0,0 +1,144 @@ + 'collection', + 'timestamp' => 'datetime', + ]; + + protected $with = ['subjects']; + + public static array $validationRules = [ + 'event' => ['required', 'string'], + 'batch' => ['nullable', 'uuid'], + 'ip' => ['required', 'string'], + 'description' => ['nullable', 'string'], + 'properties' => ['array'], + ]; + + public function actor(): MorphTo + { + $morph = $this->morphTo(); + if (method_exists($morph, 'withTrashed')) { + return $morph->withTrashed(); + } + + return $morph; + } + + public function subjects(): HasMany + { + return $this->hasMany(ActivityLogSubject::class); + } + + public function apiKey(): HasOne + { + return $this->hasOne(ApiKey::class, 'id', 'api_key_id'); + } + + public function scopeForEvent(Builder $builder, string $action): Builder + { + return $builder->where('event', $action); + } + + /** + * Scopes a query to only return results where the actor is a given model. + */ + public function scopeForActor(Builder $builder, IlluminateModel $actor): Builder + { + return $builder->whereMorphedTo('actor', $actor); + } + + /** + * Returns models to be pruned. + * + * @see https://laravel.com/docs/9.x/eloquent#pruning-models + */ + public function prunable() + { + if (is_null(config('activity.prune_days'))) { + throw new \LogicException('Cannot prune activity logs: no "prune_days" configuration value is set.'); + } + + return static::where('timestamp', '<=', Carbon::now()->subDays(config('activity.prune_days'))); + } + + /** + * Boots the model event listeners. This will trigger an activity log event every + * time a new model is inserted which can then be captured and worked with as needed. + */ + protected static function boot() + { + parent::boot(); + + static::created(function (self $model) { + Event::dispatch(new ActivityLogged($model)); + }); + } +} diff --git a/app/Models/ActivityLogSubject.php b/app/Models/ActivityLogSubject.php new file mode 100644 index 0000000..6629b3e --- /dev/null +++ b/app/Models/ActivityLogSubject.php @@ -0,0 +1,46 @@ +belongsTo(ActivityLog::class); + } + + public function subject() + { + $morph = $this->morphTo(); + if (method_exists($morph, 'withTrashed')) { + return $morph->withTrashed(); + } + + return $morph; + } +} diff --git a/app/Models/Allocation.php b/app/Models/Allocation.php new file mode 100644 index 0000000..d16e315 --- /dev/null +++ b/app/Models/Allocation.php @@ -0,0 +1,129 @@ + 'integer', + 'port' => 'integer', + 'server_id' => 'integer', + ]; + + public static array $validationRules = [ + 'node_id' => 'required|exists:nodes,id', + 'ip' => 'required|ip', + 'port' => 'required|numeric|between:1024,65535', + 'ip_alias' => 'nullable|string', + 'server_id' => 'nullable|exists:servers,id', + 'notes' => 'nullable|string|max:256', + ]; + + /** + * {@inheritDoc} + */ + public function getRouteKeyName(): string + { + return $this->getKeyName(); + } + + /** + * Return a hashid encoded string to represent the ID of the allocation. + */ + public function getHashidAttribute(): string + { + return app()->make('hashids')->encode($this->id); + } + + /** + * Accessor to automatically provide the IP alias if defined. + */ + public function getAliasAttribute(?string $value): string + { + return (is_null($this->ip_alias)) ? $this->ip : $this->ip_alias; + } + + /** + * Accessor to quickly determine if this allocation has an alias. + */ + public function getHasAliasAttribute(?string $value): bool + { + return !is_null($this->ip_alias); + } + + public function toString(): string + { + return sprintf('%s:%s', $this->ip, $this->port); + } + + /** + * Gets information for the server associated with this allocation. + */ + public function server(): BelongsTo + { + return $this->belongsTo(Server::class); + } + + /** + * Return the Node model associated with this allocation. + */ + public function node(): BelongsTo + { + return $this->belongsTo(Node::class); + } +} diff --git a/app/Models/ApiKey.php b/app/Models/ApiKey.php new file mode 100644 index 0000000..e141e89 --- /dev/null +++ b/app/Models/ApiKey.php @@ -0,0 +1,210 @@ + 'array', + 'user_id' => 'int', + 'last_used_at' => 'datetime', + 'expires_at' => 'datetime', + self::CREATED_AT => 'datetime', + self::UPDATED_AT => 'datetime', + 'r_' . AdminAcl::RESOURCE_USERS => 'int', + 'r_' . AdminAcl::RESOURCE_ALLOCATIONS => 'int', + 'r_' . AdminAcl::RESOURCE_DATABASE_HOSTS => 'int', + 'r_' . AdminAcl::RESOURCE_SERVER_DATABASES => 'int', + 'r_' . AdminAcl::RESOURCE_EGGS => 'int', + 'r_' . AdminAcl::RESOURCE_LOCATIONS => 'int', + 'r_' . AdminAcl::RESOURCE_NESTS => 'int', + 'r_' . AdminAcl::RESOURCE_NODES => 'int', + 'r_' . AdminAcl::RESOURCE_SERVERS => 'int', + ]; + + /** + * Fields that are mass assignable. + */ + protected $fillable = [ + 'identifier', + 'token', + 'allowed_ips', + 'memo', + 'last_used_at', + 'expires_at', + ]; + + /** + * Fields that should not be included when calling toArray() or toJson() + * on this model. + */ + protected $hidden = ['token']; + + /** + * Rules to protect against invalid data entry to DB. + */ + public static array $validationRules = [ + 'user_id' => 'required|exists:users,id', + 'key_type' => 'present|integer|min:0|max:4', + 'identifier' => 'required|string|size:16|unique:api_keys,identifier', + 'token' => 'required|string', + 'memo' => 'required|nullable|string|max:500', + 'allowed_ips' => 'nullable|array', + 'allowed_ips.*' => 'string', + 'last_used_at' => 'nullable|date', + 'expires_at' => 'nullable|date', + 'r_' . AdminAcl::RESOURCE_USERS => 'integer|min:0|max:3', + 'r_' . AdminAcl::RESOURCE_ALLOCATIONS => 'integer|min:0|max:3', + 'r_' . AdminAcl::RESOURCE_DATABASE_HOSTS => 'integer|min:0|max:3', + 'r_' . AdminAcl::RESOURCE_SERVER_DATABASES => 'integer|min:0|max:3', + 'r_' . AdminAcl::RESOURCE_EGGS => 'integer|min:0|max:3', + 'r_' . AdminAcl::RESOURCE_LOCATIONS => 'integer|min:0|max:3', + 'r_' . AdminAcl::RESOURCE_NESTS => 'integer|min:0|max:3', + 'r_' . AdminAcl::RESOURCE_NODES => 'integer|min:0|max:3', + 'r_' . AdminAcl::RESOURCE_SERVERS => 'integer|min:0|max:3', + ]; + + /** + * Returns the user this token is assigned to. + */ + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } + + /** + * Required for support with Laravel Sanctum. + * + * @see \Laravel\Sanctum\Guard::supportsTokens() + */ + public function tokenable(): BelongsTo + { + return $this->user(); + } + + /** + * Finds the model matching the provided token. + */ + public static function findToken(string $token): ?self + { + $identifier = substr($token, 0, self::IDENTIFIER_LENGTH); + + $model = static::where('identifier', $identifier)->first(); + if (!is_null($model) && decrypt($model->token) === substr($token, strlen($identifier))) { + return $model; + } + + return null; + } + + /** + * Returns the standard prefix for API keys in the system. + */ + public static function getPrefixForType(int $type): string + { + Assert::oneOf($type, [self::TYPE_ACCOUNT, self::TYPE_APPLICATION]); + + return $type === self::TYPE_ACCOUNT ? 'ptlc_' : 'ptla_'; + } + + /** + * Generates a new identifier for an API key. + */ + public static function generateTokenIdentifier(int $type): string + { + $prefix = self::getPrefixForType($type); + + return $prefix . Str::random(self::IDENTIFIER_LENGTH - strlen($prefix)); + } +} diff --git a/app/Models/AuditLog.php b/app/Models/AuditLog.php new file mode 100644 index 0000000..f42acbf --- /dev/null +++ b/app/Models/AuditLog.php @@ -0,0 +1,80 @@ + 'required|uuid', + 'action' => 'required|string|max:191', + 'subaction' => 'nullable|string|max:191', + 'device' => 'array', + 'device.ip_address' => 'ip', + 'device.user_agent' => 'string', + 'metadata' => 'array', + ]; + + protected $table = 'audit_logs'; + + protected bool $immutableDates = true; + + protected $casts = [ + 'is_system' => 'bool', + 'device' => 'array', + 'metadata' => 'array', + ]; + + protected $guarded = [ + 'id', + 'created_at', + ]; + + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } + + public function server(): BelongsTo + { + return $this->belongsTo(Server::class); + } + + /** + * Creates a new AuditLog model and returns it, attaching device information and the + * currently authenticated user if available. This model is not saved at this point, so + * you can always make modifications to it as needed before saving. + * + * @deprecated + */ + public static function instance(string $action, array $metadata, bool $isSystem = false): self + { + /** @var \Illuminate\Http\Request $request */ + $request = Container::getInstance()->make('request'); + if ($isSystem || !$request instanceof Request) { + $request = null; + } + + return (new self())->fill([ + 'uuid' => Uuid::uuid4()->toString(), + 'is_system' => $isSystem, + 'user_id' => ($request && $request->user()) ? $request->user()->id : null, + 'server_id' => null, + 'action' => $action, + 'device' => $request ? [ + 'ip_address' => $request->getClientIp() ?? '127.0.0.1', + 'user_agent' => $request->userAgent() ?? '', + ] : [], + 'metadata' => $metadata, + ]); + } +} diff --git a/app/Models/Backup.php b/app/Models/Backup.php new file mode 100644 index 0000000..c9a75cc --- /dev/null +++ b/app/Models/Backup.php @@ -0,0 +1,76 @@ + 'int', + 'is_successful' => 'bool', + 'is_locked' => 'bool', + 'ignored_files' => 'array', + 'bytes' => 'int', + 'completed_at' => 'datetime', + ]; + + protected $attributes = [ + 'is_successful' => false, + 'is_locked' => false, + 'checksum' => null, + 'bytes' => 0, + 'upload_id' => null, + ]; + + protected $guarded = ['id', 'created_at', 'updated_at', 'deleted_at']; + + public static array $validationRules = [ + 'server_id' => 'bail|required|numeric|exists:servers,id', + 'uuid' => 'required|uuid', + 'is_successful' => 'boolean', + 'is_locked' => 'boolean', + 'name' => 'required|string', + 'ignored_files' => 'array', + 'disk' => 'required|string', + 'checksum' => 'nullable|string', + 'bytes' => 'numeric', + 'upload_id' => 'nullable|string', + ]; + + public function server(): BelongsTo + { + return $this->belongsTo(Server::class); + } +} diff --git a/app/Models/Database.php b/app/Models/Database.php new file mode 100644 index 0000000..e3856a4 --- /dev/null +++ b/app/Models/Database.php @@ -0,0 +1,110 @@ + 'integer', + 'database_host_id' => 'integer', + 'max_connections' => 'integer', + ]; + + public static array $validationRules = [ + 'server_id' => 'required|numeric|exists:servers,id', + 'database_host_id' => 'required|exists:database_hosts,id', + 'database' => 'required|string|alpha_dash|between:3,48', + 'username' => 'string|alpha_dash|between:3,100', + 'max_connections' => 'nullable|integer', + 'remote' => 'required|string|regex:/^[\w\-\/.%:]+$/', + 'password' => 'string', + ]; + + /** + * {@inheritDoc} + */ + public function getRouteKeyName(): string + { + return $this->getKeyName(); + } + + /** + * Resolves the database using the ID by checking if the value provided is a HashID + * string value, or just the ID to the database itself. + * + * @param mixed $value + * @param string|null $field + * + * @throws \Illuminate\Contracts\Container\BindingResolutionException + */ + public function resolveRouteBinding($value, $field = null): ?\Illuminate\Database\Eloquent\Model + { + if (is_scalar($value) && ($field ?? $this->getRouteKeyName()) === 'id') { + $value = ctype_digit((string) $value) + ? $value + : Container::getInstance()->make(HashidsInterface::class)->decodeFirst($value); + } + + return $this->where($field ?? $this->getRouteKeyName(), $value)->firstOrFail(); + } + + /** + * Gets the host database server associated with a database. + */ + public function host(): BelongsTo + { + return $this->belongsTo(DatabaseHost::class, 'database_host_id'); + } + + /** + * Gets the server associated with a database. + */ + public function server(): BelongsTo + { + return $this->belongsTo(Server::class); + } +} diff --git a/app/Models/DatabaseHost.php b/app/Models/DatabaseHost.php new file mode 100644 index 0000000..df56eb7 --- /dev/null +++ b/app/Models/DatabaseHost.php @@ -0,0 +1,83 @@ + 'integer', + 'max_databases' => 'integer', + 'node_id' => 'integer', + ]; + + /** + * Validation rules to assign to this model. + */ + public static array $validationRules = [ + 'name' => 'required|string|max:191', + 'host' => 'required|string', + 'port' => 'required|numeric|between:1,65535', + 'username' => 'required|string|max:32', + 'password' => 'nullable|string', + 'node_id' => 'sometimes|nullable|integer|exists:nodes,id', + ]; + + /** + * Gets the node associated with a database host. + */ + public function node(): BelongsTo + { + return $this->belongsTo(Node::class); + } + + /** + * Gets the databases associated with this host. + */ + public function databases(): HasMany + { + return $this->hasMany(Database::class); + } +} diff --git a/app/Models/Egg.php b/app/Models/Egg.php new file mode 100644 index 0000000..f0e668b --- /dev/null +++ b/app/Models/Egg.php @@ -0,0 +1,299 @@ + $docker_images + * @property string $update_url + * @property bool $force_outgoing_ip + * @property array|null $file_denylist + * @property string|null $config_files + * @property string|null $config_startup + * @property string|null $config_logs + * @property string|null $config_stop + * @property int|null $config_from + * @property string|null $startup + * @property bool $script_is_privileged + * @property string|null $script_install + * @property string $script_entry + * @property string $script_container + * @property int|null $copy_script_from + * @property \Carbon\Carbon $created_at + * @property \Carbon\Carbon $updated_at + * @property string|null $copy_script_install + * @property string $copy_script_entry + * @property string $copy_script_container + * @property string|null $inherit_config_files + * @property string|null $inherit_config_startup + * @property string|null $inherit_config_logs + * @property string|null $inherit_config_stop + * @property string $inherit_file_denylist + * @property array|null $inherit_features + * @property \Pterodactyl\Models\Nest $nest + * @property \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\Server[] $servers + * @property \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\EggVariable[] $variables + * @property \Pterodactyl\Models\Egg|null $scriptFrom + * @property \Pterodactyl\Models\Egg|null $configFrom + */ +class Egg extends Model +{ + /** + * The resource name for this model when it is transformed into an + * API representation using fractal. + */ + public const RESOURCE_NAME = 'egg'; + + /** + * Defines the current egg export version. + */ + public const EXPORT_VERSION = 'PTDL_v2'; + + /** + * Different features that can be enabled on any given egg. These are used internally + * to determine which types of frontend functionality should be shown to the user. Eggs + * will automatically inherit features from a parent egg if they are already configured + * to copy configuration values from said egg. + * + * To skip copying the features, an empty array value should be passed in ("[]") rather + * than leaving it null. + */ + public const FEATURE_EULA_POPUP = 'eula'; + public const FEATURE_FASTDL = 'fastdl'; + + /** + * The table associated with the model. + */ + protected $table = 'eggs'; + + /** + * Fields that are not mass assignable. + */ + protected $fillable = [ + 'name', + 'description', + 'features', + 'docker_images', + 'force_outgoing_ip', + 'file_denylist', + 'config_files', + 'config_startup', + 'config_logs', + 'config_stop', + 'config_from', + 'startup', + 'script_is_privileged', + 'script_install', + 'script_entry', + 'script_container', + 'copy_script_from', + ]; + + /** + * Cast values to correct type. + */ + protected $casts = [ + 'nest_id' => 'integer', + 'config_from' => 'integer', + 'script_is_privileged' => 'boolean', + 'force_outgoing_ip' => 'boolean', + 'copy_script_from' => 'integer', + 'features' => 'array', + 'docker_images' => 'array', + 'file_denylist' => 'array', + ]; + + public static array $validationRules = [ + 'nest_id' => 'required|bail|numeric|exists:nests,id', + 'uuid' => 'required|string|size:36', + 'name' => 'required|string|max:191', + 'description' => 'string|nullable', + 'features' => 'array|nullable', + 'author' => 'required|string|email', + 'file_denylist' => 'array|nullable', + 'file_denylist.*' => 'string', + 'docker_images' => 'required|array|min:1', + 'docker_images.*' => ['required', 'string', 'max:191', 'regex:/^[\w#\.\/\- ]*\|*[\w\.\/\-:@ ]*$/'], + 'startup' => 'required|nullable|string', + 'config_from' => 'sometimes|bail|nullable|numeric|exists:eggs,id', + 'config_stop' => 'required_without:config_from|nullable|string|max:191', + 'config_startup' => 'required_without:config_from|nullable|json', + 'config_logs' => 'required_without:config_from|nullable|json', + 'config_files' => 'required_without:config_from|nullable|json', + 'update_url' => 'sometimes|nullable|string', + 'force_outgoing_ip' => 'sometimes|boolean', + ]; + + protected $attributes = [ + 'features' => null, + 'file_denylist' => null, + 'config_stop' => null, + 'config_startup' => null, + 'config_logs' => null, + 'config_files' => null, + 'update_url' => null, + ]; + + /** + * Returns the install script for the egg; if egg is copying from another + * it will return the copied script. + */ + public function getCopyScriptInstallAttribute(): ?string + { + if (!is_null($this->script_install) || is_null($this->copy_script_from)) { + return $this->script_install; + } + + return $this->scriptFrom->script_install; + } + + /** + * Returns the entry command for the egg; if egg is copying from another + * it will return the copied entry command. + */ + public function getCopyScriptEntryAttribute(): string + { + if (!is_null($this->script_entry) || is_null($this->copy_script_from)) { + return $this->script_entry; + } + + return $this->scriptFrom->script_entry; + } + + /** + * Returns the install container for the egg; if egg is copying from another + * it will return the copied install container. + */ + public function getCopyScriptContainerAttribute(): string + { + if (!is_null($this->script_container) || is_null($this->copy_script_from)) { + return $this->script_container; + } + + return $this->scriptFrom->script_container; + } + + /** + * Return the file configuration for an egg. + */ + public function getInheritConfigFilesAttribute(): ?string + { + if (!is_null($this->config_files) || is_null($this->config_from)) { + return $this->config_files; + } + + return $this->configFrom->config_files; + } + + /** + * Return the startup configuration for an egg. + */ + public function getInheritConfigStartupAttribute(): ?string + { + if (!is_null($this->config_startup) || is_null($this->config_from)) { + return $this->config_startup; + } + + return $this->configFrom->config_startup; + } + + /** + * Return the log reading configuration for an egg. + */ + public function getInheritConfigLogsAttribute(): ?string + { + if (!is_null($this->config_logs) || is_null($this->config_from)) { + return $this->config_logs; + } + + return $this->configFrom->config_logs; + } + + /** + * Return the stop command configuration for an egg. + */ + public function getInheritConfigStopAttribute(): ?string + { + if (!is_null($this->config_stop) || is_null($this->config_from)) { + return $this->config_stop; + } + + return $this->configFrom->config_stop; + } + + /** + * Returns the features available to this egg from the parent configuration if there are + * no features defined for this egg specifically and there is a parent egg configured. + */ + public function getInheritFeaturesAttribute(): ?array + { + if (!is_null($this->features) || is_null($this->config_from)) { + return $this->features; + } + + return $this->configFrom->features; + } + + /** + * Returns the features available to this egg from the parent configuration if there are + * no features defined for this egg specifically and there is a parent egg configured. + */ + public function getInheritFileDenylistAttribute(): ?array + { + if (is_null($this->config_from)) { + return $this->file_denylist; + } + + return $this->configFrom->file_denylist; + } + + /** + * Gets nest associated with an egg. + */ + public function nest(): BelongsTo + { + return $this->belongsTo(Nest::class); + } + + /** + * Gets all servers associated with this egg. + */ + public function servers(): HasMany + { + return $this->hasMany(Server::class, 'egg_id'); + } + + /** + * Gets all variables associated with this egg. + */ + public function variables(): HasMany + { + return $this->hasMany(EggVariable::class, 'egg_id'); + } + + /** + * Get the parent egg from which to copy scripts. + */ + public function scriptFrom(): BelongsTo + { + return $this->belongsTo(self::class, 'copy_script_from'); + } + + /** + * Get the parent egg from which to copy configuration settings. + */ + public function configFrom(): BelongsTo + { + return $this->belongsTo(self::class, 'config_from'); + } +} diff --git a/app/Models/EggMount.php b/app/Models/EggMount.php new file mode 100644 index 0000000..ed494a1 --- /dev/null +++ b/app/Models/EggMount.php @@ -0,0 +1,12 @@ + 'integer', + 'user_viewable' => 'bool', + 'user_editable' => 'bool', + ]; + + public static array $validationRules = [ + 'egg_id' => 'exists:eggs,id', + 'name' => 'required|string|between:1,191', + 'description' => 'string', + 'env_variable' => 'required|regex:/^[\w]{1,191}$/|notIn:' . self::RESERVED_ENV_NAMES, + 'default_value' => 'string', + 'user_viewable' => 'boolean', + 'user_editable' => 'boolean', + 'rules' => 'required|string', + ]; + + protected $attributes = [ + 'user_editable' => 0, + 'user_viewable' => 0, + ]; + + public function getRequiredAttribute(): bool + { + return in_array('required', explode('|', $this->rules)); + } + + public function egg(): HasOne + { + return $this->hasOne(Egg::class); + } + + /** + * Return server variables associated with this variable. + */ + public function serverVariable(): HasMany + { + return $this->hasMany(ServerVariable::class, 'variable_id'); + } +} diff --git a/app/Models/Filters/AdminServerFilter.php b/app/Models/Filters/AdminServerFilter.php new file mode 100644 index 0000000..6389467 --- /dev/null +++ b/app/Models/Filters/AdminServerFilter.php @@ -0,0 +1,35 @@ +getQuery()->from !== 'servers') { + throw new \BadMethodCallException('Cannot use the AdminServerFilter against a non-server model.'); + } + $query + ->select('servers.*') + ->leftJoin('users', 'users.id', '=', 'servers.owner_id') + ->where(function (Builder $builder) use ($value) { + $builder->where('servers.uuid', $value) + ->orWhere('servers.uuid', 'LIKE', "$value%") + ->orWhere('servers.uuidShort', $value) + ->orWhere('servers.external_id', $value) + ->orWhereRaw('LOWER(users.username) LIKE ?', ["%$value%"]) + ->orWhereRaw('LOWER(users.email) LIKE ?', ["$value%"]) + ->orWhereRaw('LOWER(servers.name) LIKE ?', ["%$value%"]); + }) + ->groupBy('servers.id'); + } +} diff --git a/app/Models/Filters/MultiFieldServerFilter.php b/app/Models/Filters/MultiFieldServerFilter.php new file mode 100644 index 0000000..fe0a7eb --- /dev/null +++ b/app/Models/Filters/MultiFieldServerFilter.php @@ -0,0 +1,69 @@ +getQuery()->from !== 'servers') { + throw new \BadMethodCallException('Cannot use the MultiFieldServerFilter against a non-server model.'); + } + + if (preg_match(self::IPV4_REGEX, $value) || preg_match('/^:\d{1,5}$/', $value)) { + $query + // Only select the server values, otherwise you'll end up merging the allocation and + // server objects together, resulting in incorrect behavior and returned values. + ->select('servers.*') + ->join('allocations', 'allocations.server_id', '=', 'servers.id') + ->where(function (Builder $builder) use ($value) { + $parts = explode(':', $value); + + $builder->when( + !Str::startsWith($value, ':'), + // When the string does not start with a ":" it means we're looking for an IP or IP:Port + // combo, so use a query to handle that. + function (Builder $builder) use ($parts) { + $builder->orWhere('allocations.ip', $parts[0]); + if (!is_null($parts[1] ?? null)) { + $builder->where('allocations.port', 'LIKE', "{$parts[1]}%"); + } + }, + // Otherwise, just try to search for that specific port in the allocations. + function (Builder $builder) use ($value) { + $builder->orWhere('allocations.port', 'LIKE', substr($value, 1) . '%'); + } + ); + }) + ->groupBy('servers.id'); + + return; + } + + $query + ->where(function (Builder $builder) use ($value) { + $builder->where('servers.uuid', $value) + ->orWhere('servers.uuid', 'LIKE', "$value%") + ->orWhere('servers.uuidShort', $value) + ->orWhere('servers.external_id', $value) + ->orWhereRaw('LOWER(servers.name) LIKE ?', ["%$value%"]); + }); + } +} diff --git a/app/Models/Location.php b/app/Models/Location.php new file mode 100644 index 0000000..47a3035 --- /dev/null +++ b/app/Models/Location.php @@ -0,0 +1,66 @@ + 'required|string|between:1,60|unique:locations,short', + 'long' => 'string|nullable|between:1,191', + ]; + + /** + * {@inheritDoc} + */ + public function getRouteKeyName(): string + { + return $this->getKeyName(); + } + + /** + * Gets the nodes in a specified location. + */ + public function nodes(): HasMany + { + return $this->hasMany(Node::class); + } + + /** + * Gets the servers within a given location. + */ + public function servers(): HasManyThrough + { + return $this->hasManyThrough(Server::class, Node::class); + } +} diff --git a/app/Models/Model.php b/app/Models/Model.php new file mode 100644 index 0000000..2e371d9 --- /dev/null +++ b/app/Models/Model.php @@ -0,0 +1,187 @@ +make(ValidationFactory::class); + + static::saving(function (Model $model) { + try { + $model->validate(); + } catch (ValidationException $exception) { + throw new DataValidationException($exception->validator, $model); + } + + return true; + }); + } + + /** + * Returns the model key to use for route model binding. By default, we'll + * assume every model uses a UUID field for this. If the model does not have + * a UUID and is using a different key it should be specified on the model + * itself. + * + * You may also optionally override this on a per-route basis by declaring + * the key name in the URL definition, like "{user:id}". + */ + public function getRouteKeyName(): string + { + return 'uuid'; + } + + /** + * Set the model to skip validation when saving. + */ + public function skipValidation(): self + { + $this->skipValidation = true; + + return $this; + } + + /** + * Returns the validator instance used by this model. + */ + public function getValidator(): Validator + { + $rules = $this->exists ? static::getRulesForUpdate($this) : static::getRules(); + + return static::$validatorFactory->make([], $rules, [], []); + } + + /** + * Returns the rules associated with this model. + */ + public static function getRules(): array + { + $rules = static::$validationRules; + foreach ($rules as $key => &$rule) { + $rule = is_array($rule) ? $rule : explode('|', $rule); + } + + return $rules; + } + + /** + * Returns the rules for a specific field. If the field is not found an empty + * array is returned. + */ + public static function getRulesForField(string $field): array + { + return Arr::get(static::getRules(), $field) ?? []; + } + + /** + * Returns the rules associated with the model, specifically for updating the given model + * rather than just creating it. + */ + public static function getRulesForUpdate($model, string $column = 'id'): array + { + if ($model instanceof Model) { + [$id, $column] = [$model->getKey(), $model->getKeyName()]; + } + + $rules = static::getRules(); + foreach ($rules as $key => &$data) { + // For each rule in a given field, iterate over it and confirm if the rule + // is one for a unique field. If that is the case, append the ID of the current + // working model, so we don't run into errors due to the way that field validation + // works. + foreach ($data as &$datum) { + if (!is_string($datum) || !Str::startsWith($datum, 'unique')) { + continue; + } + + [, $args] = explode(':', $datum); + $args = explode(',', $args); + + $datum = Rule::unique($args[0], $args[1] ?? $key)->ignore($id ?? $model, $column); + } + } + + return $rules; + } + + /** + * Determines if the model is in a valid state or not. + * + * @throws \Illuminate\Validation\ValidationException + */ + public function validate(): void + { + if ($this->skipValidation) { + return; + } + + $validator = $this->getValidator(); + $validator->setData( + // Trying to do self::toArray() here will leave out keys based on the whitelist/blacklist + // for that model. Doing this will return all the attributes in a format that can + // properly be validated. + $this->addCastAttributesToArray( + $this->getAttributes(), + $this->getMutatedAttributes() + ) + ); + + if (!$validator->passes()) { + throw new ValidationException($validator); + } + } + + /** + * Return a timestamp as DateTime object. + * + * @param mixed $value + */ + protected function asDateTime($value): Carbon|CarbonImmutable + { + if (!$this->immutableDates) { + return parent::asDateTime($value); + } + + return parent::asDateTime($value)->toImmutable(); + } +} diff --git a/app/Models/Mount.php b/app/Models/Mount.php new file mode 100644 index 0000000..475b3cb --- /dev/null +++ b/app/Models/Mount.php @@ -0,0 +1,118 @@ + 'int', + 'read_only' => 'bool', + 'user_mountable' => 'bool', + ]; + + /** + * Rules verifying that the data being stored matches the expectations of the database. + */ + public static array $validationRules = [ + 'name' => 'required|string|min:2|max:64|unique:mounts,name', + 'description' => 'nullable|string|max:191', + 'source' => 'required|string', + 'target' => 'required|string', + 'read_only' => 'sometimes|boolean', + 'user_mountable' => 'sometimes|boolean', + ]; + + /** + * Implement language verification by overriding Eloquence's gather + * rules function. + */ + public static function getRules(): array + { + $rules = parent::getRules(); + + $rules['source'][] = new NotIn(Mount::$invalidSourcePaths); + $rules['target'][] = new NotIn(Mount::$invalidTargetPaths); + + return $rules; + } + + /** + * Disable timestamps on this model. + */ + public $timestamps = false; + + /** + * Blacklisted source paths. + */ + public static $invalidSourcePaths = [ + '/etc/pterodactyl', + '/var/lib/pterodactyl/volumes', + '/srv/daemon-data', + ]; + + /** + * Blacklisted target paths. + */ + public static $invalidTargetPaths = [ + '/home/container', + ]; + + /** + * Returns all eggs that have this mount assigned. + */ + public function eggs(): BelongsToMany + { + return $this->belongsToMany(Egg::class); + } + + /** + * Returns all nodes that have this mount assigned. + */ + public function nodes(): BelongsToMany + { + return $this->belongsToMany(Node::class); + } + + /** + * Returns all servers that have this mount assigned. + */ + public function servers(): BelongsToMany + { + return $this->belongsToMany(Server::class); + } +} diff --git a/app/Models/MountNode.php b/app/Models/MountNode.php new file mode 100644 index 0000000..3a189c4 --- /dev/null +++ b/app/Models/MountNode.php @@ -0,0 +1,14 @@ + 'required|string|email', + 'name' => 'required|string|max:191', + 'description' => 'nullable|string', + ]; + + /** + * Gets all eggs associated with this service. + */ + public function eggs(): HasMany + { + return $this->hasMany(Egg::class); + } + + /** + * Gets all servers associated with this nest. + */ + public function servers(): HasMany + { + return $this->hasMany(Server::class); + } +} diff --git a/app/Models/Node.php b/app/Models/Node.php new file mode 100644 index 0000000..62ec828 --- /dev/null +++ b/app/Models/Node.php @@ -0,0 +1,233 @@ + 'integer', + 'memory' => 'integer', + 'disk' => 'integer', + 'daemonListen' => 'integer', + 'daemonSFTP' => 'integer', + 'behind_proxy' => 'boolean', + 'public' => 'boolean', + 'maintenance_mode' => 'boolean', + ]; + + /** + * Fields that are mass assignable. + */ + protected $fillable = [ + 'public', 'name', 'location_id', + 'fqdn', 'scheme', 'behind_proxy', + 'memory', 'memory_overallocate', 'disk', + 'disk_overallocate', 'upload_size', 'daemonBase', + 'daemonSFTP', 'daemonListen', + 'description', 'maintenance_mode', + ]; + + public static array $validationRules = [ + 'name' => 'required|regex:/^([\w .-]{1,100})$/', + 'description' => 'string|nullable', + 'location_id' => 'required|exists:locations,id', + 'public' => 'boolean', + 'fqdn' => 'required|string', + 'scheme' => 'required', + 'behind_proxy' => 'boolean', + 'memory' => 'required|numeric|min:1', + 'memory_overallocate' => 'required|numeric|min:-1', + 'disk' => 'required|numeric|min:1', + 'disk_overallocate' => 'required|numeric|min:-1', + 'daemonBase' => 'sometimes|required|regex:/^([\/][\d\w.\-\/]+)$/', + 'daemonSFTP' => 'required|numeric|between:1,65535', + 'daemonListen' => 'required|numeric|between:1,65535', + 'maintenance_mode' => 'boolean', + 'upload_size' => 'int|between:1,1024', + ]; + + /** + * Default values for specific columns that are generally not changed on base installs. + */ + protected $attributes = [ + 'public' => true, + 'behind_proxy' => false, + 'memory_overallocate' => 0, + 'disk_overallocate' => 0, + 'daemonBase' => '/var/lib/pterodactyl/volumes', + 'daemonSFTP' => 2022, + 'daemonListen' => 8080, + 'maintenance_mode' => false, + ]; + + /** + * Get the connection address to use when making calls to this node. + */ + public function getConnectionAddress(): string + { + return sprintf('%s://%s:%s', $this->scheme, $this->fqdn, $this->daemonListen); + } + + /** + * Returns the configuration as an array. + */ + public function getConfiguration(): array + { + return [ + 'debug' => false, + 'uuid' => $this->uuid, + 'token_id' => $this->daemon_token_id, + 'token' => Container::getInstance()->make(Encrypter::class)->decrypt($this->daemon_token), + 'api' => [ + 'host' => '0.0.0.0', + 'port' => $this->daemonListen, + 'ssl' => [ + 'enabled' => (!$this->behind_proxy && $this->scheme === 'https'), + 'cert' => '/etc/letsencrypt/live/' . Str::lower($this->fqdn) . '/fullchain.pem', + 'key' => '/etc/letsencrypt/live/' . Str::lower($this->fqdn) . '/privkey.pem', + ], + 'upload_limit' => $this->upload_size, + ], + 'system' => [ + 'data' => $this->daemonBase, + 'sftp' => [ + 'bind_port' => $this->daemonSFTP, + ], + ], + 'allowed_mounts' => $this->mounts->pluck('source')->toArray(), + 'remote' => route('index'), + ]; + } + + /** + * Returns the configuration in Yaml format. + */ + public function getYamlConfiguration(): string + { + return Yaml::dump($this->getConfiguration(), 4, 2, Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE); + } + + /** + * Returns the configuration in JSON format. + */ + public function getJsonConfiguration(bool $pretty = false): string + { + return json_encode($this->getConfiguration(), $pretty ? JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT : JSON_UNESCAPED_SLASHES); + } + + /** + * Helper function to return the decrypted key for a node. + */ + public function getDecryptedKey(): string + { + return (string) Container::getInstance()->make(Encrypter::class)->decrypt( + $this->daemon_token + ); + } + + public function isUnderMaintenance(): bool + { + return $this->maintenance_mode; + } + + public function mounts(): HasManyThrough + { + return $this->hasManyThrough(Mount::class, MountNode::class, 'node_id', 'id', 'id', 'mount_id'); + } + + /** + * Gets the location associated with a node. + */ + public function location(): BelongsTo + { + return $this->belongsTo(Location::class); + } + + /** + * Gets the servers associated with a node. + */ + public function servers(): HasMany + { + return $this->hasMany(Server::class); + } + + /** + * Gets the allocations associated with a node. + */ + public function allocations(): HasMany + { + return $this->hasMany(Allocation::class); + } + + /** + * Returns a boolean if the node is viable for an additional server to be placed on it. + */ + public function isViable(int $memory, int $disk): bool + { + $memoryLimit = $this->memory * (1 + ($this->memory_overallocate / 100)); + $diskLimit = $this->disk * (1 + ($this->disk_overallocate / 100)); + + return ($this->sum_memory + $memory) <= $memoryLimit && ($this->sum_disk + $disk) <= $diskLimit; + } +} diff --git a/app/Models/Objects/DeploymentObject.php b/app/Models/Objects/DeploymentObject.php new file mode 100644 index 0000000..0c44be0 --- /dev/null +++ b/app/Models/Objects/DeploymentObject.php @@ -0,0 +1,48 @@ +dedicated; + } + + public function setDedicated(bool $dedicated): self + { + $this->dedicated = $dedicated; + + return $this; + } + + public function getLocations(): array + { + return $this->locations; + } + + public function setLocations(array $locations): self + { + $this->locations = $locations; + + return $this; + } + + public function getPorts(): array + { + return $this->ports; + } + + public function setPorts(array $ports): self + { + $this->ports = $ports; + + return $this; + } +} diff --git a/app/Models/Permission.php b/app/Models/Permission.php new file mode 100644 index 0000000..b2e0007 --- /dev/null +++ b/app/Models/Permission.php @@ -0,0 +1,219 @@ + 'integer', + ]; + + public static array $validationRules = [ + 'subuser_id' => 'required|numeric|min:1', + 'permission' => 'required|string', + ]; + + /** + * All the permissions available on the system. You should use self::permissions() + * to retrieve them, and not directly access this array as it is subject to change. + * + * @see \Pterodactyl\Models\Permission::permissions() + */ + protected static array $permissions = [ + 'websocket' => [ + 'description' => 'Allows the user to connect to the server websocket, giving them access to view console output and realtime server stats.', + 'keys' => [ + 'connect' => 'Allows a user to connect to the websocket instance for a server to stream the console.', + ], + ], + + 'control' => [ + 'description' => 'Permissions that control a user\'s ability to control the power state of a server, or send commands.', + 'keys' => [ + 'console' => 'Allows a user to send commands to the server instance via the console.', + 'start' => 'Allows a user to start the server if it is stopped.', + 'stop' => 'Allows a user to stop a server if it is running.', + 'restart' => 'Allows a user to perform a server restart. This allows them to start the server if it is offline, but not put the server in a completely stopped state.', + ], + ], + + 'user' => [ + 'description' => 'Permissions that allow a user to manage other subusers on a server. They will never be able to edit their own account, or assign permissions they do not have themselves.', + 'keys' => [ + 'create' => 'Allows a user to create new subusers for the server.', + 'read' => 'Allows the user to view subusers and their permissions for the server.', + 'update' => 'Allows a user to modify other subusers.', + 'delete' => 'Allows a user to delete a subuser from the server.', + ], + ], + + 'file' => [ + 'description' => 'Permissions that control a user\'s ability to modify the filesystem for this server.', + 'keys' => [ + 'create' => 'Allows a user to create additional files and folders via the Panel or direct upload.', + 'read' => 'Allows a user to view the contents of a directory, but not view the contents of or download files.', + 'read-content' => 'Allows a user to view the contents of a given file. This will also allow the user to download files.', + 'update' => 'Allows a user to update the contents of an existing file or directory.', + 'delete' => 'Allows a user to delete files or directories.', + 'archive' => 'Allows a user to archive the contents of a directory as well as decompress existing archives on the system.', + 'sftp' => 'Allows a user to connect to SFTP and manage server files using the other assigned file permissions.', + ], + ], + + 'backup' => [ + 'description' => 'Permissions that control a user\'s ability to generate and manage server backups.', + 'keys' => [ + 'create' => 'Allows a user to create new backups for this server.', + 'read' => 'Allows a user to view all backups that exist for this server.', + 'delete' => 'Allows a user to remove backups from the system.', + 'download' => 'Allows a user to download a backup for the server. Danger: this allows a user to access all files for the server in the backup.', + 'restore' => 'Allows a user to restore a backup for the server. Danger: this allows the user to delete all of the server files in the process.', + ], + ], + + // Controls permissions for editing or viewing a server's allocations. + 'allocation' => [ + 'description' => 'Permissions that control a user\'s ability to modify the port allocations for this server.', + 'keys' => [ + 'read' => 'Allows a user to view all allocations currently assigned to this server. Users with any level of access to this server can always view the primary allocation.', + 'create' => 'Allows a user to assign additional allocations to the server.', + 'update' => 'Allows a user to change the primary server allocation and attach notes to each allocation.', + 'delete' => 'Allows a user to delete an allocation from the server.', + ], + ], + + // Controls permissions for editing or viewing a server's startup parameters. + 'startup' => [ + 'description' => 'Permissions that control a user\'s ability to view this server\'s startup parameters.', + 'keys' => [ + 'read' => 'Allows a user to view the startup variables for a server.', + 'update' => 'Allows a user to modify the startup variables for the server.', + 'docker-image' => 'Allows a user to modify the Docker image used when running the server.', + ], + ], + + 'database' => [ + 'description' => 'Permissions that control a user\'s access to the database management for this server.', + 'keys' => [ + 'create' => 'Allows a user to create a new database for this server.', + 'read' => 'Allows a user to view the database associated with this server.', + 'update' => 'Allows a user to rotate the password on a database instance. If the user does not have the view_password permission they will not see the updated password.', + 'delete' => 'Allows a user to remove a database instance from this server.', + 'view_password' => 'Allows a user to view the password associated with a database instance for this server.', + ], + ], + + 'schedule' => [ + 'description' => 'Permissions that control a user\'s access to the schedule management for this server.', + 'keys' => [ + 'create' => 'Allows a user to create new schedules for this server.', // task.create-schedule + 'read' => 'Allows a user to view schedules and the tasks associated with them for this server.', // task.view-schedule, task.list-schedules + 'update' => 'Allows a user to update schedules and schedule tasks for this server.', // task.edit-schedule, task.queue-schedule, task.toggle-schedule + 'delete' => 'Allows a user to delete schedules for this server.', // task.delete-schedule + ], + ], + + 'settings' => [ + 'description' => 'Permissions that control a user\'s access to the settings for this server.', + 'keys' => [ + 'rename' => 'Allows a user to rename this server and change the description of it.', + 'reinstall' => 'Allows a user to trigger a reinstall of this server.', + ], + ], + + 'activity' => [ + 'description' => 'Permissions that control a user\'s access to the server activity logs.', + 'keys' => [ + 'read' => 'Allows a user to view the activity logs for the server.', + ], + ], + ]; + + /** + * Returns all the permissions available on the system for a user to + * have when controlling a server. + */ + public static function permissions(): Collection + { + return Collection::make(self::$permissions); + } +} diff --git a/app/Models/RecoveryToken.php b/app/Models/RecoveryToken.php new file mode 100644 index 0000000..d4ca764 --- /dev/null +++ b/app/Models/RecoveryToken.php @@ -0,0 +1,33 @@ + 'required|string', + ]; + + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } +} diff --git a/app/Models/Schedule.php b/app/Models/Schedule.php new file mode 100644 index 0000000..a61bd3c --- /dev/null +++ b/app/Models/Schedule.php @@ -0,0 +1,150 @@ + 'integer', + 'server_id' => 'integer', + 'is_active' => 'boolean', + 'is_processing' => 'boolean', + 'only_when_online' => 'boolean', + 'last_run_at' => 'datetime', + 'next_run_at' => 'datetime', + ]; + + protected $attributes = [ + 'name' => null, + 'cron_day_of_week' => '*', + 'cron_month' => '*', + 'cron_day_of_month' => '*', + 'cron_hour' => '*', + 'cron_minute' => '*', + 'is_active' => true, + 'is_processing' => false, + 'only_when_online' => false, + ]; + + public static array $validationRules = [ + 'server_id' => 'required|exists:servers,id', + 'name' => 'required|string|max:191', + 'cron_day_of_week' => 'required|string', + 'cron_month' => 'required|string', + 'cron_day_of_month' => 'required|string', + 'cron_hour' => 'required|string', + 'cron_minute' => 'required|string', + 'is_active' => 'boolean', + 'is_processing' => 'boolean', + 'only_when_online' => 'boolean', + 'last_run_at' => 'nullable|date', + 'next_run_at' => 'nullable|date', + ]; + + /** + * {@inheritDoc} + */ + public function getRouteKeyName(): string + { + return $this->getKeyName(); + } + + /** + * Returns the schedule's execution crontab entry as a string. + * + * @throws \Exception + */ + public function getNextRunDate(): CarbonImmutable + { + $formatted = sprintf('%s %s %s %s %s', $this->cron_minute, $this->cron_hour, $this->cron_day_of_month, $this->cron_month, $this->cron_day_of_week); + + return CarbonImmutable::createFromTimestamp( + (new CronExpression($formatted))->getNextRunDate()->getTimestamp() + ); + } + + /** + * Return a hashid encoded string to represent the ID of the schedule. + */ + public function getHashidAttribute(): string + { + return Container::getInstance()->make(HashidsInterface::class)->encode($this->id); + } + + /** + * Return tasks belonging to a schedule. + */ + public function tasks(): HasMany + { + return $this->hasMany(Task::class); + } + + /** + * Return the server model that a schedule belongs to. + */ + public function server(): BelongsTo + { + return $this->belongsTo(Server::class); + } +} diff --git a/app/Models/Server.php b/app/Models/Server.php new file mode 100644 index 0000000..f95e1de --- /dev/null +++ b/app/Models/Server.php @@ -0,0 +1,382 @@ + self::STATUS_INSTALLING, + 'oom_disabled' => true, + 'installed_at' => null, + ]; + + /** + * The default relationships to load for all server models. + */ + protected $with = ['allocation']; + + /** + * Fields that are not mass assignable. + */ + protected $guarded = ['id', self::CREATED_AT, self::UPDATED_AT, 'deleted_at', 'installed_at']; + + public static array $validationRules = [ + 'external_id' => 'sometimes|nullable|string|between:1,191|unique:servers', + 'owner_id' => 'required|integer|exists:users,id', + 'name' => 'required|string|min:1|max:191', + 'node_id' => 'required|exists:nodes,id', + 'description' => 'string', + 'status' => 'nullable|string', + 'memory' => 'required|numeric|min:0', + 'swap' => 'required|numeric|min:-1', + 'io' => 'required|numeric|between:10,1000', + 'cpu' => 'required|numeric|min:0', + 'threads' => 'nullable|regex:/^[0-9-,]+$/', + 'oom_disabled' => 'sometimes|boolean', + 'disk' => 'required|numeric|min:0', + 'allocation_id' => 'required|bail|unique:servers|exists:allocations,id', + 'nest_id' => 'required|exists:nests,id', + 'egg_id' => 'required|exists:eggs,id', + 'startup' => 'required|string', + 'skip_scripts' => 'sometimes|boolean', + 'image' => ['required', 'string', 'max:191', 'regex:/^[\w\.\/\-:@ ]*$/'], + 'database_limit' => 'present|nullable|integer|min:0', + 'allocation_limit' => 'sometimes|nullable|integer|min:0', + 'backup_limit' => 'present|nullable|integer|min:0', + ]; + + /** + * Cast values to correct type. + */ + protected $casts = [ + 'node_id' => 'integer', + 'skip_scripts' => 'boolean', + 'owner_id' => 'integer', + 'memory' => 'integer', + 'swap' => 'integer', + 'disk' => 'integer', + 'io' => 'integer', + 'cpu' => 'integer', + 'oom_disabled' => 'boolean', + 'allocation_id' => 'integer', + 'nest_id' => 'integer', + 'egg_id' => 'integer', + 'database_limit' => 'integer', + 'allocation_limit' => 'integer', + 'backup_limit' => 'integer', + self::CREATED_AT => 'datetime', + self::UPDATED_AT => 'datetime', + 'deleted_at' => 'datetime', + 'installed_at' => 'datetime', + ]; + + /** + * Returns the format for server allocations when communicating with the Daemon. + */ + public function getAllocationMappings(): array + { + return $this->allocations->where('node_id', $this->node_id)->groupBy('ip')->map(function ($item) { + return $item->pluck('port'); + })->toArray(); + } + + public function isInstalled(): bool + { + return $this->status !== self::STATUS_INSTALLING && $this->status !== self::STATUS_INSTALL_FAILED; + } + + public function isSuspended(): bool + { + return $this->status === self::STATUS_SUSPENDED; + } + + /** + * Gets the user who owns the server. + */ + public function user(): BelongsTo + { + return $this->belongsTo(User::class, 'owner_id'); + } + + /** + * Gets the subusers associated with a server. + */ + public function subusers(): HasMany + { + return $this->hasMany(Subuser::class, 'server_id', 'id'); + } + + /** + * Gets the default allocation for a server. + */ + public function allocation(): HasOne + { + return $this->hasOne(Allocation::class, 'id', 'allocation_id'); + } + + /** + * Gets all allocations associated with this server. + */ + public function allocations(): HasMany + { + return $this->hasMany(Allocation::class, 'server_id'); + } + + /** + * Gets information for the nest associated with this server. + */ + public function nest(): BelongsTo + { + return $this->belongsTo(Nest::class); + } + + /** + * Gets information for the egg associated with this server. + */ + public function egg(): HasOne + { + return $this->hasOne(Egg::class, 'id', 'egg_id'); + } + + /** + * Gets information for the service variables associated with this server. + */ + public function variables(): HasMany + { + return $this->hasMany(EggVariable::class, 'egg_id', 'egg_id') + ->select(['egg_variables.*', 'server_variables.variable_value as server_value']) + ->leftJoin('server_variables', function (JoinClause $join) { + // Don't forget to join against the server ID as well since the way we're using this relationship + // would actually return all the variables and their values for _all_ servers using that egg, + // rather than only the server for this model. + // + // @see https://github.com/pterodactyl/panel/issues/2250 + $join->on('server_variables.variable_id', 'egg_variables.id') + ->where('server_variables.server_id', $this->id); + }); + } + + /** + * Gets information for the node associated with this server. + */ + public function node(): BelongsTo + { + return $this->belongsTo(Node::class); + } + + /** + * Gets information for the tasks associated with this server. + */ + public function schedules(): HasMany + { + return $this->hasMany(Schedule::class); + } + + /** + * Gets all databases associated with a server. + */ + public function databases(): HasMany + { + return $this->hasMany(Database::class); + } + + /** + * Returns the location that a server belongs to. + * + * @throws \Exception + */ + public function location(): \Znck\Eloquent\Relations\BelongsToThrough + { + return $this->belongsToThrough(Location::class, Node::class); + } + + /** + * Returns the associated server transfer. + */ + public function transfer(): HasOne + { + return $this->hasOne(ServerTransfer::class)->whereNull('successful')->orderByDesc('id'); + } + + public function backups(): HasMany + { + return $this->hasMany(Backup::class); + } + + /** + * Returns all mounts that have this server has mounted. + */ + public function mounts(): HasManyThrough + { + return $this->hasManyThrough(Mount::class, MountServer::class, 'server_id', 'id', 'id', 'mount_id'); + } + + /** + * Returns all of the activity log entries where the server is the subject. + */ + public function activity(): MorphToMany + { + return $this->morphToMany(ActivityLog::class, 'subject', 'activity_log_subjects'); + } + + /** + * Checks if the server is currently in a user-accessible state. If not, an + * exception is raised. This should be called whenever something needs to make + * sure the server is not in a weird state that should block user access. + * + * @throws \Pterodactyl\Exceptions\Http\Server\ServerStateConflictException + */ + public function validateCurrentState() + { + if ( + $this->isSuspended() || + $this->node->isUnderMaintenance() || + !$this->isInstalled() || + $this->status === self::STATUS_RESTORING_BACKUP || + !is_null($this->transfer) + ) { + throw new ServerStateConflictException($this); + } + } + + /** + * Checks if the server is currently in a transferable state. If not, an + * exception is raised. This should be called whenever something needs to make + * sure the server is able to be transferred and is not currently being transferred + * or installed. + */ + public function validateTransferState() + { + if ( + !$this->isInstalled() || + $this->status === self::STATUS_RESTORING_BACKUP || + !is_null($this->transfer) + ) { + throw new ServerStateConflictException($this); + } + } +} diff --git a/app/Models/ServerTransfer.php b/app/Models/ServerTransfer.php new file mode 100644 index 0000000..da46188 --- /dev/null +++ b/app/Models/ServerTransfer.php @@ -0,0 +1,94 @@ + 'int', + 'old_node' => 'int', + 'new_node' => 'int', + 'old_allocation' => 'int', + 'new_allocation' => 'int', + 'old_additional_allocations' => 'array', + 'new_additional_allocations' => 'array', + 'successful' => 'bool', + 'archived' => 'bool', + ]; + + public static array $validationRules = [ + 'server_id' => 'required|numeric|exists:servers,id', + 'old_node' => 'required|numeric', + 'new_node' => 'required|numeric', + 'old_allocation' => 'required|numeric', + 'new_allocation' => 'required|numeric', + 'old_additional_allocations' => 'nullable|array', + 'old_additional_allocations.*' => 'numeric', + 'new_additional_allocations' => 'nullable|array', + 'new_additional_allocations.*' => 'numeric', + 'successful' => 'sometimes|nullable|boolean', + ]; + + /** + * Gets the server associated with a server transfer. + */ + public function server(): BelongsTo + { + return $this->belongsTo(Server::class); + } + + /** + * Gets the source node associated with a server transfer. + */ + public function oldNode(): HasOne + { + return $this->hasOne(Node::class, 'id', 'old_node'); + } + + /** + * Gets the target node associated with a server transfer. + */ + public function newNode(): HasOne + { + return $this->hasOne(Node::class, 'id', 'new_node'); + } +} diff --git a/app/Models/ServerVariable.php b/app/Models/ServerVariable.php new file mode 100644 index 0000000..46ae57d --- /dev/null +++ b/app/Models/ServerVariable.php @@ -0,0 +1,57 @@ + 'integer', + 'variable_id' => 'integer', + ]; + + public static array $validationRules = [ + 'server_id' => 'required|int', + 'variable_id' => 'required|int', + 'variable_value' => 'string', + ]; + + /** + * Returns the server this variable is associated with. + */ + public function server(): BelongsTo + { + return $this->belongsTo(Server::class); + } + + /** + * Returns information about a given variables parent. + */ + public function variable(): BelongsTo + { + return $this->belongsTo(EggVariable::class, 'variable_id'); + } +} diff --git a/app/Models/Session.php b/app/Models/Session.php new file mode 100644 index 0000000..baacdb8 --- /dev/null +++ b/app/Models/Session.php @@ -0,0 +1,21 @@ + 'string', + 'user_id' => 'integer', + ]; +} diff --git a/app/Models/Setting.php b/app/Models/Setting.php new file mode 100644 index 0000000..b8812bc --- /dev/null +++ b/app/Models/Setting.php @@ -0,0 +1,27 @@ + 'required|string|between:1,191', + 'value' => 'string', + ]; +} diff --git a/app/Models/Subuser.php b/app/Models/Subuser.php new file mode 100644 index 0000000..40950d5 --- /dev/null +++ b/app/Models/Subuser.php @@ -0,0 +1,86 @@ + 'int', + 'server_id' => 'int', + 'permissions' => 'array', + ]; + + public static array $validationRules = [ + 'user_id' => 'required|numeric|exists:users,id', + 'server_id' => 'required|numeric|exists:servers,id', + 'permissions' => 'nullable|array', + 'permissions.*' => 'string', + ]; + + /** + * Return a hashid encoded string to represent the ID of the subuser. + */ + public function getHashidAttribute(): string + { + return app()->make('hashids')->encode($this->id); + } + + /** + * Gets the server associated with a subuser. + */ + public function server(): BelongsTo + { + return $this->belongsTo(Server::class); + } + + /** + * Gets the user associated with a subuser. + */ + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } + + /** + * Gets the permissions associated with a subuser. + */ + public function permissions(): HasMany + { + return $this->hasMany(Permission::class); + } +} diff --git a/app/Models/Task.php b/app/Models/Task.php new file mode 100644 index 0000000..8e77273 --- /dev/null +++ b/app/Models/Task.php @@ -0,0 +1,127 @@ + 'integer', + 'schedule_id' => 'integer', + 'sequence_id' => 'integer', + 'time_offset' => 'integer', + 'is_queued' => 'boolean', + 'continue_on_failure' => 'boolean', + ]; + + /** + * Default attributes when creating a new model. + */ + protected $attributes = [ + 'time_offset' => 0, + 'is_queued' => false, + 'continue_on_failure' => false, + ]; + + public static array $validationRules = [ + 'schedule_id' => 'required|numeric|exists:schedules,id', + 'sequence_id' => 'required|numeric|min:1', + 'action' => 'required|string', + 'payload' => 'required_unless:action,backup|string', + 'time_offset' => 'required|numeric|between:0,900', + 'is_queued' => 'boolean', + 'continue_on_failure' => 'boolean', + ]; + + /** + * {@inheritDoc} + */ + public function getRouteKeyName(): string + { + return $this->getKeyName(); + } + + /** + * Return a hashid encoded string to represent the ID of the task. + */ + public function getHashidAttribute(): string + { + return Container::getInstance()->make(HashidsInterface::class)->encode($this->id); + } + + /** + * Return the schedule that a task belongs to. + */ + public function schedule(): BelongsTo + { + return $this->belongsTo(Schedule::class); + } + + /** + * Return the server a task is assigned to, acts as a belongsToThrough. + */ + public function server(): \Znck\Eloquent\Relations\BelongsToThrough + { + return $this->belongsToThrough(Server::class, Schedule::class); + } +} diff --git a/app/Models/TaskLog.php b/app/Models/TaskLog.php new file mode 100644 index 0000000..81b7fdb --- /dev/null +++ b/app/Models/TaskLog.php @@ -0,0 +1,30 @@ + 'integer', + 'task_id' => 'integer', + 'run_status' => 'integer', + 'run_time' => 'datetime', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + ]; +} diff --git a/app/Models/Traits/HasAccessTokens.php b/app/Models/Traits/HasAccessTokens.php new file mode 100644 index 0000000..ed042cc --- /dev/null +++ b/app/Models/Traits/HasAccessTokens.php @@ -0,0 +1,41 @@ +hasMany(Sanctum::$personalAccessTokenModel); + } + + public function createToken(?string $memo, ?array $ips): NewAccessToken + { + /** @var \Pterodactyl\Models\ApiKey $token */ + $token = $this->tokens()->forceCreate([ + 'user_id' => $this->id, + 'key_type' => ApiKey::TYPE_ACCOUNT, + 'identifier' => ApiKey::generateTokenIdentifier(ApiKey::TYPE_ACCOUNT), + 'token' => encrypt($plain = Str::random(ApiKey::KEY_LENGTH)), + 'memo' => $memo ?? '', + 'allowed_ips' => $ips ?? [], + ]); + + return new NewAccessToken($token, $plain); + } +} diff --git a/app/Models/User.php b/app/Models/User.php new file mode 100644 index 0000000..c22c21c --- /dev/null +++ b/app/Models/User.php @@ -0,0 +1,273 @@ + 'boolean', + 'use_totp' => 'boolean', + 'gravatar' => 'boolean', + 'totp_authenticated_at' => 'datetime', + ]; + + /** + * The attributes excluded from the model's JSON form. + */ + protected $hidden = ['password', 'remember_token', 'totp_secret', 'totp_authenticated_at']; + + /** + * Default values for specific fields in the database. + */ + protected $attributes = [ + 'external_id' => null, + 'root_admin' => false, + 'language' => 'en', + 'use_totp' => false, + 'totp_secret' => null, + ]; + + /** + * Rules verifying that the data being stored matches the expectations of the database. + */ + public static array $validationRules = [ + 'uuid' => 'required|string|size:36|unique:users,uuid', + 'email' => 'required|email|between:1,191|unique:users,email', + 'external_id' => 'sometimes|nullable|string|max:191|unique:users,external_id', + 'username' => 'required|between:1,191|unique:users,username', + 'name_first' => 'required|string|between:1,191', + 'name_last' => 'required|string|between:1,191', + 'password' => 'sometimes|nullable|string', + 'root_admin' => 'boolean', + 'language' => 'string', + 'use_totp' => 'boolean', + 'totp_secret' => 'nullable|string', + ]; + + /** + * Implement language verification by overriding Eloquence's gather + * rules function. + */ + public static function getRules(): array + { + $rules = parent::getRules(); + + $rules['language'][] = new In(array_keys((new self())->getAvailableLanguages())); + $rules['username'][] = new Username(); + + return $rules; + } + + /** + * Return the user model in a format that can be passed over to Vue templates. + */ + public function toVueObject(): array + { + return Collection::make($this->toArray())->except(['id', 'external_id'])->toArray(); + } + + /** + * Send the password reset notification. + * + * @param string $token + */ + public function sendPasswordResetNotification($token) + { + Activity::event('auth:reset-password') + ->withRequestMetadata() + ->subject($this) + ->log('sending password reset email'); + + $this->notify(new ResetPasswordNotification($token)); + } + + /** + * Store the username as a lowercase string. + */ + public function setUsernameAttribute(string $value) + { + $this->attributes['username'] = mb_strtolower($value); + } + + /** + * Return a concatenated result for the accounts full name. + */ + public function getNameAttribute(): string + { + return trim($this->name_first . ' ' . $this->name_last); + } + + /** + * Returns all servers that a user owns. + */ + public function servers(): HasMany + { + return $this->hasMany(Server::class, 'owner_id'); + } + + public function apiKeys(): HasMany + { + return $this->hasMany(ApiKey::class) + ->where('key_type', ApiKey::TYPE_ACCOUNT); + } + + public function recoveryTokens(): HasMany + { + return $this->hasMany(RecoveryToken::class); + } + + public function sshKeys(): HasMany + { + return $this->hasMany(UserSSHKey::class); + } + + /** + * Returns all the activity logs where this user is the subject — not to + * be confused by activity logs where this user is the _actor_. + */ + public function activity(): MorphToMany + { + return $this->morphToMany(ActivityLog::class, 'subject', 'activity_log_subjects'); + } + + /** + * Returns all the servers that a user can access by way of being the owner of the + * server, or because they are assigned as a subuser for that server. + */ + public function accessibleServers(): Builder + { + return Server::query() + ->select('servers.*') + ->leftJoin('subusers', 'subusers.server_id', '=', 'servers.id') + ->where(function (Builder $builder) { + $builder->where('servers.owner_id', $this->id)->orWhere('subusers.user_id', $this->id); + }) + ->groupBy('servers.id'); + } +} diff --git a/app/Models/UserSSHKey.php b/app/Models/UserSSHKey.php new file mode 100644 index 0000000..fb3c8f3 --- /dev/null +++ b/app/Models/UserSSHKey.php @@ -0,0 +1,64 @@ + ['required', 'string'], + 'fingerprint' => ['required', 'string'], + 'public_key' => ['required', 'string'], + ]; + + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } +} diff --git a/app/Notifications/AccountCreated.php b/app/Notifications/AccountCreated.php new file mode 100644 index 0000000..fb13380 --- /dev/null +++ b/app/Notifications/AccountCreated.php @@ -0,0 +1,47 @@ +greeting('Hello ' . $this->user->name . '!') + ->line('You are receiving this email because an account has been created for you on ' . config('app.name') . '.') + ->line('Username: ' . $this->user->username) + ->line('Email: ' . $this->user->email); + + if (!is_null($this->token)) { + return $message->action('Setup Your Account', url('/auth/password/reset/' . $this->token . '?email=' . urlencode($this->user->email))); + } + + return $message; + } +} diff --git a/app/Notifications/AddedToServer.php b/app/Notifications/AddedToServer.php new file mode 100644 index 0000000..c78c7db --- /dev/null +++ b/app/Notifications/AddedToServer.php @@ -0,0 +1,43 @@ +server = (object) $server; + } + + /** + * Get the notification's delivery channels. + */ + public function via(): array + { + return ['mail']; + } + + /** + * Get the mail representation of the notification. + */ + public function toMail(): MailMessage + { + return (new MailMessage()) + ->greeting('Hello ' . $this->server->user . '!') + ->line('You have been added as a subuser for the following server, allowing you certain control over the server.') + ->line('Server Name: ' . $this->server->name) + ->action('Visit Server', url('/server/' . $this->server->uuidShort)); + } +} diff --git a/app/Notifications/MailTested.php b/app/Notifications/MailTested.php new file mode 100644 index 0000000..892ff7b --- /dev/null +++ b/app/Notifications/MailTested.php @@ -0,0 +1,27 @@ +subject('Pterodactyl Test Message') + ->greeting('Hello ' . $this->user->name . '!') + ->line('This is a test of the Pterodactyl mail system. You\'re good to go!'); + } +} diff --git a/app/Notifications/RemovedFromServer.php b/app/Notifications/RemovedFromServer.php new file mode 100644 index 0000000..492dc60 --- /dev/null +++ b/app/Notifications/RemovedFromServer.php @@ -0,0 +1,44 @@ +server = (object) $server; + } + + /** + * Get the notification's delivery channels. + */ + public function via(): array + { + return ['mail']; + } + + /** + * Get the mail representation of the notification. + */ + public function toMail(): MailMessage + { + return (new MailMessage()) + ->error() + ->greeting('Hello ' . $this->server->user . '.') + ->line('You have been removed as a subuser for the following server.') + ->line('Server Name: ' . $this->server->name) + ->action('Visit Panel', route('index')); + } +} diff --git a/app/Notifications/SendPasswordReset.php b/app/Notifications/SendPasswordReset.php new file mode 100644 index 0000000..b424c82 --- /dev/null +++ b/app/Notifications/SendPasswordReset.php @@ -0,0 +1,40 @@ +subject('Reset Password') + ->line('You are receiving this email because we received a password reset request for your account.') + ->action('Reset Password', url('/auth/password/reset/' . $this->token . '?email=' . urlencode($notifiable->email))) + ->line('If you did not request a password reset, no further action is required.'); + } +} diff --git a/app/Notifications/ServerInstalled.php b/app/Notifications/ServerInstalled.php new file mode 100644 index 0000000..46f38ff --- /dev/null +++ b/app/Notifications/ServerInstalled.php @@ -0,0 +1,60 @@ +server->loadMissing('user'); + + $this->server = $event->server; + $this->user = $event->server->user; + + // Since we are calling this notification directly from an event listener we need to fire off the dispatcher + // to send the email now. Don't use send() or you'll end up firing off two different events. + Container::getInstance()->make(Dispatcher::class)->sendNow($this->user, $this); + } + + /** + * Get the notification's delivery channels. + */ + public function via(): array + { + return ['mail']; + } + + /** + * Get the mail representation of the notification. + */ + public function toMail(): MailMessage + { + return (new MailMessage()) + ->greeting('Hello ' . $this->user->username . '.') + ->line('Your server has finished installing and is now ready for you to use.') + ->line('Server Name: ' . $this->server->name) + ->action('Login and Begin Using', route('index')); + } +} diff --git a/app/Observers/EggVariableObserver.php b/app/Observers/EggVariableObserver.php new file mode 100644 index 0000000..a18718f --- /dev/null +++ b/app/Observers/EggVariableObserver.php @@ -0,0 +1,22 @@ +field_type) { + unset($variable->field_type); + } + } + + public function updating(EggVariable $variable): void + { + if ($variable->field_type) { + unset($variable->field_type); + } + } +} diff --git a/app/Observers/ServerObserver.php b/app/Observers/ServerObserver.php new file mode 100644 index 0000000..5127652 --- /dev/null +++ b/app/Observers/ServerObserver.php @@ -0,0 +1,76 @@ +user->notify(new AddedToServer([ + 'user' => $subuser->user->name_first, + 'name' => $subuser->server->name, + 'uuidShort' => $subuser->server->uuidShort, + ])); + } + + /** + * Listen to the Subuser deleting event. + */ + public function deleting(Subuser $subuser): void + { + event(new Events\Subuser\Deleting($subuser)); + } + + /** + * Listen to the Subuser deleted event. + */ + public function deleted(Subuser $subuser): void + { + event(new Events\Subuser\Deleted($subuser)); + + $subuser->user->notify(new RemovedFromServer([ + 'user' => $subuser->user->name_first, + 'name' => $subuser->server->name, + ])); + } +} diff --git a/app/Observers/UserObserver.php b/app/Observers/UserObserver.php new file mode 100644 index 0000000..dd017a2 --- /dev/null +++ b/app/Observers/UserObserver.php @@ -0,0 +1,43 @@ +subusers->where('user_id', $user->id)->first(); + if (!$subuser || empty($permission)) { + return false; + } + + return in_array($permission, $subuser->permissions); + } + + /** + * Runs before any of the functions are called. Used to determine if user is root admin, if so, ignore permissions. + */ + public function before(User $user, string $ability, Server $server): bool + { + if ($user->root_admin || $server->owner_id === $user->id) { + return true; + } + + return $this->checkPermission($user, $server, $ability); + } + + /** + * This is a horrendous hack to avoid Laravel's "smart" behavior that does + * not call the before() function if there isn't a function matching the + * policy permission. + */ + public function __call(string $name, mixed $arguments) + { + // do nothing + } +} diff --git a/app/Providers/ActivityLogServiceProvider.php b/app/Providers/ActivityLogServiceProvider.php new file mode 100644 index 0000000..7a3de75 --- /dev/null +++ b/app/Providers/ActivityLogServiceProvider.php @@ -0,0 +1,20 @@ +app->scoped(ActivityLogBatchService::class); + $this->app->scoped(ActivityLogTargetableService::class); + } +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php new file mode 100644 index 0000000..63c1201 --- /dev/null +++ b/app/Providers/AppServiceProvider.php @@ -0,0 +1,97 @@ +versionData()['version'] ?? 'undefined'); + View::share('appIsGit', $this->versionData()['is_git'] ?? false); + + Paginator::useBootstrap(); + + // If the APP_URL value is set with https:// make sure we force it here. Theoretically + // this should just work with the proxy logic, but there are a lot of cases where it + // doesn't, and it triggers a lot of support requests, so lets just head it off here. + // + // @see https://github.com/pterodactyl/panel/issues/3623 + if (Str::startsWith(config('app.url') ?? '', 'https://')) { + URL::forceScheme('https'); + } + + Relation::enforceMorphMap([ + 'allocation' => Models\Allocation::class, + 'api_key' => Models\ApiKey::class, + 'backup' => Models\Backup::class, + 'database' => Models\Database::class, + 'egg' => Models\Egg::class, + 'egg_variable' => Models\EggVariable::class, + 'schedule' => Models\Schedule::class, + 'server' => Models\Server::class, + 'ssh_key' => Models\UserSSHKey::class, + 'task' => Models\Task::class, + 'user' => Models\User::class, + ]); + } + + /** + * Register application service providers. + */ + public function register(): void + { + // Only load the settings service provider if the environment + // is configured to allow it. + if (!config('pterodactyl.load_environment_only', false) && $this->app->environment() !== 'testing') { + $this->app->register(SettingsServiceProvider::class); + } + + $this->app->singleton('extensions.themes', function () { + return new Theme(); + }); + } + + /** + * Return version information for the footer. + */ + protected function versionData(): array + { + return Cache::remember('git-version', 5, function () { + if (file_exists(base_path('.git/HEAD'))) { + $head = explode(' ', file_get_contents(base_path('.git/HEAD'))); + + if (array_key_exists(1, $head)) { + $path = base_path('.git/' . trim($head[1])); + } + } + + if (isset($path) && file_exists($path)) { + return [ + 'version' => substr(file_get_contents($path), 0, 8), + 'is_git' => true, + ]; + } + + return [ + 'version' => config('app.version'), + 'is_git' => false, + ]; + }); + } +} diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php new file mode 100644 index 0000000..9f9556b --- /dev/null +++ b/app/Providers/AuthServiceProvider.php @@ -0,0 +1,29 @@ + ServerPolicy::class, + ]; + + public function boot(): void + { + Sanctum::usePersonalAccessTokenModel(ApiKey::class); + } + + public function register(): void + { + Sanctum::ignoreMigrations(); + } +} diff --git a/app/Providers/BackupsServiceProvider.php b/app/Providers/BackupsServiceProvider.php new file mode 100644 index 0000000..25ce9bd --- /dev/null +++ b/app/Providers/BackupsServiceProvider.php @@ -0,0 +1,25 @@ +app->singleton(BackupManager::class, function ($app) { + return new BackupManager($app); + }); + } + + public function provides(): array + { + return [BackupManager::class]; + } +} diff --git a/app/Providers/BladeServiceProvider.php b/app/Providers/BladeServiceProvider.php new file mode 100644 index 0000000..3512acf --- /dev/null +++ b/app/Providers/BladeServiceProvider.php @@ -0,0 +1,19 @@ +app->make('blade.compiler') + ->directive('datetimeHuman', function ($expression) { + return "setTimezone(config('app.timezone'))->toDateTimeString(); ?>"; + }); + } +} diff --git a/app/Providers/BroadcastServiceProvider.php b/app/Providers/BroadcastServiceProvider.php new file mode 100644 index 0000000..0630a63 --- /dev/null +++ b/app/Providers/BroadcastServiceProvider.php @@ -0,0 +1,24 @@ +id === (int) $userId; + }); + } +} diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php new file mode 100644 index 0000000..6f91f24 --- /dev/null +++ b/app/Providers/EventServiceProvider.php @@ -0,0 +1,43 @@ + [ServerInstalledNotification::class], + ]; + + protected $subscribe = [ + AuthenticationListener::class, + ]; + + /** + * Register any events for your application. + */ + public function boot(): void + { + parent::boot(); + + User::observe(UserObserver::class); + Server::observe(ServerObserver::class); + Subuser::observe(SubuserObserver::class); + EggVariable::observe(EggVariableObserver::class); + } +} diff --git a/app/Providers/HashidsServiceProvider.php b/app/Providers/HashidsServiceProvider.php new file mode 100644 index 0000000..e2493e8 --- /dev/null +++ b/app/Providers/HashidsServiceProvider.php @@ -0,0 +1,29 @@ +app->singleton(HashidsInterface::class, function () { + /** @var \Illuminate\Contracts\Config\Repository $config */ + $config = $this->app['config']; + + return new Hashids( + $config->get('hashids.salt', ''), + $config->get('hashids.length', 0), + $config->get('hashids.alphabet', 'abcdefghijkmlnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890') + ); + }); + + $this->app->alias(HashidsInterface::class, 'hashids'); + } +} diff --git a/app/Providers/RepositoryServiceProvider.php b/app/Providers/RepositoryServiceProvider.php new file mode 100644 index 0000000..b06ca7a --- /dev/null +++ b/app/Providers/RepositoryServiceProvider.php @@ -0,0 +1,67 @@ +app->bind(AllocationRepositoryInterface::class, AllocationRepository::class); + $this->app->bind(ApiKeyRepositoryInterface::class, ApiKeyRepository::class); + $this->app->bind(DatabaseRepositoryInterface::class, DatabaseRepository::class); + $this->app->bind(DatabaseHostRepositoryInterface::class, DatabaseHostRepository::class); + $this->app->bind(EggRepositoryInterface::class, EggRepository::class); + $this->app->bind(EggVariableRepositoryInterface::class, EggVariableRepository::class); + $this->app->bind(LocationRepositoryInterface::class, LocationRepository::class); + $this->app->bind(NestRepositoryInterface::class, NestRepository::class); + $this->app->bind(NodeRepositoryInterface::class, NodeRepository::class); + $this->app->bind(ScheduleRepositoryInterface::class, ScheduleRepository::class); + $this->app->bind(ServerRepositoryInterface::class, ServerRepository::class); + $this->app->bind(ServerVariableRepositoryInterface::class, ServerVariableRepository::class); + $this->app->bind(SessionRepositoryInterface::class, SessionRepository::class); + $this->app->bind(SettingsRepositoryInterface::class, SettingsRepository::class); + $this->app->bind(SubuserRepositoryInterface::class, SubuserRepository::class); + $this->app->bind(TaskRepositoryInterface::class, TaskRepository::class); + $this->app->bind(UserRepositoryInterface::class, UserRepository::class); + } +} diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php new file mode 100644 index 0000000..43c99f8 --- /dev/null +++ b/app/Providers/RouteServiceProvider.php @@ -0,0 +1,110 @@ +configureRateLimiting(); + + // Disable trimming string values when requesting file information — it isn't helpful + // and messes up the ability to actually open a directory that ends with a space. + TrimStrings::skipWhen(function (Request $request) { + return preg_match(self::FILE_PATH_REGEX, $request->getPathInfo()) === 1; + }); + + // This is needed to make use of the "resolveRouteBinding" functionality in the + // model. Without it you'll never trigger that logic flow thus resulting in a 404 + // error because we request databases with a HashID, and not with a normal ID. + Route::model('database', Database::class); + + $this->routes(function () { + Route::middleware('web')->group(function () { + Route::middleware(['auth.session', RequireTwoFactorAuthentication::class]) + ->group(base_path('routes/base.php')); + + Route::middleware(['auth.session', RequireTwoFactorAuthentication::class, AdminAuthenticate::class]) + ->prefix('/admin') + ->group(base_path('routes/admin.php')); + + Route::middleware('guest')->prefix('/auth')->group(base_path('routes/auth.php')); + }); + + Route::middleware(['api', RequireTwoFactorAuthentication::class])->group(function () { + Route::middleware(['application-api', 'throttle:api.application']) + ->prefix('/api/application') + ->scopeBindings() + ->group(base_path('routes/api-application.php')); + + Route::middleware(['client-api', 'throttle:api.client']) + ->prefix('/api/client') + ->scopeBindings() + ->group(base_path('routes/api-client.php')); + }); + + Route::middleware('daemon') + ->prefix('/api/remote') + ->scopeBindings() + ->group(base_path('routes/api-remote.php')); + }); + } + + /** + * Configure the rate limiters for the application. + */ + protected function configureRateLimiting(): void + { + // Authentication rate limiting. For login and checkpoint endpoints we'll apply + // a limit of 10 requests per minute, for the forgot password endpoint apply a + // limit of two per minute for the requester so that there is less ability to + // trigger email spam. + RateLimiter::for('authentication', function (Request $request) { + if ($request->route()->named('auth.post.forgot-password')) { + return Limit::perMinute(2)->by($request->ip()); + } + + return Limit::perMinute(10); + }); + + // Configure the throttles for both the application and client APIs below. + // This is configurable per-instance in "config/http.php". By default this + // limiter will be tied to the specific request user, and falls back to the + // request IP if there is no request user present for the key. + // + // This means that an authenticated API user cannot use IP switching to get + // around the limits. + RateLimiter::for('api.client', function (Request $request) { + $key = optional($request->user())->uuid ?: $request->ip(); + + return Limit::perMinutes( + config('http.rate_limit.client_period'), + config('http.rate_limit.client') + )->by($key); + }); + + RateLimiter::for('api.application', function (Request $request) { + $key = optional($request->user())->uuid ?: $request->ip(); + + return Limit::perMinutes( + config('http.rate_limit.application_period'), + config('http.rate_limit.application') + )->by($key); + }); + } +} diff --git a/app/Providers/SettingsServiceProvider.php b/app/Providers/SettingsServiceProvider.php new file mode 100644 index 0000000..dbcca85 --- /dev/null +++ b/app/Providers/SettingsServiceProvider.php @@ -0,0 +1,113 @@ +get('mail.default') === 'smtp') { + $this->keys = array_merge($this->keys, $this->emailKeys); + } + + try { + $values = $settings->all()->mapWithKeys(function ($setting) { + return [$setting->key => $setting->value]; + })->toArray(); + } catch (QueryException $exception) { + $log->notice('A query exception was encountered while trying to load settings from the database: ' . $exception->getMessage()); + + return; + } + + foreach ($this->keys as $key) { + $value = array_get($values, 'settings::' . $key, $config->get(str_replace(':', '.', $key))); + if (in_array($key, self::$encrypted)) { + try { + $value = $encrypter->decrypt($value); + } catch (DecryptException $exception) { + } + } + + switch (strtolower($value)) { + case 'true': + case '(true)': + $value = true; + break; + case 'false': + case '(false)': + $value = false; + break; + case 'empty': + case '(empty)': + $value = ''; + break; + case 'null': + case '(null)': + $value = null; + } + + $config->set(str_replace(':', '.', $key), $value); + } + } + + public static function getEncryptedKeys(): array + { + return self::$encrypted; + } +} diff --git a/app/Providers/ViewComposerServiceProvider.php b/app/Providers/ViewComposerServiceProvider.php new file mode 100644 index 0000000..8ab7208 --- /dev/null +++ b/app/Providers/ViewComposerServiceProvider.php @@ -0,0 +1,17 @@ +app->make('view')->composer('*', AssetComposer::class); + } +} diff --git a/app/Repositories/Eloquent/AllocationRepository.php b/app/Repositories/Eloquent/AllocationRepository.php new file mode 100644 index 0000000..6eb8b6d --- /dev/null +++ b/app/Repositories/Eloquent/AllocationRepository.php @@ -0,0 +1,100 @@ +select('id') + ->whereNull('server_id') + ->where('node_id', $node) + ->get() + ->pluck('id') + ->toArray(); + } + + /** + * Return a concatenated result set of node ips that already have at least one + * server assigned to that IP. This allows for filtering out sets for + * dedicated allocation IPs. + * + * If an array of nodes is passed the results will be limited to allocations + * in those nodes. + */ + protected function getDiscardableDedicatedAllocations(array $nodes = []): array + { + $query = Allocation::query()->selectRaw('CONCAT_WS("-", node_id, ip) as result'); + + if (!empty($nodes)) { + $query->whereIn('node_id', $nodes); + } + + return $query->whereNotNull('server_id') + ->groupByRaw('CONCAT(node_id, ip)') + ->get() + ->pluck('result') + ->toArray(); + } + + /** + * Return a single allocation from those meeting the requirements. + */ + public function getRandomAllocation(array $nodes, array $ports, bool $dedicated = false): ?Allocation + { + $query = Allocation::query()->whereNull('server_id'); + + if (!empty($nodes)) { + $query->whereIn('node_id', $nodes); + } + + if (!empty($ports)) { + $query->where(function (Builder $inner) use ($ports) { + $whereIn = []; + foreach ($ports as $port) { + if (is_array($port)) { + $inner->orWhereBetween('port', $port); + continue; + } + + $whereIn[] = $port; + } + + if (!empty($whereIn)) { + $inner->orWhereIn('port', $whereIn); + } + }); + } + + // If this allocation should not be shared with any other servers get + // the data and modify the query as necessary, + if ($dedicated) { + $discard = $this->getDiscardableDedicatedAllocations($nodes); + + if (!empty($discard)) { + $query->whereNotIn( + $this->getBuilder()->raw('CONCAT_WS("-", node_id, ip)'), + $discard + ); + } + } + + return $query->inRandomOrder()->first(); + } +} diff --git a/app/Repositories/Eloquent/ApiKeyRepository.php b/app/Repositories/Eloquent/ApiKeyRepository.php new file mode 100644 index 0000000..eb1a362 --- /dev/null +++ b/app/Repositories/Eloquent/ApiKeyRepository.php @@ -0,0 +1,61 @@ +getBuilder()->where('user_id', $user->id) + ->where('key_type', ApiKey::TYPE_ACCOUNT) + ->get($this->getColumns()); + } + + /** + * Get all the application API keys that exist for a specific user. + */ + public function getApplicationKeys(User $user): Collection + { + return $this->getBuilder()->where('user_id', $user->id) + ->where('key_type', ApiKey::TYPE_APPLICATION) + ->get($this->getColumns()); + } + + /** + * Delete an account API key from the panel for a specific user. + */ + public function deleteAccountKey(User $user, string $identifier): int + { + return $this->getBuilder()->where('user_id', $user->id) + ->where('key_type', ApiKey::TYPE_ACCOUNT) + ->where('identifier', $identifier) + ->delete(); + } + + /** + * Delete an application API key from the panel for a specific user. + */ + public function deleteApplicationKey(User $user, string $identifier): int + { + return $this->getBuilder()->where('user_id', $user->id) + ->where('key_type', ApiKey::TYPE_APPLICATION) + ->where('identifier', $identifier) + ->delete(); + } +} diff --git a/app/Repositories/Eloquent/BackupRepository.php b/app/Repositories/Eloquent/BackupRepository.php new file mode 100644 index 0000000..bbc5d2c --- /dev/null +++ b/app/Repositories/Eloquent/BackupRepository.php @@ -0,0 +1,45 @@ +getBuilder() + ->withTrashed() + ->where('server_id', $server) + ->where(function ($query) { + $query->whereNull('completed_at') + ->orWhere('is_successful', '=', true); + }) + ->where('created_at', '>=', Carbon::now()->subSeconds($seconds)->toDateTimeString()) + ->get() + ->toBase(); + } + + /** + * Returns a query filtering only non-failed backups for a specific server. + */ + public function getNonFailedBackups(Server $server): HasMany + { + return $server->backups()->where(function ($query) { + $query->whereNull('completed_at') + ->orWhere('is_successful', true); + }); + } +} diff --git a/app/Repositories/Eloquent/DatabaseHostRepository.php b/app/Repositories/Eloquent/DatabaseHostRepository.php new file mode 100644 index 0000000..2c2b9dc --- /dev/null +++ b/app/Repositories/Eloquent/DatabaseHostRepository.php @@ -0,0 +1,27 @@ +getBuilder()->withCount('databases')->with('node')->get(); + } +} diff --git a/app/Repositories/Eloquent/DatabaseRepository.php b/app/Repositories/Eloquent/DatabaseRepository.php new file mode 100644 index 0000000..9bc33d0 --- /dev/null +++ b/app/Repositories/Eloquent/DatabaseRepository.php @@ -0,0 +1,136 @@ +connection; + } + + /** + * Set the connection name to execute statements against. + */ + public function setConnection(string $connection): self + { + $this->connection = $connection; + + return $this; + } + + /** + * Return all the databases belonging to a server. + */ + public function getDatabasesForServer(int $server): Collection + { + return $this->getBuilder()->with('host')->where('server_id', $server)->get($this->getColumns()); + } + + /** + * Return all the databases for a given host with the server relationship loaded. + */ + public function getDatabasesForHost(int $host, int $count = 25): LengthAwarePaginator + { + return $this->getBuilder()->with('server') + ->where('database_host_id', $host) + ->paginate($count, $this->getColumns()); + } + + /** + * Create a new database on a given connection. + */ + public function createDatabase(string $database): bool + { + return $this->run(sprintf('CREATE DATABASE IF NOT EXISTS `%s`', $database)); + } + + /** + * Create a new database user on a given connection. + */ + public function createUser(string $username, string $remote, string $password, ?int $max_connections): bool + { + $args = [$username, $remote, $password]; + $command = 'CREATE USER `%s`@`%s` IDENTIFIED BY \'%s\''; + + if (!empty($max_connections)) { + $args[] = $max_connections; + $command .= ' WITH MAX_USER_CONNECTIONS %s'; + } + + return $this->run(sprintf($command, ...$args)); + } + + /** + * Give a specific user access to a given database. + */ + public function assignUserToDatabase(string $database, string $username, string $remote): bool + { + return $this->run(sprintf( + 'GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, REFERENCES, INDEX, LOCK TABLES, CREATE ROUTINE, ALTER ROUTINE, EXECUTE, CREATE TEMPORARY TABLES, CREATE VIEW, SHOW VIEW, EVENT, TRIGGER ON `%s`.* TO `%s`@`%s`', + $database, + $username, + $remote + )); + } + + /** + * Flush the privileges for a given connection. + */ + public function flush(): bool + { + return $this->run('FLUSH PRIVILEGES'); + } + + /** + * Drop a given database on a specific connection. + */ + public function dropDatabase(string $database): bool + { + return $this->run(sprintf('DROP DATABASE IF EXISTS `%s`', $database)); + } + + /** + * Drop a given user on a specific connection. + */ + public function dropUser(string $username, string $remote): bool + { + return $this->run(sprintf('DROP USER IF EXISTS `%s`@`%s`', $username, $remote)); + } + + /** + * Run the provided statement against the database on a given connection. + */ + private function run(string $statement): bool + { + return $this->database->connection($this->getConnection())->statement($statement); + } +} diff --git a/app/Repositories/Eloquent/EggRepository.php b/app/Repositories/Eloquent/EggRepository.php new file mode 100644 index 0000000..8c4a01a --- /dev/null +++ b/app/Repositories/Eloquent/EggRepository.php @@ -0,0 +1,86 @@ +getBuilder()->with('variables')->findOrFail($id, $this->getColumns()); + } catch (ModelNotFoundException) { + throw new RecordNotFoundException(); + } + } + + /** + * Return all eggs and their relations to be used in the daemon API. + */ + public function getAllWithCopyAttributes(): Collection + { + return $this->getBuilder()->with('scriptFrom', 'configFrom')->get($this->getColumns()); + } + + /** + * Return an egg with the scriptFrom and configFrom relations loaded onto the model. + * + * @param int|string $value + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function getWithCopyAttributes($value, string $column = 'id'): Egg + { + Assert::true(is_digit($value) || is_string($value), 'First argument passed to getWithCopyAttributes must be an integer or string, received %s.'); + + try { + return $this->getBuilder()->with('scriptFrom', 'configFrom')->where($column, '=', $value)->firstOrFail($this->getColumns()); + } catch (ModelNotFoundException) { + throw new RecordNotFoundException(); + } + } + + /** + * Return all the data needed to export a service. + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function getWithExportAttributes(int $id): Egg + { + try { + return $this->getBuilder()->with('scriptFrom', 'configFrom', 'variables')->findOrFail($id, $this->getColumns()); + } catch (ModelNotFoundException) { + throw new RecordNotFoundException(); + } + } + + /** + * Confirm a copy script belongs to the same nest as the item trying to use it. + */ + public function isCopyableScript(int $copyFromId, int $service): bool + { + return $this->getBuilder()->whereNull('copy_script_from') + ->where('id', '=', $copyFromId) + ->where('nest_id', '=', $service) + ->exists(); + } +} diff --git a/app/Repositories/Eloquent/EggVariableRepository.php b/app/Repositories/Eloquent/EggVariableRepository.php new file mode 100644 index 0000000..8ca35de --- /dev/null +++ b/app/Repositories/Eloquent/EggVariableRepository.php @@ -0,0 +1,31 @@ +getBuilder()->where([ + ['egg_id', '=', $egg], + ['user_viewable', '=', 1], + ['user_editable', '=', 1], + ])->get($this->getColumns()); + } +} diff --git a/app/Repositories/Eloquent/EloquentRepository.php b/app/Repositories/Eloquent/EloquentRepository.php new file mode 100644 index 0000000..a4e7f2f --- /dev/null +++ b/app/Repositories/Eloquent/EloquentRepository.php @@ -0,0 +1,288 @@ +useRequestFilters = $usingFilters; + + return $this; + } + + /** + * Returns the request instance. + */ + protected function request(): Request + { + return $this->app->make(Request::class); + } + + /** + * Paginate the response data based on the page para. + */ + protected function paginate(Builder $instance, int $default = 50): LengthAwarePaginator + { + if (!$this->useRequestFilters) { + return $instance->paginate($default); + } + + return $instance->paginate($this->request()->query('per_page', $default)); + } + + /** + * Return an instance of the eloquent model bound to this + * repository instance. + */ + public function getModel(): Model + { + return $this->model; + } + + /** + * Return an instance of the builder to use for this repository. + */ + public function getBuilder(): Builder + { + return $this->getModel()->newQuery(); + } + + /** + * Create a new record in the database and return the associated model. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + */ + public function create(array $fields, bool $validate = true, bool $force = false): Model|bool + { + $instance = $this->getBuilder()->newModelInstance(); + ($force) ? $instance->forceFill($fields) : $instance->fill($fields); + + if (!$validate) { + $saved = $instance->skipValidation()->save(); + } else { + if (!$saved = $instance->save()) { + throw new DataValidationException($instance->getValidator(), $instance); + } + } + + return ($this->withFresh) ? $instance->fresh() : $saved; + } + + /** + * Find a model that has the specific ID passed. + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function find(int $id): Model + { + try { + return $this->getBuilder()->findOrFail($id, $this->getColumns()); + } catch (ModelNotFoundException) { + throw new RecordNotFoundException(); + } + } + + /** + * Find a model matching an array of where clauses. + */ + public function findWhere(array $fields): Collection + { + return $this->getBuilder()->where($fields)->get($this->getColumns()); + } + + /** + * Find and return the first matching instance for the given fields. + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function findFirstWhere(array $fields): Model + { + try { + return $this->getBuilder()->where($fields)->firstOrFail($this->getColumns()); + } catch (ModelNotFoundException) { + throw new RecordNotFoundException(); + } + } + + /** + * Return a count of records matching the passed arguments. + */ + public function findCountWhere(array $fields): int + { + return $this->getBuilder()->where($fields)->count($this->getColumns()); + } + + /** + * Delete a given record from the database. + */ + public function delete(int $id, bool $destroy = false): int + { + return $this->deleteWhere(['id' => $id], $destroy); + } + + /** + * Delete records matching the given attributes. + */ + public function deleteWhere(array $attributes, bool $force = false): int + { + $instance = $this->getBuilder()->where($attributes); + + return ($force) ? $instance->forceDelete() : $instance->delete(); + } + + /** + * Update a given ID with the passed array of fields. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function update(int $id, array $fields, bool $validate = true, bool $force = false): Model|bool + { + try { + $instance = $this->getBuilder()->where('id', $id)->firstOrFail(); + } catch (ModelNotFoundException) { + throw new RecordNotFoundException(); + } + + ($force) ? $instance->forceFill($fields) : $instance->fill($fields); + + if (!$validate) { + $saved = $instance->skipValidation()->save(); + } else { + if (!$saved = $instance->save()) { + throw new DataValidationException($instance->getValidator(), $instance); + } + } + + return ($this->withFresh) ? $instance->fresh() : $saved; + } + + /** + * Update a model using the attributes passed. + */ + public function updateWhere(array $attributes, array $values): int + { + return $this->getBuilder()->where($attributes)->update($values); + } + + /** + * Perform a mass update where matching records are updated using whereIn. + * This does not perform any model data validation. + */ + public function updateWhereIn(string $column, array $values, array $fields): int + { + Assert::notEmpty($column, 'First argument passed to updateWhereIn must be a non-empty string.'); + + return $this->getBuilder()->whereIn($column, $values)->update($fields); + } + + /** + * Update a record if it exists in the database, otherwise create it. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function updateOrCreate(array $where, array $fields, bool $validate = true, bool $force = false): Model|bool + { + foreach ($where as $item) { + Assert::true(is_scalar($item) || is_null($item), 'First argument passed to updateOrCreate should be an array of scalar or null values, received an array value of %s.'); + } + + try { + $instance = $this->setColumns('id')->findFirstWhere($where); + } catch (RecordNotFoundException) { + return $this->create(array_merge($where, $fields), $validate, $force); + } + + return $this->update($instance->id, $fields, $validate, $force); + } + + /** + * Return all records associated with the given model. + * + * @deprecated Just use the model + */ + public function all(): Collection + { + return $this->getBuilder()->get($this->getColumns()); + } + + /** + * Return a paginated result set using a search term if set on the repository. + */ + public function paginated(int $perPage): LengthAwarePaginator + { + return $this->getBuilder()->paginate($perPage, $this->getColumns()); + } + + /** + * Insert a single or multiple records into the database at once skipping + * validation and mass assignment checking. + */ + public function insert(array $data): bool + { + return $this->getBuilder()->insert($data); + } + + /** + * Insert multiple records into the database and ignore duplicates. + */ + public function insertIgnore(array $values): bool + { + if (empty($values)) { + return true; + } + + foreach ($values as $key => $value) { + ksort($value); + $values[$key] = $value; + } + + $bindings = array_values(array_filter(array_flatten($values, 1), function ($binding) { + return !$binding instanceof Expression; + })); + + $grammar = $this->getBuilder()->toBase()->getGrammar(); + $table = $grammar->wrapTable($this->getModel()->getTable()); + $columns = $grammar->columnize(array_keys(reset($values))); + + $parameters = collect($values)->map(function ($record) use ($grammar) { + return sprintf('(%s)', $grammar->parameterize($record)); + })->implode(', '); + + $statement = "insert ignore into $table ($columns) values $parameters"; + + return $this->getBuilder()->getConnection()->statement($statement, $bindings); + } + + /** + * Get the amount of entries in the database. + * + * @deprecated just use the count method off a model + */ + public function count(): int + { + return $this->getBuilder()->count(); + } +} diff --git a/app/Repositories/Eloquent/LocationRepository.php b/app/Repositories/Eloquent/LocationRepository.php new file mode 100644 index 0000000..e25737c --- /dev/null +++ b/app/Repositories/Eloquent/LocationRepository.php @@ -0,0 +1,64 @@ +getBuilder()->withCount('nodes', 'servers')->get($this->getColumns()); + } + + /** + * Return all the available locations with the nodes as a relationship. + */ + public function getAllWithNodes(): Collection + { + return $this->getBuilder()->with('nodes')->get($this->getColumns()); + } + + /** + * Return all the nodes and their respective count of servers for a location. + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function getWithNodes(int $id): Location + { + try { + return $this->getBuilder()->with('nodes.servers')->findOrFail($id, $this->getColumns()); + } catch (ModelNotFoundException) { + throw new RecordNotFoundException(); + } + } + + /** + * Return a location and the count of nodes in that location. + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function getWithNodeCount(int $id): Location + { + try { + return $this->getBuilder()->withCount('nodes')->findOrFail($id, $this->getColumns()); + } catch (ModelNotFoundException) { + throw new RecordNotFoundException(); + } + } +} diff --git a/app/Repositories/Eloquent/MountRepository.php b/app/Repositories/Eloquent/MountRepository.php new file mode 100644 index 0000000..b33e6cb --- /dev/null +++ b/app/Repositories/Eloquent/MountRepository.php @@ -0,0 +1,57 @@ +getBuilder()->withCount('eggs', 'nodes')->get($this->getColumns()); + } + + /** + * Return all the mounts and their respective relations. + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function getWithRelations(string $id): Mount + { + try { + return $this->getBuilder()->with('eggs', 'nodes')->findOrFail($id, $this->getColumns()); + } catch (ModelNotFoundException $exception) { + throw new RecordNotFoundException(); + } + } + + /** + * Return mounts available to a server (ignoring if they are or are not mounted). + */ + public function getMountListForServer(Server $server): Collection + { + return $this->getBuilder() + ->whereHas('eggs', function ($q) use ($server) { + $q->where('id', '=', $server->egg_id); + }) + ->whereHas('nodes', function ($q) use ($server) { + $q->where('id', '=', $server->node_id); + }) + ->get($this->getColumns()); + } +} diff --git a/app/Repositories/Eloquent/NestRepository.php b/app/Repositories/Eloquent/NestRepository.php new file mode 100644 index 0000000..f472e4b --- /dev/null +++ b/app/Repositories/Eloquent/NestRepository.php @@ -0,0 +1,77 @@ +getBuilder()->with('eggs', 'eggs.variables'); + + if (!is_null($id)) { + $instance = $instance->find($id, $this->getColumns()); + if (!$instance) { + throw new RecordNotFoundException(); + } + + return $instance; + } + + return $instance->get($this->getColumns()); + } + + /** + * Return a nest or all nests and the count of eggs and servers for that nest. + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function getWithCounts(int $id = null): Collection|Nest + { + $instance = $this->getBuilder()->withCount(['eggs', 'servers']); + + if (!is_null($id)) { + $instance = $instance->find($id, $this->getColumns()); + if (!$instance) { + throw new RecordNotFoundException(); + } + + return $instance; + } + + return $instance->get($this->getColumns()); + } + + /** + * Return a nest along with its associated eggs and the servers relation on those eggs. + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function getWithEggServers(int $id): Nest + { + $instance = $this->getBuilder()->with('eggs.servers')->find($id, $this->getColumns()); + if (!$instance) { + throw new RecordNotFoundException(); + } + + /* @var Nest $instance */ + return $instance; + } +} diff --git a/app/Repositories/Eloquent/NodeRepository.php b/app/Repositories/Eloquent/NodeRepository.php new file mode 100644 index 0000000..fe019e5 --- /dev/null +++ b/app/Repositories/Eloquent/NodeRepository.php @@ -0,0 +1,152 @@ +getBuilder() + ->selectRaw('IFNULL(SUM(servers.memory), 0) as sum_memory, IFNULL(SUM(servers.disk), 0) as sum_disk') + ->join('servers', 'servers.node_id', '=', 'nodes.id') + ->where('node_id', '=', $node->id) + ->first(); + + return Collection::make(['disk' => $stats->sum_disk, 'memory' => $stats->sum_memory]) + ->mapWithKeys(function ($value, $key) use ($node) { + $maxUsage = $node->{$key}; + if ($node->{$key . '_overallocate'} > 0) { + $maxUsage = $node->{$key} * (1 + ($node->{$key . '_overallocate'} / 100)); + } + + $percent = ($value / $maxUsage) * 100; + + return [ + $key => [ + 'value' => number_format($value), + 'max' => number_format($maxUsage), + 'percent' => $percent, + 'css' => ($percent <= self::THRESHOLD_PERCENTAGE_LOW) ? 'green' : (($percent > self::THRESHOLD_PERCENTAGE_MEDIUM) ? 'red' : 'yellow'), + ], + ]; + }) + ->toArray(); + } + + /** + * Return the usage stats for a single node. + */ + public function getUsageStatsRaw(Node $node): array + { + $stats = $this->getBuilder()->select( + $this->getBuilder()->raw('IFNULL(SUM(servers.memory), 0) as sum_memory, IFNULL(SUM(servers.disk), 0) as sum_disk') + )->join('servers', 'servers.node_id', '=', 'nodes.id')->where('node_id', $node->id)->first(); + + return collect(['disk' => $stats->sum_disk, 'memory' => $stats->sum_memory])->mapWithKeys(function ($value, $key) use ($node) { + $maxUsage = $node->{$key}; + if ($node->{$key . '_overallocate'} > 0) { + $maxUsage = $node->{$key} * (1 + ($node->{$key . '_overallocate'} / 100)); + } + + return [ + $key => [ + 'value' => $value, + 'max' => $maxUsage, + ], + ]; + })->toArray(); + } + + /** + * Return a single node with location and server information. + */ + public function loadLocationAndServerCount(Node $node, bool $refresh = false): Node + { + if (!$node->relationLoaded('location') || $refresh) { + $node->load('location'); + } + + // This is quite ugly and can probably be improved down the road. + // And by probably, I mean it should. + if (is_null($node->servers_count) || $refresh) { + $node->load('servers'); + $node->setRelation('servers_count', count($node->getRelation('servers'))); + unset($node->servers); + } + + return $node; + } + + /** + * Attach a paginated set of allocations to a node mode including + * any servers that are also attached to those allocations. + */ + public function loadNodeAllocations(Node $node, bool $refresh = false): Node + { + $node->setRelation( + 'allocations', + $node->allocations() + ->orderByRaw('server_id IS NOT NULL DESC, server_id IS NULL') + ->orderByRaw('INET_ATON(ip) ASC') + ->orderBy('port') + ->with('server:id,name') + ->paginate(50) + ); + + return $node; + } + + /** + * Return a collection of nodes for all locations to use in server creation UI. + */ + public function getNodesForServerCreation(): Collection + { + return $this->getBuilder()->with('allocations')->get()->map(function (Node $item) { + $filtered = $item->getRelation('allocations')->where('server_id', null)->map(function ($map) { + return collect($map)->only(['id', 'ip', 'port']); + }); + + $item->ports = $filtered->map(function ($map) { + return [ + 'id' => $map['id'], + 'text' => sprintf('%s:%s', $map['ip'], $map['port']), + ]; + })->values(); + + return [ + 'id' => $item->id, + 'text' => $item->name, + 'allocations' => $item->ports, + ]; + })->values(); + } + + /** + * Returns a node with the given id with the Node's resource usage. + */ + public function getNodeWithResourceUsage(int $node_id): Node + { + $instance = $this->getBuilder() + ->select(['nodes.id', 'nodes.fqdn', 'nodes.scheme', 'nodes.daemon_token', 'nodes.daemonListen', 'nodes.memory', 'nodes.disk', 'nodes.memory_overallocate', 'nodes.disk_overallocate']) + ->selectRaw('IFNULL(SUM(servers.memory), 0) as sum_memory, IFNULL(SUM(servers.disk), 0) as sum_disk') + ->leftJoin('servers', 'servers.node_id', '=', 'nodes.id') + ->where('nodes.id', $node_id); + + return $instance->first(); + } +} diff --git a/app/Repositories/Eloquent/PermissionRepository.php b/app/Repositories/Eloquent/PermissionRepository.php new file mode 100644 index 0000000..603829b --- /dev/null +++ b/app/Repositories/Eloquent/PermissionRepository.php @@ -0,0 +1,18 @@ +getBuilder()->withCount('tasks')->where('server_id', '=', $server)->get($this->getColumns()); + } + + /** + * Return a schedule model with all the associated tasks as a relationship. + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function getScheduleWithTasks(int $schedule): Schedule + { + try { + return $this->getBuilder()->with('tasks')->findOrFail($schedule, $this->getColumns()); + } catch (ModelNotFoundException) { + throw new RecordNotFoundException(); + } + } +} diff --git a/app/Repositories/Eloquent/ServerRepository.php b/app/Repositories/Eloquent/ServerRepository.php new file mode 100644 index 0000000..a353dba --- /dev/null +++ b/app/Repositories/Eloquent/ServerRepository.php @@ -0,0 +1,179 @@ +relationLoaded('egg') || $refresh) { + $server->load('egg.scriptFrom'); + } + + return $server; + } + + /** + * Return a collection of servers with their associated data for rebuild operations. + */ + public function getDataForRebuild(int $server = null, int $node = null): Collection + { + $instance = $this->getBuilder()->with(['allocation', 'allocations', 'egg', 'node']); + + if (!is_null($server) && is_null($node)) { + $instance = $instance->where('id', '=', $server); + } elseif (is_null($server) && !is_null($node)) { + $instance = $instance->where('node_id', '=', $node); + } + + return $instance->get($this->getColumns()); + } + + /** + * Return a collection of servers with their associated data for reinstall operations. + */ + public function getDataForReinstall(int $server = null, int $node = null): Collection + { + $instance = $this->getBuilder()->with(['allocation', 'allocations', 'egg', 'node']); + + if (!is_null($server) && is_null($node)) { + $instance = $instance->where('id', '=', $server); + } elseif (is_null($server) && !is_null($node)) { + $instance = $instance->where('node_id', '=', $node); + } + + return $instance->get($this->getColumns()); + } + + /** + * Return a server model and all variables associated with the server. + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function findWithVariables(int $id): Server + { + try { + return $this->getBuilder()->with('egg.variables', 'variables') + ->where($this->getModel()->getKeyName(), '=', $id) + ->firstOrFail($this->getColumns()); + } catch (ModelNotFoundException) { + throw new RecordNotFoundException(); + } + } + + /** + * Get the primary allocation for a given server. If a model is passed into + * the function, load the allocation relationship onto it. Otherwise, find and + * return the server from the database. + */ + public function getPrimaryAllocation(Server $server, bool $refresh = false): Server + { + if (!$server->relationLoaded('allocation') || $refresh) { + $server->load('allocation'); + } + + return $server; + } + + /** + * Return enough data to be used for the creation of a server via the daemon. + */ + public function getDataForCreation(Server $server, bool $refresh = false): Server + { + foreach (['allocation', 'allocations', 'egg'] as $relation) { + if (!$server->relationLoaded($relation) || $refresh) { + $server->load($relation); + } + } + + return $server; + } + + /** + * Load associated databases onto the server model. + */ + public function loadDatabaseRelations(Server $server, bool $refresh = false): Server + { + if (!$server->relationLoaded('databases') || $refresh) { + $server->load('databases.host'); + } + + return $server; + } + + /** + * Get data for use when updating a server on the Daemon. Returns an array of + * the egg which is used for build and rebuild. Only loads relations + * if they are missing, or refresh is set to true. + */ + public function getDaemonServiceData(Server $server, bool $refresh = false): array + { + if (!$server->relationLoaded('egg') || $refresh) { + $server->load('egg'); + } + + return [ + 'egg' => $server->getRelation('egg')->uuid, + ]; + } + + /** + * Return a server by UUID. + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function getByUuid(string $uuid): Server + { + try { + /** @var \Pterodactyl\Models\Server $model */ + $model = $this->getBuilder() + ->with('nest', 'node') + ->where(function (Builder $query) use ($uuid) { + $query->where('uuidShort', $uuid)->orWhere('uuid', $uuid); + }) + ->firstOrFail($this->getColumns()); + + return $model; + } catch (ModelNotFoundException) { + throw new RecordNotFoundException(); + } + } + + /** + * Check if a given UUID and UUID-Short string are unique to a server. + */ + public function isUniqueUuidCombo(string $uuid, string $short): bool + { + return !$this->getBuilder()->where('uuid', '=', $uuid)->orWhere('uuidShort', '=', $short)->exists(); + } + + /** + * Returns all the servers that exist for a given node in a paginated response. + */ + public function loadAllServersForNode(int $node, int $limit): LengthAwarePaginator + { + return $this->getBuilder() + ->with(['user', 'nest', 'egg']) + ->where('node_id', '=', $node) + ->paginate($limit); + } +} diff --git a/app/Repositories/Eloquent/ServerVariableRepository.php b/app/Repositories/Eloquent/ServerVariableRepository.php new file mode 100644 index 0000000..26ded2c --- /dev/null +++ b/app/Repositories/Eloquent/ServerVariableRepository.php @@ -0,0 +1,17 @@ +getBuilder()->where('user_id', $user)->get($this->getColumns()); + } + + /** + * Delete a session for a given user. + */ + public function deleteUserSession(int $user, string $session): ?int + { + return $this->getBuilder()->where('user_id', $user)->where('id', $session)->delete(); + } +} diff --git a/app/Repositories/Eloquent/SettingsRepository.php b/app/Repositories/Eloquent/SettingsRepository.php new file mode 100644 index 0000000..df22fce --- /dev/null +++ b/app/Repositories/Eloquent/SettingsRepository.php @@ -0,0 +1,75 @@ +clearCache($key); + $this->withoutFreshModel()->updateOrCreate(['key' => $key], ['value' => $value ?? '']); + + self::$cache[$key] = $value; + } + + /** + * Retrieve a persistent setting from the database. + */ + public function get(string $key, mixed $default = null): mixed + { + // If item has already been requested return it from the cache. If + // we already know it is missing, immediately return the default value. + if (array_key_exists($key, self::$cache)) { + return self::$cache[$key]; + } elseif (array_key_exists($key, self::$databaseMiss)) { + return value($default); + } + + $instance = $this->getBuilder()->where('key', $key)->first(); + if (is_null($instance)) { + self::$databaseMiss[$key] = true; + + return value($default); + } + + return self::$cache[$key] = $instance->value; + } + + /** + * Remove a key from the database cache. + */ + public function forget(string $key) + { + $this->clearCache($key); + $this->deleteWhere(['key' => $key]); + } + + /** + * Remove a key from the cache. + */ + private function clearCache(string $key) + { + unset(self::$cache[$key], self::$databaseMiss[$key]); + } +} diff --git a/app/Repositories/Eloquent/SubuserRepository.php b/app/Repositories/Eloquent/SubuserRepository.php new file mode 100644 index 0000000..1413787 --- /dev/null +++ b/app/Repositories/Eloquent/SubuserRepository.php @@ -0,0 +1,69 @@ +relationLoaded('server') || $refresh) { + $subuser->load('server'); + } + + if (!$subuser->relationLoaded('user') || $refresh) { + $subuser->load('user'); + } + + return $subuser; + } + + /** + * Return a subuser with the associated permissions relationship. + */ + public function getWithPermissions(Subuser $subuser, bool $refresh = false): Subuser + { + if (!$subuser->relationLoaded('permissions') || $refresh) { + $subuser->load('permissions'); + } + + if (!$subuser->relationLoaded('user') || $refresh) { + $subuser->load('user'); + } + + return $subuser; + } + + /** + * Return a subuser and associated permissions given a user_id and server_id. + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function getWithPermissionsUsingUserAndServer(int $user, int $server): Subuser + { + $instance = $this->getBuilder()->with('permissions')->where([ + ['user_id', '=', $user], + ['server_id', '=', $server], + ])->first(); + + if (is_null($instance)) { + throw new RecordNotFoundException(); + } + + return $instance; + } +} diff --git a/app/Repositories/Eloquent/TaskRepository.php b/app/Repositories/Eloquent/TaskRepository.php new file mode 100644 index 0000000..942c54d --- /dev/null +++ b/app/Repositories/Eloquent/TaskRepository.php @@ -0,0 +1,44 @@ +getBuilder()->with('server.user', 'schedule')->findOrFail($id, $this->getColumns()); + } catch (ModelNotFoundException) { + throw new RecordNotFoundException(); + } + } + + /** + * Returns the next task in a schedule. + */ + public function getNextTask(int $schedule, int $index): ?Task + { + return $this->getBuilder()->where('schedule_id', '=', $schedule) + ->orderBy('sequence_id') + ->where('sequence_id', '>', $index) + ->first($this->getColumns()); + } +} diff --git a/app/Repositories/Eloquent/UserRepository.php b/app/Repositories/Eloquent/UserRepository.php new file mode 100644 index 0000000..4445346 --- /dev/null +++ b/app/Repositories/Eloquent/UserRepository.php @@ -0,0 +1,17 @@ +initializeModel($this->model()); + } + + /** + * Return the model backing this repository. + */ + abstract public function model(): string; + + /** + * Return the model being used for this repository. + */ + public function getModel(): Model + { + return $this->model; + } + + /** + * Setup column selection functionality. + * + * @param array|string $columns + */ + public function setColumns($columns = ['*']): self + { + $clone = clone $this; + $clone->columns = is_array($columns) ? $columns : func_get_args(); + + return $clone; + } + + /** + * Return the columns to be selected in the repository call. + */ + public function getColumns(): array + { + return $this->columns; + } + + /** + * Stop repository update functions from returning a fresh + * model when changes are committed. + */ + public function withoutFreshModel(): self + { + return $this->setFreshModel(false); + } + + /** + * Return a fresh model with a repository updates a model. + */ + public function withFreshModel(): self + { + return $this->setFreshModel(); + } + + /** + * Set whether the repository should return a fresh model + * when changes are committed. + */ + public function setFreshModel(bool $fresh = true): self + { + $clone = clone $this; + $clone->withFresh = $fresh; + + return $clone; + } + + /** + * Take the provided model and make it accessible to the rest of the repository. + */ + protected function initializeModel(string ...$model): mixed + { + switch (count($model)) { + case 1: + return $this->model = $this->app->make($model[0]); + case 2: + return $this->model = call_user_func([$this->app->make($model[0]), $model[1]]); + default: + throw new \InvalidArgumentException('Model must be a FQDN or an array with a count of two.'); + } + } +} diff --git a/app/Repositories/Wings/DaemonBackupRepository.php b/app/Repositories/Wings/DaemonBackupRepository.php new file mode 100644 index 0000000..4c164ee --- /dev/null +++ b/app/Repositories/Wings/DaemonBackupRepository.php @@ -0,0 +1,93 @@ +adapter = $adapter; + + return $this; + } + + /** + * Tells the remote Daemon to begin generating a backup for the server. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function backup(Backup $backup): ResponseInterface + { + Assert::isInstanceOf($this->server, Server::class); + + try { + return $this->getHttpClient()->post( + sprintf('/api/servers/%s/backup', $this->server->uuid), + [ + 'json' => [ + 'adapter' => $this->adapter ?? config('backups.default'), + 'uuid' => $backup->uuid, + 'ignore' => implode("\n", $backup->ignored_files), + ], + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } + + /** + * Sends a request to Wings to begin restoring a backup for a server. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function restore(Backup $backup, string $url = null, bool $truncate = false): ResponseInterface + { + Assert::isInstanceOf($this->server, Server::class); + + try { + return $this->getHttpClient()->post( + sprintf('/api/servers/%s/backup/%s/restore', $this->server->uuid, $backup->uuid), + [ + 'json' => [ + 'adapter' => $backup->disk, + 'truncate_directory' => $truncate, + 'download_url' => $url ?? '', + ], + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } + + /** + * Deletes a backup from the daemon. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function delete(Backup $backup): ResponseInterface + { + Assert::isInstanceOf($this->server, Server::class); + + try { + return $this->getHttpClient()->delete( + sprintf('/api/servers/%s/backup/%s', $this->server->uuid, $backup->uuid) + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } +} diff --git a/app/Repositories/Wings/DaemonCommandRepository.php b/app/Repositories/Wings/DaemonCommandRepository.php new file mode 100644 index 0000000..cde29ff --- /dev/null +++ b/app/Repositories/Wings/DaemonCommandRepository.php @@ -0,0 +1,33 @@ +server, Server::class); + + try { + return $this->getHttpClient()->post( + sprintf('/api/servers/%s/commands', $this->server->uuid), + [ + 'json' => ['commands' => is_array($command) ? $command : [$command]], + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } +} diff --git a/app/Repositories/Wings/DaemonConfigurationRepository.php b/app/Repositories/Wings/DaemonConfigurationRepository.php new file mode 100644 index 0000000..5580f9a --- /dev/null +++ b/app/Repositories/Wings/DaemonConfigurationRepository.php @@ -0,0 +1,46 @@ +getHttpClient()->get('/api/system' . (!is_null($version) ? '?v=' . $version : '')); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + + return json_decode($response->getBody()->__toString(), true); + } + + /** + * Updates the configuration information for a daemon. Updates the information for + * this instance using a passed-in model. This allows us to change plenty of information + * in the model, and still use the old, pre-update model to actually make the HTTP request. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function update(Node $node): ResponseInterface + { + try { + return $this->getHttpClient()->post( + '/api/update', + ['json' => $node->getConfiguration()] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } +} diff --git a/app/Repositories/Wings/DaemonFileRepository.php b/app/Repositories/Wings/DaemonFileRepository.php new file mode 100644 index 0000000..ca75715 --- /dev/null +++ b/app/Repositories/Wings/DaemonFileRepository.php @@ -0,0 +1,297 @@ +server, Server::class); + + try { + $response = $this->getHttpClient()->get( + sprintf('/api/servers/%s/files/contents', $this->server->uuid), + [ + 'query' => ['file' => $path], + ] + ); + } catch (ClientException|TransferException $exception) { + throw new DaemonConnectionException($exception); + } + + $length = (int) Arr::get($response->getHeader('Content-Length'), 0, 0); + if ($notLargerThan && $length > $notLargerThan) { + throw new FileSizeTooLargeException(); + } + + return $response->getBody()->__toString(); + } + + /** + * Save new contents to a given file. This works for both creating and updating + * a file. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function putContent(string $path, string $content): ResponseInterface + { + Assert::isInstanceOf($this->server, Server::class); + + try { + return $this->getHttpClient()->post( + sprintf('/api/servers/%s/files/write', $this->server->uuid), + [ + 'query' => ['file' => $path], + 'body' => $content, + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } + + /** + * Return a directory listing for a given path. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function getDirectory(string $path): array + { + Assert::isInstanceOf($this->server, Server::class); + + try { + $response = $this->getHttpClient()->get( + sprintf('/api/servers/%s/files/list-directory', $this->server->uuid), + [ + 'query' => ['directory' => $path], + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + + return json_decode($response->getBody(), true); + } + + /** + * Creates a new directory for the server in the given $path. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function createDirectory(string $name, string $path): ResponseInterface + { + Assert::isInstanceOf($this->server, Server::class); + + try { + return $this->getHttpClient()->post( + sprintf('/api/servers/%s/files/create-directory', $this->server->uuid), + [ + 'json' => [ + 'name' => $name, + 'path' => $path, + ], + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } + + /** + * Renames or moves a file on the remote machine. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function renameFiles(?string $root, array $files): ResponseInterface + { + Assert::isInstanceOf($this->server, Server::class); + + try { + return $this->getHttpClient()->put( + sprintf('/api/servers/%s/files/rename', $this->server->uuid), + [ + 'json' => [ + 'root' => $root ?? '/', + 'files' => $files, + ], + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } + + /** + * Copy a given file and give it a unique name. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function copyFile(string $location): ResponseInterface + { + Assert::isInstanceOf($this->server, Server::class); + + try { + return $this->getHttpClient()->post( + sprintf('/api/servers/%s/files/copy', $this->server->uuid), + [ + 'json' => [ + 'location' => $location, + ], + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } + + /** + * Delete a file or folder for the server. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function deleteFiles(?string $root, array $files): ResponseInterface + { + Assert::isInstanceOf($this->server, Server::class); + + try { + return $this->getHttpClient()->post( + sprintf('/api/servers/%s/files/delete', $this->server->uuid), + [ + 'json' => [ + 'root' => $root ?? '/', + 'files' => $files, + ], + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } + + /** + * Compress the given files or folders in the given root. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function compressFiles(?string $root, array $files): array + { + Assert::isInstanceOf($this->server, Server::class); + + try { + $response = $this->getHttpClient()->post( + sprintf('/api/servers/%s/files/compress', $this->server->uuid), + [ + 'json' => [ + 'root' => $root ?? '/', + 'files' => $files, + ], + // Wait for up to 15 minutes for the archive to be completed when calling this endpoint + // since it will likely take quite awhile for large directories. + 'timeout' => 60 * 15, + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + + return json_decode($response->getBody(), true); + } + + /** + * Decompresses a given archive file. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function decompressFile(?string $root, string $file): ResponseInterface + { + Assert::isInstanceOf($this->server, Server::class); + + try { + return $this->getHttpClient()->post( + sprintf('/api/servers/%s/files/decompress', $this->server->uuid), + [ + 'json' => [ + 'root' => $root ?? '/', + 'file' => $file, + ], + // Wait for up to 15 minutes for the decompress to be completed when calling this endpoint + // since it will likely take quite awhile for large directories. + 'timeout' => 60 * 15, + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } + + /** + * Chmods the given files. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function chmodFiles(?string $root, array $files): ResponseInterface + { + Assert::isInstanceOf($this->server, Server::class); + + try { + return $this->getHttpClient()->post( + sprintf('/api/servers/%s/files/chmod', $this->server->uuid), + [ + 'json' => [ + 'root' => $root ?? '/', + 'files' => $files, + ], + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } + + /** + * Pulls a file from the given URL and saves it to the disk. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function pull(string $url, ?string $directory, array $params = []): ResponseInterface + { + Assert::isInstanceOf($this->server, Server::class); + + $attributes = [ + 'url' => $url, + 'root' => $directory ?? '/', + 'file_name' => $params['filename'] ?? null, + 'use_header' => $params['use_header'] ?? null, + 'foreground' => $params['foreground'] ?? null, + ]; + + try { + return $this->getHttpClient()->post( + sprintf('/api/servers/%s/files/pull', $this->server->uuid), + [ + 'json' => array_filter($attributes, fn ($value) => !is_null($value)), + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } +} diff --git a/app/Repositories/Wings/DaemonPowerRepository.php b/app/Repositories/Wings/DaemonPowerRepository.php new file mode 100644 index 0000000..4e290cf --- /dev/null +++ b/app/Repositories/Wings/DaemonPowerRepository.php @@ -0,0 +1,31 @@ +server, Server::class); + + try { + return $this->getHttpClient()->post( + sprintf('/api/servers/%s/power', $this->server->uuid), + ['json' => ['action' => $action]] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } +} diff --git a/app/Repositories/Wings/DaemonRepository.php b/app/Repositories/Wings/DaemonRepository.php new file mode 100644 index 0000000..512626d --- /dev/null +++ b/app/Repositories/Wings/DaemonRepository.php @@ -0,0 +1,65 @@ +server = $server; + + $this->setNode($this->server->node); + + return $this; + } + + /** + * Set the node model this request is stemming from. + */ + public function setNode(Node $node): self + { + $this->node = $node; + + return $this; + } + + /** + * Return an instance of the Guzzle HTTP Client to be used for requests. + */ + public function getHttpClient(array $headers = []): Client + { + Assert::isInstanceOf($this->node, Node::class); + + return new Client([ + 'verify' => $this->app->environment('production'), + 'base_uri' => $this->node->getConnectionAddress(), + 'timeout' => config('pterodactyl.guzzle.timeout'), + 'connect_timeout' => config('pterodactyl.guzzle.connect_timeout'), + 'headers' => array_merge($headers, [ + 'Authorization' => 'Bearer ' . $this->node->getDecryptedKey(), + 'Accept' => 'application/json', + 'Content-Type' => 'application/json', + ]), + ]); + } +} diff --git a/app/Repositories/Wings/DaemonServerRepository.php b/app/Repositories/Wings/DaemonServerRepository.php new file mode 100644 index 0000000..f013ac0 --- /dev/null +++ b/app/Repositories/Wings/DaemonServerRepository.php @@ -0,0 +1,158 @@ +server, Server::class); + + try { + $response = $this->getHttpClient()->get( + sprintf('/api/servers/%s', $this->server->uuid) + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception, false); + } + + return json_decode($response->getBody()->__toString(), true); + } + + /** + * Creates a new server on the Wings daemon. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function create(bool $startOnCompletion = true): void + { + Assert::isInstanceOf($this->server, Server::class); + + try { + $this->getHttpClient()->post('/api/servers', [ + 'json' => [ + 'uuid' => $this->server->uuid, + 'start_on_completion' => $startOnCompletion, + ], + ]); + } catch (GuzzleException $exception) { + throw new DaemonConnectionException($exception); + } + } + + /** + * Triggers a server sync on Wings. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function sync(): void + { + Assert::isInstanceOf($this->server, Server::class); + + try { + $this->getHttpClient()->post("/api/servers/{$this->server->uuid}/sync"); + } catch (GuzzleException $exception) { + throw new DaemonConnectionException($exception); + } + } + + /** + * Delete a server from the daemon, forcibly if passed. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function delete(): void + { + Assert::isInstanceOf($this->server, Server::class); + + try { + $this->getHttpClient()->delete('/api/servers/' . $this->server->uuid); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } + + /** + * Reinstall a server on the daemon. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function reinstall(): void + { + Assert::isInstanceOf($this->server, Server::class); + + try { + $this->getHttpClient()->post(sprintf( + '/api/servers/%s/reinstall', + $this->server->uuid + )); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } + + /** + * Requests the daemon to create a full archive of the server. Once the daemon is finished + * they will send a POST request to "/api/remote/servers/{uuid}/archive" with a boolean. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function requestArchive(): void + { + Assert::isInstanceOf($this->server, Server::class); + + try { + $this->getHttpClient()->post(sprintf( + '/api/servers/%s/archive', + $this->server->uuid + )); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } + + /** + * Revokes a single user's JTI by using their ID. This is simply a helper function to + * make it easier to revoke tokens on the fly. This ensures that the JTI key is formatted + * correctly and avoids any costly mistakes in the codebase. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function revokeUserJTI(int $id): void + { + Assert::isInstanceOf($this->server, Server::class); + + $this->revokeJTIs([md5($id . $this->server->uuid)]); + } + + /** + * Revokes an array of JWT JTI's by marking any token generated before the current time on + * the Wings instance as being invalid. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + protected function revokeJTIs(array $jtis): void + { + Assert::isInstanceOf($this->server, Server::class); + + try { + $this->getHttpClient() + ->post(sprintf('/api/servers/%s/ws/deny', $this->server->uuid), [ + 'json' => ['jtis' => $jtis], + ]); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } +} diff --git a/app/Repositories/Wings/DaemonTransferRepository.php b/app/Repositories/Wings/DaemonTransferRepository.php new file mode 100644 index 0000000..9c87452 --- /dev/null +++ b/app/Repositories/Wings/DaemonTransferRepository.php @@ -0,0 +1,33 @@ +getHttpClient()->post(sprintf('/api/servers/%s/transfer', $this->server->uuid), [ + 'json' => [ + 'server_id' => $this->server->uuid, + 'url' => $targetNode->getConnectionAddress() . '/api/transfers', + 'token' => 'Bearer ' . $token->toString(), + 'server' => [ + 'uuid' => $this->server->uuid, + 'start_on_completion' => false, + ], + ], + ]); + } catch (GuzzleException $exception) { + throw new DaemonConnectionException($exception); + } + } +} diff --git a/app/Rules/Fqdn.php b/app/Rules/Fqdn.php new file mode 100644 index 0000000..47baf51 --- /dev/null +++ b/app/Rules/Fqdn.php @@ -0,0 +1,80 @@ +data = $data; + + return $this; + } + + /** + * Validates that the value provided resolves to an IP address. If a scheme is + * specified when this rule is created additional checks will be applied. + * + * @param string $attribute + * @param mixed $value + */ + public function passes($attribute, $value): bool + { + if (filter_var($value, FILTER_VALIDATE_IP)) { + // Check if the scheme is set to HTTPS. + // + // Unless someone owns their IP blocks and decides to pay who knows how much for a + // custom SSL cert, IPs will not be able to use HTTPS. This should prevent most + // home users from making this mistake and wondering why their node is not working. + if ($this->schemeField && Arr::get($this->data, $this->schemeField) === 'https') { + $this->message = 'The :attribute must not be an IP address when HTTPS is enabled.'; + + return false; + } + + return true; + } + + // Lookup A and AAAA DNS records for the FQDN. Note, this function will also resolve CNAMEs + // for us automatically, there is no need to manually resolve them here. + // + // The error suppression is intentional, see https://bugs.php.net/bug.php?id=73149 + $records = @dns_get_record($value, DNS_A + DNS_AAAA); + // If no records were returned fall back to trying to resolve the value using the hosts DNS + // resolution. This will not work for IPv6 which is why we prefer to use `dns_get_record` + // first. + if (!empty($records) || filter_var(gethostbyname($value), FILTER_VALIDATE_IP)) { + return true; + } + + $this->message = 'The :attribute could not be resolved to a valid IP address.'; + + return false; + } + + public function message(): string + { + return $this->message; + } + + /** + * Returns a new instance of the rule with a defined scheme set. + */ + public static function make(string $schemeField = null): self + { + return tap(new static(), function ($fqdn) use ($schemeField) { + $fqdn->schemeField = $schemeField; + }); + } +} diff --git a/app/Rules/Username.php b/app/Rules/Username.php new file mode 100644 index 0000000..b89184e --- /dev/null +++ b/app/Rules/Username.php @@ -0,0 +1,45 @@ +getConstants())->filter(function ($value, $key) { + return substr($key, 0, 9) === 'RESOURCE_'; + })->values()->toArray(); + } +} diff --git a/app/Services/Activity/ActivityLogBatchService.php b/app/Services/Activity/ActivityLogBatchService.php new file mode 100644 index 0000000..f4206ea --- /dev/null +++ b/app/Services/Activity/ActivityLogBatchService.php @@ -0,0 +1,59 @@ +uuid; + } + + /** + * Starts a new batch transaction. If there is already a transaction present + * this will be nested. + */ + public function start(): void + { + if ($this->transaction === 0) { + $this->uuid = Uuid::uuid4()->toString(); + } + + ++$this->transaction; + } + + /** + * Ends a batch transaction, if this is the last transaction in the stack + * the UUID will be cleared out. + */ + public function end(): void + { + $this->transaction = max(0, $this->transaction - 1); + + if ($this->transaction === 0) { + $this->uuid = null; + } + } + + /** + * Executes the logic provided within the callback in the scope of an activity + * log batch transaction. + */ + public function transaction(\Closure $callback): mixed + { + $this->start(); + $result = $callback($this->uuid()); + $this->end(); + + return $result; + } +} diff --git a/app/Services/Activity/ActivityLogService.php b/app/Services/Activity/ActivityLogService.php new file mode 100644 index 0000000..f863852 --- /dev/null +++ b/app/Services/Activity/ActivityLogService.php @@ -0,0 +1,254 @@ +getActivity()->actor_id = null; + $this->getActivity()->actor_type = null; + $this->getActivity()->setRelation('actor', null); + + return $this; + } + + /** + * Sets the action for this activity log. + */ + public function event(string $action): self + { + $this->getActivity()->event = $action; + + return $this; + } + + /** + * Set the description for this activity. + */ + public function description(?string $description): self + { + $this->getActivity()->description = $description; + + return $this; + } + + /** + * Sets the subject model instance. + * + * @template T extends \Illuminate\Database\Eloquent\Model|\Illuminate\Contracts\Auth\Authenticatable + * + * @param T|T[]|null $subjects + */ + public function subject(...$subjects): self + { + foreach (Arr::wrap($subjects) as $subject) { + if (is_null($subject)) { + continue; + } + + foreach ($this->subjects as $entry) { + // If this subject is already tracked in our array of subjects just skip over + // it and move on to the next one in the list. + if ($entry->is($subject)) { + continue 2; + } + } + + $this->subjects[] = $subject; + } + + return $this; + } + + /** + * Sets the actor model instance. + */ + public function actor(Model $actor): self + { + $this->getActivity()->actor()->associate($actor); + + return $this; + } + + /** + * Sets a custom property on the activity log instance. + * + * @param string|array $key + * @param mixed $value + */ + public function property($key, $value = null): self + { + $properties = $this->getActivity()->properties; + $this->activity->properties = is_array($key) + ? $properties->merge($key) + : $properties->put($key, $value); + + return $this; + } + + /** + * Attaches the instance request metadata to the activity log event. + */ + public function withRequestMetadata(): self + { + return $this->property([ + 'ip' => Request::getClientIp(), + 'useragent' => Request::userAgent(), + ]); + } + + /** + * Logs an activity log entry with the set values and then returns the + * model instance to the caller. If there is an exception encountered while + * performing this action it will be logged to the disk but will not interrupt + * the code flow. + */ + public function log(string $description = null): ActivityLog + { + $activity = $this->getActivity(); + + if (!is_null($description)) { + $activity->description = $description; + } + + try { + return $this->save(); + } catch (\Throwable|\Exception $exception) { + if (config('app.env') !== 'production') { + /* @noinspection PhpUnhandledExceptionInspection */ + throw $exception; + } + + Log::error($exception); + } + + return $activity; + } + + /** + * Returns a cloned instance of the service allowing for the creation of a base + * activity log with the ability to change values on the fly without impact. + */ + public function clone(): self + { + return clone $this; + } + + /** + * Executes the provided callback within the scope of a database transaction + * and will only save the activity log entry if everything else successfully + * settles. + * + * @throws \Throwable + */ + public function transaction(\Closure $callback) + { + return $this->connection->transaction(function () use ($callback) { + $response = $callback($this); + + $this->save(); + + return $response; + }); + } + + /** + * Resets the instance and clears out the log. + */ + public function reset(): void + { + $this->activity = null; + $this->subjects = []; + } + + /** + * Returns the current activity log instance. + */ + protected function getActivity(): ActivityLog + { + if ($this->activity) { + return $this->activity; + } + + $this->activity = new ActivityLog([ + 'ip' => Request::ip(), + 'batch_uuid' => $this->batch->uuid(), + 'properties' => Collection::make([]), + 'api_key_id' => $this->targetable->apiKeyId(), + ]); + + if ($subject = $this->targetable->subject()) { + $this->subject($subject); + } + + if ($actor = $this->targetable->actor()) { + $this->actor($actor); + } elseif ($user = $this->manager->guard()->user()) { + if ($user instanceof Model) { + $this->actor($user); + } + } + + return $this->activity; + } + + /** + * Saves the activity log instance and attaches all of the subject models. + * + * @throws \Throwable + */ + protected function save(): ActivityLog + { + Assert::notNull($this->activity); + + $response = $this->connection->transaction(function () { + $this->activity->save(); + + $subjects = Collection::make($this->subjects) + ->map(fn (Model $subject) => [ + 'activity_log_id' => $this->activity->id, + 'subject_id' => $subject->getKey(), + 'subject_type' => $subject->getMorphClass(), + ]) + ->values() + ->toArray(); + + ActivityLogSubject::insert($subjects); + + return $this->activity; + }); + + $this->activity = null; + $this->subjects = []; + + return $response; + } +} diff --git a/app/Services/Activity/ActivityLogTargetableService.php b/app/Services/Activity/ActivityLogTargetableService.php new file mode 100644 index 0000000..4d37e98 --- /dev/null +++ b/app/Services/Activity/ActivityLogTargetableService.php @@ -0,0 +1,51 @@ +actor = $actor; + } + + public function setSubject(Model $subject): void + { + $this->subject = $subject; + } + + public function setApiKeyId(?int $apiKeyId): void + { + $this->apiKeyId = $apiKeyId; + } + + public function actor(): ?Model + { + return $this->actor; + } + + public function subject(): ?Model + { + return $this->subject; + } + + public function apiKeyId(): ?int + { + return $this->apiKeyId; + } + + public function reset(): void + { + $this->actor = null; + $this->subject = null; + $this->apiKeyId = null; + } +} diff --git a/app/Services/Allocations/AllocationDeletionService.php b/app/Services/Allocations/AllocationDeletionService.php new file mode 100644 index 0000000..8ea6772 --- /dev/null +++ b/app/Services/Allocations/AllocationDeletionService.php @@ -0,0 +1,32 @@ +server_id)) { + throw new ServerUsingAllocationException(trans('exceptions.allocations.server_using')); + } + + return $this->repository->delete($allocation->id); + } +} diff --git a/app/Services/Allocations/AssignmentService.php b/app/Services/Allocations/AssignmentService.php new file mode 100644 index 0000000..54994ee --- /dev/null +++ b/app/Services/Allocations/AssignmentService.php @@ -0,0 +1,109 @@ + self::CIDR_MIN_BITS || $explode[1] < self::CIDR_MAX_BITS)) { + throw new CidrOutOfRangeException(); + } + } + + try { + // TODO: how should we approach supporting IPv6 with this? + // gethostbyname only supports IPv4, but the alternative (dns_get_record) returns + // an array of records, which is not ideal for this use case, we need a SINGLE + // IP to use, not multiple. + $underlying = gethostbyname($data['allocation_ip']); + $parsed = Network::parse($underlying); + } catch (\Exception $exception) { + /* @noinspection PhpUndefinedVariableInspection */ + throw new DisplayException("Could not parse provided allocation IP address ({$underlying}): {$exception->getMessage()}", $exception); + } + + $this->connection->beginTransaction(); + foreach ($parsed as $ip) { + foreach ($data['allocation_ports'] as $port) { + if (!is_digit($port) && !preg_match(self::PORT_RANGE_REGEX, $port)) { + throw new InvalidPortMappingException($port); + } + + $insertData = []; + if (preg_match(self::PORT_RANGE_REGEX, $port, $matches)) { + $block = range($matches[1], $matches[2]); + + if (count($block) > self::PORT_RANGE_LIMIT) { + throw new TooManyPortsInRangeException(); + } + + if ((int) $matches[1] <= self::PORT_FLOOR || (int) $matches[2] > self::PORT_CEIL) { + throw new PortOutOfRangeException(); + } + + foreach ($block as $unit) { + $insertData[] = [ + 'node_id' => $node->id, + 'ip' => $ip->__toString(), + 'port' => (int) $unit, + 'ip_alias' => array_get($data, 'allocation_alias'), + 'server_id' => null, + ]; + } + } else { + if ((int) $port <= self::PORT_FLOOR || (int) $port > self::PORT_CEIL) { + throw new PortOutOfRangeException(); + } + + $insertData[] = [ + 'node_id' => $node->id, + 'ip' => $ip->__toString(), + 'port' => (int) $port, + 'ip_alias' => array_get($data, 'allocation_alias'), + 'server_id' => null, + ]; + } + + $this->repository->insertIgnore($insertData); + } + } + + $this->connection->commit(); + } +} diff --git a/app/Services/Allocations/FindAssignableAllocationService.php b/app/Services/Allocations/FindAssignableAllocationService.php new file mode 100644 index 0000000..3374fa0 --- /dev/null +++ b/app/Services/Allocations/FindAssignableAllocationService.php @@ -0,0 +1,111 @@ +node->allocations() + ->where('ip', $server->allocation->ip) + ->whereNull('server_id') + ->inRandomOrder() + ->first(); + + $allocation = $allocation ?? $this->createNewAllocation($server); + + $allocation->update(['server_id' => $server->id]); + + return $allocation->refresh(); + } + + /** + * Create a new allocation on the server's node with a random port from the defined range + * in the settings. If there are no matches in that range, or something is wrong with the + * range information provided an exception will be raised. + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\Service\Allocation\CidrOutOfRangeException + * @throws \Pterodactyl\Exceptions\Service\Allocation\InvalidPortMappingException + * @throws \Pterodactyl\Exceptions\Service\Allocation\PortOutOfRangeException + * @throws \Pterodactyl\Exceptions\Service\Allocation\TooManyPortsInRangeException + */ + protected function createNewAllocation(Server $server): Allocation + { + $start = config('pterodactyl.client_features.allocations.range_start', null); + $end = config('pterodactyl.client_features.allocations.range_end', null); + + if (!$start || !$end) { + throw new NoAutoAllocationSpaceAvailableException(); + } + + Assert::integerish($start); + Assert::integerish($end); + + // Get all of the currently allocated ports for the node so that we can figure out + // which port might be available. + $ports = $server->node->allocations() + ->where('ip', $server->allocation->ip) + ->whereBetween('port', [$start, $end]) + ->pluck('port'); + + // Compute the difference of the range and the currently created ports, finding + // any port that does not already exist in the database. We will then use this + // array of ports to create a new allocation to assign to the server. + $available = array_diff(range($start, $end), $ports->toArray()); + + // If we've already allocated all of the ports, just abort. + if (empty($available)) { + throw new NoAutoAllocationSpaceAvailableException(); + } + + // Pick a random port out of the remaining available ports. + /** @var int $port */ + $port = $available[array_rand($available)]; + + $this->service->handle($server->node, [ + 'allocation_ip' => $server->allocation->ip, + 'allocation_ports' => [$port], + ]); + + /** @var \Pterodactyl\Models\Allocation $allocation */ + $allocation = $server->node->allocations() + ->where('ip', $server->allocation->ip) + ->where('port', $port) + ->firstOrFail(); + + return $allocation; + } +} diff --git a/app/Services/Api/KeyCreationService.php b/app/Services/Api/KeyCreationService.php new file mode 100644 index 0000000..f026b9f --- /dev/null +++ b/app/Services/Api/KeyCreationService.php @@ -0,0 +1,52 @@ +keyType = $type; + + return $this; + } + + /** + * Create a new API key for the Panel using the permissions passed in the data request. + * This will automatically generate an identifier and an encrypted token that are + * stored in the database. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + */ + public function handle(array $data, array $permissions = []): ApiKey + { + $data = array_merge($data, [ + 'key_type' => $this->keyType, + 'identifier' => ApiKey::generateTokenIdentifier($this->keyType), + 'token' => $this->encrypter->encrypt(str_random(ApiKey::KEY_LENGTH)), + ]); + + if ($this->keyType === ApiKey::TYPE_APPLICATION) { + $data = array_merge($data, $permissions); + } + + return $this->repository->create($data, true, true); + } +} diff --git a/app/Services/Backups/DeleteBackupService.php b/app/Services/Backups/DeleteBackupService.php new file mode 100644 index 0000000..fd65969 --- /dev/null +++ b/app/Services/Backups/DeleteBackupService.php @@ -0,0 +1,82 @@ +is_locked && ($backup->is_successful && !is_null($backup->completed_at))) { + throw new BackupLockedException(); + } + + if ($backup->disk === Backup::ADAPTER_AWS_S3) { + $this->deleteFromS3($backup); + + return; + } + + $this->connection->transaction(function () use ($backup) { + try { + $this->daemonBackupRepository->setServer($backup->server)->delete($backup); + } catch (DaemonConnectionException $exception) { + $previous = $exception->getPrevious(); + // Don't fail the request if the Daemon responds with a 404, just assume the backup + // doesn't actually exist and remove its reference from the Panel as well. + if (!$previous instanceof ClientException || $previous->getResponse()->getStatusCode() !== Response::HTTP_NOT_FOUND) { + throw $exception; + } + } + + $backup->delete(); + }); + } + + /** + * Deletes a backup from an S3 disk. + * + * @throws \Throwable + */ + protected function deleteFromS3(Backup $backup): void + { + $this->connection->transaction(function () use ($backup) { + $backup->delete(); + + /** @var \Pterodactyl\Extensions\Filesystem\S3Filesystem $adapter */ + $adapter = $this->manager->adapter(Backup::ADAPTER_AWS_S3); + + $adapter->getClient()->deleteObject([ + 'Bucket' => $adapter->getBucket(), + 'Key' => sprintf('%s/%s.tar.gz', $backup->server->uuid, $backup->uuid), + ]); + }); + } +} diff --git a/app/Services/Backups/DownloadLinkService.php b/app/Services/Backups/DownloadLinkService.php new file mode 100644 index 0000000..f3f76c8 --- /dev/null +++ b/app/Services/Backups/DownloadLinkService.php @@ -0,0 +1,62 @@ +disk === Backup::ADAPTER_AWS_S3) { + return $this->getS3BackupUrl($backup); + } + + $token = $this->jwtService + ->setExpiresAt(CarbonImmutable::now()->addMinutes(15)) + ->setUser($user) + ->setClaims([ + 'backup_uuid' => $backup->uuid, + 'server_uuid' => $backup->server->uuid, + ]) + ->handle($backup->server->node, $user->id . $backup->server->uuid); + + return sprintf('%s/download/backup?token=%s', $backup->server->node->getConnectionAddress(), $token->toString()); + } + + /** + * Returns a signed URL that allows us to download a file directly out of a non-public + * S3 bucket by using a signed URL. + */ + protected function getS3BackupUrl(Backup $backup): string + { + /** @var \Pterodactyl\Extensions\Filesystem\S3Filesystem $adapter */ + $adapter = $this->backupManager->adapter(Backup::ADAPTER_AWS_S3); + + $request = $adapter->getClient()->createPresignedRequest( + $adapter->getClient()->getCommand('GetObject', [ + 'Bucket' => $adapter->getBucket(), + 'Key' => sprintf('%s/%s.tar.gz', $backup->server->uuid, $backup->uuid), + 'ContentType' => 'application/x-gzip', + ]), + CarbonImmutable::now()->addMinutes(5) + ); + + return $request->getUri()->__toString(); + } +} diff --git a/app/Services/Backups/InitiateBackupService.php b/app/Services/Backups/InitiateBackupService.php new file mode 100644 index 0000000..be8f966 --- /dev/null +++ b/app/Services/Backups/InitiateBackupService.php @@ -0,0 +1,128 @@ +isLocked = $isLocked; + + return $this; + } + + /** + * Sets the files to be ignored by this backup. + * + * @param string[]|null $ignored + */ + public function setIgnoredFiles(?array $ignored): self + { + if (is_array($ignored)) { + foreach ($ignored as $value) { + Assert::string($value); + } + } + + // Set the ignored files to be any values that are not empty in the array. Don't use + // the PHP empty function here incase anything that is "empty" by default (0, false, etc.) + // were passed as a file or folder name. + $this->ignoredFiles = is_null($ignored) ? [] : array_filter($ignored, function ($value) { + return strlen($value) > 0; + }); + + return $this; + } + + /** + * Initiates the backup process for a server on Wings. + * + * @throws \Throwable + * @throws \Pterodactyl\Exceptions\Service\Backup\TooManyBackupsException + * @throws \Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException + */ + public function handle(Server $server, string $name = null, bool $override = false): Backup + { + $limit = config('backups.throttles.limit'); + $period = config('backups.throttles.period'); + if ($period > 0) { + $previous = $this->repository->getBackupsGeneratedDuringTimespan($server->id, $period); + if ($previous->count() >= $limit) { + $message = sprintf('Only %d backups may be generated within a %d second span of time.', $limit, $period); + + throw new TooManyRequestsHttpException(CarbonImmutable::now()->diffInSeconds($previous->last()->created_at->addSeconds($period)), $message); + } + } + + // Check if the server has reached or exceeded its backup limit. + // completed_at == null will cover any ongoing backups, while is_successful == true will cover any completed backups. + $successful = $this->repository->getNonFailedBackups($server); + if (!$server->backup_limit || $successful->count() >= $server->backup_limit) { + // Do not allow the user to continue if this server is already at its limit and can't override. + if (!$override || $server->backup_limit <= 0) { + throw new TooManyBackupsException($server->backup_limit); + } + + // Get the oldest backup the server has that is not "locked" (indicating a backup that should + // never be automatically purged). If we find a backup we will delete it and then continue with + // this process. If no backup is found that can be used an exception is thrown. + /** @var \Pterodactyl\Models\Backup $oldest */ + $oldest = $successful->where('is_locked', false)->orderBy('created_at')->first(); + if (!$oldest) { + throw new TooManyBackupsException($server->backup_limit); + } + + $this->deleteBackupService->handle($oldest); + } + + return $this->connection->transaction(function () use ($server, $name) { + /** @var \Pterodactyl\Models\Backup $backup */ + $backup = $this->repository->create([ + 'server_id' => $server->id, + 'uuid' => Uuid::uuid4()->toString(), + 'name' => trim($name) ?: sprintf('Backup at %s', CarbonImmutable::now()->toDateTimeString()), + 'ignored_files' => array_values($this->ignoredFiles ?? []), + 'disk' => $this->backupManager->getDefaultAdapter(), + 'is_locked' => $this->isLocked, + ], true, true); + + $this->daemonBackupRepository->setServer($server) + ->setBackupAdapter($this->backupManager->getDefaultAdapter()) + ->backup($backup); + + return $backup; + }); + } +} diff --git a/app/Services/Databases/DatabaseManagementService.php b/app/Services/Databases/DatabaseManagementService.php new file mode 100644 index 0000000..20de6a7 --- /dev/null +++ b/app/Services/Databases/DatabaseManagementService.php @@ -0,0 +1,174 @@ +validateDatabaseLimit = $validate; + + return $this; + } + + /** + * Create a new database that is linked to a specific host. + * + * @throws \Throwable + * @throws \Pterodactyl\Exceptions\Service\Database\TooManyDatabasesException + * @throws \Pterodactyl\Exceptions\Service\Database\DatabaseClientFeatureNotEnabledException + */ + public function create(Server $server, array $data): Database + { + if (!config('pterodactyl.client_features.databases.enabled')) { + throw new DatabaseClientFeatureNotEnabledException(); + } + + if ($this->validateDatabaseLimit) { + // If the server has a limit assigned and we've already reached that limit, throw back + // an exception and kill the process. + if (!is_null($server->database_limit) && $server->databases()->count() >= $server->database_limit) { + throw new TooManyDatabasesException(); + } + } + + // Protect against developer mistakes... + if (empty($data['database']) || !preg_match(self::MATCH_NAME_REGEX, $data['database'])) { + throw new \InvalidArgumentException('The database name passed to DatabaseManagementService::handle MUST be prefixed with "s{server_id}_".'); + } + + $data = array_merge($data, [ + 'server_id' => $server->id, + 'username' => sprintf('u%d_%s', $server->id, str_random(10)), + 'password' => $this->encrypter->encrypt( + Utilities::randomStringWithSpecialCharacters(24) + ), + ]); + + $database = null; + + try { + return $this->connection->transaction(function () use ($data, &$database) { + $database = $this->createModel($data); + + $this->dynamic->set('dynamic', $data['database_host_id']); + + $this->repository->createDatabase($database->database); + $this->repository->createUser( + $database->username, + $database->remote, + $this->encrypter->decrypt($database->password), + $database->max_connections + ); + $this->repository->assignUserToDatabase($database->database, $database->username, $database->remote); + $this->repository->flush(); + + return $database; + }); + } catch (\Exception $exception) { + try { + if ($database instanceof Database) { + $this->repository->dropDatabase($database->database); + $this->repository->dropUser($database->username, $database->remote); + $this->repository->flush(); + } + } catch (\Exception $deletionException) { + // Do nothing here. We've already encountered an issue before this point so no + // reason to prioritize this error over the initial one. + } + + throw $exception; + } + } + + /** + * Delete a database from the given host server. + * + * @throws \Exception + */ + public function delete(Database $database): ?bool + { + $this->dynamic->set('dynamic', $database->database_host_id); + + $this->repository->dropDatabase($database->database); + $this->repository->dropUser($database->username, $database->remote); + $this->repository->flush(); + + return $database->delete(); + } + + /** + * Create the database if there is not an identical match in the DB. While you can technically + * have the same name across multiple hosts, for the sake of keeping this logic easy to understand + * and avoiding user confusion we will ignore the specific host and just look across all hosts. + * + * @throws \Pterodactyl\Exceptions\Repository\DuplicateDatabaseNameException + * @throws \Throwable + */ + protected function createModel(array $data): Database + { + $exists = Database::query()->where('server_id', $data['server_id']) + ->where('database', $data['database']) + ->exists(); + + if ($exists) { + throw new DuplicateDatabaseNameException('A database with that name already exists for this server.'); + } + + $database = (new Database())->forceFill($data); + $database->saveOrFail(); + + return $database; + } +} diff --git a/app/Services/Databases/DatabasePasswordService.php b/app/Services/Databases/DatabasePasswordService.php new file mode 100644 index 0000000..3862b23 --- /dev/null +++ b/app/Services/Databases/DatabasePasswordService.php @@ -0,0 +1,49 @@ +connection->transaction(function () use ($database, $password) { + $this->dynamic->set('dynamic', $database->database_host_id); + + $this->repository->withoutFreshModel()->update($database->id, [ + 'password' => $this->encrypter->encrypt($password), + ]); + + $this->repository->dropUser($database->username, $database->remote); + $this->repository->createUser($database->username, $database->remote, $password, $database->max_connections); + $this->repository->assignUserToDatabase($database->database, $database->username, $database->remote); + $this->repository->flush(); + }); + + return $password; + } +} diff --git a/app/Services/Databases/DeployServerDatabaseService.php b/app/Services/Databases/DeployServerDatabaseService.php new file mode 100644 index 0000000..e22eba5 --- /dev/null +++ b/app/Services/Databases/DeployServerDatabaseService.php @@ -0,0 +1,49 @@ +get()->toBase(); + if ($hosts->isEmpty()) { + throw new NoSuitableDatabaseHostException(); + } else { + $nodeHosts = $hosts->where('node_id', $server->node_id)->toBase(); + + if ($nodeHosts->isEmpty() && !config('pterodactyl.client_features.databases.allow_random')) { + throw new NoSuitableDatabaseHostException(); + } + } + + return $this->managementService->create($server, [ + 'database_host_id' => $nodeHosts->isEmpty() + ? $hosts->random()->id + : $nodeHosts->random()->id, + 'database' => DatabaseManagementService::generateUniqueDatabaseName($data['database'], $server->id), + 'remote' => $data['remote'], + ]); + } +} diff --git a/app/Services/Databases/Hosts/HostCreationService.php b/app/Services/Databases/Hosts/HostCreationService.php new file mode 100644 index 0000000..d33a158 --- /dev/null +++ b/app/Services/Databases/Hosts/HostCreationService.php @@ -0,0 +1,51 @@ +connection->transaction(function () use ($data) { + $host = $this->repository->create([ + 'password' => $this->encrypter->encrypt(array_get($data, 'password')), + 'name' => array_get($data, 'name'), + 'host' => array_get($data, 'host'), + 'port' => array_get($data, 'port'), + 'username' => array_get($data, 'username'), + 'max_databases' => null, + 'node_id' => array_get($data, 'node_id'), + ]); + + // Confirm access using the provided credentials before saving data. + $this->dynamic->set('dynamic', $host); + $this->databaseManager->connection('dynamic')->select('SELECT 1 FROM dual'); + + return $host; + }); + } +} diff --git a/app/Services/Databases/Hosts/HostDeletionService.php b/app/Services/Databases/Hosts/HostDeletionService.php new file mode 100644 index 0000000..4a06af8 --- /dev/null +++ b/app/Services/Databases/Hosts/HostDeletionService.php @@ -0,0 +1,35 @@ +databaseRepository->findCountWhere([['database_host_id', '=', $host]]); + if ($count > 0) { + throw new HasActiveServersException(trans('exceptions.databases.delete_has_databases')); + } + + return $this->repository->delete($host); + } +} diff --git a/app/Services/Databases/Hosts/HostUpdateService.php b/app/Services/Databases/Hosts/HostUpdateService.php new file mode 100644 index 0000000..363deb1 --- /dev/null +++ b/app/Services/Databases/Hosts/HostUpdateService.php @@ -0,0 +1,47 @@ +encrypter->encrypt($data['password']); + } else { + unset($data['password']); + } + + return $this->connection->transaction(function () use ($data, $hostId) { + $host = $this->repository->update($hostId, $data); + $this->dynamic->set('dynamic', $host); + $this->databaseManager->connection('dynamic')->select('SELECT 1 FROM dual'); + + return $host; + }); + } +} diff --git a/app/Services/Deployment/AllocationSelectionService.php b/app/Services/Deployment/AllocationSelectionService.php new file mode 100644 index 0000000..ae334ed --- /dev/null +++ b/app/Services/Deployment/AllocationSelectionService.php @@ -0,0 +1,95 @@ +dedicated = $dedicated; + + return $this; + } + + /** + * A list of node IDs that should be used when selecting an allocation. If empty, all + * nodes will be used to filter with. + */ + public function setNodes(array $nodes): self + { + $this->nodes = $nodes; + + return $this; + } + + /** + * An array of individual ports or port ranges to use when selecting an allocation. If + * empty, all ports will be considered when finding an allocation. If set, only ports appearing + * in the array or range will be used. + * + * @throws \Pterodactyl\Exceptions\DisplayException + */ + public function setPorts(array $ports): self + { + $stored = []; + foreach ($ports as $port) { + if (is_digit($port)) { + $stored[] = $port; + } + + // Ranges are stored in the ports array as an array which can be + // better processed in the repository. + if (preg_match(AssignmentService::PORT_RANGE_REGEX, $port, $matches)) { + if (abs($matches[2] - $matches[1]) > AssignmentService::PORT_RANGE_LIMIT) { + throw new DisplayException(trans('exceptions.allocations.too_many_ports')); + } + + $stored[] = [$matches[1], $matches[2]]; + } + } + + $this->ports = $stored; + + return $this; + } + + /** + * Return a single allocation that should be used as the default allocation for a server. + * + * @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableAllocationException + */ + public function handle(): Allocation + { + $allocation = $this->repository->getRandomAllocation($this->nodes, $this->ports, $this->dedicated); + + if (is_null($allocation)) { + throw new NoViableAllocationException(trans('exceptions.deployment.no_viable_allocations')); + } + + return $allocation; + } +} diff --git a/app/Services/Deployment/FindViableNodesService.php b/app/Services/Deployment/FindViableNodesService.php new file mode 100644 index 0000000..71c830b --- /dev/null +++ b/app/Services/Deployment/FindViableNodesService.php @@ -0,0 +1,100 @@ +locations = $locations; + + return $this; + } + + /** + * Set the amount of disk that will be used by the server being created. Nodes will be + * filtered out if they do not have enough available free disk space for this server + * to be placed on. + */ + public function setDisk(int $disk): self + { + $this->disk = $disk; + + return $this; + } + + /** + * Set the amount of memory that this server will be using. As with disk space, nodes that + * do not have enough free memory will be filtered out. + */ + public function setMemory(int $memory): self + { + $this->memory = $memory; + + return $this; + } + + /** + * Returns an array of nodes that meet the provided requirements and can then + * be passed to the AllocationSelectionService to return a single allocation. + * + * This functionality is used for automatic deployments of servers and will + * attempt to find all nodes in the defined locations that meet the disk and + * memory availability requirements. Any nodes not meeting those requirements + * are tossed out, as are any nodes marked as non-public, meaning automatic + * deployments should not be done against them. + * + * @param int|null $page If provided the results will be paginated by returning + * up to 50 nodes at a time starting at the provided page. + * If "null" is provided as the value no pagination will + * be used. + * + * @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableNodeException + */ + public function handle(int $perPage = null, int $page = null): LengthAwarePaginator|Collection + { + Assert::integer($this->disk, 'Disk space must be an int, got %s'); + Assert::integer($this->memory, 'Memory usage must be an int, got %s'); + + $query = Node::query()->select('nodes.*') + ->selectRaw('IFNULL(SUM(servers.memory), 0) as sum_memory') + ->selectRaw('IFNULL(SUM(servers.disk), 0) as sum_disk') + ->leftJoin('servers', 'servers.node_id', '=', 'nodes.id') + ->where('nodes.public', 1); + + if (!empty($this->locations)) { + $query = $query->whereIn('nodes.location_id', $this->locations); + } + + $results = $query->groupBy('nodes.id') + ->havingRaw('(IFNULL(SUM(servers.memory), 0) + ?) <= (nodes.memory * (1 + (nodes.memory_overallocate / 100)))', [$this->memory]) + ->havingRaw('(IFNULL(SUM(servers.disk), 0) + ?) <= (nodes.disk * (1 + (nodes.disk_overallocate / 100)))', [$this->disk]); + + if (!is_null($page)) { + $results = $results->paginate($perPage ?? 50, ['*'], 'page', $page); + } else { + $results = $results->get()->toBase(); + } + + if ($results->isEmpty()) { + throw new NoViableNodeException(trans('exceptions.deployment.no_viable_nodes')); + } + + return $results; + } +} diff --git a/app/Services/Eggs/EggConfigurationService.php b/app/Services/Eggs/EggConfigurationService.php new file mode 100644 index 0000000..4a61c2e --- /dev/null +++ b/app/Services/Eggs/EggConfigurationService.php @@ -0,0 +1,245 @@ +replacePlaceholders( + $server, + json_decode($server->egg->inherit_config_files) + ); + + return [ + 'startup' => $this->convertStartupToNewFormat(json_decode($server->egg->inherit_config_startup, true)), + 'stop' => $this->convertStopToNewFormat($server->egg->inherit_config_stop), + 'configs' => $configs, + ]; + } + + /** + * Convert the "done" variable into an array if it is not currently one. + */ + protected function convertStartupToNewFormat(array $startup): array + { + $done = Arr::get($startup, 'done'); + + return [ + 'done' => is_string($done) ? [$done] : $done, + 'user_interaction' => [], + 'strip_ansi' => Arr::get($startup, 'strip_ansi') ?? false, + ]; + } + + /** + * Converts a legacy stop string into a new generation stop option for a server. + * + * For most eggs, this ends up just being a command sent to the server console, but + * if the stop command is something starting with a caret (^), it will be converted + * into the associated kill signal for the instance. + */ + protected function convertStopToNewFormat(string $stop): array + { + if (!Str::startsWith($stop, '^')) { + return [ + 'type' => 'command', + 'value' => $stop, + ]; + } + + $signal = substr($stop, 1); + if (strtoupper($signal) === 'C') { + return [ + 'type' => 'stop', + 'value' => null, + ]; + } + + return [ + 'type' => 'signal', + 'value' => strtoupper($signal), + ]; + } + + protected function replacePlaceholders(Server $server, object $configs): array + { + // Get the legacy configuration structure for the server so that we + // can property map the egg placeholders to values. + $structure = $this->configurationStructureService->handle($server, [], true); + + $response = []; + // Normalize the output of the configuration for the new Wings Daemon to more + // easily ingest, as well as make things more flexible down the road. + foreach ($configs as $file => $data) { + // Try to head off any errors relating to parsing a set of configuration files + // or other JSON data for the egg. This should probably be blocked at the time + // of egg creation/update, but it isn't so this check will at least prevent a + // 500 error which would crash the entire Wings boot process. + // + // @see https://github.com/pterodactyl/panel/issues/3055 + if (!is_object($data) || !isset($data->find)) { + continue; + } + + $append = array_merge((array) $data, ['file' => $file, 'replace' => []]); + + foreach ($this->iterate($data->find, $structure) as $find => $replace) { + if (is_object($replace)) { + foreach ($replace as $match => $replaceWith) { + $append['replace'][] = [ + 'match' => $find, + 'if_value' => $match, + 'replace_with' => $replaceWith, + ]; + } + + continue; + } + + $append['replace'][] = [ + 'match' => $find, + 'replace_with' => $replace, + ]; + } + + unset($append['find']); + + $response[] = $append; + } + + return $response; + } + + /** + * Replaces the legacy modifies from eggs with their new counterpart. The legacy Daemon would + * set SERVER_MEMORY, SERVER_IP, and SERVER_PORT with their respective values on the Daemon + * side. Ensure that anything referencing those properly replaces them with the matching config + * value. + */ + protected function replaceLegacyModifiers(string $key, string $value): string + { + switch ($key) { + case 'config.docker.interface': + $replace = 'config.docker.network.interface'; + break; + case 'server.build.env.SERVER_MEMORY': + case 'env.SERVER_MEMORY': + $replace = 'server.build.memory'; + break; + case 'server.build.env.SERVER_IP': + case 'env.SERVER_IP': + $replace = 'server.build.default.ip'; + break; + case 'server.build.env.SERVER_PORT': + case 'env.SERVER_PORT': + $replace = 'server.build.default.port'; + break; + default: + // By default, we don't need to change anything, only if we ended up matching a specific legacy item. + $replace = $key; + } + + return str_replace("{{{$key}}}", "{{{$replace}}}", $value); + } + + protected function matchAndReplaceKeys(mixed $value, array $structure): mixed + { + preg_match_all('/{{(?[\w.-]*)}}/', $value, $matches); + + foreach ($matches['key'] as $key) { + // Matched something in {{server.X}} format, now replace that with the actual + // value from the server properties. + // + // The Daemon supports server.X, env.X, and config.X placeholders. + if (!Str::startsWith($key, ['server.', 'env.', 'config.'])) { + continue; + } + + // Don't do a replacement on anything that is not a string, we don't want to unintentionally + // modify the resulting output. + if (!is_string($value)) { + continue; + } + + $value = $this->replaceLegacyModifiers($key, $value); + + // We don't want to do anything with config keys since the Daemon will need to handle + // that. For example, the Spigot egg uses "config.docker.interface" to identify the Docker + // interface to proxy through, but the Panel would be unaware of that. + if (Str::startsWith($key, 'config.')) { + continue; + } + + // Replace anything starting with "server." with the value out of the server configuration + // array that used to be created for the old daemon. + if (Str::startsWith($key, 'server.')) { + $plucked = Arr::get($structure, preg_replace('/^server\./', '', $key), ''); + + $value = str_replace("{{{$key}}}", $plucked, $value); + continue; + } + + // Finally, replace anything starting with env. with the expected environment + // variable from the server configuration. + $plucked = Arr::get( + $structure, + preg_replace('/^env\./', 'build.env.', $key), + '' + ); + + $value = str_replace("{{{$key}}}", $plucked, $value); + } + + return $value; + } + + /** + * Iterates over a set of "find" values for a given file in the parser configuration. If + * the value of the line match is something iterable, continue iterating, otherwise perform + * a match & replace. + */ + private function iterate(mixed $data, array $structure): mixed + { + if (!is_iterable($data) && !is_object($data)) { + return $data; + } + + // Remember, in PHP objects are always passed by reference, so if we do not clone this object + // instance we'll end up making modifications to the object outside the scope of this function + // which leads to some fun behavior in the parser. + if (is_array($data)) { + // Copy the array. + // NOTE: if the array contains any objects, they will be passed by reference. + $clone = $data; + } else { + $clone = clone $data; + } + foreach ($clone as $key => &$value) { + if (is_iterable($value) || is_object($value)) { + $value = $this->iterate($value, $structure); + + continue; + } + + $value = $this->matchAndReplaceKeys($value, $structure); + } + + return $clone; + } +} diff --git a/app/Services/Eggs/EggCreationService.php b/app/Services/Eggs/EggCreationService.php new file mode 100644 index 0000000..b084b0c --- /dev/null +++ b/app/Services/Eggs/EggCreationService.php @@ -0,0 +1,46 @@ +repository->findCountWhere([ + ['nest_id', '=', array_get($data, 'nest_id')], + ['id', '=', array_get($data, 'config_from')], + ]); + + if ($results !== 1) { + throw new NoParentConfigurationFoundException(trans('exceptions.nest.egg.must_be_child')); + } + } + + return $this->repository->create(array_merge($data, [ + 'uuid' => Uuid::uuid4()->toString(), + 'author' => $this->config->get('pterodactyl.service.author'), + ]), true, true); + } +} diff --git a/app/Services/Eggs/EggDeletionService.php b/app/Services/Eggs/EggDeletionService.php new file mode 100644 index 0000000..7e40133 --- /dev/null +++ b/app/Services/Eggs/EggDeletionService.php @@ -0,0 +1,41 @@ +serverRepository->findCountWhere([['egg_id', '=', $egg]]); + if ($servers > 0) { + throw new HasActiveServersException(trans('exceptions.nest.egg.delete_has_servers')); + } + + $children = $this->repository->findCountWhere([['config_from', '=', $egg]]); + if ($children > 0) { + throw new HasChildrenException(trans('exceptions.nest.egg.has_children')); + } + + return $this->repository->delete($egg); + } +} diff --git a/app/Services/Eggs/EggParserService.php b/app/Services/Eggs/EggParserService.php new file mode 100644 index 0000000..6d8545b --- /dev/null +++ b/app/Services/Eggs/EggParserService.php @@ -0,0 +1,90 @@ +getError() !== UPLOAD_ERR_OK || !$file->isFile()) { + throw new InvalidFileUploadException('The selected file is not valid and cannot be imported.'); + } + + /** @var array $parsed */ + $parsed = json_decode($file->openFile()->fread($file->getSize()), true, 512, JSON_THROW_ON_ERROR); + if (!in_array(Arr::get($parsed, 'meta.version') ?? '', ['PTDL_v1', 'PTDL_v2'])) { + throw new InvalidFileUploadException('The JSON file provided is not in a format that can be recognized.'); + } + + return $this->convertToV2($parsed); + } + + /** + * Fills the provided model with the parsed JSON data. + */ + public function fillFromParsed(Egg $model, array $parsed): Egg + { + return $model->forceFill([ + 'name' => Arr::get($parsed, 'name'), + 'description' => Arr::get($parsed, 'description'), + 'features' => Arr::get($parsed, 'features'), + 'docker_images' => Arr::get($parsed, 'docker_images'), + 'file_denylist' => Collection::make(Arr::get($parsed, 'file_denylist')) + ->filter(fn ($value) => !empty($value)), + 'update_url' => Arr::get($parsed, 'meta.update_url'), + 'config_files' => Arr::get($parsed, 'config.files'), + 'config_startup' => Arr::get($parsed, 'config.startup'), + 'config_logs' => Arr::get($parsed, 'config.logs'), + 'config_stop' => Arr::get($parsed, 'config.stop'), + 'startup' => Arr::get($parsed, 'startup'), + 'script_install' => Arr::get($parsed, 'scripts.installation.script'), + 'script_entry' => Arr::get($parsed, 'scripts.installation.entrypoint'), + 'script_container' => Arr::get($parsed, 'scripts.installation.container'), + ]); + } + + /** + * Converts a PTDL_V1 egg into the expected PTDL_V2 egg format. This just handles + * the "docker_images" field potentially not being present, and not being in the + * expected "key => value" format. + */ + protected function convertToV2(array $parsed): array + { + if (Arr::get($parsed, 'meta.version') === Egg::EXPORT_VERSION) { + return $parsed; + } + + // Maintain backwards compatability for eggs that are still using the old single image + // string format. New eggs can provide an array of Docker images that can be used. + if (!isset($parsed['images'])) { + $images = [Arr::get($parsed, 'image') ?? 'nil']; + } else { + $images = $parsed['images']; + } + + unset($parsed['images'], $parsed['image']); + + $parsed['docker_images'] = []; + foreach ($images as $image) { + $parsed['docker_images'][$image] = $image; + } + + $parsed['variables'] = array_map(function ($value) { + return array_merge($value, ['field_type' => 'text']); + }, $parsed['variables']); + + return $parsed; + } +} diff --git a/app/Services/Eggs/EggUpdateService.php b/app/Services/Eggs/EggUpdateService.php new file mode 100644 index 0000000..7ca442b --- /dev/null +++ b/app/Services/Eggs/EggUpdateService.php @@ -0,0 +1,44 @@ +repository->findCountWhere([ + ['nest_id', '=', $egg->nest_id], + ['id', '=', array_get($data, 'config_from')], + ]); + + if ($results !== 1) { + throw new NoParentConfigurationFoundException(trans('exceptions.nest.egg.must_be_child')); + } + } + + // TODO(dane): Once the admin UI is done being reworked and this is exposed + // in said UI, remove this so that you can actually update the denylist. + unset($data['file_denylist']); + + $this->repository->withoutFreshModel()->update($egg->id, $data); + } +} diff --git a/app/Services/Eggs/Scripts/InstallScriptService.php b/app/Services/Eggs/Scripts/InstallScriptService.php new file mode 100644 index 0000000..3341572 --- /dev/null +++ b/app/Services/Eggs/Scripts/InstallScriptService.php @@ -0,0 +1,41 @@ +repository->isCopyableScript(array_get($data, 'copy_script_from'), $egg->nest_id)) { + throw new InvalidCopyFromException(trans('exceptions.nest.egg.invalid_copy_id')); + } + } + + $this->repository->withoutFreshModel()->update($egg->id, [ + 'script_install' => array_get($data, 'script_install'), + 'script_is_privileged' => array_get($data, 'script_is_privileged', 1), + 'script_entry' => array_get($data, 'script_entry'), + 'script_container' => array_get($data, 'script_container'), + 'copy_script_from' => array_get($data, 'copy_script_from'), + ]); + } +} diff --git a/app/Services/Eggs/Sharing/EggExporterService.php b/app/Services/Eggs/Sharing/EggExporterService.php new file mode 100644 index 0000000..706297b --- /dev/null +++ b/app/Services/Eggs/Sharing/EggExporterService.php @@ -0,0 +1,68 @@ +repository->getWithExportAttributes($egg); + + $struct = [ + '_comment' => 'DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO', + 'meta' => [ + 'version' => Egg::EXPORT_VERSION, + 'update_url' => $egg->update_url, + ], + 'exported_at' => Carbon::now()->toAtomString(), + 'name' => $egg->name, + 'author' => $egg->author, + 'description' => $egg->description, + 'features' => $egg->features, + 'docker_images' => $egg->docker_images, + 'file_denylist' => Collection::make($egg->inherit_file_denylist)->filter(function ($value) { + return !empty($value); + }), + 'startup' => $egg->startup, + 'config' => [ + 'files' => $egg->inherit_config_files, + 'startup' => $egg->inherit_config_startup, + 'logs' => $egg->inherit_config_logs, + 'stop' => $egg->inherit_config_stop, + ], + 'scripts' => [ + 'installation' => [ + 'script' => $egg->copy_script_install, + 'container' => $egg->copy_script_container, + 'entrypoint' => $egg->copy_script_entry, + ], + ], + 'variables' => $egg->variables->transform(function (EggVariable $item) { + return Collection::make($item->toArray()) + ->except(['id', 'egg_id', 'created_at', 'updated_at']) + ->merge(['field_type' => 'text']) + ->toArray(); + }), + ]; + + return json_encode($struct, JSON_PRETTY_PRINT); + } +} diff --git a/app/Services/Eggs/Sharing/EggImporterService.php b/app/Services/Eggs/Sharing/EggImporterService.php new file mode 100644 index 0000000..ecd6ead --- /dev/null +++ b/app/Services/Eggs/Sharing/EggImporterService.php @@ -0,0 +1,50 @@ +parser->handle($file); + + /** @var \Pterodactyl\Models\Nest $nest */ + $nest = Nest::query()->with('eggs', 'eggs.variables')->findOrFail($nest); + + return $this->connection->transaction(function () use ($nest, $parsed) { + $egg = (new Egg())->forceFill([ + 'uuid' => Uuid::uuid4()->toString(), + 'nest_id' => $nest->id, + 'author' => Arr::get($parsed, 'author'), + 'copy_script_from' => null, + ]); + + $egg = $this->parser->fillFromParsed($egg, $parsed); + $egg->save(); + + foreach ($parsed['variables'] ?? [] as $variable) { + EggVariable::query()->forceCreate(array_merge($variable, ['egg_id' => $egg->id])); + } + + return $egg; + }); + } +} diff --git a/app/Services/Eggs/Sharing/EggUpdateImporterService.php b/app/Services/Eggs/Sharing/EggUpdateImporterService.php new file mode 100644 index 0000000..89a1f92 --- /dev/null +++ b/app/Services/Eggs/Sharing/EggUpdateImporterService.php @@ -0,0 +1,50 @@ +parser->handle($file); + + return $this->connection->transaction(function () use ($egg, $parsed) { + $egg = $this->parser->fillFromParsed($egg, $parsed); + $egg->save(); + + // Update existing variables or create new ones. + foreach ($parsed['variables'] ?? [] as $variable) { + EggVariable::unguarded(function () use ($egg, $variable) { + $egg->variables()->updateOrCreate([ + 'env_variable' => $variable['env_variable'], + ], Collection::make($variable)->except('egg_id', 'env_variable')->toArray()); + }); + } + + $imported = array_map(fn ($value) => $value['env_variable'], $parsed['variables'] ?? []); + + $egg->variables()->whereNotIn('env_variable', $imported)->delete(); + + return $egg->refresh(); + }); + } +} diff --git a/app/Services/Eggs/Variables/VariableCreationService.php b/app/Services/Eggs/Variables/VariableCreationService.php new file mode 100644 index 0000000..a7e19f6 --- /dev/null +++ b/app/Services/Eggs/Variables/VariableCreationService.php @@ -0,0 +1,61 @@ +validator; + } + + /** + * Create a new variable for a given Egg. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Service\Egg\Variable\BadValidationRuleException + * @throws \Pterodactyl\Exceptions\Service\Egg\Variable\ReservedVariableNameException + */ + public function handle(int $egg, array $data): EggVariable + { + if (in_array(strtoupper(array_get($data, 'env_variable')), explode(',', EggVariable::RESERVED_ENV_NAMES))) { + throw new ReservedVariableNameException(sprintf('Cannot use the protected name %s for this environment variable.', array_get($data, 'env_variable'))); + } + + if (!empty($data['rules'] ?? '')) { + $this->validateRules($data['rules']); + } + + $options = array_get($data, 'options') ?? []; + + return $this->repository->create([ + 'egg_id' => $egg, + 'name' => $data['name'] ?? '', + 'description' => $data['description'] ?? '', + 'env_variable' => $data['env_variable'] ?? '', + 'default_value' => $data['default_value'] ?? '', + 'user_viewable' => in_array('user_viewable', $options), + 'user_editable' => in_array('user_editable', $options), + 'rules' => $data['rules'] ?? '', + ]); + } +} diff --git a/app/Services/Eggs/Variables/VariableUpdateService.php b/app/Services/Eggs/Variables/VariableUpdateService.php new file mode 100644 index 0000000..ed70b26 --- /dev/null +++ b/app/Services/Eggs/Variables/VariableUpdateService.php @@ -0,0 +1,79 @@ +validator; + } + + /** + * Update a specific egg variable. + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Pterodactyl\Exceptions\Service\Egg\Variable\ReservedVariableNameException + */ + public function handle(EggVariable $variable, array $data): mixed + { + if (!is_null(array_get($data, 'env_variable'))) { + if (in_array(strtoupper(array_get($data, 'env_variable')), explode(',', EggVariable::RESERVED_ENV_NAMES))) { + throw new ReservedVariableNameException(trans('exceptions.service.variables.reserved_name', ['name' => array_get($data, 'env_variable')])); + } + + $search = $this->repository->setColumns('id')->findCountWhere([ + ['env_variable', '=', $data['env_variable']], + ['egg_id', '=', $variable->egg_id], + ['id', '!=', $variable->id], + ]); + + if ($search > 0) { + throw new DisplayException(trans('exceptions.service.variables.env_not_unique', ['name' => array_get($data, 'env_variable')])); + } + } + + if (!empty($data['rules'] ?? '')) { + $this->validateRules( + (is_string($data['rules']) && Str::contains($data['rules'], ';;')) + ? explode(';;', $data['rules']) + : $data['rules'] + ); + } + + $options = array_get($data, 'options') ?? []; + + return $this->repository->withoutFreshModel()->update($variable->id, [ + 'name' => $data['name'] ?? '', + 'description' => $data['description'] ?? '', + 'env_variable' => $data['env_variable'] ?? '', + 'default_value' => $data['default_value'] ?? '', + 'user_viewable' => in_array('user_viewable', $options), + 'user_editable' => in_array('user_editable', $options), + 'rules' => $data['rules'] ?? '', + ]); + } +} diff --git a/app/Services/Helpers/AssetHashService.php b/app/Services/Helpers/AssetHashService.php new file mode 100644 index 0000000..725a566 --- /dev/null +++ b/app/Services/Helpers/AssetHashService.php @@ -0,0 +1,117 @@ +filesystem = $filesystem->createLocalDriver(['root' => public_path()]); + } + + /** + * Modify a URL to append the asset hash. + */ + public function url(string $resource): string + { + $file = last(explode('/', $resource)); + $data = Arr::get($this->manifest(), $file) ?? $file; + + return str_replace($file, Arr::get($data, 'src') ?? $file, $resource); + } + + /** + * Return the data integrity hash for a resource. + */ + public function integrity(string $resource): string + { + $file = last(explode('/', $resource)); + $data = array_get($this->manifest(), $file, $file); + + return Arr::get($data, 'integrity') ?? ''; + } + + /** + * Return a built CSS import using the provided URL. + */ + public function css(string $resource): string + { + $attributes = [ + 'href' => $this->url($resource), + 'rel' => 'stylesheet preload', + 'as' => 'style', + 'crossorigin' => 'anonymous', + 'referrerpolicy' => 'no-referrer', + ]; + + if (config('pterodactyl.assets.use_hash')) { + $attributes['integrity'] = $this->integrity($resource); + } + + $output = ' $value) { + $output .= " $key=\"$value\""; + } + + return $output . '>'; + } + + /** + * Return a built JS import using the provided URL. + */ + public function js(string $resource): string + { + $attributes = [ + 'src' => $this->url($resource), + 'crossorigin' => 'anonymous', + ]; + + if (config('pterodactyl.assets.use_hash')) { + $attributes['integrity'] = $this->integrity($resource); + } + + $output = ' $value) { + $output .= " $key=\"$value\""; + } + + return $output . '>'; + } + + /** + * Get the asset manifest and store it in the cache for quicker lookups. + */ + protected function manifest(): array + { + if (static::$manifest === null) { + self::$manifest = json_decode( + $this->filesystem->get(self::MANIFEST_PATH), + true + ); + } + + $manifest = static::$manifest; + if ($manifest === null) { + throw new ManifestDoesNotExistException(); + } + + return $manifest; + } +} diff --git a/app/Services/Helpers/SoftwareVersionService.php b/app/Services/Helpers/SoftwareVersionService.php new file mode 100644 index 0000000..2122b39 --- /dev/null +++ b/app/Services/Helpers/SoftwareVersionService.php @@ -0,0 +1,103 @@ +cacheVersionData(); + } + + /** + * Get the latest version of the panel from the CDN servers. + */ + public function getPanel(): string + { + return Arr::get(self::$result, 'panel') ?? 'error'; + } + + /** + * Get the latest version of the daemon from the CDN servers. + */ + public function getDaemon(): string + { + return Arr::get(self::$result, 'wings') ?? 'error'; + } + + /** + * Get the URL to the discord server. + */ + public function getDiscord(): string + { + return Arr::get(self::$result, 'discord') ?? 'https://pterodactyl.io/discord'; + } + + /** + * Get the URL for donations. + */ + public function getDonations(): string + { + return Arr::get(self::$result, 'donations') ?? 'https://github.com/sponsors/matthewpi'; + } + + /** + * Determine if the current version of the panel is the latest. + */ + public function isLatestPanel(): bool + { + if (config('app.version') === 'canary') { + return true; + } + + return version_compare(config('app.version'), $this->getPanel()) >= 0; + } + + /** + * Determine if a passed daemon version string is the latest. + */ + public function isLatestDaemon(string $version): bool + { + if ($version === 'develop') { + return true; + } + + return version_compare($version, $this->getDaemon()) >= 0; + } + + /** + * Keeps the versioning cache up-to-date with the latest results from the CDN. + */ + protected function cacheVersionData(): array + { + return $this->cache->remember(self::VERSION_CACHE_KEY, CarbonImmutable::now()->addMinutes(config('pterodactyl.cdn.cache_time', 60)), function () { + try { + $response = $this->client->request('GET', config('pterodactyl.cdn.url')); + + if ($response->getStatusCode() === 200) { + return json_decode($response->getBody(), true); + } + + throw new CdnVersionFetchingException(); + } catch (Exception) { + return []; + } + }); + } +} diff --git a/app/Services/Locations/LocationCreationService.php b/app/Services/Locations/LocationCreationService.php new file mode 100644 index 0000000..b1a3ec9 --- /dev/null +++ b/app/Services/Locations/LocationCreationService.php @@ -0,0 +1,26 @@ +repository->create($data); + } +} diff --git a/app/Services/Locations/LocationDeletionService.php b/app/Services/Locations/LocationDeletionService.php new file mode 100644 index 0000000..5b4b9eb --- /dev/null +++ b/app/Services/Locations/LocationDeletionService.php @@ -0,0 +1,40 @@ +id : $location; + + Assert::integerish($location, 'First argument passed to handle must be numeric or an instance of ' . Location::class . ', received %s.'); + + $count = $this->nodeRepository->findCountWhere([['location_id', '=', $location]]); + if ($count > 0) { + throw new HasActiveNodesException(trans('exceptions.locations.has_nodes')); + } + + return $this->repository->delete($location); + } +} diff --git a/app/Services/Locations/LocationUpdateService.php b/app/Services/Locations/LocationUpdateService.php new file mode 100644 index 0000000..cf24459 --- /dev/null +++ b/app/Services/Locations/LocationUpdateService.php @@ -0,0 +1,29 @@ +id : $location; + + return $this->repository->update($location, $data); + } +} diff --git a/app/Services/Nests/NestCreationService.php b/app/Services/Nests/NestCreationService.php new file mode 100644 index 0000000..c3513ae --- /dev/null +++ b/app/Services/Nests/NestCreationService.php @@ -0,0 +1,33 @@ +repository->create([ + 'uuid' => Uuid::uuid4()->toString(), + 'author' => $author ?? $this->config->get('pterodactyl.service.author'), + 'name' => array_get($data, 'name'), + 'description' => array_get($data, 'description'), + ], true, true); + } +} diff --git a/app/Services/Nests/NestDeletionService.php b/app/Services/Nests/NestDeletionService.php new file mode 100644 index 0000000..6babf45 --- /dev/null +++ b/app/Services/Nests/NestDeletionService.php @@ -0,0 +1,34 @@ +serverRepository->findCountWhere([['nest_id', '=', $nest]]); + if ($count > 0) { + throw new HasActiveServersException(trans('exceptions.nest.delete_has_servers')); + } + + return $this->repository->delete($nest); + } +} diff --git a/app/Services/Nests/NestUpdateService.php b/app/Services/Nests/NestUpdateService.php new file mode 100644 index 0000000..772421e --- /dev/null +++ b/app/Services/Nests/NestUpdateService.php @@ -0,0 +1,30 @@ +repository->withoutFreshModel()->update($nest, $data); + } +} diff --git a/app/Services/Nodes/NodeCreationService.php b/app/Services/Nodes/NodeCreationService.php new file mode 100644 index 0000000..b21589c --- /dev/null +++ b/app/Services/Nodes/NodeCreationService.php @@ -0,0 +1,33 @@ +toString(); + $data['daemon_token'] = app(Encrypter::class)->encrypt(Str::random(Node::DAEMON_TOKEN_LENGTH)); + $data['daemon_token_id'] = Str::random(Node::DAEMON_TOKEN_ID_LENGTH); + + return $this->repository->create($data, true, true); + } +} diff --git a/app/Services/Nodes/NodeDeletionService.php b/app/Services/Nodes/NodeDeletionService.php new file mode 100644 index 0000000..adb9a06 --- /dev/null +++ b/app/Services/Nodes/NodeDeletionService.php @@ -0,0 +1,41 @@ +id; + } + + $servers = $this->serverRepository->setColumns('id')->findCountWhere([['node_id', '=', $node]]); + if ($servers > 0) { + throw new HasActiveServersException($this->translator->get('exceptions.node.servers_attached')); + } + + return $this->repository->delete($node); + } +} diff --git a/app/Services/Nodes/NodeJWTService.php b/app/Services/Nodes/NodeJWTService.php new file mode 100644 index 0000000..105715a --- /dev/null +++ b/app/Services/Nodes/NodeJWTService.php @@ -0,0 +1,103 @@ +claims = $claims; + + return $this; + } + + /** + * Attaches a user to the JWT being created and will automatically inject the + * "user_uuid" key into the final claims array with the user's UUID. + */ + public function setUser(User $user): self + { + $this->user = $user; + + return $this; + } + + public function setExpiresAt(\DateTimeImmutable $date): self + { + $this->expiresAt = $date; + + return $this; + } + + public function setSubject(string $subject): self + { + $this->subject = $subject; + + return $this; + } + + /** + * Generate a new JWT for a given node. + */ + public function handle(Node $node, ?string $identifiedBy, string $algo = 'md5'): Plain + { + $identifier = hash($algo, $identifiedBy); + $config = Configuration::forSymmetricSigner(new Sha256(), InMemory::plainText($node->getDecryptedKey())); + + $builder = $config->builder(new TimestampDates()) + ->issuedBy(config('app.url')) + ->permittedFor($node->getConnectionAddress()) + ->identifiedBy($identifier) + ->withHeader('jti', $identifier) + ->issuedAt(CarbonImmutable::now()) + ->canOnlyBeUsedAfter(CarbonImmutable::now()->subMinutes(5)); + + if ($this->expiresAt) { + $builder = $builder->expiresAt($this->expiresAt); + } + + if (!empty($this->subject)) { + $builder = $builder->relatedTo($this->subject)->withHeader('sub', $this->subject); + } + + foreach ($this->claims as $key => $value) { + $builder = $builder->withClaim($key, $value); + } + + if (!is_null($this->user)) { + $builder = $builder + ->withClaim('user_uuid', $this->user->uuid) + // The "user_id" claim is deprecated and should not be referenced — it remains + // here solely to ensure older versions of Wings are unaffected when the Panel + // is updated. + // + // This claim will be removed in Panel@1.11 or later. + ->withClaim('user_id', $this->user->id); + } + + return $builder + ->withClaim('unique_id', Str::random()) + ->getToken($config->signer(), $config->signingKey()); + } +} diff --git a/app/Services/Nodes/NodeUpdateService.php b/app/Services/Nodes/NodeUpdateService.php new file mode 100644 index 0000000..28733e3 --- /dev/null +++ b/app/Services/Nodes/NodeUpdateService.php @@ -0,0 +1,80 @@ +encrypter->encrypt(Str::random(Node::DAEMON_TOKEN_LENGTH)); + $data['daemon_token_id'] = Str::random(Node::DAEMON_TOKEN_ID_LENGTH); + } + + [$updated, $exception] = $this->connection->transaction(function () use ($data, $node) { + /** @var \Pterodactyl\Models\Node $updated */ + $updated = $this->repository->withFreshModel()->update($node->id, $data, true, true); + + try { + // If we're changing the FQDN for the node, use the newly provided FQDN for the connection + // address. This should alleviate issues where the node gets pointed to a "valid" FQDN that + // isn't actually running the daemon software, and therefore you can't actually change it + // back. + // + // This makes more sense anyways, because only the Panel uses the FQDN for connecting, the + // node doesn't actually care about this. + // + // @see https://github.com/pterodactyl/panel/issues/1931 + $node->fqdn = $updated->fqdn; + + $this->configurationRepository->setNode($node)->update($updated); + } catch (DaemonConnectionException $exception) { + Log::warning($exception, ['node_id' => $node->id]); + + // Never actually throw these exceptions up the stack. If we were able to change the settings + // but something went wrong with Wings we just want to store the update and let the user manually + // make changes as needed. + // + // This avoids issues with proxies such as Cloudflare which will see Wings as offline and then + // inject their own response pages, causing this logic to get fucked up. + // + // @see https://github.com/pterodactyl/panel/issues/2712 + return [$updated, true]; + } + + return [$updated, false]; + }); + + if ($exception) { + throw new ConfigurationNotPersistedException(trans('exceptions.node.daemon_off_config_updated')); + } + + return $updated; + } +} diff --git a/app/Services/Schedules/ProcessScheduleService.php b/app/Services/Schedules/ProcessScheduleService.php new file mode 100644 index 0000000..56b7084 --- /dev/null +++ b/app/Services/Schedules/ProcessScheduleService.php @@ -0,0 +1,88 @@ +tasks()->orderBy('sequence_id')->first(); + + if (is_null($task)) { + throw new DisplayException('Cannot process schedule for task execution: no tasks are registered.'); + } + + $this->connection->transaction(function () use ($schedule, $task) { + $schedule->forceFill([ + 'is_processing' => true, + 'next_run_at' => $schedule->getNextRunDate(), + ])->saveOrFail(); + + $task->update(['is_queued' => true]); + }); + + $job = new RunTaskJob($task, $now); + if ($schedule->only_when_online) { + // Check that the server is currently in a starting or running state before executing + // this schedule if this option has been set. + try { + $details = $this->serverRepository->setServer($schedule->server)->getDetails(); + $state = $details['state'] ?? 'offline'; + // If the server is stopping or offline just do nothing with this task. + if (in_array($state, ['offline', 'stopping'])) { + $job->failed(); + + return; + } + } catch (\Exception $exception) { + if (!$exception instanceof DaemonConnectionException) { + // If we encountered some exception during this process that wasn't just an + // issue connecting to Wings run the failed sequence for a job. Otherwise we + // can just quietly mark the task as completed without actually running anything. + $job->failed($exception); + } + $job->failed(); + + return; + } + } + + if (!$now) { + $this->dispatcher->dispatch($job->delay($task->time_offset)); + } else { + // When using dispatchNow the RunTaskJob::failed() function is not called automatically + // so we need to manually trigger it and then continue with the exception throw. + // + // @see https://github.com/pterodactyl/panel/issues/2550 + try { + $this->dispatcher->dispatchNow($job); + } catch (\Exception $exception) { + $job->failed($exception); + + throw $exception; + } + } + } +} diff --git a/app/Services/Servers/BuildModificationService.php b/app/Services/Servers/BuildModificationService.php new file mode 100644 index 0000000..05553d7 --- /dev/null +++ b/app/Services/Servers/BuildModificationService.php @@ -0,0 +1,131 @@ +connection->transaction(function () use ($server, $data) { + $this->processAllocations($server, $data); + + if (isset($data['allocation_id']) && $data['allocation_id'] != $server->allocation_id) { + try { + Allocation::query()->where('id', $data['allocation_id'])->where('server_id', $server->id)->firstOrFail(); + } catch (ModelNotFoundException) { + throw new DisplayException('The requested default allocation is not currently assigned to this server.'); + } + } + + // If any of these values are passed through in the data array go ahead and set + // them correctly on the server model. + $merge = Arr::only($data, ['oom_disabled', 'memory', 'swap', 'io', 'cpu', 'threads', 'disk', 'allocation_id']); + + $server->forceFill(array_merge($merge, [ + 'database_limit' => Arr::get($data, 'database_limit', 0) ?? null, + 'allocation_limit' => Arr::get($data, 'allocation_limit', 0) ?? null, + 'backup_limit' => Arr::get($data, 'backup_limit', 0) ?? 0, + ]))->saveOrFail(); + + return $server->refresh(); + }); + + $updateData = $this->structureService->handle($server); + + // Because Wings always fetches an updated configuration from the Panel when booting + // a server this type of exception can be safely "ignored" and just written to the logs. + // Ideally this request succeeds, so we can apply resource modifications on the fly, but + // if it fails we can just continue on as normal. + if (!empty($updateData['build'])) { + try { + $this->daemonServerRepository->setServer($server)->sync(); + } catch (DaemonConnectionException $exception) { + Log::warning($exception, ['server_id' => $server->id]); + } + } + + return $server; + } + + /** + * Process the allocations being assigned in the data and ensure they are available for a server. + * + * @throws \Pterodactyl\Exceptions\DisplayException + */ + private function processAllocations(Server $server, array &$data): void + { + if (empty($data['add_allocations']) && empty($data['remove_allocations'])) { + return; + } + + // Handle the addition of allocations to this server. Only assign allocations that are not currently + // assigned to a different server, and only allocations on the same node as the server. + if (!empty($data['add_allocations'])) { + $query = Allocation::query() + ->where('node_id', $server->node_id) + ->whereIn('id', $data['add_allocations']) + ->whereNull('server_id'); + + // Keep track of all the allocations we're just now adding so that we can use the first + // one to reset the default allocation to. + $freshlyAllocated = $query->pluck('id')->first(); + + $query->update(['server_id' => $server->id, 'notes' => null]); + } + + if (!empty($data['remove_allocations'])) { + foreach ($data['remove_allocations'] as $allocation) { + // If we are attempting to remove the default allocation for the server, see if we can reassign + // to the first provided value in add_allocations. If there is no new first allocation then we + // will throw an exception back. + if ($allocation === ($data['allocation_id'] ?? $server->allocation_id)) { + if (empty($freshlyAllocated)) { + throw new DisplayException('You are attempting to delete the default allocation for this server but there is no fallback allocation to use.'); + } + + // Update the default allocation to be the first allocation that we are creating. + $data['allocation_id'] = $freshlyAllocated; + } + } + + // Remove any of the allocations we got that are currently assigned to this server on + // this node. Also set the notes to null, otherwise when re-allocated to a new server those + // notes will be carried over. + Allocation::query()->where('node_id', $server->node_id) + ->where('server_id', $server->id) + // Only remove the allocations that we didn't also attempt to add to the server... + ->whereIn('id', array_diff($data['remove_allocations'], $data['add_allocations'] ?? [])) + ->update([ + 'notes' => null, + 'server_id' => null, + ]); + } + } +} diff --git a/app/Services/Servers/DetailsModificationService.php b/app/Services/Servers/DetailsModificationService.php new file mode 100644 index 0000000..d135c04 --- /dev/null +++ b/app/Services/Servers/DetailsModificationService.php @@ -0,0 +1,56 @@ +connection->transaction(function () use ($data, $server) { + $owner = $server->owner_id; + + $server->forceFill([ + 'external_id' => Arr::get($data, 'external_id'), + 'owner_id' => Arr::get($data, 'owner_id'), + 'name' => Arr::get($data, 'name'), + 'description' => Arr::get($data, 'description') ?? '', + ])->saveOrFail(); + + // If the owner_id value is changed we need to revoke any tokens that exist for the server + // on the Wings instance so that the old owner no longer has any permission to access the + // websockets. + if ($server->owner_id !== $owner) { + try { + $this->serverRepository->setServer($server)->revokeUserJTI($owner); + } catch (DaemonConnectionException $exception) { + // Do nothing. A failure here is not ideal, but it is likely to be caused by Wings + // being offline, or in an entirely broken state. Remember, these tokens reset every + // few minutes by default, we're just trying to help it along a little quicker. + } + } + + return $server; + }); + } +} diff --git a/app/Services/Servers/EnvironmentService.php b/app/Services/Servers/EnvironmentService.php new file mode 100644 index 0000000..8f45bab --- /dev/null +++ b/app/Services/Servers/EnvironmentService.php @@ -0,0 +1,73 @@ +additional[$key] = $closure; + } + + /** + * Return the dynamically added additional keys. + */ + public function getEnvironmentKeys(): array + { + return $this->additional; + } + + /** + * Take all of the environment variables configured for this server and return + * them in an easy to process format. + */ + public function handle(Server $server): array + { + $variables = $server->variables->toBase()->mapWithKeys(function (EggVariable $variable) { + return [$variable->env_variable => $variable->server_value ?? $variable->default_value]; + }); + + // Process environment variables defined in this file. This is done first + // in order to allow run-time and config defined variables to take + // priority over built-in values. + foreach ($this->getEnvironmentMappings() as $key => $object) { + $variables->put($key, object_get($server, $object)); + } + + // Process variables set in the configuration file. + foreach (config('pterodactyl.environment_variables', []) as $key => $object) { + $variables->put( + $key, + is_callable($object) ? call_user_func($object, $server) : object_get($server, $object) + ); + } + + // Process dynamically included environment variables. + foreach ($this->additional as $key => $closure) { + $variables->put($key, call_user_func($closure, $server)); + } + + return $variables->toArray(); + } + + /** + * Return a mapping of Panel default environment variables. + */ + private function getEnvironmentMappings(): array + { + return [ + 'STARTUP' => 'startup', + 'P_SERVER_LOCATION' => 'location.short', + 'P_SERVER_UUID' => 'uuid', + ]; + } +} diff --git a/app/Services/Servers/GetUserPermissionsService.php b/app/Services/Servers/GetUserPermissionsService.php new file mode 100644 index 0000000..ae91cb3 --- /dev/null +++ b/app/Services/Servers/GetUserPermissionsService.php @@ -0,0 +1,34 @@ +root_admin || $user->id === $server->owner_id) { + $permissions = ['*']; + + if ($user->root_admin) { + $permissions[] = 'admin.websocket.errors'; + $permissions[] = 'admin.websocket.install'; + $permissions[] = 'admin.websocket.transfer'; + } + + return $permissions; + } + + /** @var \Pterodactyl\Models\Subuser|null $subuserPermissions */ + $subuserPermissions = $server->subusers()->where('user_id', $user->id)->first(); + + return $subuserPermissions ? $subuserPermissions->permissions : []; + } +} diff --git a/app/Services/Servers/ReinstallServerService.php b/app/Services/Servers/ReinstallServerService.php new file mode 100644 index 0000000..5881d1f --- /dev/null +++ b/app/Services/Servers/ReinstallServerService.php @@ -0,0 +1,35 @@ +connection->transaction(function () use ($server) { + $server->fill(['status' => Server::STATUS_INSTALLING])->save(); + + $this->daemonServerRepository->setServer($server)->reinstall(); + + return $server->refresh(); + }); + } +} diff --git a/app/Services/Servers/ServerConfigurationStructureService.php b/app/Services/Servers/ServerConfigurationStructureService.php new file mode 100644 index 0000000..72f3277 --- /dev/null +++ b/app/Services/Servers/ServerConfigurationStructureService.php @@ -0,0 +1,130 @@ +fresh(); + foreach ($override as $key => $value) { + $clone->setAttribute($key, $value); + } + } + + return $legacy + ? $this->returnLegacyFormat($clone) + : $this->returnCurrentFormat($clone); + } + + /** + * Returns the new data format used for the Wings daemon. + */ + protected function returnCurrentFormat(Server $server): array + { + return [ + 'uuid' => $server->uuid, + 'meta' => [ + 'name' => $server->name, + 'description' => $server->description, + ], + 'suspended' => $server->isSuspended(), + 'environment' => $this->environment->handle($server), + 'invocation' => $server->startup, + 'skip_egg_scripts' => $server->skip_scripts, + 'build' => [ + 'memory_limit' => $server->memory, + 'swap' => $server->swap, + 'io_weight' => $server->io, + 'cpu_limit' => $server->cpu, + 'threads' => $server->threads, + 'disk_space' => $server->disk, + 'oom_disabled' => $server->oom_disabled, + ], + 'container' => [ + 'image' => $server->image, + // This field is deprecated — use the value in the "build" block. + // + // TODO: remove this key in V2. + 'oom_disabled' => $server->oom_disabled, + 'requires_rebuild' => false, + ], + 'allocations' => [ + 'force_outgoing_ip' => $server->egg->force_outgoing_ip, + 'default' => [ + 'ip' => $server->allocation->ip, + 'port' => $server->allocation->port, + ], + 'mappings' => $server->getAllocationMappings(), + ], + 'mounts' => $server->mounts->map(function (Mount $mount) { + return [ + 'source' => $mount->source, + 'target' => $mount->target, + 'read_only' => $mount->read_only, + ]; + }), + 'egg' => [ + 'id' => $server->egg->uuid, + 'file_denylist' => $server->egg->inherit_file_denylist, + ], + ]; + } + + /** + * Returns the legacy server data format to continue support for old egg configurations + * that have not yet been updated. + * + * @deprecated + */ + protected function returnLegacyFormat(Server $server): array + { + return [ + 'uuid' => $server->uuid, + 'build' => [ + 'default' => [ + 'ip' => $server->allocation->ip, + 'port' => $server->allocation->port, + ], + 'ports' => $server->allocations->groupBy('ip')->map(function ($item) { + return $item->pluck('port'); + })->toArray(), + 'env' => $this->environment->handle($server), + 'oom_disabled' => $server->oom_disabled, + 'memory' => (int) $server->memory, + 'swap' => (int) $server->swap, + 'io' => (int) $server->io, + 'cpu' => (int) $server->cpu, + 'threads' => $server->threads, + 'disk' => (int) $server->disk, + 'image' => $server->image, + ], + 'service' => [ + 'egg' => $server->egg->uuid, + 'skip_scripts' => $server->skip_scripts, + ], + 'rebuild' => false, + 'suspended' => $server->isSuspended() ? 1 : 0, + ]; + } +} diff --git a/app/Services/Servers/ServerCreationService.php b/app/Services/Servers/ServerCreationService.php new file mode 100644 index 0000000..2c9b4cf --- /dev/null +++ b/app/Services/Servers/ServerCreationService.php @@ -0,0 +1,216 @@ +configureDeployment($data, $deployment); + $data['allocation_id'] = $allocation->id; + $data['node_id'] = $allocation->node_id; + } + + // Auto-configure the node based on the selected allocation + // if no node was defined. + if (empty($data['node_id'])) { + Assert::false(empty($data['allocation_id']), 'Expected a non-empty allocation_id in server creation data.'); + + $data['node_id'] = Allocation::query()->findOrFail($data['allocation_id'])->node_id; + } + + if (empty($data['nest_id'])) { + Assert::false(empty($data['egg_id']), 'Expected a non-empty egg_id in server creation data.'); + + $data['nest_id'] = Egg::query()->findOrFail($data['egg_id'])->nest_id; + } + + $eggVariableData = $this->validatorService + ->setUserLevel(User::USER_LEVEL_ADMIN) + ->handle(Arr::get($data, 'egg_id'), Arr::get($data, 'environment', [])); + + // Due to the design of the Daemon, we need to persist this server to the disk + // before we can actually create it on the Daemon. + // + // If that connection fails out we will attempt to perform a cleanup by just + // deleting the server itself from the system. + /** @var \Pterodactyl\Models\Server $server */ + $server = $this->connection->transaction(function () use ($data, $eggVariableData) { + // Create the server and assign any additional allocations to it. + $server = $this->createModel($data); + + $this->storeAssignedAllocations($server, $data); + $this->storeEggVariables($server, $eggVariableData); + + return $server; + }, 5); + + try { + $this->daemonServerRepository->setServer($server)->create( + Arr::get($data, 'start_on_completion', false) ?? false + ); + } catch (DaemonConnectionException $exception) { + $this->serverDeletionService->withForce()->handle($server); + + throw $exception; + } + + return $server; + } + + /** + * Gets an allocation to use for automatic deployment. + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableAllocationException + * @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableNodeException + */ + private function configureDeployment(array $data, DeploymentObject $deployment): Allocation + { + /** @var \Illuminate\Support\Collection $nodes */ + $nodes = $this->findViableNodesService->setLocations($deployment->getLocations()) + ->setDisk(Arr::get($data, 'disk')) + ->setMemory(Arr::get($data, 'memory')) + ->handle(); + + return $this->allocationSelectionService->setDedicated($deployment->isDedicated()) + ->setNodes($nodes->pluck('id')->toArray()) + ->setPorts($deployment->getPorts()) + ->handle(); + } + + /** + * Store the server in the database and return the model. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + */ + private function createModel(array $data): Server + { + $uuid = $this->generateUniqueUuidCombo(); + + /** @var \Pterodactyl\Models\Server $model */ + $model = $this->repository->create([ + 'external_id' => Arr::get($data, 'external_id'), + 'uuid' => $uuid, + 'uuidShort' => substr($uuid, 0, 8), + 'node_id' => Arr::get($data, 'node_id'), + 'name' => Arr::get($data, 'name'), + 'description' => Arr::get($data, 'description') ?? '', + 'status' => Server::STATUS_INSTALLING, + 'skip_scripts' => Arr::get($data, 'skip_scripts') ?? isset($data['skip_scripts']), + 'owner_id' => Arr::get($data, 'owner_id'), + 'memory' => Arr::get($data, 'memory'), + 'swap' => Arr::get($data, 'swap'), + 'disk' => Arr::get($data, 'disk'), + 'io' => Arr::get($data, 'io'), + 'cpu' => Arr::get($data, 'cpu'), + 'threads' => Arr::get($data, 'threads'), + 'oom_disabled' => Arr::get($data, 'oom_disabled') ?? true, + 'allocation_id' => Arr::get($data, 'allocation_id'), + 'nest_id' => Arr::get($data, 'nest_id'), + 'egg_id' => Arr::get($data, 'egg_id'), + 'startup' => Arr::get($data, 'startup'), + 'image' => Arr::get($data, 'image'), + 'database_limit' => Arr::get($data, 'database_limit') ?? 0, + 'allocation_limit' => Arr::get($data, 'allocation_limit') ?? 0, + 'backup_limit' => Arr::get($data, 'backup_limit') ?? 0, + ]); + + return $model; + } + + /** + * Configure the allocations assigned to this server. + */ + private function storeAssignedAllocations(Server $server, array $data): void + { + $records = [$data['allocation_id']]; + if (isset($data['allocation_additional']) && is_array($data['allocation_additional'])) { + $records = array_merge($records, $data['allocation_additional']); + } + + Allocation::query()->whereIn('id', $records)->update([ + 'server_id' => $server->id, + ]); + } + + /** + * Process environment variables passed for this server and store them in the database. + */ + private function storeEggVariables(Server $server, Collection $variables): void + { + $records = $variables->map(function ($result) use ($server) { + return [ + 'server_id' => $server->id, + 'variable_id' => $result->id, + 'variable_value' => $result->value ?? '', + ]; + })->toArray(); + + if (!empty($records)) { + $this->serverVariableRepository->insert($records); + } + } + + /** + * Create a unique UUID and UUID-Short combo for a server. + */ + private function generateUniqueUuidCombo(): string + { + $uuid = Uuid::uuid4()->toString(); + + if (!$this->repository->isUniqueUuidCombo($uuid, substr($uuid, 0, 8))) { + return $this->generateUniqueUuidCombo(); + } + + return $uuid; + } +} diff --git a/app/Services/Servers/ServerDeletionService.php b/app/Services/Servers/ServerDeletionService.php new file mode 100644 index 0000000..a83c8fe --- /dev/null +++ b/app/Services/Servers/ServerDeletionService.php @@ -0,0 +1,83 @@ +force = $bool; + + return $this; + } + + /** + * Delete a server from the panel and remove any associated databases from hosts. + * + * @throws \Throwable + * @throws \Pterodactyl\Exceptions\DisplayException + */ + public function handle(Server $server): void + { + try { + $this->daemonServerRepository->setServer($server)->delete(); + } catch (DaemonConnectionException $exception) { + // If there is an error not caused a 404 error and this isn't a forced delete, + // go ahead and bail out. We specifically ignore a 404 since that can be assumed + // to be a safe error, meaning the server doesn't exist at all on Wings so there + // is no reason we need to bail out from that. + if (!$this->force && $exception->getStatusCode() !== Response::HTTP_NOT_FOUND) { + throw $exception; + } + + Log::warning($exception); + } + + $this->connection->transaction(function () use ($server) { + foreach ($server->databases as $database) { + try { + $this->databaseManagementService->delete($database); + } catch (\Exception $exception) { + if (!$this->force) { + throw $exception; + } + + // Oh well, just try to delete the database entry we have from the database + // so that the server itself can be deleted. This will leave it dangling on + // the host instance, but we couldn't delete it anyways so not sure how we would + // handle this better anyways. + // + // @see https://github.com/pterodactyl/panel/issues/2085 + $database->delete(); + + Log::warning($exception); + } + } + + $server->delete(); + }); + } +} diff --git a/app/Services/Servers/StartupCommandService.php b/app/Services/Servers/StartupCommandService.php new file mode 100644 index 0000000..efdbbc5 --- /dev/null +++ b/app/Services/Servers/StartupCommandService.php @@ -0,0 +1,24 @@ +memory, $server->allocation->ip, $server->allocation->port]; + + foreach ($server->variables as $variable) { + $find[] = '{{' . $variable->env_variable . '}}'; + $replace[] = ($variable->user_viewable && !$hideAllValues) ? ($variable->server_value ?? $variable->default_value) : '[hidden]'; + } + + return str_replace($find, $replace, $server->startup); + } +} diff --git a/app/Services/Servers/StartupModificationService.php b/app/Services/Servers/StartupModificationService.php new file mode 100644 index 0000000..a0a5c4c --- /dev/null +++ b/app/Services/Servers/StartupModificationService.php @@ -0,0 +1,88 @@ +connection->transaction(function () use ($server, $data) { + if (!empty($data['environment'])) { + $egg = $this->isUserLevel(User::USER_LEVEL_ADMIN) ? ($data['egg_id'] ?? $server->egg_id) : $server->egg_id; + + $results = $this->validatorService + ->setUserLevel($this->getUserLevel()) + ->handle($egg, $data['environment']); + + foreach ($results as $result) { + ServerVariable::query()->updateOrCreate( + [ + 'server_id' => $server->id, + 'variable_id' => $result->id, + ], + ['variable_value' => $result->value ?? ''] + ); + } + } + + if ($this->isUserLevel(User::USER_LEVEL_ADMIN)) { + $this->updateAdministrativeSettings($data, $server); + } + + // Calling ->refresh() rather than ->fresh() here causes it to return the + // variables as triplicates for some reason? Not entirely sure, should dig + // in more to figure it out, but luckily we have a test case covering this + // specific call so we can be assured we're not breaking it _here_ at least. + // + // TODO(dane): this seems like a red-flag for the code powering the relationship + // that should be looked into more. + return $server->fresh(); + }); + } + + /** + * Update certain administrative settings for a server in the DB. + */ + protected function updateAdministrativeSettings(array $data, Server &$server): void + { + $eggId = Arr::get($data, 'egg_id'); + + if (is_digit($eggId) && $server->egg_id !== (int) $eggId) { + /** @var \Pterodactyl\Models\Egg $egg */ + $egg = Egg::query()->findOrFail($data['egg_id']); + + $server = $server->forceFill([ + 'egg_id' => $egg->id, + 'nest_id' => $egg->nest_id, + ]); + } + + $server->fill([ + 'startup' => $data['startup'] ?? $server->startup, + 'skip_scripts' => $data['skip_scripts'] ?? isset($data['skip_scripts']), + 'image' => $data['docker_image'] ?? $server->image, + ])->save(); + } +} diff --git a/app/Services/Servers/SuspensionService.php b/app/Services/Servers/SuspensionService.php new file mode 100644 index 0000000..d11ace2 --- /dev/null +++ b/app/Services/Servers/SuspensionService.php @@ -0,0 +1,61 @@ +isSuspended()) { + return; + } + + // Check if the server is currently being transferred. + if (!is_null($server->transfer)) { + throw new ConflictHttpException('Cannot toggle suspension status on a server that is currently being transferred.'); + } + + // Update the server's suspension status. + $server->update([ + 'status' => $isSuspending ? Server::STATUS_SUSPENDED : null, + ]); + + try { + // Tell wings to re-sync the server state. + $this->daemonServerRepository->setServer($server)->sync(); + } catch (\Exception $exception) { + // Rollback the server's suspension status if wings fails to sync the server. + $server->update([ + 'status' => $isSuspending ? null : Server::STATUS_SUSPENDED, + ]); + throw $exception; + } + } +} diff --git a/app/Services/Servers/VariableValidatorService.php b/app/Services/Servers/VariableValidatorService.php new file mode 100644 index 0000000..a198692 --- /dev/null +++ b/app/Services/Servers/VariableValidatorService.php @@ -0,0 +1,60 @@ +where('egg_id', $egg); + if (!$this->isUserLevel(User::USER_LEVEL_ADMIN)) { + // Don't attempt to validate variables if they aren't user editable, + // and we're not running this at an admin level. + $query = $query->where('user_editable', true)->where('user_viewable', true); + } + + /** @var \Pterodactyl\Models\EggVariable[] $variables */ + $variables = $query->get(); + + $data = $rules = $customAttributes = []; + foreach ($variables as $variable) { + $data['environment'][$variable->env_variable] = array_get($fields, $variable->env_variable); + $rules['environment.' . $variable->env_variable] = $variable->rules; + $customAttributes['environment.' . $variable->env_variable] = trans('validation.internal.variable_value', ['env' => $variable->name]); + } + + $validator = $this->validator->make($data, $rules, [], $customAttributes); + if ($validator->fails()) { + throw new ValidationException($validator); + } + + return Collection::make($variables)->map(function ($item) use ($fields) { + return (object) [ + 'id' => $item->id, + 'key' => $item->env_variable, + 'value' => $fields[$item->env_variable] ?? null, + ]; + }); + } +} diff --git a/app/Services/Subusers/SubuserCreationService.php b/app/Services/Subusers/SubuserCreationService.php new file mode 100644 index 0000000..c207b0d --- /dev/null +++ b/app/Services/Subusers/SubuserCreationService.php @@ -0,0 +1,74 @@ +connection->transaction(function () use ($server, $email, $permissions) { + try { + $user = $this->userRepository->findFirstWhere([['email', '=', $email]]); + + if ($server->owner_id === $user->id) { + throw new UserIsServerOwnerException(trans('exceptions.subusers.user_is_owner')); + } + + $subuserCount = $this->subuserRepository->findCountWhere([['user_id', '=', $user->id], ['server_id', '=', $server->id]]); + if ($subuserCount !== 0) { + throw new ServerSubuserExistsException(trans('exceptions.subusers.subuser_exists')); + } + } catch (RecordNotFoundException) { + // Just cap the username generated at 64 characters at most and then append a random string + // to the end to make it "unique"... + $username = substr(preg_replace('/([^\w\.-]+)/', '', strtok($email, '@')), 0, 64) . Str::random(3); + + $user = $this->userCreationService->handle([ + 'email' => $email, + 'username' => $username, + 'name_first' => 'Server', + 'name_last' => 'Subuser', + 'root_admin' => false, + ]); + } + + return $this->subuserRepository->create([ + 'user_id' => $user->id, + 'server_id' => $server->id, + 'permissions' => array_unique($permissions), + ]); + }); + } +} diff --git a/app/Services/Telemetry/TelemetryCollectionService.php b/app/Services/Telemetry/TelemetryCollectionService.php new file mode 100644 index 0000000..c2fd128 --- /dev/null +++ b/app/Services/Telemetry/TelemetryCollectionService.php @@ -0,0 +1,187 @@ +collect(); + } catch (Exception) { + return; + } + + Http::post('https://telemetry.pterodactyl.io', $data); + } + + /** + * Collects telemetry data and returns it as an array. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + */ + public function collect(): array + { + $uuid = $this->settingsRepository->get('app:telemetry:uuid'); + if (is_null($uuid)) { + $uuid = Uuid::uuid4()->toString(); + $this->settingsRepository->set('app:telemetry:uuid', $uuid); + } + + $nodes = Node::all()->map(function ($node) { + try { + $info = $this->daemonConfigurationRepository->setNode($node)->getSystemInformation(2); + } catch (Exception) { + return null; + } + + return [ + 'id' => $node->uuid, + 'version' => Arr::get($info, 'version', ''), + + 'docker' => [ + 'version' => Arr::get($info, 'docker.version', ''), + + 'cgroups' => [ + 'driver' => Arr::get($info, 'docker.cgroups.driver', ''), + 'version' => Arr::get($info, 'docker.cgroups.version', ''), + ], + + 'containers' => [ + 'total' => Arr::get($info, 'docker.containers.total', -1), + 'running' => Arr::get($info, 'docker.containers.running', -1), + 'paused' => Arr::get($info, 'docker.containers.paused', -1), + 'stopped' => Arr::get($info, 'docker.containers.stopped', -1), + ], + + 'storage' => [ + 'driver' => Arr::get($info, 'docker.storage.driver', ''), + 'filesystem' => Arr::get($info, 'docker.storage.filesystem', ''), + ], + + 'runc' => [ + 'version' => Arr::get($info, 'docker.runc.version', ''), + ], + ], + + 'system' => [ + 'architecture' => Arr::get($info, 'system.architecture', ''), + 'cpuThreads' => Arr::get($info, 'system.cpu_threads', ''), + 'memoryBytes' => Arr::get($info, 'system.memory_bytes', ''), + 'kernelVersion' => Arr::get($info, 'system.kernel_version', ''), + 'os' => Arr::get($info, 'system.os', ''), + 'osType' => Arr::get($info, 'system.os_type', ''), + ], + ]; + })->filter(fn ($node) => !is_null($node))->toArray(); + + return [ + 'id' => $uuid, + + 'panel' => [ + 'version' => config('app.version'), + 'phpVersion' => phpversion(), + + 'drivers' => [ + 'backup' => [ + 'type' => config('backups.default'), + ], + + 'cache' => [ + 'type' => config('cache.default'), + ], + + 'database' => [ + 'type' => config('database.default'), + 'version' => DB::getPdo()->getAttribute(\PDO::ATTR_SERVER_VERSION), + ], + ], + ], + + 'resources' => [ + 'allocations' => [ + 'count' => Allocation::count(), + 'used' => Allocation::whereNotNull('server_id')->count(), + ], + + 'backups' => [ + 'count' => Backup::count(), + 'bytes' => Backup::sum('bytes'), + ], + + 'eggs' => [ + 'count' => Egg::count(), + // Egg UUIDs are generated randomly on import, so there is not a consistent way to + // determine if servers are using default eggs or not. +// 'server_usage' => Egg::all() +// ->flatMap(fn (Egg $egg) => [$egg->uuid => $egg->servers->count()]) +// ->filter(fn (int $count) => $count > 0) +// ->toArray(), + ], + + 'locations' => [ + 'count' => Location::count(), + ], + + 'mounts' => [ + 'count' => Mount::count(), + ], + + 'nests' => [ + 'count' => Nest::count(), + // Nest UUIDs are generated randomly on import, so there is not a consistent way to + // determine if servers are using default eggs or not. +// 'server_usage' => Nest::all() +// ->flatMap(fn (Nest $nest) => [$nest->uuid => $nest->eggs->sum(fn (Egg $egg) => $egg->servers->count())]) +// ->filter(fn (int $count) => $count > 0) +// ->toArray(), + ], + + 'nodes' => [ + 'count' => Node::count(), + ], + + 'servers' => [ + 'count' => Server::count(), + 'suspended' => Server::where('status', Server::STATUS_SUSPENDED)->count(), + ], + + 'users' => [ + 'count' => User::count(), + 'admins' => User::where('root_admin', true)->count(), + ], + ], + + 'nodes' => $nodes, + ]; + } +} diff --git a/app/Services/Users/ToggleTwoFactorService.php b/app/Services/Users/ToggleTwoFactorService.php new file mode 100644 index 0000000..a91615b --- /dev/null +++ b/app/Services/Users/ToggleTwoFactorService.php @@ -0,0 +1,89 @@ +encrypter->decrypt($user->totp_secret); + + $isValidToken = $this->google2FA->verifyKey($secret, $token, config()->get('pterodactyl.auth.2fa.window')); + + if (!$isValidToken) { + throw new TwoFactorAuthenticationTokenInvalid(); + } + + return $this->connection->transaction(function () use ($user, $toggleState) { + // Now that we're enabling 2FA on the account, generate 10 recovery tokens for the account + // and store them hashed in the database. We'll return them to the caller so that the user + // can see and save them. + // + // If a user is unable to login with a 2FA token they can provide one of these backup codes + // which will then be marked as deleted from the database and will also bypass 2FA protections + // on their account. + $tokens = []; + if ((!$toggleState && !$user->use_totp) || $toggleState) { + $inserts = []; + for ($i = 0; $i < 10; ++$i) { + $token = Str::random(10); + + $inserts[] = [ + 'user_id' => $user->id, + 'token' => password_hash($token, PASSWORD_DEFAULT), + // insert() won't actually set the time on the models, so make sure we do this + // manually here. + 'created_at' => Carbon::now(), + ]; + + $tokens[] = $token; + } + + // Before inserting any new records make sure all of the old ones are deleted to avoid + // any issues or storing an unnecessary number of tokens in the database. + $this->recoveryTokenRepository->deleteWhere(['user_id' => $user->id]); + + // Bulk insert the hashed tokens. + $this->recoveryTokenRepository->insert($inserts); + } + + $this->repository->withoutFreshModel()->update($user->id, [ + 'totp_authenticated_at' => Carbon::now(), + 'use_totp' => (is_null($toggleState) ? !$user->use_totp : $toggleState), + ]); + + return $tokens; + }); + } +} diff --git a/app/Services/Users/TwoFactorSetupService.php b/app/Services/Users/TwoFactorSetupService.php new file mode 100644 index 0000000..9ce8322 --- /dev/null +++ b/app/Services/Users/TwoFactorSetupService.php @@ -0,0 +1,59 @@ +config->get('pterodactyl.auth.2fa.bytes', 16); ++$i) { + $secret .= substr(self::VALID_BASE32_CHARACTERS, random_int(0, 31), 1); + } + } catch (\Exception $exception) { + throw new \RuntimeException($exception->getMessage(), 0, $exception); + } + + $this->repository->withoutFreshModel()->update($user->id, [ + 'totp_secret' => $this->encrypter->encrypt($secret), + ]); + + $company = urlencode(preg_replace('/\s/', '', $this->config->get('app.name'))); + + return [ + 'image_url_data' => sprintf( + 'otpauth://totp/%1$s:%2$s?secret=%3$s&issuer=%1$s', + rawurlencode($company), + rawurlencode($user->email), + rawurlencode($secret), + ), + 'secret' => $secret, + ]; + } +} diff --git a/app/Services/Users/UserCreationService.php b/app/Services/Users/UserCreationService.php new file mode 100644 index 0000000..130ae1a --- /dev/null +++ b/app/Services/Users/UserCreationService.php @@ -0,0 +1,58 @@ +hasher->make($data['password']); + } + + $this->connection->beginTransaction(); + if (!isset($data['password']) || empty($data['password'])) { + $generateResetToken = true; + $data['password'] = $this->hasher->make(str_random(30)); + } + + /** @var \Pterodactyl\Models\User $user */ + $user = $this->repository->create(array_merge($data, [ + 'uuid' => Uuid::uuid4()->toString(), + ]), true, true); + + if (isset($generateResetToken)) { + $token = $this->passwordBroker->createToken($user); + } + + $this->connection->commit(); + $user->notify(new AccountCreated($user, $token ?? null)); + + return $user; + } +} diff --git a/app/Services/Users/UserDeletionService.php b/app/Services/Users/UserDeletionService.php new file mode 100644 index 0000000..f7f060c --- /dev/null +++ b/app/Services/Users/UserDeletionService.php @@ -0,0 +1,41 @@ +id; + } + + $servers = $this->serverRepository->setColumns('id')->findCountWhere([['owner_id', '=', $user]]); + if ($servers > 0) { + throw new DisplayException($this->translator->get('admin/user.exceptions.user_has_servers')); + } + + return $this->repository->delete($user); + } +} diff --git a/app/Services/Users/UserUpdateService.php b/app/Services/Users/UserUpdateService.php new file mode 100644 index 0000000..770fbdf --- /dev/null +++ b/app/Services/Users/UserUpdateService.php @@ -0,0 +1,37 @@ +hasher->make($data['password']); + } else { + unset($data['password']); + } + + $user->forceFill($data)->saveOrFail(); + + return $user->refresh(); + } +} diff --git a/app/Traits/Commands/EnvironmentWriterTrait.php b/app/Traits/Commands/EnvironmentWriterTrait.php new file mode 100644 index 0000000..3d1aa08 --- /dev/null +++ b/app/Traits/Commands/EnvironmentWriterTrait.php @@ -0,0 +1,49 @@ +each(function ($value, $key) use (&$saveContents) { + $key = strtoupper($key); + $saveValue = sprintf('%s=%s', $key, $this->escapeEnvironmentValue($value)); + + if (preg_match_all('/^' . $key . '=(.*)$/m', $saveContents) < 1) { + $saveContents = $saveContents . PHP_EOL . $saveValue; + } else { + $saveContents = preg_replace('/^' . $key . '=(.*)$/m', $saveValue, $saveContents); + } + }); + + file_put_contents($path, $saveContents); + } +} diff --git a/app/Traits/Controllers/JavascriptInjection.php b/app/Traits/Controllers/JavascriptInjection.php new file mode 100644 index 0000000..44fec98 --- /dev/null +++ b/app/Traits/Controllers/JavascriptInjection.php @@ -0,0 +1,28 @@ +request = $request; + + return $this; + } + + /** + * Injects the exact array passed in, nothing more. + */ + public function plainInject(array $args = []): string + { + return \JavaScript::put($args); + } +} diff --git a/app/Traits/Controllers/PlainJavascriptInjection.php b/app/Traits/Controllers/PlainJavascriptInjection.php new file mode 100644 index 0000000..2c0832c --- /dev/null +++ b/app/Traits/Controllers/PlainJavascriptInjection.php @@ -0,0 +1,16 @@ +getFilesystemInstance()->directories(resource_path('lang')))->mapWithKeys(function ($path) use ($localize) { + $code = basename($path); + $value = $localize ? $this->getIsoInstance()->nativeByCode1($code) : $this->getIsoInstance()->languageByCode1($code); + + return [$code => title_case($value)]; + })->toArray(); + } + + /** + * Return an instance of the filesystem for getting a folder listing. + */ + private function getFilesystemInstance(): Filesystem + { + return $this->filesystem = $this->filesystem ?: app()->make(Filesystem::class); + } + + /** + * Return an instance of the ISO639 class for generating names. + */ + private function getIsoInstance(): ISO639 + { + return $this->iso639 = $this->iso639 ?: app()->make(ISO639::class); + } +} diff --git a/app/Traits/Services/HasUserLevels.php b/app/Traits/Services/HasUserLevels.php new file mode 100644 index 0000000..33be0fb --- /dev/null +++ b/app/Traits/Services/HasUserLevels.php @@ -0,0 +1,36 @@ +userLevel = $level; + + return $this; + } + + /** + * Determine which level this function is running at. + */ + public function getUserLevel(): int + { + return $this->userLevel; + } + + /** + * Determine if the current user level is set to a specific level. + */ + public function isUserLevel(int $level): bool + { + return $this->getUserLevel() === $level; + } +} diff --git a/app/Traits/Services/ReturnsUpdatedModels.php b/app/Traits/Services/ReturnsUpdatedModels.php new file mode 100644 index 0000000..055e897 --- /dev/null +++ b/app/Traits/Services/ReturnsUpdatedModels.php @@ -0,0 +1,25 @@ +updatedModel; + } + + /** + * If called a fresh model will be returned from the database. This is used + * for API calls, but is unnecessary for UI based updates where the page is + * being reloaded and a fresh model will be pulled anyways. + */ + public function returnUpdatedModel(bool $toggle = true): self + { + $this->updatedModel = $toggle; + + return $this; + } +} diff --git a/app/Traits/Services/ValidatesValidationRules.php b/app/Traits/Services/ValidatesValidationRules.php new file mode 100644 index 0000000..5456cb0 --- /dev/null +++ b/app/Traits/Services/ValidatesValidationRules.php @@ -0,0 +1,32 @@ +getValidator()->make(['__TEST' => 'test'], ['__TEST' => $rules])->fails(); + } catch (\BadMethodCallException $exception) { + $matches = []; + if (preg_match('/Method \[(.+)\] does not exist\./', $exception->getMessage(), $matches)) { + throw new BadValidationRuleException(trans('exceptions.nest.variables.bad_validation_rule', ['rule' => Str::snake(str_replace('validate', '', array_get($matches, 1, 'unknownRule')))]), $exception); + } + + throw $exception; + } + } +} diff --git a/app/Transformers/Api/Application/AllocationTransformer.php b/app/Transformers/Api/Application/AllocationTransformer.php new file mode 100644 index 0000000..fcd65f9 --- /dev/null +++ b/app/Transformers/Api/Application/AllocationTransformer.php @@ -0,0 +1,77 @@ + $allocation->id, + 'ip' => $allocation->ip, + 'alias' => $allocation->ip_alias, + 'port' => $allocation->port, + 'notes' => $allocation->notes, + 'assigned' => !is_null($allocation->server_id), + ]; + } + + /** + * Load the node relationship onto a given transformation. + * + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + */ + public function includeNode(Allocation $allocation): Item|NullResource + { + if (!$this->authorize(AdminAcl::RESOURCE_NODES)) { + return $this->null(); + } + + return $this->item( + $allocation->node, + $this->makeTransformer(NodeTransformer::class), + Node::RESOURCE_NAME + ); + } + + /** + * Load the server relationship onto a given transformation. + * + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + */ + public function includeServer(Allocation $allocation): Item|NullResource + { + if (!$this->authorize(AdminAcl::RESOURCE_SERVERS) || !$allocation->server) { + return $this->null(); + } + + return $this->item( + $allocation->server, + $this->makeTransformer(ServerTransformer::class), + Server::RESOURCE_NAME + ); + } +} diff --git a/app/Transformers/Api/Application/BaseTransformer.php b/app/Transformers/Api/Application/BaseTransformer.php new file mode 100644 index 0000000..723caa3 --- /dev/null +++ b/app/Transformers/Api/Application/BaseTransformer.php @@ -0,0 +1,114 @@ +call([$this, 'handle']); + } + } + + /** + * Return the resource name for the JSONAPI output. + */ + abstract public function getResourceName(): string; + + /** + * Sets the request on the instance. + */ + public function setRequest(Request $request): self + { + $this->request = $request; + + return $this; + } + + /** + * Returns a new transformer instance with the request set on the instance. + */ + public static function fromRequest(Request $request): BaseTransformer + { + return app(static::class)->setRequest($request); + } + + /** + * Determine if the API key loaded onto the transformer has permission + * to access a different resource. This is used when including other + * models on a transformation request. + * + * @deprecated — prefer $user->can/cannot methods + */ + protected function authorize(string $resource): bool + { + $allowed = [ApiKey::TYPE_ACCOUNT, ApiKey::TYPE_APPLICATION]; + + $token = $this->request->user()->currentAccessToken(); + if (!$token instanceof ApiKey || !in_array($token->key_type, $allowed)) { + return false; + } + + // If this is not a deprecated application token type we can only check that + // the user is a root admin at the moment. In a future release we'll be rolling + // out more specific permissions for keys. + if ($token->key_type === ApiKey::TYPE_ACCOUNT) { + return $this->request->user()->root_admin; + } + + return AdminAcl::check($token, $resource); + } + + /** + * Create a new instance of the transformer and pass along the currently + * set API key. + * + * @template T of \Pterodactyl\Transformers\Api\Application\BaseTransformer + * + * @param class-string $abstract + * + * @return T + * + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + * + * @noinspection PhpDocSignatureInspection + */ + protected function makeTransformer(string $abstract) + { + Assert::subclassOf($abstract, self::class); + + return $abstract::fromRequest($this->request); + } + + /** + * Return an ISO-8601 formatted timestamp to use in the API response. + */ + protected function formatTimestamp(string $timestamp): string + { + return CarbonImmutable::createFromFormat(CarbonInterface::DEFAULT_TO_STRING_FORMAT, $timestamp) + ->setTimezone(self::RESPONSE_TIMEZONE) + ->toAtomString(); + } +} diff --git a/app/Transformers/Api/Application/DatabaseHostTransformer.php b/app/Transformers/Api/Application/DatabaseHostTransformer.php new file mode 100644 index 0000000..019fdf2 --- /dev/null +++ b/app/Transformers/Api/Application/DatabaseHostTransformer.php @@ -0,0 +1,57 @@ + $model->id, + 'name' => $model->name, + 'host' => $model->host, + 'port' => $model->port, + 'username' => $model->username, + 'node' => $model->node_id, + 'created_at' => $model->created_at->toAtomString(), + 'updated_at' => $model->updated_at->toAtomString(), + ]; + } + + /** + * Include the databases associated with this host. + * + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + */ + public function includeDatabases(DatabaseHost $model): Collection|NullResource + { + if (!$this->authorize(AdminAcl::RESOURCE_SERVER_DATABASES)) { + return $this->null(); + } + + $model->loadMissing('databases'); + + return $this->collection($model->getRelation('databases'), $this->makeTransformer(ServerDatabaseTransformer::class), Database::RESOURCE_NAME); + } +} diff --git a/app/Transformers/Api/Application/EggTransformer.php b/app/Transformers/Api/Application/EggTransformer.php new file mode 100644 index 0000000..9ed5736 --- /dev/null +++ b/app/Transformers/Api/Application/EggTransformer.php @@ -0,0 +1,177 @@ +config_files, true, 512, JSON_THROW_ON_ERROR); + if (empty($files)) { + $files = new \stdClass(); + } + + return [ + 'id' => $model->id, + 'uuid' => $model->uuid, + 'name' => $model->name, + 'nest' => $model->nest_id, + 'author' => $model->author, + 'description' => $model->description, + // "docker_image" is deprecated, but left here to avoid breaking too many things at once + // in external software. We'll remove it down the road once things have gotten the chance + // to upgrade to using "docker_images". + 'docker_image' => count($model->docker_images) > 0 ? Arr::first($model->docker_images) : '', + 'docker_images' => $model->docker_images, + 'config' => [ + 'files' => $files, + 'startup' => json_decode($model->config_startup, true), + 'stop' => $model->config_stop, + 'logs' => json_decode($model->config_logs, true), + 'file_denylist' => $model->file_denylist, + 'extends' => $model->config_from, + ], + 'startup' => $model->startup, + 'script' => [ + 'privileged' => $model->script_is_privileged, + 'install' => $model->script_install, + 'entry' => $model->script_entry, + 'container' => $model->script_container, + 'extends' => $model->copy_script_from, + ], + $model->getCreatedAtColumn() => $this->formatTimestamp($model->created_at), + $model->getUpdatedAtColumn() => $this->formatTimestamp($model->updated_at), + ]; + } + + /** + * Include the Nest relationship for the given Egg in the transformation. + * + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + */ + public function includeNest(Egg $model): Item|NullResource + { + if (!$this->authorize(AdminAcl::RESOURCE_NESTS)) { + return $this->null(); + } + + $model->loadMissing('nest'); + + return $this->item($model->getRelation('nest'), $this->makeTransformer(NestTransformer::class), Nest::RESOURCE_NAME); + } + + /** + * Include the Servers relationship for the given Egg in the transformation. + * + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + */ + public function includeServers(Egg $model): Collection|NullResource + { + if (!$this->authorize(AdminAcl::RESOURCE_SERVERS)) { + return $this->null(); + } + + $model->loadMissing('servers'); + + return $this->collection($model->getRelation('servers'), $this->makeTransformer(ServerTransformer::class), Server::RESOURCE_NAME); + } + + /** + * Include more detailed information about the configuration if this Egg is + * extending another. + */ + public function includeConfig(Egg $model): Item|NullResource + { + if (is_null($model->config_from)) { + return $this->null(); + } + + $model->loadMissing('configFrom'); + + return $this->item($model, function (Egg $model) { + return [ + 'files' => json_decode($model->inherit_config_files), + 'startup' => json_decode($model->inherit_config_startup), + 'stop' => $model->inherit_config_stop, + 'logs' => json_decode($model->inherit_config_logs), + ]; + }); + } + + /** + * Include more detailed information about the script configuration if the + * Egg is extending another. + */ + public function includeScript(Egg $model): Item|NullResource + { + if (is_null($model->copy_script_from)) { + return $this->null(); + } + + $model->loadMissing('scriptFrom'); + + return $this->item($model, function (Egg $model) { + return [ + 'privileged' => $model->script_is_privileged, + 'install' => $model->copy_script_install, + 'entry' => $model->copy_script_entry, + 'container' => $model->copy_script_container, + ]; + }); + } + + /** + * Include the variables that are defined for this Egg. + * + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + */ + public function includeVariables(Egg $model): Collection|NullResource + { + if (!$this->authorize(AdminAcl::RESOURCE_EGGS)) { + return $this->null(); + } + + $model->loadMissing('variables'); + + return $this->collection( + $model->getRelation('variables'), + $this->makeTransformer(EggVariableTransformer::class), + EggVariable::RESOURCE_NAME + ); + } +} diff --git a/app/Transformers/Api/Application/EggVariableTransformer.php b/app/Transformers/Api/Application/EggVariableTransformer.php new file mode 100644 index 0000000..2088806 --- /dev/null +++ b/app/Transformers/Api/Application/EggVariableTransformer.php @@ -0,0 +1,22 @@ +toArray(); + } +} diff --git a/app/Transformers/Api/Application/LocationTransformer.php b/app/Transformers/Api/Application/LocationTransformer.php new file mode 100644 index 0000000..8fea3fe --- /dev/null +++ b/app/Transformers/Api/Application/LocationTransformer.php @@ -0,0 +1,70 @@ + $location->id, + 'short' => $location->short, + 'long' => $location->long, + $location->getUpdatedAtColumn() => $this->formatTimestamp($location->updated_at), + $location->getCreatedAtColumn() => $this->formatTimestamp($location->created_at), + ]; + } + + /** + * Return the nodes associated with this location. + * + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + */ + public function includeServers(Location $location): Collection|NullResource + { + if (!$this->authorize(AdminAcl::RESOURCE_SERVERS)) { + return $this->null(); + } + + $location->loadMissing('servers'); + + return $this->collection($location->getRelation('servers'), $this->makeTransformer(ServerTransformer::class), 'server'); + } + + /** + * Return the nodes associated with this location. + * + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + */ + public function includeNodes(Location $location): Collection|NullResource + { + if (!$this->authorize(AdminAcl::RESOURCE_NODES)) { + return $this->null(); + } + + $location->loadMissing('nodes'); + + return $this->collection($location->getRelation('nodes'), $this->makeTransformer(NodeTransformer::class), 'node'); + } +} diff --git a/app/Transformers/Api/Application/NestTransformer.php b/app/Transformers/Api/Application/NestTransformer.php new file mode 100644 index 0000000..2f530d4 --- /dev/null +++ b/app/Transformers/Api/Application/NestTransformer.php @@ -0,0 +1,74 @@ +toArray(); + + $response[$model->getUpdatedAtColumn()] = $this->formatTimestamp($model->updated_at); + $response[$model->getCreatedAtColumn()] = $this->formatTimestamp($model->created_at); + + return $response; + } + + /** + * Include the Eggs relationship on the given Nest model transformation. + * + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + */ + public function includeEggs(Nest $model): Collection|NullResource + { + if (!$this->authorize(AdminAcl::RESOURCE_EGGS)) { + return $this->null(); + } + + $model->loadMissing('eggs'); + + return $this->collection($model->getRelation('eggs'), $this->makeTransformer(EggTransformer::class), Egg::RESOURCE_NAME); + } + + /** + * Include the servers relationship on the given Nest model. + * + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + */ + public function includeServers(Nest $model): Collection|NullResource + { + if (!$this->authorize(AdminAcl::RESOURCE_SERVERS)) { + return $this->null(); + } + + $model->loadMissing('servers'); + + return $this->collection($model->getRelation('servers'), $this->makeTransformer(ServerTransformer::class), Server::RESOURCE_NAME); + } +} diff --git a/app/Transformers/Api/Application/NodeTransformer.php b/app/Transformers/Api/Application/NodeTransformer.php new file mode 100644 index 0000000..6347dfe --- /dev/null +++ b/app/Transformers/Api/Application/NodeTransformer.php @@ -0,0 +1,112 @@ +toArray())->mapWithKeys(function ($value, $key) { + // I messed up early in 2016 when I named this column as poorly + // as I did. This is the tragic result of my mistakes. + $key = ($key === 'daemonSFTP') ? 'daemonSftp' : $key; + + return [snake_case($key) => $value]; + })->toArray(); + + $response[$node->getUpdatedAtColumn()] = $this->formatTimestamp($node->updated_at); + $response[$node->getCreatedAtColumn()] = $this->formatTimestamp($node->created_at); + + $resources = $node->servers()->select(['memory', 'disk'])->get(); + + $response['allocated_resources'] = [ + 'memory' => $resources->sum('memory'), + 'disk' => $resources->sum('disk'), + ]; + + return $response; + } + + /** + * Return the nodes associated with this location. + * + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + */ + public function includeAllocations(Node $node): Collection|NullResource + { + if (!$this->authorize(AdminAcl::RESOURCE_ALLOCATIONS)) { + return $this->null(); + } + + $node->loadMissing('allocations'); + + return $this->collection( + $node->getRelation('allocations'), + $this->makeTransformer(AllocationTransformer::class), + 'allocation' + ); + } + + /** + * Return the nodes associated with this location. + * + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + */ + public function includeLocation(Node $node): Item|NullResource + { + if (!$this->authorize(AdminAcl::RESOURCE_LOCATIONS)) { + return $this->null(); + } + + $node->loadMissing('location'); + + return $this->item( + $node->getRelation('location'), + $this->makeTransformer(LocationTransformer::class), + 'location' + ); + } + + /** + * Return the nodes associated with this location. + * + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + */ + public function includeServers(Node $node): Collection|NullResource + { + if (!$this->authorize(AdminAcl::RESOURCE_SERVERS)) { + return $this->null(); + } + + $node->loadMissing('servers'); + + return $this->collection( + $node->getRelation('servers'), + $this->makeTransformer(ServerTransformer::class), + 'server' + ); + } +} diff --git a/app/Transformers/Api/Application/ServerDatabaseTransformer.php b/app/Transformers/Api/Application/ServerDatabaseTransformer.php new file mode 100644 index 0000000..2590482 --- /dev/null +++ b/app/Transformers/Api/Application/ServerDatabaseTransformer.php @@ -0,0 +1,83 @@ +encrypter = $encrypter; + } + + /** + * Return the resource name for the JSONAPI output. + */ + public function getResourceName(): string + { + return Database::RESOURCE_NAME; + } + + /** + * Transform a database model in a representation for the application API. + */ + public function transform(Database $model): array + { + return [ + 'id' => $model->id, + 'server' => $model->server_id, + 'host' => $model->database_host_id, + 'database' => $model->database, + 'username' => $model->username, + 'remote' => $model->remote, + 'max_connections' => $model->max_connections, + 'created_at' => $model->created_at->toAtomString(), + 'updated_at' => $model->updated_at->toAtomString(), + ]; + } + + /** + * Include the database password in the request. + */ + public function includePassword(Database $model): Item + { + return $this->item($model, function (Database $model) { + return [ + 'password' => $this->encrypter->decrypt($model->password), + ]; + }, 'database_password'); + } + + /** + * Return the database host relationship for this server database. + * + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + */ + public function includeHost(Database $model): Item|NullResource + { + if (!$this->authorize(AdminAcl::RESOURCE_DATABASE_HOSTS)) { + return $this->null(); + } + + $model->loadMissing('host'); + + return $this->item( + $model->getRelation('host'), + $this->makeTransformer(DatabaseHostTransformer::class), + DatabaseHost::RESOURCE_NAME + ); + } +} diff --git a/app/Transformers/Api/Application/ServerTransformer.php b/app/Transformers/Api/Application/ServerTransformer.php new file mode 100644 index 0000000..e5db01f --- /dev/null +++ b/app/Transformers/Api/Application/ServerTransformer.php @@ -0,0 +1,237 @@ +environmentService = $environmentService; + } + + /** + * Return the resource name for the JSONAPI output. + */ + public function getResourceName(): string + { + return Server::RESOURCE_NAME; + } + + /** + * Return a generic transformed server array. + */ + public function transform(Server $server): array + { + return [ + 'id' => $server->getKey(), + 'external_id' => $server->external_id, + 'uuid' => $server->uuid, + 'identifier' => $server->uuidShort, + 'name' => $server->name, + 'description' => $server->description, + 'status' => $server->status, + // This field is deprecated, please use "status". + 'suspended' => $server->isSuspended(), + 'limits' => [ + 'memory' => $server->memory, + 'swap' => $server->swap, + 'disk' => $server->disk, + 'io' => $server->io, + 'cpu' => $server->cpu, + 'threads' => $server->threads, + 'oom_disabled' => $server->oom_disabled, + ], + 'feature_limits' => [ + 'databases' => $server->database_limit, + 'allocations' => $server->allocation_limit, + 'backups' => $server->backup_limit, + ], + 'user' => $server->owner_id, + 'node' => $server->node_id, + 'allocation' => $server->allocation_id, + 'nest' => $server->nest_id, + 'egg' => $server->egg_id, + 'container' => [ + 'startup_command' => $server->startup, + 'image' => $server->image, + // This field is deprecated, please use "status". + 'installed' => $server->isInstalled() ? 1 : 0, + 'environment' => $this->environmentService->handle($server), + ], + $server->getUpdatedAtColumn() => $this->formatTimestamp($server->updated_at), + $server->getCreatedAtColumn() => $this->formatTimestamp($server->created_at), + ]; + } + + /** + * Return a generic array of allocations for this server. + * + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + */ + public function includeAllocations(Server $server): Collection|NullResource + { + if (!$this->authorize(AdminAcl::RESOURCE_ALLOCATIONS)) { + return $this->null(); + } + + $server->loadMissing('allocations'); + + return $this->collection($server->getRelation('allocations'), $this->makeTransformer(AllocationTransformer::class), 'allocation'); + } + + /** + * Return a generic array of data about subusers for this server. + * + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + */ + public function includeSubusers(Server $server): Collection|NullResource + { + if (!$this->authorize(AdminAcl::RESOURCE_USERS)) { + return $this->null(); + } + + $server->loadMissing('subusers'); + + return $this->collection($server->getRelation('subusers'), $this->makeTransformer(SubuserTransformer::class), 'subuser'); + } + + /** + * Return a generic array of data about subusers for this server. + * + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + */ + public function includeUser(Server $server): Item|NullResource + { + if (!$this->authorize(AdminAcl::RESOURCE_USERS)) { + return $this->null(); + } + + $server->loadMissing('user'); + + return $this->item($server->getRelation('user'), $this->makeTransformer(UserTransformer::class), 'user'); + } + + /** + * Return a generic array with nest information for this server. + * + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + */ + public function includeNest(Server $server): Item|NullResource + { + if (!$this->authorize(AdminAcl::RESOURCE_NESTS)) { + return $this->null(); + } + + $server->loadMissing('nest'); + + return $this->item($server->getRelation('nest'), $this->makeTransformer(NestTransformer::class), 'nest'); + } + + /** + * Return a generic array with egg information for this server. + * + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + */ + public function includeEgg(Server $server): Item|NullResource + { + if (!$this->authorize(AdminAcl::RESOURCE_EGGS)) { + return $this->null(); + } + + $server->loadMissing('egg'); + + return $this->item($server->getRelation('egg'), $this->makeTransformer(EggTransformer::class), 'egg'); + } + + /** + * Return a generic array of data about subusers for this server. + * + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + */ + public function includeVariables(Server $server): Collection|NullResource + { + if (!$this->authorize(AdminAcl::RESOURCE_SERVERS)) { + return $this->null(); + } + + $server->loadMissing('variables'); + + return $this->collection($server->getRelation('variables'), $this->makeTransformer(ServerVariableTransformer::class), 'server_variable'); + } + + /** + * Return a generic array with location information for this server. + * + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + */ + public function includeLocation(Server $server): Item|NullResource + { + if (!$this->authorize(AdminAcl::RESOURCE_LOCATIONS)) { + return $this->null(); + } + + $server->loadMissing('location'); + + return $this->item($server->getRelation('location'), $this->makeTransformer(LocationTransformer::class), 'location'); + } + + /** + * Return a generic array with node information for this server. + * + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + */ + public function includeNode(Server $server): Item|NullResource + { + if (!$this->authorize(AdminAcl::RESOURCE_NODES)) { + return $this->null(); + } + + $server->loadMissing('node'); + + return $this->item($server->getRelation('node'), $this->makeTransformer(NodeTransformer::class), 'node'); + } + + /** + * Return a generic array with database information for this server. + * + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + */ + public function includeDatabases(Server $server): Collection|NullResource + { + if (!$this->authorize(AdminAcl::RESOURCE_SERVER_DATABASES)) { + return $this->null(); + } + + $server->loadMissing('databases'); + + return $this->collection($server->getRelation('databases'), $this->makeTransformer(ServerDatabaseTransformer::class), 'databases'); + } +} diff --git a/app/Transformers/Api/Application/ServerVariableTransformer.php b/app/Transformers/Api/Application/ServerVariableTransformer.php new file mode 100644 index 0000000..25e8f87 --- /dev/null +++ b/app/Transformers/Api/Application/ServerVariableTransformer.php @@ -0,0 +1,48 @@ +toArray(); + } + + /** + * Return the parent service variable data. + * + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + */ + public function includeParent(EggVariable $variable): Item|NullResource + { + if (!$this->authorize(AdminAcl::RESOURCE_EGGS)) { + return $this->null(); + } + + $variable->loadMissing('variable'); + + return $this->item($variable->getRelation('variable'), $this->makeTransformer(EggVariableTransformer::class), 'variable'); + } +} diff --git a/app/Transformers/Api/Application/SubuserTransformer.php b/app/Transformers/Api/Application/SubuserTransformer.php new file mode 100644 index 0000000..0a51d61 --- /dev/null +++ b/app/Transformers/Api/Application/SubuserTransformer.php @@ -0,0 +1,71 @@ + $subuser->id, + 'user_id' => $subuser->user_id, + 'server_id' => $subuser->server_id, + 'permissions' => $subuser->permissions, + 'created_at' => $this->formatTimestamp($subuser->created_at), + 'updated_at' => $this->formatTimestamp($subuser->updated_at), + ]; + } + + /** + * Return a generic item of user for this subuser. + * + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + */ + public function includeUser(Subuser $subuser): Item|NullResource + { + if (!$this->authorize(AdminAcl::RESOURCE_USERS)) { + return $this->null(); + } + + $subuser->loadMissing('user'); + + return $this->item($subuser->getRelation('user'), $this->makeTransformer(UserTransformer::class), 'user'); + } + + /** + * Return a generic item of server for this subuser. + * + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + */ + public function includeServer(Subuser $subuser): Item|NullResource + { + if (!$this->authorize(AdminAcl::RESOURCE_SERVERS)) { + return $this->null(); + } + + $subuser->loadMissing('server'); + + return $this->item($subuser->getRelation('server'), $this->makeTransformer(ServerTransformer::class), 'server'); + } +} diff --git a/app/Transformers/Api/Application/UserTransformer.php b/app/Transformers/Api/Application/UserTransformer.php new file mode 100644 index 0000000..14e354b --- /dev/null +++ b/app/Transformers/Api/Application/UserTransformer.php @@ -0,0 +1,61 @@ + $user->id, + 'external_id' => $user->external_id, + 'uuid' => $user->uuid, + 'username' => $user->username, + 'email' => $user->email, + 'first_name' => $user->name_first, + 'last_name' => $user->name_last, + 'language' => $user->language, + 'root_admin' => (bool) $user->root_admin, + '2fa' => (bool) $user->use_totp, + 'created_at' => $this->formatTimestamp($user->created_at), + 'updated_at' => $this->formatTimestamp($user->updated_at), + ]; + } + + /** + * Return the servers associated with this user. + * + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + */ + public function includeServers(User $user): Collection|NullResource + { + if (!$this->authorize(AdminAcl::RESOURCE_SERVERS)) { + return $this->null(); + } + + $user->loadMissing('servers'); + + return $this->collection($user->getRelation('servers'), $this->makeTransformer(ServerTransformer::class), 'server'); + } +} diff --git a/app/Transformers/Api/Client/AccountTransformer.php b/app/Transformers/Api/Client/AccountTransformer.php new file mode 100644 index 0000000..1a14555 --- /dev/null +++ b/app/Transformers/Api/Client/AccountTransformer.php @@ -0,0 +1,32 @@ + $model->id, + 'admin' => $model->root_admin, + 'username' => $model->username, + 'email' => $model->email, + 'first_name' => $model->name_first, + 'last_name' => $model->name_last, + 'language' => $model->language, + ]; + } +} diff --git a/app/Transformers/Api/Client/ActivityLogTransformer.php b/app/Transformers/Api/Client/ActivityLogTransformer.php new file mode 100644 index 0000000..57c8ac3 --- /dev/null +++ b/app/Transformers/Api/Client/ActivityLogTransformer.php @@ -0,0 +1,118 @@ + sha1($model->id), + 'batch' => $model->batch, + 'event' => $model->event, + 'is_api' => !is_null($model->api_key_id), + 'ip' => $this->canViewIP($model->actor) ? $model->ip : null, + 'description' => $model->description, + 'properties' => $this->properties($model), + 'has_additional_metadata' => $this->hasAdditionalMetadata($model), + 'timestamp' => $model->timestamp->toAtomString(), + ]; + } + + public function includeActor(ActivityLog $model) + { + if (!$model->actor instanceof User) { + return $this->null(); + } + + return $this->item($model->actor, $this->makeTransformer(UserTransformer::class), User::RESOURCE_NAME); + } + + /** + * Transforms any array values in the properties into a countable field for easier + * use within the translation outputs. + */ + protected function properties(ActivityLog $model): object + { + if (!$model->properties || $model->properties->isEmpty()) { + return (object) []; + } + + $properties = $model->properties + ->mapWithKeys(function ($value, $key) use ($model) { + if ($key === 'ip' && !optional($model->actor)->is($this->request->user())) { + return [$key => '[hidden]']; + } + + if (!is_array($value)) { + // Perform some directory normalization at this point. + if ($key === 'directory') { + $value = str_replace('//', '/', '/' . trim($value, '/') . '/'); + } + + return [$key => $value]; + } + + return [$key => $value, "{$key}_count" => count($value)]; + }); + + $keys = $properties->keys()->filter(fn ($key) => Str::endsWith($key, '_count'))->values(); + if ($keys->containsOneItem()) { + $properties = $properties->merge(['count' => $properties->get($keys[0])])->except($keys[0]); + } + + return (object) $properties->toArray(); + } + + /** + * Determines if there are any log properties that we've not already exposed + * in the response language string and that are not just the IP address or + * the browser useragent. + * + * This is used by the front-end to selectively display an "additional metadata" + * button that is pointless if there is nothing the user can't already see from + * the event description. + */ + protected function hasAdditionalMetadata(ActivityLog $model): bool + { + if (is_null($model->properties) || $model->properties->isEmpty()) { + return false; + } + + $str = trans('activity.' . str_replace(':', '.', $model->event)); + preg_match_all('/:(?[\w.-]+\w)(?:[^\w:]?|$)/', $str, $matches); + + $exclude = array_merge($matches['key'], ['ip', 'useragent', 'using_sftp']); + foreach ($model->properties->keys() as $key) { + if (!in_array($key, $exclude, true)) { + return true; + } + } + + return false; + } + + /** + * Determines if the user can view the IP address in the output either because they are the + * actor that performed the action, or because they are an administrator on the Panel. + */ + protected function canViewIP(Model $actor = null): bool + { + return optional($actor)->is($this->request->user()) || $this->request->user()->root_admin; + } +} diff --git a/app/Transformers/Api/Client/AllocationTransformer.php b/app/Transformers/Api/Client/AllocationTransformer.php new file mode 100644 index 0000000..2e63e2b --- /dev/null +++ b/app/Transformers/Api/Client/AllocationTransformer.php @@ -0,0 +1,28 @@ + $model->id, + 'ip' => $model->ip, + 'ip_alias' => $model->ip_alias, + 'port' => $model->port, + 'notes' => $model->notes, + 'is_default' => $model->server->allocation_id === $model->id, + ]; + } +} diff --git a/app/Transformers/Api/Client/ApiKeyTransformer.php b/app/Transformers/Api/Client/ApiKeyTransformer.php new file mode 100644 index 0000000..92ee1a5 --- /dev/null +++ b/app/Transformers/Api/Client/ApiKeyTransformer.php @@ -0,0 +1,30 @@ + $model->identifier, + 'description' => $model->memo, + 'allowed_ips' => $model->allowed_ips, + 'last_used_at' => $model->last_used_at ? $model->last_used_at->toAtomString() : null, + 'created_at' => $model->created_at->toAtomString(), + ]; + } +} diff --git a/app/Transformers/Api/Client/BackupTransformer.php b/app/Transformers/Api/Client/BackupTransformer.php new file mode 100644 index 0000000..298c996 --- /dev/null +++ b/app/Transformers/Api/Client/BackupTransformer.php @@ -0,0 +1,28 @@ + $backup->uuid, + 'is_successful' => $backup->is_successful, + 'is_locked' => $backup->is_locked, + 'name' => $backup->name, + 'ignored_files' => $backup->ignored_files, + 'checksum' => $backup->checksum, + 'bytes' => $backup->bytes, + 'created_at' => $backup->created_at->toAtomString(), + 'completed_at' => $backup->completed_at ? $backup->completed_at->toAtomString() : null, + ]; + } +} diff --git a/app/Transformers/Api/Client/BaseClientTransformer.php b/app/Transformers/Api/Client/BaseClientTransformer.php new file mode 100644 index 0000000..0388eff --- /dev/null +++ b/app/Transformers/Api/Client/BaseClientTransformer.php @@ -0,0 +1,43 @@ +request->user(); + } + + /** + * Determine if the API key loaded onto the transformer has permission + * to access a different resource. This is used when including other + * models on a transformation request. + * + * @noinspection PhpParameterNameChangedDuringInheritanceInspection + */ + protected function authorize(string $ability, Server $server = null): bool + { + Assert::isInstanceOf($server, Server::class); + + return $this->request->user()->can($ability, [$server]); + } + + /** + * {@inheritDoc} + */ + protected function makeTransformer(string $abstract) + { + Assert::subclassOf($abstract, self::class); + + return parent::makeTransformer($abstract); + } +} diff --git a/app/Transformers/Api/Client/DatabaseTransformer.php b/app/Transformers/Api/Client/DatabaseTransformer.php new file mode 100644 index 0000000..23e9666 --- /dev/null +++ b/app/Transformers/Api/Client/DatabaseTransformer.php @@ -0,0 +1,66 @@ +encrypter = $encrypter; + $this->hashids = $hashids; + } + + public function getResourceName(): string + { + return Database::RESOURCE_NAME; + } + + public function transform(Database $model): array + { + $model->loadMissing('host'); + + return [ + 'id' => $this->hashids->encode($model->id), + 'host' => [ + 'address' => $model->getRelation('host')->host, + 'port' => $model->getRelation('host')->port, + ], + 'name' => $model->database, + 'username' => $model->username, + 'connections_from' => $model->remote, + 'max_connections' => $model->max_connections, + ]; + } + + /** + * Include the database password in the request. + */ + public function includePassword(Database $database): Item|NullResource + { + if (!$this->request->user()->can(Permission::ACTION_DATABASE_VIEW_PASSWORD, $database->server)) { + return $this->null(); + } + + return $this->item($database, function (Database $model) { + return [ + 'password' => $this->encrypter->decrypt($model->password), + ]; + }, 'database_password'); + } +} diff --git a/app/Transformers/Api/Client/EggTransformer.php b/app/Transformers/Api/Client/EggTransformer.php new file mode 100644 index 0000000..8e2e347 --- /dev/null +++ b/app/Transformers/Api/Client/EggTransformer.php @@ -0,0 +1,24 @@ + $egg->uuid, + 'name' => $egg->name, + ]; + } +} diff --git a/app/Transformers/Api/Client/EggVariableTransformer.php b/app/Transformers/Api/Client/EggVariableTransformer.php new file mode 100644 index 0000000..09c3442 --- /dev/null +++ b/app/Transformers/Api/Client/EggVariableTransformer.php @@ -0,0 +1,33 @@ +user_viewable) { + throw new \BadMethodCallException('Cannot transform a hidden egg variable in a client transformer.'); + } + + return [ + 'name' => $variable->name, + 'description' => $variable->description, + 'env_variable' => $variable->env_variable, + 'default_value' => $variable->default_value, + 'server_value' => $variable->server_value, + 'is_editable' => $variable->user_editable, + 'rules' => $variable->rules, + ]; + } +} diff --git a/app/Transformers/Api/Client/FileObjectTransformer.php b/app/Transformers/Api/Client/FileObjectTransformer.php new file mode 100644 index 0000000..6278dad --- /dev/null +++ b/app/Transformers/Api/Client/FileObjectTransformer.php @@ -0,0 +1,32 @@ + Arr::get($item, 'name'), + 'mode' => Arr::get($item, 'mode'), + 'mode_bits' => Arr::get($item, 'mode_bits'), + 'size' => Arr::get($item, 'size'), + 'is_file' => Arr::get($item, 'file', true), + 'is_symlink' => Arr::get($item, 'symlink', false), + 'mimetype' => Arr::get($item, 'mime', 'application/octet-stream'), + 'created_at' => Carbon::parse(Arr::get($item, 'created', ''))->toAtomString(), + 'modified_at' => Carbon::parse(Arr::get($item, 'modified', ''))->toAtomString(), + ]; + } + + public function getResourceName(): string + { + return 'file_object'; + } +} diff --git a/app/Transformers/Api/Client/ScheduleTransformer.php b/app/Transformers/Api/Client/ScheduleTransformer.php new file mode 100644 index 0000000..98c783f --- /dev/null +++ b/app/Transformers/Api/Client/ScheduleTransformer.php @@ -0,0 +1,61 @@ + $model->id, + 'name' => $model->name, + 'cron' => [ + 'day_of_week' => $model->cron_day_of_week, + 'day_of_month' => $model->cron_day_of_month, + 'month' => $model->cron_month, + 'hour' => $model->cron_hour, + 'minute' => $model->cron_minute, + ], + 'is_active' => $model->is_active, + 'is_processing' => $model->is_processing, + 'only_when_online' => $model->only_when_online, + 'last_run_at' => $model->last_run_at?->toAtomString(), + 'next_run_at' => $model->next_run_at?->toAtomString(), + 'created_at' => $model->created_at->toAtomString(), + 'updated_at' => $model->updated_at->toAtomString(), + ]; + } + + /** + * Allows attaching the tasks specific to the schedule in the response. + * + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + */ + public function includeTasks(Schedule $model): Collection + { + return $this->collection( + $model->tasks, + $this->makeTransformer(TaskTransformer::class), + Task::RESOURCE_NAME + ); + } +} diff --git a/app/Transformers/Api/Client/ServerTransformer.php b/app/Transformers/Api/Client/ServerTransformer.php new file mode 100644 index 0000000..9f7bce9 --- /dev/null +++ b/app/Transformers/Api/Client/ServerTransformer.php @@ -0,0 +1,144 @@ +make(StartupCommandService::class); + + $user = $this->request->user(); + + return [ + 'server_owner' => $user->id === $server->owner_id, + 'identifier' => $server->uuidShort, + 'internal_id' => $server->id, + 'uuid' => $server->uuid, + 'name' => $server->name, + 'node' => $server->node->name, + 'is_node_under_maintenance' => $server->node->isUnderMaintenance(), + 'sftp_details' => [ + 'ip' => $server->node->fqdn, + 'port' => $server->node->daemonSFTP, + ], + 'description' => $server->description, + 'limits' => [ + 'memory' => $server->memory, + 'swap' => $server->swap, + 'disk' => $server->disk, + 'io' => $server->io, + 'cpu' => $server->cpu, + 'threads' => $server->threads, + 'oom_disabled' => $server->oom_disabled, + ], + 'invocation' => $service->handle($server, !$user->can(Permission::ACTION_STARTUP_READ, $server)), + 'docker_image' => $server->image, + 'egg_features' => $server->egg->inherit_features, + 'feature_limits' => [ + 'databases' => $server->database_limit, + 'allocations' => $server->allocation_limit, + 'backups' => $server->backup_limit, + ], + 'status' => $server->status, + // This field is deprecated, please use "status". + 'is_suspended' => $server->isSuspended(), + // This field is deprecated, please use "status". + 'is_installing' => !$server->isInstalled(), + 'is_transferring' => !is_null($server->transfer), + ]; + } + + /** + * Returns the allocations associated with this server. + * + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + */ + public function includeAllocations(Server $server): Collection + { + $transformer = $this->makeTransformer(AllocationTransformer::class); + + $user = $this->request->user(); + // While we include this permission, we do need to actually handle it slightly different here + // for the purpose of keeping things functionally working. If the user doesn't have read permissions + // for the allocations we'll only return the primary server allocation, and any notes associated + // with it will be hidden. + // + // This allows us to avoid too much permission regression, without also hiding information that + // is generally needed for the frontend to make sense when browsing or searching results. + if (!$user->can(Permission::ACTION_ALLOCATION_READ, $server)) { + $primary = clone $server->allocation; + $primary->notes = null; + + return $this->collection([$primary], $transformer, Allocation::RESOURCE_NAME); + } + + return $this->collection($server->allocations, $transformer, Allocation::RESOURCE_NAME); + } + + /** + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + */ + public function includeVariables(Server $server): Collection|NullResource + { + if (!$this->request->user()->can(Permission::ACTION_STARTUP_READ, $server)) { + return $this->null(); + } + + return $this->collection( + $server->variables->where('user_viewable', true), + $this->makeTransformer(EggVariableTransformer::class), + EggVariable::RESOURCE_NAME + ); + } + + /** + * Returns the egg associated with this server. + * + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + */ + public function includeEgg(Server $server): Item + { + return $this->item($server->egg, $this->makeTransformer(EggTransformer::class), Egg::RESOURCE_NAME); + } + + /** + * Returns the subusers associated with this server. + * + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + */ + public function includeSubusers(Server $server): Collection|NullResource + { + if (!$this->request->user()->can(Permission::ACTION_USER_READ, $server)) { + return $this->null(); + } + + return $this->collection($server->subusers, $this->makeTransformer(SubuserTransformer::class), Subuser::RESOURCE_NAME); + } +} diff --git a/app/Transformers/Api/Client/StatsTransformer.php b/app/Transformers/Api/Client/StatsTransformer.php new file mode 100644 index 0000000..6b323b3 --- /dev/null +++ b/app/Transformers/Api/Client/StatsTransformer.php @@ -0,0 +1,33 @@ + Arr::get($data, 'state', 'stopped'), + 'is_suspended' => Arr::get($data, 'is_suspended', false), + 'resources' => [ + 'memory_bytes' => Arr::get($data, 'utilization.memory_bytes', 0), + 'cpu_absolute' => Arr::get($data, 'utilization.cpu_absolute', 0), + 'disk_bytes' => Arr::get($data, 'utilization.disk_bytes', 0), + 'network_rx_bytes' => Arr::get($data, 'utilization.network.rx_bytes', 0), + 'network_tx_bytes' => Arr::get($data, 'utilization.network.tx_bytes', 0), + 'uptime' => Arr::get($data, 'utilization.uptime', 0), + ], + ]; + } +} diff --git a/app/Transformers/Api/Client/SubuserTransformer.php b/app/Transformers/Api/Client/SubuserTransformer.php new file mode 100644 index 0000000..2902d1f --- /dev/null +++ b/app/Transformers/Api/Client/SubuserTransformer.php @@ -0,0 +1,29 @@ +makeTransformer(UserTransformer::class)->transform($model->user), + ['permissions' => $model->permissions] + ); + } +} diff --git a/app/Transformers/Api/Client/TaskTransformer.php b/app/Transformers/Api/Client/TaskTransformer.php new file mode 100644 index 0000000..52f0e2b --- /dev/null +++ b/app/Transformers/Api/Client/TaskTransformer.php @@ -0,0 +1,34 @@ + $model->id, + 'sequence_id' => $model->sequence_id, + 'action' => $model->action, + 'payload' => $model->payload, + 'time_offset' => $model->time_offset, + 'is_queued' => $model->is_queued, + 'continue_on_failure' => $model->continue_on_failure, + 'created_at' => $model->created_at->toAtomString(), + 'updated_at' => $model->updated_at->toAtomString(), + ]; + } +} diff --git a/app/Transformers/Api/Client/UserSSHKeyTransformer.php b/app/Transformers/Api/Client/UserSSHKeyTransformer.php new file mode 100644 index 0000000..015a017 --- /dev/null +++ b/app/Transformers/Api/Client/UserSSHKeyTransformer.php @@ -0,0 +1,26 @@ + $model->name, + 'fingerprint' => $model->fingerprint, + 'public_key' => $model->public_key, + 'created_at' => $model->created_at->toAtomString(), + ]; + } +} diff --git a/app/Transformers/Api/Client/UserTransformer.php b/app/Transformers/Api/Client/UserTransformer.php new file mode 100644 index 0000000..04bc70e --- /dev/null +++ b/app/Transformers/Api/Client/UserTransformer.php @@ -0,0 +1,33 @@ + $model->uuid, + 'username' => $model->username, + 'email' => $model->email, + 'image' => 'https://gravatar.com/avatar/' . md5(Str::lower($model->email)), + '2fa_enabled' => $model->use_totp, + 'created_at' => $model->created_at->toAtomString(), + ]; + } +} diff --git a/app/helpers.php b/app/helpers.php new file mode 100644 index 0000000..59aa716 --- /dev/null +++ b/app/helpers.php @@ -0,0 +1,35 @@ +{$segment}; + } + + return $object; + } +} diff --git a/artisan b/artisan new file mode 100644 index 0000000..240ab7a --- /dev/null +++ b/artisan @@ -0,0 +1,53 @@ +#!/usr/bin/env php +make(Illuminate\Contracts\Console\Kernel::class); + +$status = $kernel->handle( + $input = new Symfony\Component\Console\Input\ArgvInput(), + new Symfony\Component\Console\Output\ConsoleOutput() +); + +/* +|-------------------------------------------------------------------------- +| Shutdown The Application +|-------------------------------------------------------------------------- +| +| Once Artisan has finished running, we will fire off the shutdown events +| so that any final work may be done by the application before we shut +| down the process. This is the last thing to happen to the request. +| +*/ + +$kernel->terminate($input, $status); + +exit($status); diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 0000000..808bb18 --- /dev/null +++ b/babel.config.js @@ -0,0 +1,34 @@ +module.exports = function (api) { + let targets = {}; + const plugins = [ + 'babel-plugin-macros', + 'styled-components', + 'react-hot-loader/babel', + '@babel/transform-runtime', + '@babel/transform-react-jsx', + '@babel/proposal-class-properties', + '@babel/proposal-object-rest-spread', + '@babel/proposal-optional-chaining', + '@babel/proposal-nullish-coalescing-operator', + '@babel/syntax-dynamic-import', + ]; + + if (api.env('test')) { + targets = { node: 'current' }; + plugins.push('@babel/transform-modules-commonjs'); + } + + return { + plugins, + presets: [ + '@babel/typescript', + ['@babel/env', { + modules: false, + useBuiltIns: 'entry', + corejs: 3, + targets, + }], + '@babel/react', + ] + }; +}; diff --git a/bootstrap/app.php b/bootstrap/app.php new file mode 100644 index 0000000..ee5f1ba --- /dev/null +++ b/bootstrap/app.php @@ -0,0 +1,55 @@ +singleton( + Illuminate\Contracts\Http\Kernel::class, + Pterodactyl\Http\Kernel::class +); + +$app->singleton( + Illuminate\Contracts\Console\Kernel::class, + Pterodactyl\Console\Kernel::class +); + +$app->singleton( + Illuminate\Contracts\Debug\ExceptionHandler::class, + Pterodactyl\Exceptions\Handler::class +); + +/* +|-------------------------------------------------------------------------- +| Return The Application +|-------------------------------------------------------------------------- +| +| This script returns the application instance. The instance is given to +| the calling script so we can separate the building of the instances +| from the actual running of the application and sending responses. +| +*/ + +return $app; diff --git a/bootstrap/cache/.gitignore b/bootstrap/cache/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/bootstrap/cache/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/bootstrap/tests.php b/bootstrap/tests.php new file mode 100644 index 0000000..5b54493 --- /dev/null +++ b/bootstrap/tests.php @@ -0,0 +1,45 @@ +make(Kernel::class); + +/* + * Bootstrap the kernel and prepare application for testing. + */ +$kernel->bootstrap(); + +// Register the collision service provider so that errors during the test +// setup process are output nicely. +(new Provider())->register(); + +$output = new ConsoleOutput(); + +$prefix = 'database.connections.' . config('database.default'); +if (!Str::contains(config("$prefix.database"), 'test')) { + $output->writeln(PHP_EOL . 'Cannot run test process against non-testing database.'); + $output->writeln(PHP_EOL . 'Environment is currently pointed at: "' . config("$prefix.database") . '".'); + exit(1); +} + +/* + * Perform database migrations and reseeding before continuing with + * running the tests. + */ +if (!env('SKIP_MIGRATIONS')) { + $output->writeln(PHP_EOL . 'Refreshing database for Integration tests...'); + $kernel->call('migrate:fresh'); + + $output->writeln('Seeding database for Integration tests...' . PHP_EOL); + $kernel->call('db:seed'); +} else { + $output->writeln(PHP_EOL . 'Skipping database migrations...' . PHP_EOL); +} diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..74f7beb --- /dev/null +++ b/composer.json @@ -0,0 +1,106 @@ +{ + "name": "pterodactyl/panel", + "description": "The free, open-source game management panel. Supporting Minecraft, Spigot, BungeeCord, and SRCDS servers.", + "license": "MIT", + "authors": [ + { + "name": "Matthew Penner", + "email": "matthew@pterodactyl.io", + "homepage": "https://github.com/matthewpi", + "role": "Lead Developer" + }, + { + "name": "Dane Everitt", + "email": "dane@daneeveritt.com", + "homepage": "https://github.com/DaneEveritt", + "role": "Developer" + } + ], + "require": { + "php": "^8.2 || ^8.3", + "ext-json": "*", + "ext-mbstring": "*", + "ext-pdo": "*", + "ext-pdo_mysql": "*", + "ext-posix": "*", + "ext-zip": "*", + "aws/aws-sdk-php": "~3.316.10", + "doctrine/dbal": "~3.8.6", + "guzzlehttp/guzzle": "~7.9.2", + "hashids/hashids": "~5.0.2", + "laracasts/utilities": "~3.2.3", + "laravel/framework": "~10.48.23", + "laravel/helpers": "~1.7.0", + "laravel/sanctum": "~3.3.3", + "laravel/tinker": "~2.9.0", + "laravel/ui": "~4.5.2", + "lcobucci/jwt": "~4.3.0", + "league/flysystem-aws-s3-v3": "~3.28.0", + "league/flysystem-memory": "~3.28.0", + "matriphe/iso-639": "~1.3", + "phpseclib/phpseclib": "~3.0.39", + "pragmarx/google2fa": "~8.0.1", + "predis/predis": "~2.2.2", + "prologue/alerts": "~1.2.0", + "psr/cache": "~3.0.0", + "s1lentium/iptools": "~1.2.0", + "spatie/laravel-fractal": "~6.2.1", + "spatie/laravel-query-builder": "~5.8.1", + "staudenmeir/belongs-to-through": "~2.13", + "symfony/http-client": "~6.4.10", + "symfony/mailgun-mailer": "~6.4.10", + "symfony/postmark-mailer": "~6.4.8", + "symfony/yaml": "~6.4.8", + "webmozart/assert": "~1.11.0" + }, + "require-dev": { + "barryvdh/laravel-ide-helper": "~2.15.1", + "fakerphp/faker": "~1.23.1", + "friendsofphp/php-cs-fixer": "~3.61.1", + "itsgoingd/clockwork": "~5.2.2", + "laravel/sail": "~1.31.0", + "mockery/mockery": "~1.6.12", + "nunomaduro/collision": "~7.10.0", + "phpunit/phpunit": "~10.5.29", + "spatie/laravel-ignition": "~2.8.0" + }, + "autoload": { + "files": [ + "app/helpers.php" + ], + "psr-4": { + "Pterodactyl\\": "app/", + "Database\\Factories\\": "database/Factories/", + "Database\\Seeders\\": "database/Seeders/" + } + }, + "autoload-dev": { + "psr-4": { + "Pterodactyl\\Tests\\": "tests/" + } + }, + "scripts": { + "cs:fix": "php-cs-fixer fix", + "cs:check": "php-cs-fixer fix --dry-run --diff --verbose", + "post-autoload-dump": [ + "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", + "@php artisan package:discover --ansi || true" + ], + "post-root-package-install": [ + "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" + ], + "post-create-project-cmd": [ + "@php artisan key:generate --ansi" + ] + }, + "config": { + "optimize-autoloader": true, + "preferred-install": "dist", + "sort-packages": true, + "platform": { + "php": "8.2.23" + } + }, + "minimum-stability": "stable", + "prefer-stable": true +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..2dfa0af --- /dev/null +++ b/composer.lock @@ -0,0 +1,12163 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "5cefbd287c43ced95fe3d3635ea730eb", + "packages": [ + { + "name": "aws/aws-crt-php", + "version": "v1.2.7", + "source": { + "type": "git", + "url": "https://github.com/awslabs/aws-crt-php.git", + "reference": "d71d9906c7bb63a28295447ba12e74723bd3730e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/awslabs/aws-crt-php/zipball/d71d9906c7bb63a28295447ba12e74723bd3730e", + "reference": "d71d9906c7bb63a28295447ba12e74723bd3730e", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35||^5.6.3||^9.5", + "yoast/phpunit-polyfills": "^1.0" + }, + "suggest": { + "ext-awscrt": "Make sure you install awscrt native extension to use any of the functionality." + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "AWS SDK Common Runtime Team", + "email": "aws-sdk-common-runtime@amazon.com" + } + ], + "description": "AWS Common Runtime for PHP", + "homepage": "https://github.com/awslabs/aws-crt-php", + "keywords": [ + "amazon", + "aws", + "crt", + "sdk" + ], + "support": { + "issues": "https://github.com/awslabs/aws-crt-php/issues", + "source": "https://github.com/awslabs/aws-crt-php/tree/v1.2.7" + }, + "time": "2024-10-18T22:15:13+00:00" + }, + { + "name": "aws/aws-sdk-php", + "version": "3.316.10", + "source": { + "type": "git", + "url": "https://github.com/aws/aws-sdk-php.git", + "reference": "eeb8df6ff6caa428e8bcd631ad2a96430900a249" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/eeb8df6ff6caa428e8bcd631ad2a96430900a249", + "reference": "eeb8df6ff6caa428e8bcd631ad2a96430900a249", + "shasum": "" + }, + "require": { + "aws/aws-crt-php": "^1.2.3", + "ext-json": "*", + "ext-pcre": "*", + "ext-simplexml": "*", + "guzzlehttp/guzzle": "^6.5.8 || ^7.4.5", + "guzzlehttp/promises": "^1.4.0 || ^2.0", + "guzzlehttp/psr7": "^1.9.1 || ^2.4.5", + "mtdowling/jmespath.php": "^2.6", + "php": ">=7.2.5", + "psr/http-message": "^1.0 || ^2.0" + }, + "require-dev": { + "andrewsville/php-token-reflection": "^1.4", + "aws/aws-php-sns-message-validator": "~1.0", + "behat/behat": "~3.0", + "composer/composer": "^1.10.22", + "dms/phpunit-arraysubset-asserts": "^0.4.0", + "doctrine/cache": "~1.4", + "ext-dom": "*", + "ext-openssl": "*", + "ext-pcntl": "*", + "ext-sockets": "*", + "nette/neon": "^2.3", + "paragonie/random_compat": ">= 2", + "phpunit/phpunit": "^5.6.3 || ^8.5 || ^9.5", + "psr/cache": "^1.0", + "psr/simple-cache": "^1.0", + "sebastian/comparator": "^1.2.3 || ^4.0", + "yoast/phpunit-polyfills": "^1.0" + }, + "suggest": { + "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications", + "doctrine/cache": "To use the DoctrineCacheAdapter", + "ext-curl": "To send requests using cURL", + "ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages", + "ext-sockets": "To use client-side monitoring" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Aws\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Amazon Web Services", + "homepage": "http://aws.amazon.com" + } + ], + "description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project", + "homepage": "http://aws.amazon.com/sdkforphp", + "keywords": [ + "amazon", + "aws", + "cloud", + "dynamodb", + "ec2", + "glacier", + "s3", + "sdk" + ], + "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.316.10" + }, + "time": "2024-07-30T18:10:20+00:00" + }, + { + "name": "brick/math", + "version": "0.12.1", + "source": { + "type": "git", + "url": "https://github.com/brick/math.git", + "reference": "f510c0a40911935b77b86859eb5223d58d660df1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/brick/math/zipball/f510c0a40911935b77b86859eb5223d58d660df1", + "reference": "f510c0a40911935b77b86859eb5223d58d660df1", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.2", + "phpunit/phpunit": "^10.1", + "vimeo/psalm": "5.16.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Brick\\Math\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Arbitrary-precision arithmetic library", + "keywords": [ + "Arbitrary-precision", + "BigInteger", + "BigRational", + "arithmetic", + "bigdecimal", + "bignum", + "bignumber", + "brick", + "decimal", + "integer", + "math", + "mathematics", + "rational" + ], + "support": { + "issues": "https://github.com/brick/math/issues", + "source": "https://github.com/brick/math/tree/0.12.1" + }, + "funding": [ + { + "url": "https://github.com/BenMorel", + "type": "github" + } + ], + "time": "2023-11-29T23:19:16+00:00" + }, + { + "name": "carbonphp/carbon-doctrine-types", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/CarbonPHP/carbon-doctrine-types.git", + "reference": "99f76ffa36cce3b70a4a6abce41dba15ca2e84cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/99f76ffa36cce3b70a4a6abce41dba15ca2e84cb", + "reference": "99f76ffa36cce3b70a4a6abce41dba15ca2e84cb", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "doctrine/dbal": "<3.7.0 || >=4.0.0" + }, + "require-dev": { + "doctrine/dbal": "^3.7.0", + "nesbot/carbon": "^2.71.0 || ^3.0.0", + "phpunit/phpunit": "^10.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Carbon\\Doctrine\\": "src/Carbon/Doctrine/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "KyleKatarn", + "email": "kylekatarnls@gmail.com" + } + ], + "description": "Types to use Carbon in Doctrine", + "keywords": [ + "carbon", + "date", + "datetime", + "doctrine", + "time" + ], + "support": { + "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues", + "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/2.1.0" + }, + "funding": [ + { + "url": "https://github.com/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon", + "type": "tidelift" + } + ], + "time": "2023-12-11T17:09:12+00:00" + }, + { + "name": "dflydev/dot-access-data", + "version": "v3.0.3", + "source": { + "type": "git", + "url": "https://github.com/dflydev/dflydev-dot-access-data.git", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.42", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.3", + "scrutinizer/ocular": "1.6.0", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Dflydev\\DotAccessData\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dragonfly Development Inc.", + "email": "info@dflydev.com", + "homepage": "http://dflydev.com" + }, + { + "name": "Beau Simensen", + "email": "beau@dflydev.com", + "homepage": "http://beausimensen.com" + }, + { + "name": "Carlos Frutos", + "email": "carlos@kiwing.it", + "homepage": "https://github.com/cfrutos" + }, + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com" + } + ], + "description": "Given a deep data structure, access data by dot notation.", + "homepage": "https://github.com/dflydev/dflydev-dot-access-data", + "keywords": [ + "access", + "data", + "dot", + "notation" + ], + "support": { + "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues", + "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.3" + }, + "time": "2024-07-08T12:26:09+00:00" + }, + { + "name": "doctrine/cache", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/cache.git", + "reference": "1ca8f21980e770095a31456042471a57bc4c68fb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/cache/zipball/1ca8f21980e770095a31456042471a57bc4c68fb", + "reference": "1ca8f21980e770095a31456042471a57bc4c68fb", + "shasum": "" + }, + "require": { + "php": "~7.1 || ^8.0" + }, + "conflict": { + "doctrine/common": ">2.2,<2.4" + }, + "require-dev": { + "cache/integration-tests": "dev-master", + "doctrine/coding-standard": "^9", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psr/cache": "^1.0 || ^2.0 || ^3.0", + "symfony/cache": "^4.4 || ^5.4 || ^6", + "symfony/var-exporter": "^4.4 || ^5.4 || ^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, apc, mongodb and others.", + "homepage": "https://www.doctrine-project.org/projects/cache.html", + "keywords": [ + "abstraction", + "apcu", + "cache", + "caching", + "couchdb", + "memcached", + "php", + "redis", + "xcache" + ], + "support": { + "issues": "https://github.com/doctrine/cache/issues", + "source": "https://github.com/doctrine/cache/tree/2.2.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcache", + "type": "tidelift" + } + ], + "time": "2022-05-20T20:07:39+00:00" + }, + { + "name": "doctrine/dbal", + "version": "3.8.7", + "source": { + "type": "git", + "url": "https://github.com/doctrine/dbal.git", + "reference": "2093d670ca17f634f3c095ec10a20687eccebd99" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/2093d670ca17f634f3c095ec10a20687eccebd99", + "reference": "2093d670ca17f634f3c095ec10a20687eccebd99", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2", + "doctrine/cache": "^1.11|^2.0", + "doctrine/deprecations": "^0.5.3|^1", + "doctrine/event-manager": "^1|^2", + "php": "^7.4 || ^8.0", + "psr/cache": "^1|^2|^3", + "psr/log": "^1|^2|^3" + }, + "require-dev": { + "doctrine/coding-standard": "12.0.0", + "fig/log-test": "^1", + "jetbrains/phpstorm-stubs": "2023.1", + "phpstan/phpstan": "1.11.7", + "phpstan/phpstan-strict-rules": "^1.6", + "phpunit/phpunit": "9.6.20", + "psalm/plugin-phpunit": "0.18.4", + "slevomat/coding-standard": "8.13.1", + "squizlabs/php_codesniffer": "3.10.2", + "symfony/cache": "^5.4|^6.0|^7.0", + "symfony/console": "^4.4|^5.4|^6.0|^7.0", + "vimeo/psalm": "4.30.0" + }, + "suggest": { + "symfony/console": "For helpful console commands such as SQL execution and import of files." + }, + "bin": [ + "bin/doctrine-dbal" + ], + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\DBAL\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + } + ], + "description": "Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management.", + "homepage": "https://www.doctrine-project.org/projects/dbal.html", + "keywords": [ + "abstraction", + "database", + "db2", + "dbal", + "mariadb", + "mssql", + "mysql", + "oci8", + "oracle", + "pdo", + "pgsql", + "postgresql", + "queryobject", + "sasql", + "sql", + "sqlite", + "sqlserver", + "sqlsrv" + ], + "support": { + "issues": "https://github.com/doctrine/dbal/issues", + "source": "https://github.com/doctrine/dbal/tree/3.8.7" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdbal", + "type": "tidelift" + } + ], + "time": "2024-08-07T11:57:25+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "phpstan/phpstan": "1.4.10 || 1.10.15", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "0.18.4", + "psr/log": "^1 || ^2 || ^3", + "vimeo/psalm": "4.30.0 || 5.12.0" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.3" + }, + "time": "2024-01-30T19:34:25+00:00" + }, + { + "name": "doctrine/event-manager", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/event-manager.git", + "reference": "b680156fa328f1dfd874fd48c7026c41570b9c6e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/event-manager/zipball/b680156fa328f1dfd874fd48c7026c41570b9c6e", + "reference": "b680156fa328f1dfd874fd48c7026c41570b9c6e", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "conflict": { + "doctrine/common": "<2.9" + }, + "require-dev": { + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "^1.8.8", + "phpunit/phpunit": "^10.5", + "vimeo/psalm": "^5.24" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + }, + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } + ], + "description": "The Doctrine Event Manager is a simple PHP event system that was built to be used with the various Doctrine projects.", + "homepage": "https://www.doctrine-project.org/projects/event-manager.html", + "keywords": [ + "event", + "event dispatcher", + "event manager", + "event system", + "events" + ], + "support": { + "issues": "https://github.com/doctrine/event-manager/issues", + "source": "https://github.com/doctrine/event-manager/tree/2.0.1" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fevent-manager", + "type": "tidelift" + } + ], + "time": "2024-05-22T20:47:39+00:00" + }, + { + "name": "doctrine/inflector", + "version": "2.0.10", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/5817d0659c5b50c9b950feb9af7b9668e2c436bc", + "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^11.0", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.3", + "phpunit/phpunit": "^8.5 || ^9.5", + "vimeo/psalm": "^4.25 || ^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Inflector\\": "lib/Doctrine/Inflector" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.", + "homepage": "https://www.doctrine-project.org/projects/inflector.html", + "keywords": [ + "inflection", + "inflector", + "lowercase", + "manipulation", + "php", + "plural", + "singular", + "strings", + "uppercase", + "words" + ], + "support": { + "issues": "https://github.com/doctrine/inflector/issues", + "source": "https://github.com/doctrine/inflector/tree/2.0.10" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", + "type": "tidelift" + } + ], + "time": "2024-02-18T20:23:39+00:00" + }, + { + "name": "doctrine/lexer", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^5.21" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "keywords": [ + "annotations", + "docblock", + "lexer", + "parser", + "php" + ], + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/3.0.1" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" + } + ], + "time": "2024-02-05T11:56:58+00:00" + }, + { + "name": "dragonmantank/cron-expression", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/dragonmantank/cron-expression.git", + "reference": "8c784d071debd117328803d86b2097615b457500" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/8c784d071debd117328803d86b2097615b457500", + "reference": "8c784d071debd117328803d86b2097615b457500", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0", + "webmozart/assert": "^1.0" + }, + "replace": { + "mtdowling/cron-expression": "^1.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.0", + "phpunit/phpunit": "^7.0|^8.0|^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Cron\\": "src/Cron/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Tankersley", + "email": "chris@ctankersley.com", + "homepage": "https://github.com/dragonmantank" + } + ], + "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", + "keywords": [ + "cron", + "schedule" + ], + "support": { + "issues": "https://github.com/dragonmantank/cron-expression/issues", + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.4.0" + }, + "funding": [ + { + "url": "https://github.com/dragonmantank", + "type": "github" + } + ], + "time": "2024-10-09T13:47:03+00:00" + }, + { + "name": "egulias/email-validator", + "version": "4.0.2", + "source": { + "type": "git", + "url": "https://github.com/egulias/EmailValidator.git", + "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/ebaaf5be6c0286928352e054f2d5125608e5405e", + "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e", + "shasum": "" + }, + "require": { + "doctrine/lexer": "^2.0 || ^3.0", + "php": ">=8.1", + "symfony/polyfill-intl-idn": "^1.26" + }, + "require-dev": { + "phpunit/phpunit": "^10.2", + "vimeo/psalm": "^5.12" + }, + "suggest": { + "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Egulias\\EmailValidator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eduardo Gulias Davis" + } + ], + "description": "A library for validating emails against several RFCs", + "homepage": "https://github.com/egulias/EmailValidator", + "keywords": [ + "email", + "emailvalidation", + "emailvalidator", + "validation", + "validator" + ], + "support": { + "issues": "https://github.com/egulias/EmailValidator/issues", + "source": "https://github.com/egulias/EmailValidator/tree/4.0.2" + }, + "funding": [ + { + "url": "https://github.com/egulias", + "type": "github" + } + ], + "time": "2023-10-06T06:47:41+00:00" + }, + { + "name": "fruitcake/php-cors", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/fruitcake/php-cors.git", + "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/3d158f36e7875e2f040f37bc0573956240a5a38b", + "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0", + "symfony/http-foundation": "^4.4|^5.4|^6|^7" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "phpunit/phpunit": "^9", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "Fruitcake\\Cors\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fruitcake", + "homepage": "https://fruitcake.nl" + }, + { + "name": "Barryvdh", + "email": "barryvdh@gmail.com" + } + ], + "description": "Cross-origin resource sharing library for the Symfony HttpFoundation", + "homepage": "https://github.com/fruitcake/php-cors", + "keywords": [ + "cors", + "laravel", + "symfony" + ], + "support": { + "issues": "https://github.com/fruitcake/php-cors/issues", + "source": "https://github.com/fruitcake/php-cors/tree/v1.3.0" + }, + "funding": [ + { + "url": "https://fruitcake.nl", + "type": "custom" + }, + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2023-10-12T05:21:21+00:00" + }, + { + "name": "graham-campbell/result-type", + "version": "v1.1.3", + "source": { + "type": "git", + "url": "https://github.com/GrahamCampbell/Result-Type.git", + "reference": "3ba905c11371512af9d9bdd27d99b782216b6945" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/3ba905c11371512af9d9bdd27d99b782216b6945", + "reference": "3ba905c11371512af9d9bdd27d99b782216b6945", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.3" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" + }, + "type": "library", + "autoload": { + "psr-4": { + "GrahamCampbell\\ResultType\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "An Implementation Of The Result Type", + "keywords": [ + "Graham Campbell", + "GrahamCampbell", + "Result Type", + "Result-Type", + "result" + ], + "support": { + "issues": "https://github.com/GrahamCampbell/Result-Type/issues", + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", + "type": "tidelift" + } + ], + "time": "2024-07-20T21:45:45+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.9.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "d281ed313b989f213357e3be1a179f02196ac99b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b", + "reference": "d281ed313b989f213357e3be1a179f02196ac99b", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.5.3 || ^2.0.3", + "guzzlehttp/psr7": "^2.7.0", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-curl": "*", + "guzzle/client-integration-tests": "3.0.2", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.9.2" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2024-07-24T11:22:20+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/f9c436286ab2892c7db7be8c8da4ef61ccf7b455", + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2024-10-17T10:06:22+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.7.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "0.9.0", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.7.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2024-07-18T11:15:46+00:00" + }, + { + "name": "guzzlehttp/uri-template", + "version": "v1.0.3", + "source": { + "type": "git", + "url": "https://github.com/guzzle/uri-template.git", + "reference": "ecea8feef63bd4fef1f037ecb288386999ecc11c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/ecea8feef63bd4fef1f037ecb288386999ecc11c", + "reference": "ecea8feef63bd4fef1f037ecb288386999ecc11c", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "symfony/polyfill-php80": "^1.24" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.36 || ^9.6.15", + "uri-template/tests": "1.0.0" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\UriTemplate\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + } + ], + "description": "A polyfill class for uri_template of PHP", + "keywords": [ + "guzzlehttp", + "uri-template" + ], + "support": { + "issues": "https://github.com/guzzle/uri-template/issues", + "source": "https://github.com/guzzle/uri-template/tree/v1.0.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/uri-template", + "type": "tidelift" + } + ], + "time": "2023-12-03T19:50:20+00:00" + }, + { + "name": "hashids/hashids", + "version": "5.0.2", + "source": { + "type": "git", + "url": "https://github.com/vinkla/hashids.git", + "reference": "197171016b77ddf14e259e186559152eb3f8cf33" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vinkla/hashids/zipball/197171016b77ddf14e259e186559152eb3f8cf33", + "reference": "197171016b77ddf14e259e186559152eb3f8cf33", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-bcmath": "Required to use BC Math arbitrary precision mathematics (*).", + "ext-gmp": "Required to use GNU multiple precision mathematics (*)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "Hashids\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ivan Akimov", + "email": "ivan@barreleye.com" + }, + { + "name": "Vincent Klaiber", + "email": "hello@doubledip.se" + } + ], + "description": "Generate short, unique, non-sequential ids (like YouTube and Bitly) from numbers", + "homepage": "https://hashids.org/php", + "keywords": [ + "bitly", + "decode", + "encode", + "hash", + "hashid", + "hashids", + "ids", + "obfuscate", + "youtube" + ], + "support": { + "issues": "https://github.com/vinkla/hashids/issues", + "source": "https://github.com/vinkla/hashids/tree/5.0.2" + }, + "time": "2023-02-23T15:00:54+00:00" + }, + { + "name": "laracasts/utilities", + "version": "3.2.3", + "source": { + "type": "git", + "url": "https://github.com/laracasts/PHP-Vars-To-Js-Transformer.git", + "reference": "4826fe062f58d07b13e72460141b21c8c07b736d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laracasts/PHP-Vars-To-Js-Transformer/zipball/4826fe062f58d07b13e72460141b21c8c07b736d", + "reference": "4826fe062f58d07b13e72460141b21c8c07b736d", + "shasum": "" + }, + "require": { + "illuminate/support": "^5.0|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", + "php": ">=5.5.0|>=7.2.5|>=8.0.0" + }, + "require-dev": { + "phpspec/phpspec": ">=2.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laracasts\\Utilities\\JavaScript\\JavaScriptServiceProvider" + ], + "aliases": { + "JavaScript": "Laracasts\\Utilities\\JavaScript\\JavaScriptFacade" + } + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Laracasts\\Utilities\\JavaScript\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jeffrey Way", + "email": "jeffrey@laracasts.com" + } + ], + "description": "Transform your PHP to JavaScript", + "keywords": [ + "javascript", + "laravel" + ], + "support": { + "issues": "https://github.com/laracasts/PHP-Vars-To-Js-Transformer/issues", + "source": "https://github.com/laracasts/PHP-Vars-To-Js-Transformer/tree/3.2.3" + }, + "time": "2024-03-03T16:53:37+00:00" + }, + { + "name": "laravel/framework", + "version": "v10.48.23", + "source": { + "type": "git", + "url": "https://github.com/laravel/framework.git", + "reference": "625269ca4881d2b50eded2045cb930960a181d98" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/framework/zipball/625269ca4881d2b50eded2045cb930960a181d98", + "reference": "625269ca4881d2b50eded2045cb930960a181d98", + "shasum": "" + }, + "require": { + "brick/math": "^0.9.3|^0.10.2|^0.11|^0.12", + "composer-runtime-api": "^2.2", + "doctrine/inflector": "^2.0.5", + "dragonmantank/cron-expression": "^3.3.2", + "egulias/email-validator": "^3.2.1|^4.0", + "ext-ctype": "*", + "ext-filter": "*", + "ext-hash": "*", + "ext-mbstring": "*", + "ext-openssl": "*", + "ext-session": "*", + "ext-tokenizer": "*", + "fruitcake/php-cors": "^1.2", + "guzzlehttp/uri-template": "^1.0", + "laravel/prompts": "^0.1.9", + "laravel/serializable-closure": "^1.3", + "league/commonmark": "^2.2.1", + "league/flysystem": "^3.8.0", + "monolog/monolog": "^3.0", + "nesbot/carbon": "^2.67", + "nunomaduro/termwind": "^1.13", + "php": "^8.1", + "psr/container": "^1.1.1|^2.0.1", + "psr/log": "^1.0|^2.0|^3.0", + "psr/simple-cache": "^1.0|^2.0|^3.0", + "ramsey/uuid": "^4.7", + "symfony/console": "^6.2", + "symfony/error-handler": "^6.2", + "symfony/finder": "^6.2", + "symfony/http-foundation": "^6.4", + "symfony/http-kernel": "^6.2", + "symfony/mailer": "^6.2", + "symfony/mime": "^6.2", + "symfony/process": "^6.2", + "symfony/routing": "^6.2", + "symfony/uid": "^6.2", + "symfony/var-dumper": "^6.2", + "tijsverkoyen/css-to-inline-styles": "^2.2.5", + "vlucas/phpdotenv": "^5.4.1", + "voku/portable-ascii": "^2.0" + }, + "conflict": { + "carbonphp/carbon-doctrine-types": ">=3.0", + "doctrine/dbal": ">=4.0", + "mockery/mockery": "1.6.8", + "phpunit/phpunit": ">=11.0.0", + "tightenco/collect": "<5.5.33" + }, + "provide": { + "psr/container-implementation": "1.1|2.0", + "psr/simple-cache-implementation": "1.0|2.0|3.0" + }, + "replace": { + "illuminate/auth": "self.version", + "illuminate/broadcasting": "self.version", + "illuminate/bus": "self.version", + "illuminate/cache": "self.version", + "illuminate/collections": "self.version", + "illuminate/conditionable": "self.version", + "illuminate/config": "self.version", + "illuminate/console": "self.version", + "illuminate/container": "self.version", + "illuminate/contracts": "self.version", + "illuminate/cookie": "self.version", + "illuminate/database": "self.version", + "illuminate/encryption": "self.version", + "illuminate/events": "self.version", + "illuminate/filesystem": "self.version", + "illuminate/hashing": "self.version", + "illuminate/http": "self.version", + "illuminate/log": "self.version", + "illuminate/macroable": "self.version", + "illuminate/mail": "self.version", + "illuminate/notifications": "self.version", + "illuminate/pagination": "self.version", + "illuminate/pipeline": "self.version", + "illuminate/process": "self.version", + "illuminate/queue": "self.version", + "illuminate/redis": "self.version", + "illuminate/routing": "self.version", + "illuminate/session": "self.version", + "illuminate/support": "self.version", + "illuminate/testing": "self.version", + "illuminate/translation": "self.version", + "illuminate/validation": "self.version", + "illuminate/view": "self.version" + }, + "require-dev": { + "ably/ably-php": "^1.0", + "aws/aws-sdk-php": "^3.235.5", + "doctrine/dbal": "^3.5.1", + "ext-gmp": "*", + "fakerphp/faker": "^1.21", + "guzzlehttp/guzzle": "^7.5", + "league/flysystem-aws-s3-v3": "^3.0", + "league/flysystem-ftp": "^3.0", + "league/flysystem-path-prefixing": "^3.3", + "league/flysystem-read-only": "^3.3", + "league/flysystem-sftp-v3": "^3.0", + "mockery/mockery": "^1.5.1", + "nyholm/psr7": "^1.2", + "orchestra/testbench-core": "^8.23.4", + "pda/pheanstalk": "^4.0", + "phpstan/phpstan": "^1.4.7", + "phpunit/phpunit": "^10.0.7", + "predis/predis": "^2.0.2", + "symfony/cache": "^6.2", + "symfony/http-client": "^6.2.4", + "symfony/psr-http-message-bridge": "^2.0" + }, + "suggest": { + "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).", + "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.235.5).", + "brianium/paratest": "Required to run tests in parallel (^6.0).", + "doctrine/dbal": "Required to rename columns and drop SQLite columns (^3.5.1).", + "ext-apcu": "Required to use the APC cache driver.", + "ext-fileinfo": "Required to use the Filesystem class.", + "ext-ftp": "Required to use the Flysystem FTP driver.", + "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().", + "ext-memcached": "Required to use the memcache cache driver.", + "ext-pcntl": "Required to use all features of the queue worker and console signal trapping.", + "ext-pdo": "Required to use all database features.", + "ext-posix": "Required to use all features of the queue worker.", + "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0).", + "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).", + "filp/whoops": "Required for friendly error pages in development (^2.14.3).", + "guzzlehttp/guzzle": "Required to use the HTTP Client and the ping methods on schedules (^7.5).", + "laravel/tinker": "Required to use the tinker console command (^2.0).", + "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.0).", + "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.0).", + "league/flysystem-path-prefixing": "Required to use the scoped driver (^3.3).", + "league/flysystem-read-only": "Required to use read-only disks (^3.3)", + "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.0).", + "mockery/mockery": "Required to use mocking (^1.5.1).", + "nyholm/psr7": "Required to use PSR-7 bridging features (^1.2).", + "pda/pheanstalk": "Required to use the beanstalk queue driver (^4.0).", + "phpunit/phpunit": "Required to use assertions and run tests (^9.5.8|^10.0.7).", + "predis/predis": "Required to use the predis connector (^2.0.2).", + "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", + "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0|^7.0).", + "symfony/cache": "Required to PSR-6 cache bridge (^6.2).", + "symfony/filesystem": "Required to enable support for relative symbolic links (^6.2).", + "symfony/http-client": "Required to enable support for the Symfony API mail transports (^6.2).", + "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^6.2).", + "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^6.2).", + "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^2.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "10.x-dev" + } + }, + "autoload": { + "files": [ + "src/Illuminate/Collections/helpers.php", + "src/Illuminate/Events/functions.php", + "src/Illuminate/Filesystem/functions.php", + "src/Illuminate/Foundation/helpers.php", + "src/Illuminate/Support/helpers.php" + ], + "psr-4": { + "Illuminate\\": "src/Illuminate/", + "Illuminate\\Support\\": [ + "src/Illuminate/Macroable/", + "src/Illuminate/Collections/", + "src/Illuminate/Conditionable/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Laravel Framework.", + "homepage": "https://laravel.com", + "keywords": [ + "framework", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2024-11-12T15:39:10+00:00" + }, + { + "name": "laravel/helpers", + "version": "v1.7.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/helpers.git", + "reference": "6caaa242a23bc39b4e3cf57304b5409260a7a346" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/helpers/zipball/6caaa242a23bc39b4e3cf57304b5409260a7a346", + "reference": "6caaa242a23bc39b4e3cf57304b5409260a7a346", + "shasum": "" + }, + "require": { + "illuminate/support": "~5.8.0|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", + "php": "^7.2.0|^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^7.0|^8.0|^9.0|^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Dries Vints", + "email": "dries@laravel.com" + } + ], + "description": "Provides backwards compatibility for helpers in the latest Laravel release.", + "keywords": [ + "helpers", + "laravel" + ], + "support": { + "source": "https://github.com/laravel/helpers/tree/v1.7.0" + }, + "time": "2023-11-30T14:09:05+00:00" + }, + { + "name": "laravel/prompts", + "version": "v0.1.25", + "source": { + "type": "git", + "url": "https://github.com/laravel/prompts.git", + "reference": "7b4029a84c37cb2725fc7f011586e2997040bc95" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/prompts/zipball/7b4029a84c37cb2725fc7f011586e2997040bc95", + "reference": "7b4029a84c37cb2725fc7f011586e2997040bc95", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "illuminate/collections": "^10.0|^11.0", + "php": "^8.1", + "symfony/console": "^6.2|^7.0" + }, + "conflict": { + "illuminate/console": ">=10.17.0 <10.25.0", + "laravel/framework": ">=10.17.0 <10.25.0" + }, + "require-dev": { + "mockery/mockery": "^1.5", + "pestphp/pest": "^2.3", + "phpstan/phpstan": "^1.11", + "phpstan/phpstan-mockery": "^1.1" + }, + "suggest": { + "ext-pcntl": "Required for the spinner to be animated." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "0.1.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Laravel\\Prompts\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Add beautiful and user-friendly forms to your command-line applications.", + "support": { + "issues": "https://github.com/laravel/prompts/issues", + "source": "https://github.com/laravel/prompts/tree/v0.1.25" + }, + "time": "2024-08-12T22:06:33+00:00" + }, + { + "name": "laravel/sanctum", + "version": "v3.3.3", + "source": { + "type": "git", + "url": "https://github.com/laravel/sanctum.git", + "reference": "8c104366459739f3ada0e994bcd3e6fd681ce3d5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/sanctum/zipball/8c104366459739f3ada0e994bcd3e6fd681ce3d5", + "reference": "8c104366459739f3ada0e994bcd3e6fd681ce3d5", + "shasum": "" + }, + "require": { + "ext-json": "*", + "illuminate/console": "^9.21|^10.0", + "illuminate/contracts": "^9.21|^10.0", + "illuminate/database": "^9.21|^10.0", + "illuminate/support": "^9.21|^10.0", + "php": "^8.0.2" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "orchestra/testbench": "^7.28.2|^8.8.3", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + }, + "laravel": { + "providers": [ + "Laravel\\Sanctum\\SanctumServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Sanctum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.", + "keywords": [ + "auth", + "laravel", + "sanctum" + ], + "support": { + "issues": "https://github.com/laravel/sanctum/issues", + "source": "https://github.com/laravel/sanctum" + }, + "time": "2023-12-19T18:44:48+00:00" + }, + { + "name": "laravel/serializable-closure", + "version": "v1.3.6", + "source": { + "type": "git", + "url": "https://github.com/laravel/serializable-closure.git", + "reference": "f865a58ea3a0107c336b7045104c75243fa59d96" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/f865a58ea3a0107c336b7045104c75243fa59d96", + "reference": "f865a58ea3a0107c336b7045104c75243fa59d96", + "shasum": "" + }, + "require": { + "php": "^7.3|^8.0" + }, + "require-dev": { + "illuminate/support": "^8.0|^9.0|^10.0|^11.0", + "nesbot/carbon": "^2.61|^3.0", + "pestphp/pest": "^1.21.3", + "phpstan/phpstan": "^1.8.2", + "symfony/var-dumper": "^5.4.11|^6.2.0|^7.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\SerializableClosure\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Nuno Maduro", + "email": "nuno@laravel.com" + } + ], + "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.", + "keywords": [ + "closure", + "laravel", + "serializable" + ], + "support": { + "issues": "https://github.com/laravel/serializable-closure/issues", + "source": "https://github.com/laravel/serializable-closure" + }, + "time": "2024-11-11T17:06:04+00:00" + }, + { + "name": "laravel/tinker", + "version": "v2.9.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/tinker.git", + "reference": "502e0fe3f0415d06d5db1f83a472f0f3b754bafe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/tinker/zipball/502e0fe3f0415d06d5db1f83a472f0f3b754bafe", + "reference": "502e0fe3f0415d06d5db1f83a472f0f3b754bafe", + "shasum": "" + }, + "require": { + "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", + "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", + "php": "^7.2.5|^8.0", + "psy/psysh": "^0.11.1|^0.12.0", + "symfony/var-dumper": "^4.3.4|^5.0|^6.0|^7.0" + }, + "require-dev": { + "mockery/mockery": "~1.3.3|^1.4.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8.5.8|^9.3.3" + }, + "suggest": { + "illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0|^9.0|^10.0|^11.0)." + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Tinker\\TinkerServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Tinker\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Powerful REPL for the Laravel framework.", + "keywords": [ + "REPL", + "Tinker", + "laravel", + "psysh" + ], + "support": { + "issues": "https://github.com/laravel/tinker/issues", + "source": "https://github.com/laravel/tinker/tree/v2.9.0" + }, + "time": "2024-01-04T16:10:04+00:00" + }, + { + "name": "laravel/ui", + "version": "v4.5.2", + "source": { + "type": "git", + "url": "https://github.com/laravel/ui.git", + "reference": "c75396f63268c95b053c8e4814eb70e0875e9628" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/ui/zipball/c75396f63268c95b053c8e4814eb70e0875e9628", + "reference": "c75396f63268c95b053c8e4814eb70e0875e9628", + "shasum": "" + }, + "require": { + "illuminate/console": "^9.21|^10.0|^11.0", + "illuminate/filesystem": "^9.21|^10.0|^11.0", + "illuminate/support": "^9.21|^10.0|^11.0", + "illuminate/validation": "^9.21|^10.0|^11.0", + "php": "^8.0", + "symfony/console": "^6.0|^7.0" + }, + "require-dev": { + "orchestra/testbench": "^7.35|^8.15|^9.0", + "phpunit/phpunit": "^9.3|^10.4|^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.x-dev" + }, + "laravel": { + "providers": [ + "Laravel\\Ui\\UiServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Ui\\": "src/", + "Illuminate\\Foundation\\Auth\\": "auth-backend/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel UI utilities and presets.", + "keywords": [ + "laravel", + "ui" + ], + "support": { + "source": "https://github.com/laravel/ui/tree/v4.5.2" + }, + "time": "2024-05-08T18:07:10+00:00" + }, + { + "name": "lcobucci/clock", + "version": "3.3.1", + "source": { + "type": "git", + "url": "https://github.com/lcobucci/clock.git", + "reference": "db3713a61addfffd615b79bf0bc22f0ccc61b86b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lcobucci/clock/zipball/db3713a61addfffd615b79bf0bc22f0ccc61b86b", + "reference": "db3713a61addfffd615b79bf0bc22f0ccc61b86b", + "shasum": "" + }, + "require": { + "php": "~8.2.0 || ~8.3.0 || ~8.4.0", + "psr/clock": "^1.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "infection/infection": "^0.29", + "lcobucci/coding-standard": "^11.1.0", + "phpstan/extension-installer": "^1.3.1", + "phpstan/phpstan": "^1.10.25", + "phpstan/phpstan-deprecation-rules": "^1.1.3", + "phpstan/phpstan-phpunit": "^1.3.13", + "phpstan/phpstan-strict-rules": "^1.5.1", + "phpunit/phpunit": "^11.3.6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Lcobucci\\Clock\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Luís Cobucci", + "email": "lcobucci@gmail.com" + } + ], + "description": "Yet another clock abstraction", + "support": { + "issues": "https://github.com/lcobucci/clock/issues", + "source": "https://github.com/lcobucci/clock/tree/3.3.1" + }, + "funding": [ + { + "url": "https://github.com/lcobucci", + "type": "github" + }, + { + "url": "https://www.patreon.com/lcobucci", + "type": "patreon" + } + ], + "time": "2024-09-24T20:45:14+00:00" + }, + { + "name": "lcobucci/jwt", + "version": "4.3.0", + "source": { + "type": "git", + "url": "https://github.com/lcobucci/jwt.git", + "reference": "4d7de2fe0d51a96418c0d04004986e410e87f6b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lcobucci/jwt/zipball/4d7de2fe0d51a96418c0d04004986e410e87f6b4", + "reference": "4d7de2fe0d51a96418c0d04004986e410e87f6b4", + "shasum": "" + }, + "require": { + "ext-hash": "*", + "ext-json": "*", + "ext-mbstring": "*", + "ext-openssl": "*", + "ext-sodium": "*", + "lcobucci/clock": "^2.0 || ^3.0", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "infection/infection": "^0.21", + "lcobucci/coding-standard": "^6.0", + "mikey179/vfsstream": "^1.6.7", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/php-invoker": "^3.1", + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Lcobucci\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Luís Cobucci", + "email": "lcobucci@gmail.com", + "role": "Developer" + } + ], + "description": "A simple library to work with JSON Web Token and JSON Web Signature", + "keywords": [ + "JWS", + "jwt" + ], + "support": { + "issues": "https://github.com/lcobucci/jwt/issues", + "source": "https://github.com/lcobucci/jwt/tree/4.3.0" + }, + "funding": [ + { + "url": "https://github.com/lcobucci", + "type": "github" + }, + { + "url": "https://www.patreon.com/lcobucci", + "type": "patreon" + } + ], + "time": "2023-01-02T13:28:00+00:00" + }, + { + "name": "league/commonmark", + "version": "2.5.3", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/commonmark.git", + "reference": "b650144166dfa7703e62a22e493b853b58d874b0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/b650144166dfa7703e62a22e493b853b58d874b0", + "reference": "b650144166dfa7703e62a22e493b853b58d874b0", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "league/config": "^1.1.1", + "php": "^7.4 || ^8.0", + "psr/event-dispatcher": "^1.0", + "symfony/deprecation-contracts": "^2.1 || ^3.0", + "symfony/polyfill-php80": "^1.16" + }, + "require-dev": { + "cebe/markdown": "^1.0", + "commonmark/cmark": "0.31.1", + "commonmark/commonmark.js": "0.31.1", + "composer/package-versions-deprecated": "^1.8", + "embed/embed": "^4.4", + "erusev/parsedown": "^1.0", + "ext-json": "*", + "github/gfm": "0.29.0", + "michelf/php-markdown": "^1.4 || ^2.0", + "nyholm/psr7": "^1.5", + "phpstan/phpstan": "^1.8.2", + "phpunit/phpunit": "^9.5.21 || ^10.5.9 || ^11.0.0", + "scrutinizer/ocular": "^1.8.1", + "symfony/finder": "^5.3 | ^6.0 || ^7.0", + "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 || ^7.0", + "unleashedtech/php-coding-standard": "^3.1.1", + "vimeo/psalm": "^4.24.0 || ^5.0.0" + }, + "suggest": { + "symfony/yaml": "v2.3+ required if using the Front Matter extension" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.6-dev" + } + }, + "autoload": { + "psr-4": { + "League\\CommonMark\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM)", + "homepage": "https://commonmark.thephpleague.com", + "keywords": [ + "commonmark", + "flavored", + "gfm", + "github", + "github-flavored", + "markdown", + "md", + "parser" + ], + "support": { + "docs": "https://commonmark.thephpleague.com/", + "forum": "https://github.com/thephpleague/commonmark/discussions", + "issues": "https://github.com/thephpleague/commonmark/issues", + "rss": "https://github.com/thephpleague/commonmark/releases.atom", + "source": "https://github.com/thephpleague/commonmark" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/commonmark", + "type": "tidelift" + } + ], + "time": "2024-08-16T11:46:16+00:00" + }, + { + "name": "league/config", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/config.git", + "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/config/zipball/754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", + "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", + "shasum": "" + }, + "require": { + "dflydev/dot-access-data": "^3.0.1", + "nette/schema": "^1.2", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.8.2", + "phpunit/phpunit": "^9.5.5", + "scrutinizer/ocular": "^1.8.1", + "unleashedtech/php-coding-standard": "^3.1", + "vimeo/psalm": "^4.7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Config\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Define configuration arrays with strict schemas and access values with dot notation", + "homepage": "https://config.thephpleague.com", + "keywords": [ + "array", + "config", + "configuration", + "dot", + "dot-access", + "nested", + "schema" + ], + "support": { + "docs": "https://config.thephpleague.com/", + "issues": "https://github.com/thephpleague/config/issues", + "rss": "https://github.com/thephpleague/config/releases.atom", + "source": "https://github.com/thephpleague/config" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + } + ], + "time": "2022-12-11T20:36:23+00:00" + }, + { + "name": "league/flysystem", + "version": "3.29.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "edc1bb7c86fab0776c3287dbd19b5fa278347319" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/edc1bb7c86fab0776c3287dbd19b5fa278347319", + "reference": "edc1bb7c86fab0776c3287dbd19b5fa278347319", + "shasum": "" + }, + "require": { + "league/flysystem-local": "^3.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "conflict": { + "async-aws/core": "<1.19.0", + "async-aws/s3": "<1.14.0", + "aws/aws-sdk-php": "3.209.31 || 3.210.0", + "guzzlehttp/guzzle": "<7.0", + "guzzlehttp/ringphp": "<1.1.1", + "phpseclib/phpseclib": "3.0.15", + "symfony/http-client": "<5.2" + }, + "require-dev": { + "async-aws/s3": "^1.5 || ^2.0", + "async-aws/simple-s3": "^1.1 || ^2.0", + "aws/aws-sdk-php": "^3.295.10", + "composer/semver": "^3.0", + "ext-fileinfo": "*", + "ext-ftp": "*", + "ext-mongodb": "^1.3", + "ext-zip": "*", + "friendsofphp/php-cs-fixer": "^3.5", + "google/cloud-storage": "^1.23", + "guzzlehttp/psr7": "^2.6", + "microsoft/azure-storage-blob": "^1.1", + "mongodb/mongodb": "^1.2", + "phpseclib/phpseclib": "^3.0.36", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.11|^10.0", + "sabre/dav": "^4.6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "File storage abstraction for PHP", + "keywords": [ + "WebDAV", + "aws", + "cloud", + "file", + "files", + "filesystem", + "filesystems", + "ftp", + "s3", + "sftp", + "storage" + ], + "support": { + "issues": "https://github.com/thephpleague/flysystem/issues", + "source": "https://github.com/thephpleague/flysystem/tree/3.29.1" + }, + "time": "2024-10-08T08:58:34+00:00" + }, + { + "name": "league/flysystem-aws-s3-v3", + "version": "3.28.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git", + "reference": "22071ef1604bc776f5ff2468ac27a752514665c8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/22071ef1604bc776f5ff2468ac27a752514665c8", + "reference": "22071ef1604bc776f5ff2468ac27a752514665c8", + "shasum": "" + }, + "require": { + "aws/aws-sdk-php": "^3.295.10", + "league/flysystem": "^3.10.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "conflict": { + "guzzlehttp/guzzle": "<7.0", + "guzzlehttp/ringphp": "<1.1.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\AwsS3V3\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "AWS S3 filesystem adapter for Flysystem.", + "keywords": [ + "Flysystem", + "aws", + "file", + "files", + "filesystem", + "s3", + "storage" + ], + "support": { + "source": "https://github.com/thephpleague/flysystem-aws-s3-v3/tree/3.28.0" + }, + "time": "2024-05-06T20:05:52+00:00" + }, + { + "name": "league/flysystem-local", + "version": "3.29.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-local.git", + "reference": "e0e8d52ce4b2ed154148453d321e97c8e931bd27" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/e0e8d52ce4b2ed154148453d321e97c8e931bd27", + "reference": "e0e8d52ce4b2ed154148453d321e97c8e931bd27", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "league/flysystem": "^3.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\Local\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "Local filesystem adapter for Flysystem.", + "keywords": [ + "Flysystem", + "file", + "files", + "filesystem", + "local" + ], + "support": { + "source": "https://github.com/thephpleague/flysystem-local/tree/3.29.0" + }, + "time": "2024-08-09T21:24:39+00:00" + }, + { + "name": "league/flysystem-memory", + "version": "3.28.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-memory.git", + "reference": "009a4a1b64dfbfb709fad9ced8dc3d2ed2224f85" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-memory/zipball/009a4a1b64dfbfb709fad9ced8dc3d2ed2224f85", + "reference": "009a4a1b64dfbfb709fad9ced8dc3d2ed2224f85", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "league/flysystem": "^3.0.0", + "php": "^8.0.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\InMemory\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "In-memory filesystem adapter for Flysystem.", + "keywords": [ + "Flysystem", + "file", + "files", + "filesystem", + "memory" + ], + "support": { + "source": "https://github.com/thephpleague/flysystem-memory/tree/3.28.0" + }, + "time": "2024-05-06T20:05:52+00:00" + }, + { + "name": "league/fractal", + "version": "0.20.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/fractal.git", + "reference": "8b9d39b67624db9195c06f9c1ffd0355151eaf62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/fractal/zipball/8b9d39b67624db9195c06f9c1ffd0355151eaf62", + "reference": "8b9d39b67624db9195c06f9c1ffd0355151eaf62", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "doctrine/orm": "^2.5", + "illuminate/contracts": "~5.0", + "mockery/mockery": "^1.3", + "pagerfanta/pagerfanta": "~1.0.0", + "phpstan/phpstan": "^1.4", + "phpunit/phpunit": "^9.5", + "squizlabs/php_codesniffer": "~3.4", + "vimeo/psalm": "^4.22", + "zendframework/zend-paginator": "~2.3" + }, + "suggest": { + "illuminate/pagination": "The Illuminate Pagination component.", + "pagerfanta/pagerfanta": "Pagerfanta Paginator", + "zendframework/zend-paginator": "Zend Framework Paginator" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.20.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Fractal\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Phil Sturgeon", + "email": "me@philsturgeon.uk", + "homepage": "http://philsturgeon.uk/", + "role": "Developer" + } + ], + "description": "Handle the output of complex data structures ready for API output.", + "homepage": "http://fractal.thephpleague.com/", + "keywords": [ + "api", + "json", + "league", + "rest" + ], + "support": { + "issues": "https://github.com/thephpleague/fractal/issues", + "source": "https://github.com/thephpleague/fractal/tree/0.20.1" + }, + "time": "2022-04-11T12:47:17+00:00" + }, + { + "name": "league/mime-type-detection", + "version": "1.16.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/mime-type-detection.git", + "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/2d6702ff215bf922936ccc1ad31007edc76451b9", + "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.2", + "phpstan/phpstan": "^0.12.68", + "phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\MimeTypeDetection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "Mime-type detection for Flysystem", + "support": { + "issues": "https://github.com/thephpleague/mime-type-detection/issues", + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.16.0" + }, + "funding": [ + { + "url": "https://github.com/frankdejonge", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/flysystem", + "type": "tidelift" + } + ], + "time": "2024-09-21T08:32:55+00:00" + }, + { + "name": "matriphe/iso-639", + "version": "1.3", + "source": { + "type": "git", + "url": "https://github.com/matriphe/php-iso-639.git", + "reference": "9a4a5823147890e70e0e0f60f3baea95e8d3b5f1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/matriphe/php-iso-639/zipball/9a4a5823147890e70e0e0f60f3baea95e8d3b5f1", + "reference": "9a4a5823147890e70e0e0f60f3baea95e8d3b5f1", + "shasum": "" + }, + "require-dev": { + "phpunit/phpunit": "^4.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Matriphe\\ISO639\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Muhammad Zamroni", + "email": "halo@matriphe.com" + } + ], + "description": "PHP library to convert ISO-639-1 code to language name.", + "keywords": [ + "639", + "iso", + "iso-639", + "lang", + "language", + "laravel" + ], + "support": { + "issues": "https://github.com/matriphe/php-iso-639/issues", + "source": "https://github.com/matriphe/php-iso-639/tree/1.3" + }, + "time": "2024-03-17T21:30:14+00:00" + }, + { + "name": "monolog/monolog", + "version": "3.8.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "32e515fdc02cdafbe4593e30a9350d486b125b67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/32e515fdc02cdafbe4593e30a9350d486b125b67", + "reference": "32e515fdc02cdafbe4593e30a9350d486b125b67", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^2.0 || ^3.0" + }, + "provide": { + "psr/log-implementation": "3.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^3.0", + "doctrine/couchdb": "~1.0@dev", + "elasticsearch/elasticsearch": "^7 || ^8", + "ext-json": "*", + "graylog2/gelf-php": "^1.4.2 || ^2.0", + "guzzlehttp/guzzle": "^7.4.5", + "guzzlehttp/psr7": "^2.2", + "mongodb/mongodb": "^1.8", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "php-console/php-console": "^3.1.8", + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^10.5.17 || ^11.0.7", + "predis/predis": "^1.1 || ^2", + "rollbar/rollbar": "^4.0", + "ruflin/elastica": "^7 || ^8", + "symfony/mailer": "^5.4 || ^6", + "symfony/mime": "^5.4 || ^6" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "ext-openssl": "Required to send log messages using SSL", + "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "https://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "support": { + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/3.8.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "time": "2024-11-12T13:57:08+00:00" + }, + { + "name": "mtdowling/jmespath.php", + "version": "2.8.0", + "source": { + "type": "git", + "url": "https://github.com/jmespath/jmespath.php.git", + "reference": "a2a865e05d5f420b50cc2f85bb78d565db12a6bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/a2a865e05d5f420b50cc2f85bb78d565db12a6bc", + "reference": "a2a865e05d5f420b50cc2f85bb78d565db12a6bc", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "symfony/polyfill-mbstring": "^1.17" + }, + "require-dev": { + "composer/xdebug-handler": "^3.0.3", + "phpunit/phpunit": "^8.5.33" + }, + "bin": [ + "bin/jp.php" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.8-dev" + } + }, + "autoload": { + "files": [ + "src/JmesPath.php" + ], + "psr-4": { + "JmesPath\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Declaratively specify how to extract elements from a JSON document", + "keywords": [ + "json", + "jsonpath" + ], + "support": { + "issues": "https://github.com/jmespath/jmespath.php/issues", + "source": "https://github.com/jmespath/jmespath.php/tree/2.8.0" + }, + "time": "2024-09-04T18:46:31+00:00" + }, + { + "name": "nesbot/carbon", + "version": "2.72.5", + "source": { + "type": "git", + "url": "https://github.com/briannesbitt/Carbon.git", + "reference": "afd46589c216118ecd48ff2b95d77596af1e57ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/afd46589c216118ecd48ff2b95d77596af1e57ed", + "reference": "afd46589c216118ecd48ff2b95d77596af1e57ed", + "shasum": "" + }, + "require": { + "carbonphp/carbon-doctrine-types": "*", + "ext-json": "*", + "php": "^7.1.8 || ^8.0", + "psr/clock": "^1.0", + "symfony/polyfill-mbstring": "^1.0", + "symfony/polyfill-php80": "^1.16", + "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "doctrine/dbal": "^2.0 || ^3.1.4 || ^4.0", + "doctrine/orm": "^2.7 || ^3.0", + "friendsofphp/php-cs-fixer": "^3.0", + "kylekatarnls/multi-tester": "^2.0", + "ondrejmirtes/better-reflection": "*", + "phpmd/phpmd": "^2.9", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.12.99 || ^1.7.14", + "phpunit/php-file-iterator": "^2.0.5 || ^3.0.6", + "phpunit/phpunit": "^7.5.20 || ^8.5.26 || ^9.5.20", + "squizlabs/php_codesniffer": "^3.4" + }, + "bin": [ + "bin/carbon" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev", + "dev-2.x": "2.x-dev" + }, + "laravel": { + "providers": [ + "Carbon\\Laravel\\ServiceProvider" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "psr-4": { + "Carbon\\": "src/Carbon/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "https://markido.com" + }, + { + "name": "kylekatarnls", + "homepage": "https://github.com/kylekatarnls" + } + ], + "description": "An API extension for DateTime that supports 281 different languages.", + "homepage": "https://carbon.nesbot.com", + "keywords": [ + "date", + "datetime", + "time" + ], + "support": { + "docs": "https://carbon.nesbot.com/docs", + "issues": "https://github.com/briannesbitt/Carbon/issues", + "source": "https://github.com/briannesbitt/Carbon" + }, + "funding": [ + { + "url": "https://github.com/sponsors/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon#sponsor", + "type": "opencollective" + }, + { + "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme", + "type": "tidelift" + } + ], + "time": "2024-06-03T19:18:41+00:00" + }, + { + "name": "nette/schema", + "version": "v1.3.2", + "source": { + "type": "git", + "url": "https://github.com/nette/schema.git", + "reference": "da801d52f0354f70a638673c4a0f04e16529431d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/schema/zipball/da801d52f0354f70a638673c4a0f04e16529431d", + "reference": "da801d52f0354f70a638673c4a0f04e16529431d", + "shasum": "" + }, + "require": { + "nette/utils": "^4.0", + "php": "8.1 - 8.4" + }, + "require-dev": { + "nette/tester": "^2.5.2", + "phpstan/phpstan-nette": "^1.0", + "tracy/tracy": "^2.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "📐 Nette Schema: validating data structures against a given Schema.", + "homepage": "https://nette.org", + "keywords": [ + "config", + "nette" + ], + "support": { + "issues": "https://github.com/nette/schema/issues", + "source": "https://github.com/nette/schema/tree/v1.3.2" + }, + "time": "2024-10-06T23:10:23+00:00" + }, + { + "name": "nette/utils", + "version": "v4.0.5", + "source": { + "type": "git", + "url": "https://github.com/nette/utils.git", + "reference": "736c567e257dbe0fcf6ce81b4d6dbe05c6899f96" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/utils/zipball/736c567e257dbe0fcf6ce81b4d6dbe05c6899f96", + "reference": "736c567e257dbe0fcf6ce81b4d6dbe05c6899f96", + "shasum": "" + }, + "require": { + "php": "8.0 - 8.4" + }, + "conflict": { + "nette/finder": "<3", + "nette/schema": "<1.2.2" + }, + "require-dev": { + "jetbrains/phpstorm-attributes": "dev-master", + "nette/tester": "^2.5", + "phpstan/phpstan": "^1.0", + "tracy/tracy": "^2.9" + }, + "suggest": { + "ext-gd": "to use Image", + "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()", + "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()", + "ext-json": "to use Nette\\Utils\\Json", + "ext-mbstring": "to use Strings::lower() etc...", + "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.", + "homepage": "https://nette.org", + "keywords": [ + "array", + "core", + "datetime", + "images", + "json", + "nette", + "paginator", + "password", + "slugify", + "string", + "unicode", + "utf-8", + "utility", + "validation" + ], + "support": { + "issues": "https://github.com/nette/utils/issues", + "source": "https://github.com/nette/utils/tree/v4.0.5" + }, + "time": "2024-08-07T15:39:19+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.3.1", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b", + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1" + }, + "time": "2024-10-08T18:51:32+00:00" + }, + { + "name": "nunomaduro/termwind", + "version": "v1.16.0", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/termwind.git", + "reference": "dcf1ec3dfa36137b7ce41d43866644a7ab8fc257" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/dcf1ec3dfa36137b7ce41d43866644a7ab8fc257", + "reference": "dcf1ec3dfa36137b7ce41d43866644a7ab8fc257", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^8.1", + "symfony/console": "^6.4.12" + }, + "require-dev": { + "illuminate/console": "^10.48.22", + "illuminate/support": "^10.48.22", + "laravel/pint": "^1.18.1", + "pestphp/pest": "^2", + "pestphp/pest-plugin-mock": "2.0.0", + "phpstan/phpstan": "^1.12.6", + "phpstan/phpstan-strict-rules": "^1.6.1", + "symfony/var-dumper": "^6.4.11", + "thecodingmachine/phpstan-strict-rules": "^1.0.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Termwind\\Laravel\\TermwindServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "src/Functions.php" + ], + "psr-4": { + "Termwind\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Its like Tailwind CSS, but for the console.", + "keywords": [ + "cli", + "console", + "css", + "package", + "php", + "style" + ], + "support": { + "issues": "https://github.com/nunomaduro/termwind/issues", + "source": "https://github.com/nunomaduro/termwind/tree/v1.16.0" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://github.com/xiCO2k", + "type": "github" + } + ], + "time": "2024-10-15T15:27:12+00:00" + }, + { + "name": "paragonie/constant_time_encoding", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/paragonie/constant_time_encoding.git", + "reference": "df1e7fde177501eee2037dd159cf04f5f301a512" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/df1e7fde177501eee2037dd159cf04f5f301a512", + "reference": "df1e7fde177501eee2037dd159cf04f5f301a512", + "shasum": "" + }, + "require": { + "php": "^8" + }, + "require-dev": { + "phpunit/phpunit": "^9", + "vimeo/psalm": "^4|^5" + }, + "type": "library", + "autoload": { + "psr-4": { + "ParagonIE\\ConstantTime\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com", + "role": "Maintainer" + }, + { + "name": "Steve 'Sc00bz' Thomas", + "email": "steve@tobtu.com", + "homepage": "https://www.tobtu.com", + "role": "Original Developer" + } + ], + "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)", + "keywords": [ + "base16", + "base32", + "base32_decode", + "base32_encode", + "base64", + "base64_decode", + "base64_encode", + "bin2hex", + "encoding", + "hex", + "hex2bin", + "rfc4648" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/constant_time_encoding/issues", + "source": "https://github.com/paragonie/constant_time_encoding" + }, + "time": "2024-05-08T12:36:18+00:00" + }, + { + "name": "paragonie/random_compat", + "version": "v9.99.100", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a", + "shasum": "" + }, + "require": { + "php": ">= 7" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*", + "vimeo/psalm": "^1" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "polyfill", + "pseudorandom", + "random" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/random_compat/issues", + "source": "https://github.com/paragonie/random_compat" + }, + "time": "2020-10-15T08:29:30+00:00" + }, + { + "name": "phpoption/phpoption", + "version": "1.9.3", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/e3fac8b24f56113f7cb96af14958c0dd16330f54", + "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, + "branch-alias": { + "dev-master": "1.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpOption\\": "src/PhpOption/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh" + }, + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "Option Type for PHP", + "keywords": [ + "language", + "option", + "php", + "type" + ], + "support": { + "issues": "https://github.com/schmittjoh/php-option/issues", + "source": "https://github.com/schmittjoh/php-option/tree/1.9.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", + "type": "tidelift" + } + ], + "time": "2024-07-20T21:41:07+00:00" + }, + { + "name": "phpseclib/phpseclib", + "version": "3.0.42", + "source": { + "type": "git", + "url": "https://github.com/phpseclib/phpseclib.git", + "reference": "db92f1b1987b12b13f248fe76c3a52cadb67bb98" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/db92f1b1987b12b13f248fe76c3a52cadb67bb98", + "reference": "db92f1b1987b12b13f248fe76c3a52cadb67bb98", + "shasum": "" + }, + "require": { + "paragonie/constant_time_encoding": "^1|^2|^3", + "paragonie/random_compat": "^1.4|^2.0|^9.99.99", + "php": ">=5.6.1" + }, + "require-dev": { + "phpunit/phpunit": "*" + }, + "suggest": { + "ext-dom": "Install the DOM extension to load XML formatted public keys.", + "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.", + "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.", + "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", + "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." + }, + "type": "library", + "autoload": { + "files": [ + "phpseclib/bootstrap.php" + ], + "psr-4": { + "phpseclib3\\": "phpseclib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jim Wigginton", + "email": "terrafrost@php.net", + "role": "Lead Developer" + }, + { + "name": "Patrick Monnerat", + "email": "pm@datasphere.ch", + "role": "Developer" + }, + { + "name": "Andreas Fischer", + "email": "bantu@phpbb.com", + "role": "Developer" + }, + { + "name": "Hans-Jürgen Petrich", + "email": "petrich@tronic-media.com", + "role": "Developer" + }, + { + "name": "Graham Campbell", + "email": "graham@alt-three.com", + "role": "Developer" + } + ], + "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", + "homepage": "http://phpseclib.sourceforge.net", + "keywords": [ + "BigInteger", + "aes", + "asn.1", + "asn1", + "blowfish", + "crypto", + "cryptography", + "encryption", + "rsa", + "security", + "sftp", + "signature", + "signing", + "ssh", + "twofish", + "x.509", + "x509" + ], + "support": { + "issues": "https://github.com/phpseclib/phpseclib/issues", + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.42" + }, + "funding": [ + { + "url": "https://github.com/terrafrost", + "type": "github" + }, + { + "url": "https://www.patreon.com/phpseclib", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib", + "type": "tidelift" + } + ], + "time": "2024-09-16T03:06:04+00:00" + }, + { + "name": "pragmarx/google2fa", + "version": "v8.0.3", + "source": { + "type": "git", + "url": "https://github.com/antonioribeiro/google2fa.git", + "reference": "6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/antonioribeiro/google2fa/zipball/6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad", + "reference": "6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad", + "shasum": "" + }, + "require": { + "paragonie/constant_time_encoding": "^1.0|^2.0|^3.0", + "php": "^7.1|^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.9", + "phpunit/phpunit": "^7.5.15|^8.5|^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "PragmaRX\\Google2FA\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Antonio Carlos Ribeiro", + "email": "acr@antoniocarlosribeiro.com", + "role": "Creator & Designer" + } + ], + "description": "A One Time Password Authentication package, compatible with Google Authenticator.", + "keywords": [ + "2fa", + "Authentication", + "Two Factor Authentication", + "google2fa" + ], + "support": { + "issues": "https://github.com/antonioribeiro/google2fa/issues", + "source": "https://github.com/antonioribeiro/google2fa/tree/v8.0.3" + }, + "time": "2024-09-05T11:56:40+00:00" + }, + { + "name": "predis/predis", + "version": "v2.2.2", + "source": { + "type": "git", + "url": "https://github.com/predis/predis.git", + "reference": "b1d3255ed9ad4d7254f9f9bba386c99f4bb983d1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/predis/predis/zipball/b1d3255ed9ad4d7254f9f9bba386c99f4bb983d1", + "reference": "b1d3255ed9ad4d7254f9f9bba386c99f4bb983d1", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.3", + "phpstan/phpstan": "^1.9", + "phpunit/phpunit": "^8.0 || ~9.4.4" + }, + "suggest": { + "ext-relay": "Faster connection with in-memory caching (>=0.6.2)" + }, + "type": "library", + "autoload": { + "psr-4": { + "Predis\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Till Krüss", + "homepage": "https://till.im", + "role": "Maintainer" + } + ], + "description": "A flexible and feature-complete Redis client for PHP.", + "homepage": "http://github.com/predis/predis", + "keywords": [ + "nosql", + "predis", + "redis" + ], + "support": { + "issues": "https://github.com/predis/predis/issues", + "source": "https://github.com/predis/predis/tree/v2.2.2" + }, + "funding": [ + { + "url": "https://github.com/sponsors/tillkruss", + "type": "github" + } + ], + "time": "2023-09-13T16:42:03+00:00" + }, + { + "name": "prologue/alerts", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/prologuephp/alerts.git", + "reference": "00c4662ce50a633c39d3424698baa5cbfb880da3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/prologuephp/alerts/zipball/00c4662ce50a633c39d3424698baa5cbfb880da3", + "reference": "00c4662ce50a633c39d3424698baa5cbfb880da3", + "shasum": "" + }, + "require": { + "illuminate/config": "~9|^10|^11.0", + "illuminate/session": "~9|^10|^11.0", + "illuminate/support": "~9|^10|^11.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^9|^10.5" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Prologue\\Alerts\\AlertsServiceProvider" + ], + "aliases": { + "Alert": "Prologue\\Alerts\\Facades\\Alert" + } + } + }, + "autoload": { + "psr-4": { + "Prologue\\Alerts\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dries Vints", + "email": "dries.vints@gmail.com", + "homepage": "http://driesvints.com", + "role": "Creator" + }, + { + "name": "Cristian Tabacitu", + "email": "hello@tabacitu.ro", + "homepage": "http://tabacitu.ro", + "role": "Maintainer" + } + ], + "description": "Prologue Alerts is a package that handles global site messages.", + "keywords": [ + "alerts", + "laravel", + "messages" + ], + "support": { + "issues": "https://github.com/prologuephp/alerts/issues", + "source": "https://github.com/prologuephp/alerts/tree/1.2.0" + }, + "time": "2024-03-12T11:23:19+00:00" + }, + { + "name": "psr/cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "support": { + "source": "https://github.com/php-fig/cache/tree/3.0.0" + }, + "time": "2021-02-03T23:26:27+00:00" + }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "time": "2021-10-29T13:26:27+00:00" + }, + { + "name": "psy/psysh", + "version": "v0.12.4", + "source": { + "type": "git", + "url": "https://github.com/bobthecow/psysh.git", + "reference": "2fd717afa05341b4f8152547f142cd2f130f6818" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/2fd717afa05341b4f8152547f142cd2f130f6818", + "reference": "2fd717afa05341b4f8152547f142cd2f130f6818", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-tokenizer": "*", + "nikic/php-parser": "^5.0 || ^4.0", + "php": "^8.0 || ^7.4", + "symfony/console": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4", + "symfony/var-dumper": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4" + }, + "conflict": { + "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.2" + }, + "suggest": { + "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", + "ext-pdo-sqlite": "The doc command requires SQLite to work.", + "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well." + }, + "bin": [ + "bin/psysh" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "0.12.x-dev" + }, + "bamarni-bin": { + "bin-links": false, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Psy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Justin Hileman", + "email": "justin@justinhileman.info", + "homepage": "http://justinhileman.com" + } + ], + "description": "An interactive shell for modern PHP.", + "homepage": "http://psysh.org", + "keywords": [ + "REPL", + "console", + "interactive", + "shell" + ], + "support": { + "issues": "https://github.com/bobthecow/psysh/issues", + "source": "https://github.com/bobthecow/psysh/tree/v0.12.4" + }, + "time": "2024-06-10T01:18:23+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "ramsey/collection", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/ramsey/collection.git", + "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", + "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "captainhook/plugin-composer": "^5.3", + "ergebnis/composer-normalize": "^2.28.3", + "fakerphp/faker": "^1.21", + "hamcrest/hamcrest-php": "^2.0", + "jangregor/phpstan-prophecy": "^1.0", + "mockery/mockery": "^1.5", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.3", + "phpcsstandards/phpcsutils": "^1.0.0-rc1", + "phpspec/prophecy-phpunit": "^2.0", + "phpstan/extension-installer": "^1.2", + "phpstan/phpstan": "^1.9", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5", + "psalm/plugin-mockery": "^1.1", + "psalm/plugin-phpunit": "^0.18.4", + "ramsey/coding-standard": "^2.0.3", + "ramsey/conventional-commits": "^1.3", + "vimeo/psalm": "^5.4" + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + }, + "ramsey/conventional-commits": { + "configFile": "conventional-commits.json" + } + }, + "autoload": { + "psr-4": { + "Ramsey\\Collection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + } + ], + "description": "A PHP library for representing and manipulating collections.", + "keywords": [ + "array", + "collection", + "hash", + "map", + "queue", + "set" + ], + "support": { + "issues": "https://github.com/ramsey/collection/issues", + "source": "https://github.com/ramsey/collection/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/ramsey", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ramsey/collection", + "type": "tidelift" + } + ], + "time": "2022-12-31T21:50:55+00:00" + }, + { + "name": "ramsey/uuid", + "version": "4.7.6", + "source": { + "type": "git", + "url": "https://github.com/ramsey/uuid.git", + "reference": "91039bc1faa45ba123c4328958e620d382ec7088" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088", + "reference": "91039bc1faa45ba123c4328958e620d382ec7088", + "shasum": "" + }, + "require": { + "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12", + "ext-json": "*", + "php": "^8.0", + "ramsey/collection": "^1.2 || ^2.0" + }, + "replace": { + "rhumsaa/uuid": "self.version" + }, + "require-dev": { + "captainhook/captainhook": "^5.10", + "captainhook/plugin-composer": "^5.3", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", + "doctrine/annotations": "^1.8", + "ergebnis/composer-normalize": "^2.15", + "mockery/mockery": "^1.3", + "paragonie/random-lib": "^2", + "php-mock/php-mock": "^2.2", + "php-mock/php-mock-mockery": "^1.3", + "php-parallel-lint/php-parallel-lint": "^1.1", + "phpbench/phpbench": "^1.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^8.5 || ^9", + "ramsey/composer-repl": "^1.4", + "slevomat/coding-standard": "^8.4", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.9" + }, + "suggest": { + "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", + "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", + "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", + "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", + "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Ramsey\\Uuid\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", + "keywords": [ + "guid", + "identifier", + "uuid" + ], + "support": { + "issues": "https://github.com/ramsey/uuid/issues", + "source": "https://github.com/ramsey/uuid/tree/4.7.6" + }, + "funding": [ + { + "url": "https://github.com/ramsey", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid", + "type": "tidelift" + } + ], + "time": "2024-04-27T21:32:50+00:00" + }, + { + "name": "s1lentium/iptools", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/S1lentium/IPTools.git", + "reference": "88be1aaaab3c50fc131ebe778e246215ff006d8e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/S1lentium/IPTools/zipball/88be1aaaab3c50fc131ebe778e246215ff006d8e", + "reference": "88be1aaaab3c50fc131ebe778e246215ff006d8e", + "shasum": "" + }, + "require": { + "ext-bcmath": "*", + "php": "^8.0" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.5", + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "IPTools\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Safarov Alisher", + "email": "alisher.safarov@outlook.com", + "homepage": "https://github.com/S1lentium" + } + ], + "description": "PHP Library for manipulating network addresses (IPv4 and IPv6)", + "keywords": [ + "IP", + "IP-Tools", + "cidr", + "ipv4", + "ipv6", + "network", + "subnet" + ], + "support": { + "issues": "https://github.com/S1lentium/IPTools/issues", + "source": "https://github.com/S1lentium/IPTools/tree/v1.2.0" + }, + "time": "2022-08-17T14:28:59+00:00" + }, + { + "name": "spatie/fractalistic", + "version": "2.9.5", + "source": { + "type": "git", + "url": "https://github.com/spatie/fractalistic.git", + "reference": "6f12686a03d035f4558d166989c62aa93bde2151" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/fractalistic/zipball/6f12686a03d035f4558d166989c62aa93bde2151", + "reference": "6f12686a03d035f4558d166989c62aa93bde2151", + "shasum": "" + }, + "require": { + "league/fractal": "^0.20.1", + "php": "^7.4|^8.0" + }, + "require-dev": { + "illuminate/pagination": "~5.3.0|~5.4.0", + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\Fractalistic\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "A developer friendly wrapper around Fractal", + "homepage": "https://github.com/spatie/fractalistic", + "keywords": [ + "api", + "fractal", + "fractalistic", + "spatie", + "transform" + ], + "support": { + "issues": "https://github.com/spatie/fractalistic/issues", + "source": "https://github.com/spatie/fractalistic/tree/2.9.5" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2022-04-21T12:26:22+00:00" + }, + { + "name": "spatie/laravel-fractal", + "version": "6.2.1", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-fractal.git", + "reference": "0a30630d2ab49590af118172c03c99c0d838dab1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-fractal/zipball/0a30630d2ab49590af118172c03c99c0d838dab1", + "reference": "0a30630d2ab49590af118172c03c99c0d838dab1", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^8.0|^9.0|^10.0|^11.0", + "illuminate/support": "^8.0|^9.0|^10.0|^11.0", + "league/fractal": "^0.20.1|^0.20", + "nesbot/carbon": "^2.63|^3.0", + "php": "^8.0", + "spatie/fractalistic": "^2.9.5|^2.9", + "spatie/laravel-package-tools": "^1.11" + }, + "require-dev": { + "ext-json": "*", + "orchestra/testbench": "^7.0|^8.0|^9.0", + "pestphp/pest": "^1.22|^2.34" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\Fractal\\FractalServiceProvider" + ], + "aliases": { + "Fractal": "Spatie\\Fractal\\Facades\\Fractal" + } + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Spatie\\Fractal\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "An easy to use Fractal integration for Laravel applications", + "homepage": "https://github.com/spatie/laravel-fractal", + "keywords": [ + "api", + "fractal", + "laravel", + "laravel-fractal", + "lumen", + "spatie", + "transform" + ], + "support": { + "source": "https://github.com/spatie/laravel-fractal/tree/6.2.1" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + } + ], + "time": "2024-06-04T09:33:08+00:00" + }, + { + "name": "spatie/laravel-package-tools", + "version": "1.16.5", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-package-tools.git", + "reference": "c7413972cf22ffdff97b68499c22baa04eddb6a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/c7413972cf22ffdff97b68499c22baa04eddb6a2", + "reference": "c7413972cf22ffdff97b68499c22baa04eddb6a2", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^9.28|^10.0|^11.0", + "php": "^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.5", + "orchestra/testbench": "^7.7|^8.0", + "pestphp/pest": "^1.22", + "phpunit/phpunit": "^9.5.24", + "spatie/pest-plugin-test-time": "^1.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\LaravelPackageTools\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "role": "Developer" + } + ], + "description": "Tools for creating Laravel packages", + "homepage": "https://github.com/spatie/laravel-package-tools", + "keywords": [ + "laravel-package-tools", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/laravel-package-tools/issues", + "source": "https://github.com/spatie/laravel-package-tools/tree/1.16.5" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2024-08-27T18:56:10+00:00" + }, + { + "name": "spatie/laravel-query-builder", + "version": "5.8.1", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-query-builder.git", + "reference": "caa8467fa9e127ba7ea9e0aac93c71324365bae2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-query-builder/zipball/caa8467fa9e127ba7ea9e0aac93c71324365bae2", + "reference": "caa8467fa9e127ba7ea9e0aac93c71324365bae2", + "shasum": "" + }, + "require": { + "illuminate/database": "^10.0|^11.0", + "illuminate/http": "^10.0|^11.0", + "illuminate/support": "^10.0|^11.0", + "php": "^8.2", + "spatie/laravel-package-tools": "^1.11" + }, + "require-dev": { + "ext-json": "*", + "mockery/mockery": "^1.4", + "orchestra/testbench": "^7.0|^8.0", + "pestphp/pest": "^2.0", + "spatie/invade": "^2.0", + "spatie/laravel-ray": "^1.28" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\QueryBuilder\\QueryBuilderServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Spatie\\QueryBuilder\\": "src", + "Spatie\\QueryBuilder\\Database\\Factories\\": "database/factories" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alex Vanderbist", + "email": "alex@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "Easily build Eloquent queries from API requests", + "homepage": "https://github.com/spatie/laravel-query-builder", + "keywords": [ + "laravel-query-builder", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/laravel-query-builder/issues", + "source": "https://github.com/spatie/laravel-query-builder" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + } + ], + "time": "2024-05-10T08:19:35+00:00" + }, + { + "name": "staudenmeir/belongs-to-through", + "version": "v2.15.1", + "source": { + "type": "git", + "url": "https://github.com/staudenmeir/belongs-to-through.git", + "reference": "002b2eab60c03a41c0be709710300d22776b07a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staudenmeir/belongs-to-through/zipball/002b2eab60c03a41c0be709710300d22776b07a5", + "reference": "002b2eab60c03a41c0be709710300d22776b07a5", + "shasum": "" + }, + "require": { + "illuminate/database": "^10.0", + "php": "^8.1" + }, + "require-dev": { + "barryvdh/laravel-ide-helper": "^2.13", + "orchestra/testbench": "^8.17", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.1" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Staudenmeir\\BelongsToThrough\\IdeHelperServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Znck\\Eloquent\\": "src/", + "Staudenmeir\\BelongsToThrough\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Rahul Kadyan", + "email": "hi@znck.me" + }, + { + "name": "Jonas Staudenmeir", + "email": "mail@jonas-staudenmeir.de" + } + ], + "description": "Laravel Eloquent BelongsToThrough relationships", + "support": { + "issues": "https://github.com/staudenmeir/belongs-to-through/issues", + "source": "https://github.com/staudenmeir/belongs-to-through/tree/v2.15.1" + }, + "funding": [ + { + "url": "https://paypal.me/JonasStaudenmeir", + "type": "custom" + } + ], + "time": "2023-12-19T11:58:06+00:00" + }, + { + "name": "symfony/console", + "version": "v6.4.15", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "f1fc6f47283e27336e7cebb9e8946c8de7bff9bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/f1fc6f47283e27336e7cebb9e8946c8de7bff9bd", + "reference": "f1fc6f47283e27336e7cebb9e8946c8de7bff9bd", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^5.4|^6.0|^7.0" + }, + "conflict": { + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v6.4.15" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-11-06T14:19:14+00:00" + }, + { + "name": "symfony/css-selector", + "version": "v7.1.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/css-selector.git", + "reference": "4aa4f6b3d6749c14d3aa815eef8226632e7bbc66" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/4aa4f6b3d6749c14d3aa815eef8226632e7bbc66", + "reference": "4aa4f6b3d6749c14d3aa815eef8226632e7bbc66", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Converts CSS selectors to XPath expressions", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/css-selector/tree/v7.1.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:20:29+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:32:20+00:00" + }, + { + "name": "symfony/error-handler", + "version": "v6.4.14", + "source": { + "type": "git", + "url": "https://github.com/symfony/error-handler.git", + "reference": "9e024324511eeb00983ee76b9aedc3e6ecd993d9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/9e024324511eeb00983ee76b9aedc3e6ecd993d9", + "reference": "9e024324511eeb00983ee76b9aedc3e6ecd993d9", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^1|^2|^3", + "symfony/var-dumper": "^5.4|^6.0|^7.0" + }, + "conflict": { + "symfony/deprecation-contracts": "<2.5", + "symfony/http-kernel": "<6.4" + }, + "require-dev": { + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/serializer": "^5.4|^6.0|^7.0" + }, + "bin": [ + "Resources/bin/patch-type-declarations" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\ErrorHandler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to manage errors and ease debugging PHP code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/error-handler/tree/v6.4.14" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-11-05T15:34:40+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v7.1.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "87254c78dd50721cfd015b62277a8281c5589702" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/87254c78dd50721cfd015b62277a8281c5589702", + "reference": "87254c78dd50721cfd015b62277a8281c5589702", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/event-dispatcher-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/service-contracts": "<2.5" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/error-handler": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v7.1.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:20:29+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/8f93aec25d41b72493c6ddff14e916177c9efc50", + "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/event-dispatcher": "^1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:32:20+00:00" + }, + { + "name": "symfony/finder", + "version": "v6.4.13", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "daea9eca0b08d0ed1dc9ab702a46128fd1be4958" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/daea9eca0b08d0ed1dc9ab702a46128fd1be4958", + "reference": "daea9eca0b08d0ed1dc9ab702a46128fd1be4958", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "symfony/filesystem": "^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v6.4.13" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-10-01T08:30:56+00:00" + }, + { + "name": "symfony/http-client", + "version": "v6.4.15", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client.git", + "reference": "cb4073c905cd12b8496d24ac428a9228c1750670" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client/zipball/cb4073c905cd12b8496d24ac428a9228c1750670", + "reference": "cb4073c905cd12b8496d24ac428a9228c1750670", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-client-contracts": "^3.4.1", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "php-http/discovery": "<1.15", + "symfony/http-foundation": "<6.3" + }, + "provide": { + "php-http/async-client-implementation": "*", + "php-http/client-implementation": "*", + "psr/http-client-implementation": "1.0", + "symfony/http-client-implementation": "3.0" + }, + "require-dev": { + "amphp/amp": "^2.5", + "amphp/http-client": "^4.2.1", + "amphp/http-tunnel": "^1.0", + "amphp/socket": "^1.1", + "guzzlehttp/promises": "^1.4|^2.0", + "nyholm/psr7": "^1.0", + "php-http/httplug": "^1.0|^2.0", + "psr/http-client": "^1.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", + "homepage": "https://symfony.com", + "keywords": [ + "http" + ], + "support": { + "source": "https://github.com/symfony/http-client/tree/v6.4.15" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-11-13T13:40:18+00:00" + }, + { + "name": "symfony/http-client-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client-contracts.git", + "reference": "20414d96f391677bf80078aa55baece78b82647d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/20414d96f391677bf80078aa55baece78b82647d", + "reference": "20414d96f391677bf80078aa55baece78b82647d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to HTTP clients", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/http-client-contracts/tree/v3.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:32:20+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v6.4.15", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "9b3165eb2f04aeaa1a5a2cfef73e63fe3b22dff6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/9b3165eb2f04aeaa1a5a2cfef73e63fe3b22dff6", + "reference": "9b3165eb2f04aeaa1a5a2cfef73e63fe3b22dff6", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.1", + "symfony/polyfill-php83": "^1.27" + }, + "conflict": { + "symfony/cache": "<6.4.12|>=7.0,<7.1.5" + }, + "require-dev": { + "doctrine/dbal": "^2.13.1|^3|^4", + "predis/predis": "^1.1|^2.0", + "symfony/cache": "^6.4.12|^7.1.5", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4.12|^6.0.12|^6.1.4|^7.0", + "symfony/mime": "^5.4|^6.0|^7.0", + "symfony/rate-limiter": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Defines an object-oriented layer for the HTTP specification", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-foundation/tree/v6.4.15" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-11-08T16:09:24+00:00" + }, + { + "name": "symfony/http-kernel", + "version": "v6.4.15", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-kernel.git", + "reference": "b002a5b3947653c5aee3adac2a024ea615fd3ff5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/b002a5b3947653c5aee3adac2a024ea615fd3ff5", + "reference": "b002a5b3947653c5aee3adac2a024ea615fd3ff5", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/error-handler": "^6.4|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/browser-kit": "<5.4", + "symfony/cache": "<5.4", + "symfony/config": "<6.1", + "symfony/console": "<5.4", + "symfony/dependency-injection": "<6.4", + "symfony/doctrine-bridge": "<5.4", + "symfony/form": "<5.4", + "symfony/http-client": "<5.4", + "symfony/http-client-contracts": "<2.5", + "symfony/mailer": "<5.4", + "symfony/messenger": "<5.4", + "symfony/translation": "<5.4", + "symfony/translation-contracts": "<2.5", + "symfony/twig-bridge": "<5.4", + "symfony/validator": "<6.4", + "symfony/var-dumper": "<6.3", + "twig/twig": "<2.13" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/cache": "^1.0|^2.0|^3.0", + "symfony/browser-kit": "^5.4|^6.0|^7.0", + "symfony/clock": "^6.2|^7.0", + "symfony/config": "^6.1|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/css-selector": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/dom-crawler": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", + "symfony/http-client-contracts": "^2.5|^3", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/property-access": "^5.4.5|^6.0.5|^7.0", + "symfony/routing": "^5.4|^6.0|^7.0", + "symfony/serializer": "^6.4.4|^7.0.4", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/translation": "^5.4|^6.0|^7.0", + "symfony/translation-contracts": "^2.5|^3", + "symfony/uid": "^5.4|^6.0|^7.0", + "symfony/validator": "^6.4|^7.0", + "symfony/var-dumper": "^5.4|^6.4|^7.0", + "symfony/var-exporter": "^6.2|^7.0", + "twig/twig": "^2.13|^3.0.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpKernel\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a structured process for converting a Request into a Response", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-kernel/tree/v6.4.15" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-11-13T13:57:37+00:00" + }, + { + "name": "symfony/mailer", + "version": "v6.4.13", + "source": { + "type": "git", + "url": "https://github.com/symfony/mailer.git", + "reference": "c2f7e0d8d7ac8fe25faccf5d8cac462805db2663" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mailer/zipball/c2f7e0d8d7ac8fe25faccf5d8cac462805db2663", + "reference": "c2f7e0d8d7ac8fe25faccf5d8cac462805db2663", + "shasum": "" + }, + "require": { + "egulias/email-validator": "^2.1.10|^3|^4", + "php": ">=8.1", + "psr/event-dispatcher": "^1", + "psr/log": "^1|^2|^3", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/mime": "^6.2|^7.0", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<5.4", + "symfony/messenger": "<6.2", + "symfony/mime": "<6.2", + "symfony/twig-bridge": "<6.2.1" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/messenger": "^6.2|^7.0", + "symfony/twig-bridge": "^6.2|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mailer\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Helps sending emails", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/mailer/tree/v6.4.13" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:18:03+00:00" + }, + { + "name": "symfony/mailgun-mailer", + "version": "v6.4.13", + "source": { + "type": "git", + "url": "https://github.com/symfony/mailgun-mailer.git", + "reference": "ad4e79798a5eb80af99004a4871b4fe5effe33a3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mailgun-mailer/zipball/ad4e79798a5eb80af99004a4871b4fe5effe33a3", + "reference": "ad4e79798a5eb80af99004a4871b4fe5effe33a3", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/mailer": "^5.4.21|^6.2.7|^7.0" + }, + "conflict": { + "symfony/http-foundation": "<6.2" + }, + "require-dev": { + "symfony/http-client": "^6.3|^7.0", + "symfony/webhook": "^6.3|^7.0" + }, + "type": "symfony-mailer-bridge", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mailer\\Bridge\\Mailgun\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Mailgun Mailer Bridge", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/mailgun-mailer/tree/v6.4.13" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:18:03+00:00" + }, + { + "name": "symfony/mime", + "version": "v6.4.13", + "source": { + "type": "git", + "url": "https://github.com/symfony/mime.git", + "reference": "1de1cf14d99b12c7ebbb850491ec6ae3ed468855" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mime/zipball/1de1cf14d99b12c7ebbb850491ec6ae3ed468855", + "reference": "1de1cf14d99b12c7ebbb850491ec6ae3ed468855", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "egulias/email-validator": "~3.0.0", + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/mailer": "<5.4", + "symfony/serializer": "<6.4.3|>7.0,<7.0.3" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10|^3.1|^4", + "league/html-to-markdown": "^5.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.4|^7.0", + "symfony/property-access": "^5.4|^6.0|^7.0", + "symfony/property-info": "^5.4|^6.0|^7.0", + "symfony/serializer": "^6.4.3|^7.0.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mime\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows manipulating MIME messages", + "homepage": "https://symfony.com", + "keywords": [ + "mime", + "mime-type" + ], + "support": { + "source": "https://github.com/symfony/mime/tree/v6.4.13" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-10-25T15:07:50+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/c36586dcf89a12315939e00ec9b4474adcb1d773", + "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "symfony/polyfill-intl-normalizer": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-php83", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php83.git", + "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/2fb86d65e2d424369ad2905e83b236a8805ba491", + "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php83\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php83/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-uuid", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-uuid.git", + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-uuid": "*" + }, + "suggest": { + "ext-uuid": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Uuid\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for uuid functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/postmark-mailer", + "version": "v6.4.13", + "source": { + "type": "git", + "url": "https://github.com/symfony/postmark-mailer.git", + "reference": "8b364bcd051a201730d940d592183e79b5af5e6c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/postmark-mailer/zipball/8b364bcd051a201730d940d592183e79b5af5e6c", + "reference": "8b364bcd051a201730d940d592183e79b5af5e6c", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/event-dispatcher": "^1", + "symfony/mailer": "^5.4.21|^6.2.7|^7.0" + }, + "conflict": { + "symfony/http-foundation": "<6.2" + }, + "require-dev": { + "symfony/http-client": "^6.3|^7.0", + "symfony/webhook": "^6.3|^7.0" + }, + "type": "symfony-mailer-bridge", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mailer\\Bridge\\Postmark\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Postmark Mailer Bridge", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/postmark-mailer/tree/v6.4.13" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:18:03+00:00" + }, + { + "name": "symfony/process", + "version": "v6.4.15", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "3cb242f059c14ae08591c5c4087d1fe443564392" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/3cb242f059c14ae08591c5c4087d1fe443564392", + "reference": "3cb242f059c14ae08591c5c4087d1fe443564392", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v6.4.15" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-11-06T14:19:14+00:00" + }, + { + "name": "symfony/routing", + "version": "v6.4.13", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "640a74250d13f9c30d5ca045b6aaaabcc8215278" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/640a74250d13f9c30d5ca045b6aaaabcc8215278", + "reference": "640a74250d13f9c30d5ca045b6aaaabcc8215278", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "doctrine/annotations": "<1.12", + "symfony/config": "<6.2", + "symfony/dependency-injection": "<5.4", + "symfony/yaml": "<5.4" + }, + "require-dev": { + "doctrine/annotations": "^1.12|^2", + "psr/log": "^1|^2|^3", + "symfony/config": "^6.2|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/yaml": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Routing\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Maps an HTTP request to a set of configuration variables", + "homepage": "https://symfony.com", + "keywords": [ + "router", + "routing", + "uri", + "url" + ], + "support": { + "source": "https://github.com/symfony/routing/tree/v6.4.13" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-10-01T08:30:56+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:32:20+00:00" + }, + { + "name": "symfony/string", + "version": "v7.1.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "591ebd41565f356fcd8b090fe64dbb5878f50281" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/591ebd41565f356fcd8b090fe64dbb5878f50281", + "reference": "591ebd41565f356fcd8b090fe64dbb5878f50281", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/emoji": "^7.1", + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v7.1.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-11-13T13:31:21+00:00" + }, + { + "name": "symfony/translation", + "version": "v6.4.13", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "bee9bfabfa8b4045a66bf82520e492cddbaffa66" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/bee9bfabfa8b4045a66bf82520e492cddbaffa66", + "reference": "bee9bfabfa8b4045a66bf82520e492cddbaffa66", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation-contracts": "^2.5|^3.0" + }, + "conflict": { + "symfony/config": "<5.4", + "symfony/console": "<5.4", + "symfony/dependency-injection": "<5.4", + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<5.4", + "symfony/service-contracts": "<2.5", + "symfony/twig-bundle": "<5.4", + "symfony/yaml": "<5.4" + }, + "provide": { + "symfony/translation-implementation": "2.3|3.0" + }, + "require-dev": { + "nikic/php-parser": "^4.18|^5.0", + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", + "symfony/http-client-contracts": "^2.5|^3.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/intl": "^5.4|^6.0|^7.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/routing": "^5.4|^6.0|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to internationalize your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/translation/tree/v6.4.13" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-27T18:14:25+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "b9d2189887bb6b2e0367a9fc7136c5239ab9b05a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/b9d2189887bb6b2e0367a9fc7136c5239ab9b05a", + "reference": "b9d2189887bb6b2e0367a9fc7136c5239ab9b05a", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to translation", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v3.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:32:20+00:00" + }, + { + "name": "symfony/uid", + "version": "v6.4.13", + "source": { + "type": "git", + "url": "https://github.com/symfony/uid.git", + "reference": "18eb207f0436a993fffbdd811b5b8fa35fa5e007" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/uid/zipball/18eb207f0436a993fffbdd811b5b8fa35fa5e007", + "reference": "18eb207f0436a993fffbdd811b5b8fa35fa5e007", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-uuid": "^1.15" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Uid\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to generate and represent UIDs", + "homepage": "https://symfony.com", + "keywords": [ + "UID", + "ulid", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/uid/tree/v6.4.13" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:18:03+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v6.4.15", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "38254d5a5ac2e61f2b52f9caf54e7aa3c9d36b80" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/38254d5a5ac2e61f2b52f9caf54e7aa3c9d36b80", + "reference": "38254d5a5ac2e61f2b52f9caf54e7aa3c9d36b80", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/console": "<5.4" + }, + "require-dev": { + "ext-iconv": "*", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/error-handler": "^6.3|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/uid": "^5.4|^6.0|^7.0", + "twig/twig": "^2.13|^3.0.4" + }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides mechanisms for walking through any arbitrary PHP variable", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v6.4.15" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-11-08T15:28:48+00:00" + }, + { + "name": "symfony/yaml", + "version": "v6.4.13", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "e99b4e94d124b29ee4cf3140e1b537d2dad8cec9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/e99b4e94d124b29ee4cf3140e1b537d2dad8cec9", + "reference": "e99b4e94d124b29ee4cf3140e1b537d2dad8cec9", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<5.4" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0|^7.0" + }, + "bin": [ + "Resources/bin/yaml-lint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Loads and dumps YAML files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v6.4.13" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:18:03+00:00" + }, + { + "name": "tijsverkoyen/css-to-inline-styles", + "version": "v2.2.7", + "source": { + "type": "git", + "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", + "reference": "83ee6f38df0a63106a9e4536e3060458b74ccedb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/83ee6f38df0a63106a9e4536e3060458b74ccedb", + "reference": "83ee6f38df0a63106a9e4536e3060458b74ccedb", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "php": "^5.5 || ^7.0 || ^8.0", + "symfony/css-selector": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^7.5 || ^8.5.21 || ^9.5.10" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TijsVerkoyen\\CssToInlineStyles\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Tijs Verkoyen", + "email": "css_to_inline_styles@verkoyen.eu", + "role": "Developer" + } + ], + "description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.", + "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", + "support": { + "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues", + "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.2.7" + }, + "time": "2023-12-08T13:03:43+00:00" + }, + { + "name": "vlucas/phpdotenv", + "version": "v5.6.1", + "source": { + "type": "git", + "url": "https://github.com/vlucas/phpdotenv.git", + "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/a59a13791077fe3d44f90e7133eb68e7d22eaff2", + "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "graham-campbell/result-type": "^1.1.3", + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.3", + "symfony/polyfill-ctype": "^1.24", + "symfony/polyfill-mbstring": "^1.24", + "symfony/polyfill-php80": "^1.24" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-filter": "*", + "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" + }, + "suggest": { + "ext-filter": "Required to use the boolean validator." + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, + "branch-alias": { + "dev-master": "5.6-dev" + } + }, + "autoload": { + "psr-4": { + "Dotenv\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Vance Lucas", + "email": "vance@vancelucas.com", + "homepage": "https://github.com/vlucas" + } + ], + "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "support": { + "issues": "https://github.com/vlucas/phpdotenv/issues", + "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", + "type": "tidelift" + } + ], + "time": "2024-07-20T21:52:34+00:00" + }, + { + "name": "voku/portable-ascii", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/voku/portable-ascii.git", + "reference": "b56450eed252f6801410d810c8e1727224ae0743" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b56450eed252f6801410d810c8e1727224ae0743", + "reference": "b56450eed252f6801410d810c8e1727224ae0743", + "shasum": "" + }, + "require": { + "php": ">=7.0.0" + }, + "require-dev": { + "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" + }, + "suggest": { + "ext-intl": "Use Intl for transliterator_transliterate() support" + }, + "type": "library", + "autoload": { + "psr-4": { + "voku\\": "src/voku/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Lars Moelleken", + "homepage": "http://www.moelleken.org/" + } + ], + "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", + "homepage": "https://github.com/voku/portable-ascii", + "keywords": [ + "ascii", + "clean", + "php" + ], + "support": { + "issues": "https://github.com/voku/portable-ascii/issues", + "source": "https://github.com/voku/portable-ascii/tree/2.0.1" + }, + "funding": [ + { + "url": "https://www.paypal.me/moelleken", + "type": "custom" + }, + { + "url": "https://github.com/voku", + "type": "github" + }, + { + "url": "https://opencollective.com/portable-ascii", + "type": "open_collective" + }, + { + "url": "https://www.patreon.com/voku", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii", + "type": "tidelift" + } + ], + "time": "2022-03-08T17:03:00+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" + } + ], + "packages-dev": [ + { + "name": "barryvdh/laravel-ide-helper", + "version": "v2.15.1", + "source": { + "type": "git", + "url": "https://github.com/barryvdh/laravel-ide-helper.git", + "reference": "77831852bb7bc54f287246d32eb91274eaf87f8b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/77831852bb7bc54f287246d32eb91274eaf87f8b", + "reference": "77831852bb7bc54f287246d32eb91274eaf87f8b", + "shasum": "" + }, + "require": { + "barryvdh/reflection-docblock": "^2.0.6", + "composer/class-map-generator": "^1.0", + "doctrine/dbal": "^2.6 || ^3.1.4", + "ext-json": "*", + "illuminate/console": "^9 || ^10", + "illuminate/filesystem": "^9 || ^10", + "illuminate/support": "^9 || ^10", + "nikic/php-parser": "^4.18 || ^5", + "php": "^8.0", + "phpdocumentor/type-resolver": "^1.1.0" + }, + "require-dev": { + "ext-pdo_sqlite": "*", + "friendsofphp/php-cs-fixer": "^3", + "illuminate/config": "^9 || ^10", + "illuminate/view": "^9 || ^10", + "mockery/mockery": "^1.4", + "orchestra/testbench": "^7 || ^8", + "phpunit/phpunit": "^9", + "spatie/phpunit-snapshot-assertions": "^4", + "vimeo/psalm": "^5.4" + }, + "suggest": { + "illuminate/events": "Required for automatic helper generation (^6|^7|^8|^9|^10)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.15-dev" + }, + "laravel": { + "providers": [ + "Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Barryvdh\\LaravelIdeHelper\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Barry vd. Heuvel", + "email": "barryvdh@gmail.com" + } + ], + "description": "Laravel IDE Helper, generates correct PHPDocs for all Facade classes, to improve auto-completion.", + "keywords": [ + "autocomplete", + "codeintel", + "helper", + "ide", + "laravel", + "netbeans", + "phpdoc", + "phpstorm", + "sublime" + ], + "support": { + "issues": "https://github.com/barryvdh/laravel-ide-helper/issues", + "source": "https://github.com/barryvdh/laravel-ide-helper/tree/v2.15.1" + }, + "funding": [ + { + "url": "https://fruitcake.nl", + "type": "custom" + }, + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2024-02-15T14:23:20+00:00" + }, + { + "name": "barryvdh/reflection-docblock", + "version": "v2.1.3", + "source": { + "type": "git", + "url": "https://github.com/barryvdh/ReflectionDocBlock.git", + "reference": "c6fad15f7c878be21650c51e1f841bca7e49752e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/barryvdh/ReflectionDocBlock/zipball/c6fad15f7c878be21650c51e1f841bca7e49752e", + "reference": "c6fad15f7c878be21650c51e1f841bca7e49752e", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.14|^9" + }, + "suggest": { + "dflydev/markdown": "~1.0", + "erusev/parsedown": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Barryvdh": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "mike.vanriel@naenius.com" + } + ], + "support": { + "source": "https://github.com/barryvdh/ReflectionDocBlock/tree/v2.1.3" + }, + "time": "2024-10-23T11:41:03+00:00" + }, + { + "name": "clue/ndjson-react", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/clue/reactphp-ndjson.git", + "reference": "392dc165fce93b5bb5c637b67e59619223c931b0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/clue/reactphp-ndjson/zipball/392dc165fce93b5bb5c637b67e59619223c931b0", + "reference": "392dc165fce93b5bb5c637b67e59619223c931b0", + "shasum": "" + }, + "require": { + "php": ">=5.3", + "react/stream": "^1.2" + }, + "require-dev": { + "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35", + "react/event-loop": "^1.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Clue\\React\\NDJson\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering" + } + ], + "description": "Streaming newline-delimited JSON (NDJSON) parser and encoder for ReactPHP.", + "homepage": "https://github.com/clue/reactphp-ndjson", + "keywords": [ + "NDJSON", + "json", + "jsonlines", + "newline", + "reactphp", + "streaming" + ], + "support": { + "issues": "https://github.com/clue/reactphp-ndjson/issues", + "source": "https://github.com/clue/reactphp-ndjson/tree/v1.3.0" + }, + "funding": [ + { + "url": "https://clue.engineering/support", + "type": "custom" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2022-12-23T10:58:28+00:00" + }, + { + "name": "composer/class-map-generator", + "version": "1.4.0", + "source": { + "type": "git", + "url": "https://github.com/composer/class-map-generator.git", + "reference": "98bbf6780e56e0fd2404fe4b82eb665a0f93b783" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/class-map-generator/zipball/98bbf6780e56e0fd2404fe4b82eb665a0f93b783", + "reference": "98bbf6780e56e0fd2404fe4b82eb665a0f93b783", + "shasum": "" + }, + "require": { + "composer/pcre": "^2.1 || ^3.1", + "php": "^7.2 || ^8.0", + "symfony/finder": "^4.4 || ^5.3 || ^6 || ^7" + }, + "require-dev": { + "phpstan/phpstan": "^1.6", + "phpstan/phpstan-deprecation-rules": "^1", + "phpstan/phpstan-phpunit": "^1", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8", + "symfony/filesystem": "^5.4 || ^6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\ClassMapGenerator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Utilities to scan PHP code and generate class maps.", + "keywords": [ + "classmap" + ], + "support": { + "issues": "https://github.com/composer/class-map-generator/issues", + "source": "https://github.com/composer/class-map-generator/tree/1.4.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-10-03T18:14:00+00:00" + }, + { + "name": "composer/pcre", + "version": "3.3.2", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<1.11.10" + }, + "require-dev": { + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-strict-rules": "^1 || ^2", + "phpunit/phpunit": "^8 || ^9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.3.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-11-12T16:29:46+00:00" + }, + { + "name": "composer/semver", + "version": "3.4.3", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.3" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-09-19T14:15:21+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "3.0.5", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", + "shasum": "" + }, + "require": { + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-05-06T16:37:16+00:00" + }, + { + "name": "evenement/evenement", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/igorw/evenement.git", + "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/igorw/evenement/zipball/0a16b0d71ab13284339abb99d9d2bd813640efbc", + "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc", + "shasum": "" + }, + "require": { + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "^9 || ^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Evenement\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + } + ], + "description": "Événement is a very simple event dispatching library for PHP", + "keywords": [ + "event-dispatcher", + "event-emitter" + ], + "support": { + "issues": "https://github.com/igorw/evenement/issues", + "source": "https://github.com/igorw/evenement/tree/v3.0.2" + }, + "time": "2023-08-08T05:53:35+00:00" + }, + { + "name": "fakerphp/faker", + "version": "v1.23.1", + "source": { + "type": "git", + "url": "https://github.com/FakerPHP/Faker.git", + "reference": "bfb4fe148adbf78eff521199619b93a52ae3554b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/bfb4fe148adbf78eff521199619b93a52ae3554b", + "reference": "bfb4fe148adbf78eff521199619b93a52ae3554b", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "psr/container": "^1.0 || ^2.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "conflict": { + "fzaninotto/faker": "*" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "doctrine/persistence": "^1.3 || ^2.0", + "ext-intl": "*", + "phpunit/phpunit": "^9.5.26", + "symfony/phpunit-bridge": "^5.4.16" + }, + "suggest": { + "doctrine/orm": "Required to use Faker\\ORM\\Doctrine", + "ext-curl": "Required by Faker\\Provider\\Image to download images.", + "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.", + "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.", + "ext-mbstring": "Required for multibyte Unicode string functionality." + }, + "type": "library", + "autoload": { + "psr-4": { + "Faker\\": "src/Faker/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "François Zaninotto" + } + ], + "description": "Faker is a PHP library that generates fake data for you.", + "keywords": [ + "data", + "faker", + "fixtures" + ], + "support": { + "issues": "https://github.com/FakerPHP/Faker/issues", + "source": "https://github.com/FakerPHP/Faker/tree/v1.23.1" + }, + "time": "2024-01-02T13:46:09+00:00" + }, + { + "name": "fidry/cpu-core-counter", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "8520451a140d3f46ac33042715115e290cf5785f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f", + "reference": "8520451a140d3f46ac33042715115e290cf5785f", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "fidry/makefile": "^0.2.0", + "fidry/php-cs-fixer-config": "^1.1.2", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^1.9.2", + "phpstan/phpstan-deprecation-rules": "^1.0.0", + "phpstan/phpstan-phpunit": "^1.2.2", + "phpstan/phpstan-strict-rules": "^1.4.4", + "phpunit/phpunit": "^8.5.31 || ^9.5.26", + "webmozarts/strict-phpunit": "^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Fidry\\CpuCoreCounter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Tiny utility to get the number of CPU cores.", + "keywords": [ + "CPU", + "core" + ], + "support": { + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.2.0" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2024-08-06T10:04:20+00:00" + }, + { + "name": "filp/whoops", + "version": "2.16.0", + "source": { + "type": "git", + "url": "https://github.com/filp/whoops.git", + "reference": "befcdc0e5dce67252aa6322d82424be928214fa2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/filp/whoops/zipball/befcdc0e5dce67252aa6322d82424be928214fa2", + "reference": "befcdc0e5dce67252aa6322d82424be928214fa2", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3", + "symfony/var-dumper": "^4.0 || ^5.0" + }, + "suggest": { + "symfony/var-dumper": "Pretty print complex values better with var-dumper available", + "whoops/soap": "Formats errors as SOAP responses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Whoops\\": "src/Whoops/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Filipe Dobreira", + "homepage": "https://github.com/filp", + "role": "Developer" + } + ], + "description": "php error handling for cool kids", + "homepage": "https://filp.github.io/whoops/", + "keywords": [ + "error", + "exception", + "handling", + "library", + "throwable", + "whoops" + ], + "support": { + "issues": "https://github.com/filp/whoops/issues", + "source": "https://github.com/filp/whoops/tree/2.16.0" + }, + "funding": [ + { + "url": "https://github.com/denis-sokolov", + "type": "github" + } + ], + "time": "2024-09-25T12:00:00+00:00" + }, + { + "name": "friendsofphp/php-cs-fixer", + "version": "v3.61.1", + "source": { + "type": "git", + "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", + "reference": "94a87189f55814e6cabca2d9a33b06de384a2ab8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/94a87189f55814e6cabca2d9a33b06de384a2ab8", + "reference": "94a87189f55814e6cabca2d9a33b06de384a2ab8", + "shasum": "" + }, + "require": { + "clue/ndjson-react": "^1.0", + "composer/semver": "^3.4", + "composer/xdebug-handler": "^3.0.3", + "ext-filter": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "fidry/cpu-core-counter": "^1.0", + "php": "^7.4 || ^8.0", + "react/child-process": "^0.6.5", + "react/event-loop": "^1.0", + "react/promise": "^2.0 || ^3.0", + "react/socket": "^1.0", + "react/stream": "^1.0", + "sebastian/diff": "^4.0 || ^5.0 || ^6.0", + "symfony/console": "^5.4 || ^6.0 || ^7.0", + "symfony/event-dispatcher": "^5.4 || ^6.0 || ^7.0", + "symfony/filesystem": "^5.4 || ^6.0 || ^7.0", + "symfony/finder": "^5.4 || ^6.0 || ^7.0", + "symfony/options-resolver": "^5.4 || ^6.0 || ^7.0", + "symfony/polyfill-mbstring": "^1.28", + "symfony/polyfill-php80": "^1.28", + "symfony/polyfill-php81": "^1.28", + "symfony/process": "^5.4 || ^6.0 || ^7.0", + "symfony/stopwatch": "^5.4 || ^6.0 || ^7.0" + }, + "require-dev": { + "facile-it/paraunit": "^1.3 || ^2.3", + "infection/infection": "^0.29.5", + "justinrainbow/json-schema": "^5.2", + "keradus/cli-executor": "^2.1", + "mikey179/vfsstream": "^1.6.11", + "php-coveralls/php-coveralls": "^2.7", + "php-cs-fixer/accessible-object": "^1.1", + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.5", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.5", + "phpunit/phpunit": "^9.6.19 || ^10.5.21 || ^11.2", + "symfony/var-dumper": "^5.4 || ^6.0 || ^7.0", + "symfony/yaml": "^5.4 || ^6.0 || ^7.0" + }, + "suggest": { + "ext-dom": "For handling output formats in XML", + "ext-mbstring": "For handling non-UTF8 characters." + }, + "bin": [ + "php-cs-fixer" + ], + "type": "application", + "autoload": { + "psr-4": { + "PhpCsFixer\\": "src/" + }, + "exclude-from-classmap": [ + "src/Fixer/Internal/*" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" + } + ], + "description": "A tool to automatically fix PHP code style", + "keywords": [ + "Static code analysis", + "fixer", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.61.1" + }, + "funding": [ + { + "url": "https://github.com/keradus", + "type": "github" + } + ], + "time": "2024-07-31T14:33:15+00:00" + }, + { + "name": "hamcrest/hamcrest-php", + "version": "v2.0.1", + "source": { + "type": "git", + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "shasum": "" + }, + "require": { + "php": "^5.3|^7.0|^8.0" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" + }, + "require-dev": { + "phpunit/php-file-iterator": "^1.4 || ^2.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "classmap": [ + "hamcrest" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "This is the PHP port of Hamcrest Matchers", + "keywords": [ + "test" + ], + "support": { + "issues": "https://github.com/hamcrest/hamcrest-php/issues", + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1" + }, + "time": "2020-07-09T08:09:16+00:00" + }, + { + "name": "itsgoingd/clockwork", + "version": "v5.2.2", + "source": { + "type": "git", + "url": "https://github.com/itsgoingd/clockwork.git", + "reference": "29bc4cedfbe742b419544c30b7b6e15cd9da08ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/itsgoingd/clockwork/zipball/29bc4cedfbe742b419544c30b7b6e15cd9da08ef", + "reference": "29bc4cedfbe742b419544c30b7b6e15cd9da08ef", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": ">=5.6" + }, + "suggest": { + "ext-pdo": "Needed in order to use a SQL database for metadata storage", + "ext-pdo_mysql": "Needed in order to use MySQL for metadata storage", + "ext-pdo_postgres": "Needed in order to use Postgres for metadata storage", + "ext-pdo_sqlite": "Needed in order to use a SQLite for metadata storage", + "ext-redis": "Needed in order to use Redis for metadata storage" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Clockwork\\Support\\Laravel\\ClockworkServiceProvider" + ], + "aliases": { + "Clockwork": "Clockwork\\Support\\Laravel\\Facade" + } + } + }, + "autoload": { + "psr-4": { + "Clockwork\\": "Clockwork/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "itsgoingd", + "email": "itsgoingd@luzer.sk", + "homepage": "https://twitter.com/itsgoingd" + } + ], + "description": "php dev tools in your browser", + "homepage": "https://underground.works/clockwork", + "keywords": [ + "Devtools", + "debugging", + "laravel", + "logging", + "lumen", + "profiling", + "slim" + ], + "support": { + "issues": "https://github.com/itsgoingd/clockwork/issues", + "source": "https://github.com/itsgoingd/clockwork/tree/v5.2.2" + }, + "funding": [ + { + "url": "https://github.com/itsgoingd", + "type": "github" + } + ], + "time": "2024-04-14T10:49:22+00:00" + }, + { + "name": "laravel/sail", + "version": "v1.31.3", + "source": { + "type": "git", + "url": "https://github.com/laravel/sail.git", + "reference": "0a7e2891a85eba2d448a9ffc6fc5ce367e924bc1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/sail/zipball/0a7e2891a85eba2d448a9ffc6fc5ce367e924bc1", + "reference": "0a7e2891a85eba2d448a9ffc6fc5ce367e924bc1", + "shasum": "" + }, + "require": { + "illuminate/console": "^9.52.16|^10.0|^11.0", + "illuminate/contracts": "^9.52.16|^10.0|^11.0", + "illuminate/support": "^9.52.16|^10.0|^11.0", + "php": "^8.0", + "symfony/console": "^6.0|^7.0", + "symfony/yaml": "^6.0|^7.0" + }, + "require-dev": { + "orchestra/testbench": "^7.0|^8.0|^9.0", + "phpstan/phpstan": "^1.10" + }, + "bin": [ + "bin/sail" + ], + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Sail\\SailServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Sail\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Docker files for running a basic Laravel application.", + "keywords": [ + "docker", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/sail/issues", + "source": "https://github.com/laravel/sail" + }, + "time": "2024-09-03T20:05:33+00:00" + }, + { + "name": "mockery/mockery", + "version": "1.6.12", + "source": { + "type": "git", + "url": "https://github.com/mockery/mockery.git", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "shasum": "" + }, + "require": { + "hamcrest/hamcrest-php": "^2.0.1", + "lib-pcre": ">=7.0", + "php": ">=7.3" + }, + "conflict": { + "phpunit/phpunit": "<8.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5 || ^9.6.17", + "symplify/easy-coding-standard": "^12.1.14" + }, + "type": "library", + "autoload": { + "files": [ + "library/helpers.php", + "library/Mockery.php" + ], + "psr-4": { + "Mockery\\": "library/Mockery" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "https://github.com/padraic", + "role": "Author" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "https://davedevelopment.co.uk", + "role": "Developer" + }, + { + "name": "Nathanael Esayeas", + "email": "nathanael.esayeas@protonmail.com", + "homepage": "https://github.com/ghostwriter", + "role": "Lead Developer" + } + ], + "description": "Mockery is a simple yet flexible PHP mock object framework", + "homepage": "https://github.com/mockery/mockery", + "keywords": [ + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "support": { + "docs": "https://docs.mockery.io/", + "issues": "https://github.com/mockery/mockery/issues", + "rss": "https://github.com/mockery/mockery/releases.atom", + "security": "https://github.com/mockery/mockery/security/advisories", + "source": "https://github.com/mockery/mockery" + }, + "time": "2024-05-16T03:13:13+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.12.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845", + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.1" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2024-11-08T17:47:46+00:00" + }, + { + "name": "nunomaduro/collision", + "version": "v7.10.0", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/collision.git", + "reference": "49ec67fa7b002712da8526678abd651c09f375b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/49ec67fa7b002712da8526678abd651c09f375b2", + "reference": "49ec67fa7b002712da8526678abd651c09f375b2", + "shasum": "" + }, + "require": { + "filp/whoops": "^2.15.3", + "nunomaduro/termwind": "^1.15.1", + "php": "^8.1.0", + "symfony/console": "^6.3.4" + }, + "conflict": { + "laravel/framework": ">=11.0.0" + }, + "require-dev": { + "brianium/paratest": "^7.3.0", + "laravel/framework": "^10.28.0", + "laravel/pint": "^1.13.3", + "laravel/sail": "^1.25.0", + "laravel/sanctum": "^3.3.1", + "laravel/tinker": "^2.8.2", + "nunomaduro/larastan": "^2.6.4", + "orchestra/testbench-core": "^8.13.0", + "pestphp/pest": "^2.23.2", + "phpunit/phpunit": "^10.4.1", + "sebastian/environment": "^6.0.1", + "spatie/laravel-ignition": "^2.3.1" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "./src/Adapters/Phpunit/Autoload.php" + ], + "psr-4": { + "NunoMaduro\\Collision\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Cli error handling for console/command-line PHP applications.", + "keywords": [ + "artisan", + "cli", + "command-line", + "console", + "error", + "handling", + "laravel", + "laravel-zero", + "php", + "symfony" + ], + "support": { + "issues": "https://github.com/nunomaduro/collision/issues", + "source": "https://github.com/nunomaduro/collision" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2023-10-11T15:45:01+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.10.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/679e3ce485b99e84c775d28e2e96fade9a7fb50a", + "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.3 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.18|^2.0" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.10.0" + }, + "time": "2024-11-09T15:12:26+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "c00d78fb6b29658347f9d37ebe104bffadf36299" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/c00d78fb6b29658347f9d37ebe104bffadf36299", + "reference": "c00d78fb6b29658347f9d37ebe104bffadf36299", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^5.3.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.0.0" + }, + "time": "2024-10-13T11:29:49+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "10.1.16", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "7e308268858ed6baedc8704a304727d20bc07c77" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77", + "reference": "7e308268858ed6baedc8704a304727d20bc07c77", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.19.1 || ^5.1.0", + "php": ">=8.1", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-text-template": "^3.0.1", + "sebastian/code-unit-reverse-lookup": "^3.0.0", + "sebastian/complexity": "^3.2.0", + "sebastian/environment": "^6.1.0", + "sebastian/lines-of-code": "^2.0.2", + "sebastian/version": "^4.0.1", + "theseer/tokenizer": "^1.2.3" + }, + "require-dev": { + "phpunit/phpunit": "^10.1" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "10.1.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "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.16" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-08-22T04:31:57+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "4.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-31T06:24:48+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:56:09+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-31T14:07:24+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:57:52+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "10.5.38", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "a86773b9e887a67bc53efa9da9ad6e3f2498c132" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a86773b9e887a67bc53efa9da9ad6e3f2498c132", + "reference": "a86773b9e887a67bc53efa9da9ad6e3f2498c132", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.12.0", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.1", + "phpunit/php-code-coverage": "^10.1.16", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-invoker": "^4.0.0", + "phpunit/php-text-template": "^3.0.1", + "phpunit/php-timer": "^6.0.0", + "sebastian/cli-parser": "^2.0.1", + "sebastian/code-unit": "^2.0.0", + "sebastian/comparator": "^5.0.3", + "sebastian/diff": "^5.1.1", + "sebastian/environment": "^6.1.0", + "sebastian/exporter": "^5.1.2", + "sebastian/global-state": "^6.0.2", + "sebastian/object-enumerator": "^5.0.0", + "sebastian/recursion-context": "^5.0.0", + "sebastian/type": "^4.0.0", + "sebastian/version": "^4.0.1" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "10.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "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.38" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2024-10-28T13:06:21+00:00" + }, + { + "name": "react/cache", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/cache.git", + "reference": "d47c472b64aa5608225f47965a484b75c7817d5b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/cache/zipball/d47c472b64aa5608225f47965a484b75c7817d5b", + "reference": "d47c472b64aa5608225f47965a484b75c7817d5b", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "react/promise": "^3.0 || ^2.0 || ^1.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Async, Promise-based cache interface for ReactPHP", + "keywords": [ + "cache", + "caching", + "promise", + "reactphp" + ], + "support": { + "issues": "https://github.com/reactphp/cache/issues", + "source": "https://github.com/reactphp/cache/tree/v1.2.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2022-11-30T15:59:55+00:00" + }, + { + "name": "react/child-process", + "version": "v0.6.5", + "source": { + "type": "git", + "url": "https://github.com/reactphp/child-process.git", + "reference": "e71eb1aa55f057c7a4a0d08d06b0b0a484bead43" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/child-process/zipball/e71eb1aa55f057c7a4a0d08d06b0b0a484bead43", + "reference": "e71eb1aa55f057c7a4a0d08d06b0b0a484bead43", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.0", + "react/event-loop": "^1.2", + "react/stream": "^1.2" + }, + "require-dev": { + "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35", + "react/socket": "^1.8", + "sebastian/environment": "^5.0 || ^3.0 || ^2.0 || ^1.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\ChildProcess\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Event-driven library for executing child processes with ReactPHP.", + "keywords": [ + "event-driven", + "process", + "reactphp" + ], + "support": { + "issues": "https://github.com/reactphp/child-process/issues", + "source": "https://github.com/reactphp/child-process/tree/v0.6.5" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2022-09-16T13:41:56+00:00" + }, + { + "name": "react/dns", + "version": "v1.13.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/dns.git", + "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/dns/zipball/eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", + "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "react/cache": "^1.0 || ^0.6 || ^0.5", + "react/event-loop": "^1.2", + "react/promise": "^3.2 || ^2.7 || ^1.2.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/async": "^4.3 || ^3 || ^2", + "react/promise-timer": "^1.11" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Dns\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Async DNS resolver for ReactPHP", + "keywords": [ + "async", + "dns", + "dns-resolver", + "reactphp" + ], + "support": { + "issues": "https://github.com/reactphp/dns/issues", + "source": "https://github.com/reactphp/dns/tree/v1.13.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2024-06-13T14:18:03+00:00" + }, + { + "name": "react/event-loop", + "version": "v1.5.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/event-loop.git", + "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", + "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" + }, + "suggest": { + "ext-pcntl": "For signal handling support when using the StreamSelectLoop" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\EventLoop\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", + "keywords": [ + "asynchronous", + "event-loop" + ], + "support": { + "issues": "https://github.com/reactphp/event-loop/issues", + "source": "https://github.com/reactphp/event-loop/tree/v1.5.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2023-11-13T13:48:05+00:00" + }, + { + "name": "react/promise", + "version": "v3.2.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise.git", + "reference": "8a164643313c71354582dc850b42b33fa12a4b63" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise/zipball/8a164643313c71354582dc850b42b33fa12a4b63", + "reference": "8a164643313c71354582dc850b42b33fa12a4b63", + "shasum": "" + }, + "require": { + "php": ">=7.1.0" + }, + "require-dev": { + "phpstan/phpstan": "1.10.39 || 1.4.10", + "phpunit/phpunit": "^9.6 || ^7.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "React\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "keywords": [ + "promise", + "promises" + ], + "support": { + "issues": "https://github.com/reactphp/promise/issues", + "source": "https://github.com/reactphp/promise/tree/v3.2.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2024-05-24T10:39:05+00:00" + }, + { + "name": "react/socket", + "version": "v1.16.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/socket.git", + "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/socket/zipball/23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", + "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.0", + "react/dns": "^1.13", + "react/event-loop": "^1.2", + "react/promise": "^3.2 || ^2.6 || ^1.2.1", + "react/stream": "^1.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/async": "^4.3 || ^3.3 || ^2", + "react/promise-stream": "^1.4", + "react/promise-timer": "^1.11" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Socket\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP", + "keywords": [ + "Connection", + "Socket", + "async", + "reactphp", + "stream" + ], + "support": { + "issues": "https://github.com/reactphp/socket/issues", + "source": "https://github.com/reactphp/socket/tree/v1.16.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2024-07-26T10:38:09+00:00" + }, + { + "name": "react/stream", + "version": "v1.4.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/stream.git", + "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/stream/zipball/1e5b0acb8fe55143b5b426817155190eb6f5b18d", + "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.8", + "react/event-loop": "^1.2" + }, + "require-dev": { + "clue/stream-filter": "~1.2", + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Stream\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", + "keywords": [ + "event-driven", + "io", + "non-blocking", + "pipe", + "reactphp", + "readable", + "stream", + "writable" + ], + "support": { + "issues": "https://github.com/reactphp/stream/issues", + "source": "https://github.com/reactphp/stream/tree/v1.4.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2024-06-11T12:45:25+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:12:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:58:43+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:59:15+00:00" + }, + { + "name": "sebastian/comparator", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", + "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/diff": "^5.0", + "sebastian/exporter": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-10-18T14:56:07+00:00" + }, + { + "name": "sebastian/complexity", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "68ff824baeae169ec9f2137158ee529584553799" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", + "reference": "68ff824baeae169ec9f2137158ee529584553799", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-21T08:37:17+00:00" + }, + { + "name": "sebastian/diff", + "version": "5.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0", + "symfony/process": "^6.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:15:17+00:00" + }, + { + "name": "sebastian/environment", + "version": "6.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-23T08:47:14+00:00" + }, + { + "name": "sebastian/exporter", + "version": "5.1.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "955288482d97c19a372d3f31006ab3f37da47adf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf", + "reference": "955288482d97c19a372d3f31006ab3f37da47adf", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:17:12+00:00" + }, + { + "name": "sebastian/global-state", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:19:19+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-21T08:38:20+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:08:32+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:06:18+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:05:40+00:00" + }, + { + "name": "sebastian/type", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:10:45+00:00" + }, + { + "name": "sebastian/version", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-07T11:34:05+00:00" + }, + { + "name": "spatie/backtrace", + "version": "1.6.2", + "source": { + "type": "git", + "url": "https://github.com/spatie/backtrace.git", + "reference": "1a9a145b044677ae3424693f7b06479fc8c137a9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/backtrace/zipball/1a9a145b044677ae3424693f7b06479fc8c137a9", + "reference": "1a9a145b044677ae3424693f7b06479fc8c137a9", + "shasum": "" + }, + "require": { + "php": "^7.3|^8.0" + }, + "require-dev": { + "ext-json": "*", + "laravel/serializable-closure": "^1.3", + "phpunit/phpunit": "^9.3", + "spatie/phpunit-snapshot-assertions": "^4.2", + "symfony/var-dumper": "^5.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\Backtrace\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van de Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "A better backtrace", + "homepage": "https://github.com/spatie/backtrace", + "keywords": [ + "Backtrace", + "spatie" + ], + "support": { + "source": "https://github.com/spatie/backtrace/tree/1.6.2" + }, + "funding": [ + { + "url": "https://github.com/sponsors/spatie", + "type": "github" + }, + { + "url": "https://spatie.be/open-source/support-us", + "type": "other" + } + ], + "time": "2024-07-22T08:21:24+00:00" + }, + { + "name": "spatie/error-solutions", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/spatie/error-solutions.git", + "reference": "ae7393122eda72eed7cc4f176d1e96ea444f2d67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/error-solutions/zipball/ae7393122eda72eed7cc4f176d1e96ea444f2d67", + "reference": "ae7393122eda72eed7cc4f176d1e96ea444f2d67", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "require-dev": { + "illuminate/broadcasting": "^10.0|^11.0", + "illuminate/cache": "^10.0|^11.0", + "illuminate/support": "^10.0|^11.0", + "livewire/livewire": "^2.11|^3.3.5", + "openai-php/client": "^0.10.1", + "orchestra/testbench": "^7.0|8.22.3|^9.0", + "pestphp/pest": "^2.20", + "phpstan/phpstan": "^1.11", + "psr/simple-cache": "^3.0", + "psr/simple-cache-implementation": "^3.0", + "spatie/ray": "^1.28", + "symfony/cache": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "vlucas/phpdotenv": "^5.5" + }, + "suggest": { + "openai-php/client": "Require get solutions from OpenAI", + "simple-cache-implementation": "To cache solutions from OpenAI" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\Ignition\\": "legacy/ignition", + "Spatie\\ErrorSolutions\\": "src", + "Spatie\\LaravelIgnition\\": "legacy/laravel-ignition" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ruben Van Assche", + "email": "ruben@spatie.be", + "role": "Developer" + } + ], + "description": "This is my package error-solutions", + "homepage": "https://github.com/spatie/error-solutions", + "keywords": [ + "error-solutions", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/error-solutions/issues", + "source": "https://github.com/spatie/error-solutions/tree/1.1.1" + }, + "funding": [ + { + "url": "https://github.com/Spatie", + "type": "github" + } + ], + "time": "2024-07-25T11:06:04+00:00" + }, + { + "name": "spatie/flare-client-php", + "version": "1.8.0", + "source": { + "type": "git", + "url": "https://github.com/spatie/flare-client-php.git", + "reference": "180f8ca4c0d0d6fc51477bd8c53ce37ab5a96122" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/flare-client-php/zipball/180f8ca4c0d0d6fc51477bd8c53ce37ab5a96122", + "reference": "180f8ca4c0d0d6fc51477bd8c53ce37ab5a96122", + "shasum": "" + }, + "require": { + "illuminate/pipeline": "^8.0|^9.0|^10.0|^11.0", + "php": "^8.0", + "spatie/backtrace": "^1.6.1", + "symfony/http-foundation": "^5.2|^6.0|^7.0", + "symfony/mime": "^5.2|^6.0|^7.0", + "symfony/process": "^5.2|^6.0|^7.0", + "symfony/var-dumper": "^5.2|^6.0|^7.0" + }, + "require-dev": { + "dms/phpunit-arraysubset-asserts": "^0.5.0", + "pestphp/pest": "^1.20|^2.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "spatie/pest-plugin-snapshots": "^1.0|^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.3.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Spatie\\FlareClient\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Send PHP errors to Flare", + "homepage": "https://github.com/spatie/flare-client-php", + "keywords": [ + "exception", + "flare", + "reporting", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/flare-client-php/issues", + "source": "https://github.com/spatie/flare-client-php/tree/1.8.0" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2024-08-01T08:27:26+00:00" + }, + { + "name": "spatie/ignition", + "version": "1.15.0", + "source": { + "type": "git", + "url": "https://github.com/spatie/ignition.git", + "reference": "e3a68e137371e1eb9edc7f78ffa733f3b98991d2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/ignition/zipball/e3a68e137371e1eb9edc7f78ffa733f3b98991d2", + "reference": "e3a68e137371e1eb9edc7f78ffa733f3b98991d2", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "php": "^8.0", + "spatie/error-solutions": "^1.0", + "spatie/flare-client-php": "^1.7", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" + }, + "require-dev": { + "illuminate/cache": "^9.52|^10.0|^11.0", + "mockery/mockery": "^1.4", + "pestphp/pest": "^1.20|^2.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "psr/simple-cache-implementation": "*", + "symfony/cache": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "vlucas/phpdotenv": "^5.5" + }, + "suggest": { + "openai-php/client": "Require get solutions from OpenAI", + "simple-cache-implementation": "To cache solutions from OpenAI" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.5.x-dev" + } + }, + "autoload": { + "psr-4": { + "Spatie\\Ignition\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Spatie", + "email": "info@spatie.be", + "role": "Developer" + } + ], + "description": "A beautiful error page for PHP applications.", + "homepage": "https://flareapp.io/ignition", + "keywords": [ + "error", + "flare", + "laravel", + "page" + ], + "support": { + "docs": "https://flareapp.io/docs/ignition-for-laravel/introduction", + "forum": "https://twitter.com/flareappio", + "issues": "https://github.com/spatie/ignition/issues", + "source": "https://github.com/spatie/ignition" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2024-06-12T14:55:22+00:00" + }, + { + "name": "spatie/laravel-ignition", + "version": "2.8.0", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-ignition.git", + "reference": "3c067b75bfb50574db8f7e2c3978c65eed71126c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/3c067b75bfb50574db8f7e2c3978c65eed71126c", + "reference": "3c067b75bfb50574db8f7e2c3978c65eed71126c", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "illuminate/support": "^10.0|^11.0", + "php": "^8.1", + "spatie/ignition": "^1.15", + "symfony/console": "^6.2.3|^7.0", + "symfony/var-dumper": "^6.2.3|^7.0" + }, + "require-dev": { + "livewire/livewire": "^2.11|^3.3.5", + "mockery/mockery": "^1.5.1", + "openai-php/client": "^0.8.1", + "orchestra/testbench": "8.22.3|^9.0", + "pestphp/pest": "^2.34", + "phpstan/extension-installer": "^1.3.1", + "phpstan/phpstan-deprecation-rules": "^1.1.1", + "phpstan/phpstan-phpunit": "^1.3.16", + "vlucas/phpdotenv": "^5.5" + }, + "suggest": { + "openai-php/client": "Require get solutions from OpenAI", + "psr/simple-cache-implementation": "Needed to cache solutions from OpenAI" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\LaravelIgnition\\IgnitionServiceProvider" + ], + "aliases": { + "Flare": "Spatie\\LaravelIgnition\\Facades\\Flare" + } + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Spatie\\LaravelIgnition\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Spatie", + "email": "info@spatie.be", + "role": "Developer" + } + ], + "description": "A beautiful error page for Laravel applications.", + "homepage": "https://flareapp.io/ignition", + "keywords": [ + "error", + "flare", + "laravel", + "page" + ], + "support": { + "docs": "https://flareapp.io/docs/ignition-for-laravel/introduction", + "forum": "https://twitter.com/flareappio", + "issues": "https://github.com/spatie/laravel-ignition/issues", + "source": "https://github.com/spatie/laravel-ignition" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2024-06-12T15:01:18+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v7.1.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "c835867b3c62bb05c7fe3d637c871c7ae52024d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/c835867b3c62bb05c7fe3d637c871c7ae52024d4", + "reference": "c835867b3c62bb05c7fe3d637c871c7ae52024d4", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "require-dev": { + "symfony/process": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v7.1.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-10-25T15:11:02+00:00" + }, + { + "name": "symfony/options-resolver", + "version": "v7.1.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "85e95eeede2d41cd146146e98c9c81d9214cae85" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/85e95eeede2d41cd146146e98c9c81d9214cae85", + "reference": "85e95eeede2d41cd146146e98c9c81d9214cae85", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an improved replacement for the array_replace PHP function", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "support": { + "source": "https://github.com/symfony/options-resolver/tree/v7.1.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:20:29+00:00" + }, + { + "name": "symfony/polyfill-php81", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/stopwatch", + "version": "v7.1.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "8b4a434e6e7faf6adedffb48783a5c75409a1a05" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/8b4a434e6e7faf6adedffb48783a5c75409a1a05", + "reference": "8b4a434e6e7faf6adedffb48783a5c75409a1a05", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/service-contracts": "^2.5|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Stopwatch\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a way to profile code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/stopwatch/tree/v7.1.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:20:29+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:36:25+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": true, + "prefer-lowest": false, + "platform": { + "php": "^8.2 || ^8.3", + "ext-json": "*", + "ext-mbstring": "*", + "ext-pdo": "*", + "ext-pdo_mysql": "*", + "ext-posix": "*", + "ext-zip": "*" + }, + "platform-dev": [], + "platform-overrides": { + "php": "8.2.23" + }, + "plugin-api-version": "2.6.0" +} diff --git a/config/activity.php b/config/activity.php new file mode 100644 index 0000000..6c492d3 --- /dev/null +++ b/config/activity.php @@ -0,0 +1,12 @@ + env('APP_ACTIVITY_PRUNE_DAYS', 90), + + // If set to true activity log entries generated by an admin user that is not also + // a part of the server in question will be hidden from the activity logs API response. + // + // Activity will still be properly tracked, just not displayed. + 'hide_admin_activity' => env('APP_ACTIVITY_HIDE_ADMIN', false), +]; diff --git a/config/app.php b/config/app.php new file mode 100644 index 0000000..e18e62b --- /dev/null +++ b/config/app.php @@ -0,0 +1,244 @@ + '1.11.11', + + /* + |-------------------------------------------------------------------------- + | NookTheme Version + |-------------------------------------------------------------------------- + | This value is set when creating a NookTheme release. You should not + | change this value if you are not maintaining your own internal versions. + */ + + 'fork-version' => '1.3.2', + + /* + |-------------------------------------------------------------------------- + | Application Name + |-------------------------------------------------------------------------- + | + | This value is the name of your application. This value is used when the + | framework needs to place the application's name in a notification or + | any other location as required by the application or its packages. + */ + + 'name' => env('APP_NAME', 'Pterodactyl'), + + /* + |-------------------------------------------------------------------------- + | Application Environment + |-------------------------------------------------------------------------- + | + | This value determines the "environment" your application is currently + | running in. This may determine how you prefer to configure various + | services your application utilizes. Set this in your ".env" file. + | + */ + + 'env' => env('APP_ENV', 'production'), + + /* + |-------------------------------------------------------------------------- + | Application Debug Mode + |-------------------------------------------------------------------------- + | + | When your application is in debug mode, detailed error messages with + | stack traces will be shown on every error that occurs within your + | application. If disabled, a simple generic error page is shown. + | + */ + + 'debug' => env('APP_DEBUG', false), + + /* + |-------------------------------------------------------------------------- + | Application URL + |-------------------------------------------------------------------------- + | + | This URL is used by the console to properly generate URLs when using + | the Artisan command line tool. You should set this to the root of + | your application so that it is used when running Artisan tasks. + | + */ + + 'url' => env('APP_URL', 'http://localhost'), + + /* + |-------------------------------------------------------------------------- + | Application Timezone + |-------------------------------------------------------------------------- + | + | Here you may specify the default timezone for your application, which + | will be used by the PHP date and date-time functions. We have gone + | ahead and set this to a sensible default for you out of the box. + | + */ + + 'timezone' => env('APP_TIMEZONE', 'UTC'), + + /* + |-------------------------------------------------------------------------- + | Application Locale Configuration + |-------------------------------------------------------------------------- + | + | The application locale determines the default locale that will be used + | by the translation service provider. You are free to set this value + | to any of the locales which will be supported by the application. + | + */ + + 'locale' => env('APP_LOCALE', 'en'), + + /* + |-------------------------------------------------------------------------- + | Application Fallback Locale + |-------------------------------------------------------------------------- + | + | The fallback locale determines the locale to use when the current one + | is not available. You may change the value to correspond to any of + | the language folders that are provided through your application. + | + */ + + 'fallback_locale' => 'en', + + /* + |-------------------------------------------------------------------------- + | Encryption Key + |-------------------------------------------------------------------------- + | + | This key is used by the Illuminate encrypter service and should be set + | to a random, 32 character string, otherwise these encrypted strings + | will not be safe. Please do this before deploying an application! + | + */ + + 'key' => env('APP_KEY'), + + 'cipher' => 'AES-256-CBC', + + /* + |-------------------------------------------------------------------------- + | Exception Reporter Configuration + |-------------------------------------------------------------------------- + | + | If you're encountering weird behavior with the Panel and no exceptions + | are being logged try changing the environment variable below to be true. + | This will override the default "don't report" behavior of the Panel and log + | all exceptions. This will be quite noisy. + | + */ + + 'exceptions' => [ + 'report_all' => env('APP_REPORT_ALL_EXCEPTIONS', false), + ], + + /* + |-------------------------------------------------------------------------- + | Maintenance Mode Driver + |-------------------------------------------------------------------------- + | + | These configuration options determine the driver used to determine and + | manage Laravel's "maintenance mode" status. The "cache" driver will + | allow maintenance mode to be controlled across multiple machines. + | + | Supported drivers: "file", "cache" + | + */ + + 'maintenance' => [ + 'driver' => 'file', + ], + + /* + |-------------------------------------------------------------------------- + | Autoloaded Service Providers + |-------------------------------------------------------------------------- + | + | The service providers listed here will be automatically loaded on the + | request to your application. Feel free to add your own services to + | this array to grant expanded functionality to your applications. + | + */ + + 'providers' => [ + /* + * Laravel Framework Service Providers... + */ + Illuminate\Auth\AuthServiceProvider::class, + Illuminate\Broadcasting\BroadcastServiceProvider::class, + Illuminate\Bus\BusServiceProvider::class, + Illuminate\Cache\CacheServiceProvider::class, + Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class, + Illuminate\Cookie\CookieServiceProvider::class, + Illuminate\Database\DatabaseServiceProvider::class, + Illuminate\Encryption\EncryptionServiceProvider::class, + Illuminate\Filesystem\FilesystemServiceProvider::class, + Illuminate\Foundation\Providers\FoundationServiceProvider::class, + Illuminate\Hashing\HashServiceProvider::class, + Illuminate\Mail\MailServiceProvider::class, + Illuminate\Notifications\NotificationServiceProvider::class, + Illuminate\Pagination\PaginationServiceProvider::class, + Illuminate\Pipeline\PipelineServiceProvider::class, + Illuminate\Queue\QueueServiceProvider::class, + Illuminate\Redis\RedisServiceProvider::class, + Illuminate\Auth\Passwords\PasswordResetServiceProvider::class, + Illuminate\Session\SessionServiceProvider::class, + Illuminate\Translation\TranslationServiceProvider::class, + Illuminate\Validation\ValidationServiceProvider::class, + Illuminate\View\ViewServiceProvider::class, + + /* + * Application Service Providers... + */ + Pterodactyl\Providers\ActivityLogServiceProvider::class, + Pterodactyl\Providers\AppServiceProvider::class, + Pterodactyl\Providers\AuthServiceProvider::class, + Pterodactyl\Providers\BackupsServiceProvider::class, + Pterodactyl\Providers\BladeServiceProvider::class, + Pterodactyl\Providers\EventServiceProvider::class, + Pterodactyl\Providers\HashidsServiceProvider::class, + Pterodactyl\Providers\RouteServiceProvider::class, + Pterodactyl\Providers\RepositoryServiceProvider::class, + Pterodactyl\Providers\ViewComposerServiceProvider::class, + + /* + * Additional Dependencies + */ + Prologue\Alerts\AlertsServiceProvider::class, + ], + + /* + |-------------------------------------------------------------------------- + | Class Aliases + |-------------------------------------------------------------------------- + | + | This array of class aliases will be registered when this application + | is started. However, feel free to register as many as you wish as + | the aliases are "lazy" loaded, so they don't hinder performance. + | + */ + + 'aliases' => Facade::defaultAliases()->merge([ + 'Alert' => Prologue\Alerts\Facades\Alert::class, + 'Carbon' => Carbon\Carbon::class, + 'JavaScript' => Laracasts\Utilities\JavaScript\JavaScriptFacade::class, + 'Theme' => Pterodactyl\Extensions\Facades\Theme::class, + + // Custom Facades + 'Activity' => Pterodactyl\Facades\Activity::class, + 'LogBatch' => Pterodactyl\Facades\LogBatch::class, + 'LogTarget' => Pterodactyl\Facades\LogTarget::class, + ])->toArray(), +]; diff --git a/config/auth.php b/config/auth.php new file mode 100644 index 0000000..21e6be7 --- /dev/null +++ b/config/auth.php @@ -0,0 +1,128 @@ + [ + 'time' => 2, + 'attempts' => 3, + ], + + /* + |-------------------------------------------------------------------------- + | Authentication Defaults + |-------------------------------------------------------------------------- + | + | This option controls the default authentication "guard" and password + | reset options for your application. You may change these defaults + | as required, but they're a perfect start for most applications. + | + */ + + 'defaults' => [ + 'guard' => 'web', + 'passwords' => 'users', + ], + + /* + |-------------------------------------------------------------------------- + | Authentication Guards + |-------------------------------------------------------------------------- + | + | Next, you may define every authentication guard for your application. + | Of course, a great default configuration has been defined for you + | here which uses session storage and the Eloquent user provider. + | + | All authentication drivers have a user provider. This defines how the + | users are actually retrieved out of your database or other storage + | mechanisms used by this application to persist your user's data. + | + | Supported: "session", "token" + | + */ + + 'guards' => [ + 'web' => [ + 'driver' => 'session', + 'provider' => 'users', + ], + + 'api' => [ + 'driver' => 'token', + 'provider' => 'users', + ], + ], + + /* + |-------------------------------------------------------------------------- + | User Providers + |-------------------------------------------------------------------------- + | + | All authentication drivers have a user provider. This defines how the + | users are actually retrieved out of your database or other storage + | mechanisms used by this application to persist your user's data. + | + | If you have multiple user tables or models you may configure multiple + | sources which represent each model / table. These sources may then + | be assigned to any extra authentication guards you have defined. + | + | Supported: "database", "eloquent" + | + */ + + 'providers' => [ + 'users' => [ + 'driver' => 'eloquent', + 'model' => Pterodactyl\Models\User::class, + ], + ], + + /* + |-------------------------------------------------------------------------- + | Resetting Passwords + |-------------------------------------------------------------------------- + | + | Here you may set the options for resetting passwords including the view + | that is your password reset e-mail. You may also set the name of the + | table that maintains all of the reset tokens for your application. + | + | You may specify multiple password reset configurations if you have more + | than one user table or model in the application and you want to have + | separate password reset settings based on the specific user types. + | + | The expire time is the number of minutes that the reset token should be + | considered valid. This security feature keeps tokens short-lived so + | they have less time to be guessed. You may change this as needed. + | + */ + + 'passwords' => [ + 'users' => [ + 'provider' => 'users', + 'table' => 'password_resets', + 'expire' => 60, + 'throttle' => 60, + ], + ], + + /* + |-------------------------------------------------------------------------- + | Password Confirmation Timeout + |-------------------------------------------------------------------------- + | + | Here you may define the amount of seconds before a password confirmation + | times out and the user is prompted to re-enter their password via the + | confirmation screen. By default, the timeout lasts for three hours. + | + */ + + 'password_timeout' => 10800, +]; diff --git a/config/backups.php b/config/backups.php new file mode 100644 index 0000000..f466ea4 --- /dev/null +++ b/config/backups.php @@ -0,0 +1,66 @@ + env('APP_BACKUP_DRIVER', Backup::ADAPTER_WINGS), + + // This value is used to determine the lifespan of UploadPart presigned urls that wings + // uses to upload backups to S3 storage. Value is in minutes, so this would default to an hour. + 'presigned_url_lifespan' => env('BACKUP_PRESIGNED_URL_LIFESPAN', 60), + + // This value defines the maximal size of a single part for the S3 multipart upload during backups + // The maximal part size must be given in bytes. The default value is 5GB. + // Note that 5GB is the maximum for a single part when using AWS S3. + 'max_part_size' => env('BACKUP_MAX_PART_SIZE', 5 * 1024 * 1024 * 1024), + + // The time to wait before automatically failing a backup, time is in minutes and defaults + // to 6 hours. To disable this feature, set the value to `0`. + 'prune_age' => env('BACKUP_PRUNE_AGE', 360), + + // Defines the backup creation throttle limits for users. In this default example, we allow + // a user to create two (successful or pending) backups per 10 minutes. Even if they delete + // a backup it will be included in the throttle count. + // + // Set the period to "0" to disable this throttle. The period is defined in seconds. + 'throttles' => [ + 'limit' => env('BACKUP_THROTTLE_LIMIT', 2), + 'period' => env('BACKUP_THROTTLE_PERIOD', 600), + ], + + 'disks' => [ + // There is no configuration for the local disk for Wings. That configuration + // is determined by the Daemon configuration, and not the Panel. + 'wings' => [ + 'adapter' => Backup::ADAPTER_WINGS, + ], + + // Configuration for storing backups in Amazon S3. This uses the same credentials + // specified in filesystems.php but does include some more specific settings for + // backups, notably bucket, location, and use_accelerate_endpoint. + 's3' => [ + 'adapter' => Backup::ADAPTER_AWS_S3, + + 'region' => env('AWS_DEFAULT_REGION'), + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + + // The S3 bucket to use for backups. + 'bucket' => env('AWS_BACKUPS_BUCKET'), + + // The location within the S3 bucket where backups will be stored. Backups + // are stored within a folder using the server's UUID as the name. Each + // backup for that server lives within that folder. + 'prefix' => env('AWS_BACKUPS_BUCKET') ?? '', + + 'endpoint' => env('AWS_ENDPOINT'), + 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), + 'use_accelerate_endpoint' => env('AWS_BACKUPS_USE_ACCELERATE', false), + + 'storage_class' => env('AWS_BACKUPS_STORAGE_CLASS'), + ], + ], +]; diff --git a/config/broadcasting.php b/config/broadcasting.php new file mode 100644 index 0000000..81add6d --- /dev/null +++ b/config/broadcasting.php @@ -0,0 +1,66 @@ + env('BROADCAST_DRIVER', 'null'), + + /* + |-------------------------------------------------------------------------- + | Broadcast Connections + |-------------------------------------------------------------------------- + | + | Here you may define all of the broadcast connections that will be used + | to broadcast events to other systems or over websockets. Samples of + | each available type of connection are provided inside this array. + | + */ + + 'connections' => [ + 'pusher' => [ + 'driver' => 'pusher', + 'key' => env('PUSHER_APP_KEY'), + 'secret' => env('PUSHER_APP_SECRET'), + 'app_id' => env('PUSHER_APP_ID'), + 'options' => [ + 'host' => env('PUSHER_HOST', 'api-' . env('PUSHER_APP_CLUSTER', 'mt1') . '.pusher.com') ?: 'api-' . env('PUSHER_APP_CLUSTER', 'mt1') . '.pusher.com', + 'port' => env('PUSHER_PORT', 443), + 'scheme' => env('PUSHER_SCHEME', 'https'), + 'encrypted' => true, + 'useTLS' => env('PUSHER_SCHEME', 'https') === 'https', + ], + 'client_options' => [ + // Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html + ], + ], + + 'ably' => [ + 'driver' => 'ably', + 'key' => env('ABLY_KEY'), + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => 'default', + ], + + 'log' => [ + 'driver' => 'log', + ], + + 'null' => [ + 'driver' => 'null', + ], + ], +]; diff --git a/config/cache.php b/config/cache.php new file mode 100644 index 0000000..14b00b5 --- /dev/null +++ b/config/cache.php @@ -0,0 +1,103 @@ + env('CACHE_DRIVER', 'redis'), + + /* + |-------------------------------------------------------------------------- + | Cache Stores + |-------------------------------------------------------------------------- + | + | Here you may define all of the cache "stores" for your application as + | well as their drivers. You may even define multiple stores for the + | same cache driver to group types of items stored in your caches. + | + | Supported drivers: "apc", "array", "database", "file", + | "memcached", "redis", "dynamodb", "octane", "null" + | + */ + + 'stores' => [ + 'apc' => [ + 'driver' => 'apc', + ], + + 'array' => [ + 'driver' => 'array', + 'serialize' => false, + ], + + 'database' => [ + 'driver' => 'database', + 'table' => 'cache', + 'connection' => null, + 'lock_connection' => null, + ], + + 'file' => [ + 'driver' => 'file', + 'path' => storage_path('framework/cache/data'), + ], + + 'memcached' => [ + 'driver' => 'memcached', + 'persistent_id' => env('MEMCACHED_PERSISTENT_ID'), + 'sasl' => [ + env('MEMCACHED_USERNAME'), + env('MEMCACHED_PASSWORD'), + ], + 'options' => [ + // Memcached::OPT_CONNECT_TIMEOUT => 2000, + ], + 'servers' => [ + [ + 'host' => env('MEMCACHED_HOST', '127.0.0.1'), + 'port' => env('MEMCACHED_PORT', 11211), + 'weight' => 100, + ], + ], + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => 'default', + 'lock_connection' => 'default', + ], + + 'sessions' => [ + 'driver' => env('SESSION_DRIVER', 'database'), + 'table' => 'sessions', + 'connection' => env('SESSION_DRIVER') === 'redis' ? 'sessions' : null, + ], + + 'octane' => [ + 'driver' => 'octane', + ], + ], + + /* + |-------------------------------------------------------------------------- + | Cache Key Prefix + |-------------------------------------------------------------------------- + | + | When utilizing the APC, database, memcached, Redis, or DynamoDB cache + | stores there might be other applications using the same cache. For + | that reason, you may prefix every cache key to avoid collisions. + | + */ + + 'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'pterodactyl'), '_') . '_cache_'), +]; diff --git a/config/compile.php b/config/compile.php new file mode 100644 index 0000000..cbf7739 --- /dev/null +++ b/config/compile.php @@ -0,0 +1,31 @@ + [ + ], + + /* + |-------------------------------------------------------------------------- + | Compiled File Providers + |-------------------------------------------------------------------------- + | + | Here you may list service providers which define a "compiles" function + | that returns additional files that should be compiled, providing an + | easy way to get common files from any packages you are utilizing. + | + */ + + 'providers' => [ + ], +]; diff --git a/config/cors.php b/config/cors.php new file mode 100644 index 0000000..bf72895 --- /dev/null +++ b/config/cors.php @@ -0,0 +1,57 @@ + ['/api/client', '/api/application', '/api/client/*', '/api/application/*'], + + /* + * Matches the request method. `['*']` allows all methods. + */ + 'allowed_methods' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD'], + + /* + * Matches the request origin. `['*']` allows all origins. Wildcards can be used, eg `*.mydomain.com` + */ + 'allowed_origins' => explode(',', env('APP_CORS_ALLOWED_ORIGINS') ?? ''), + + /* + * Patterns that can be used with `preg_match` to match the origin. + */ + 'allowed_origins_patterns' => [], + + /* + * Sets the Access-Control-Allow-Headers response header. `['*']` allows all headers. + */ + 'allowed_headers' => ['*'], + + /* + * Sets the Access-Control-Expose-Headers response header with these headers. + */ + 'exposed_headers' => [], + + /* + * Sets the Access-Control-Max-Age response header when > 0. + */ + 'max_age' => 0, + + /* + * Sets the Access-Control-Allow-Credentials header. + */ + 'supports_credentials' => true, +]; diff --git a/config/database.php b/config/database.php new file mode 100644 index 0000000..b3a460b --- /dev/null +++ b/config/database.php @@ -0,0 +1,132 @@ + env('DB_CONNECTION', 'mysql'), + + /* + |-------------------------------------------------------------------------- + | Database Connections + |-------------------------------------------------------------------------- + | + | Here are each of the database connections setup for your application. + | Of course, examples of configuring each database platform that is + | supported by Laravel is shown below to make development simple. + | + | + | All database work in Laravel is done through the PHP PDO facilities + | so make sure you have the driver for your particular database of + | choice installed on your machine before you begin development. + | + */ + + 'connections' => [ + 'mysql' => [ + 'driver' => 'mysql', + 'url' => env('DATABASE_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '3306'), + 'database' => env('DB_DATABASE', 'panel'), + 'username' => env('DB_USERNAME', 'pterodactyl'), + 'password' => env('DB_PASSWORD', ''), + 'unix_socket' => env('DB_SOCKET', ''), + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_unicode_ci', + 'prefix' => env('DB_PREFIX', ''), + 'prefix_indexes' => true, + 'strict' => env('DB_STRICT_MODE', false), + 'timezone' => env('DB_TIMEZONE', Time::getMySQLTimezoneOffset(env('APP_TIMEZONE', 'UTC'))), + 'sslmode' => env('DB_SSLMODE', 'prefer'), + 'options' => extension_loaded('pdo_mysql') ? array_filter([ + PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), + PDO::MYSQL_ATTR_SSL_CERT => env('MYSQL_ATTR_SSL_CERT'), + PDO::MYSQL_ATTR_SSL_KEY => env('MYSQL_ATTR_SSL_KEY'), + PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => env('MYSQL_ATTR_SSL_VERIFY_SERVER_CERT', true), + ]) : [], + ], + ], + + /* + |-------------------------------------------------------------------------- + | Migration Repository Table + |-------------------------------------------------------------------------- + | + | This table keeps track of all the migrations that have already run for + | your application. Using this information, we can determine which of + | the migrations on disk haven't actually been run in the database. + | + */ + + 'migrations' => 'migrations', + + /* + |-------------------------------------------------------------------------- + | Redis Databases + |-------------------------------------------------------------------------- + | + | Redis is an open source, fast, and advanced key-value store that also + | provides a richer set of commands than a typical key-value systems + | such as APC or Memcached. Laravel makes it easy to dig right in. + | + */ + + 'redis' => [ + 'client' => env('REDIS_CLIENT', 'predis'), + + 'options' => [ + 'cluster' => env('REDIS_CLUSTER', 'redis'), + 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'pterodactyl'), '_') . '_database_'), + ], + + 'default' => [ + 'scheme' => env('REDIS_SCHEME', 'tcp'), + 'path' => env('REDIS_PATH', '/run/redis/redis.sock'), + 'host' => env('REDIS_HOST', 'localhost'), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), + 'port' => env('REDIS_PORT', 6379), + 'database' => env('REDIS_DATABASE', 0), + 'context' => extension_loaded('redis') && env('REDIS_CLIENT') === 'phpredis' ? [ + 'stream' => array_filter([ + 'verify_peer' => env('REDIS_VERIFY_PEER', true), + 'verify_peer_name' => env('REDIS_VERIFY_PEER_NAME', true), + 'cafile' => env('REDIS_CAFILE'), + 'local_cert' => env('REDIS_LOCAL_CERT'), + 'local_pk' => env('REDIS_LOCAL_PK'), + ]), + ] : [], + ], + + 'sessions' => [ + 'scheme' => env('REDIS_SCHEME', 'tcp'), + 'path' => env('REDIS_PATH', '/run/redis/redis.sock'), + 'host' => env('REDIS_HOST', 'localhost'), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), + 'port' => env('REDIS_PORT', 6379), + 'database' => env('REDIS_DATABASE_SESSIONS', 1), + 'context' => extension_loaded('redis') && env('REDIS_CLIENT') === 'phpredis' ? [ + 'stream' => array_filter([ + 'verify_peer' => env('REDIS_VERIFY_PEER', true), + 'verify_peer_name' => env('REDIS_VERIFY_PEER_NAME', true), + 'cafile' => env('REDIS_CAFILE'), + 'local_cert' => env('REDIS_LOCAL_CERT'), + 'local_pk' => env('REDIS_LOCAL_PK'), + ]), + ] : [], + ], + ], +]; diff --git a/config/egg_features/eula.php b/config/egg_features/eula.php new file mode 100644 index 0000000..e5fb872 --- /dev/null +++ b/config/egg_features/eula.php @@ -0,0 +1,16 @@ + env('FILESYSTEM_DISK', 'local'), + + /* + |-------------------------------------------------------------------------- + | Filesystem Disks + |-------------------------------------------------------------------------- + | + | Here you may configure as many filesystem "disks" as you wish, and you + | may even configure multiple disks of the same driver. Defaults have + | been set up for each driver as an example of the required values. + | + | Supported Drivers: "local", "ftp", "sftp", "s3" + | + */ + + 'disks' => [ + 'local' => [ + 'driver' => 'local', + 'root' => storage_path('app'), + 'throw' => false, + ], + + 'public' => [ + 'driver' => 'local', + 'root' => storage_path('app/public'), + 'url' => env('APP_URL') . '/storage', + 'visibility' => 'public', + 'throw' => false, + ], + + 's3' => [ + 'driver' => 's3', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION'), + 'bucket' => env('AWS_BUCKET'), + 'url' => env('AWS_URL'), + 'endpoint' => env('AWS_ENDPOINT'), + 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), + 'throw' => false, + ], + ], + + /* + |-------------------------------------------------------------------------- + | Symbolic Links + |-------------------------------------------------------------------------- + | + | Here you may configure the symbolic links that will be created when the + | `storage:link` Artisan command is executed. The array keys should be + | the locations of the links and the values should be their targets. + | + */ + + 'links' => [ + public_path('storage') => storage_path('app/public'), + ], +]; diff --git a/config/fractal.php b/config/fractal.php new file mode 100644 index 0000000..833cf2b --- /dev/null +++ b/config/fractal.php @@ -0,0 +1,31 @@ + League\Fractal\Serializer\JsonApiSerializer::class, + + /* + |-------------------------------------------------------------------------- + | Auto Includes + |-------------------------------------------------------------------------- + | + | If enabled Fractal will automatically add the includes who's + | names are present in the `include` request parameter. + | + */ + + 'auto_includes' => [ + 'enabled' => true, + 'request_key' => 'include', + ], +]; diff --git a/config/hashids.php b/config/hashids.php new file mode 100644 index 0000000..199de1a --- /dev/null +++ b/config/hashids.php @@ -0,0 +1,15 @@ + env('HASHIDS_SALT'), + 'length' => env('HASHIDS_LENGTH', 8), + 'alphabet' => env('HASHIDS_ALPHABET', 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'), +]; diff --git a/config/hashing.php b/config/hashing.php new file mode 100644 index 0000000..552f2bb --- /dev/null +++ b/config/hashing.php @@ -0,0 +1,50 @@ + 'bcrypt', + + /* + |-------------------------------------------------------------------------- + | Bcrypt Options + |-------------------------------------------------------------------------- + | + | Here you may specify the configuration options that should be used when + | passwords are hashed using the Bcrypt algorithm. This will allow you + | to control the amount of time it takes to hash the given password. + | + */ + + 'bcrypt' => [ + 'rounds' => env('BCRYPT_ROUNDS', 10), + ], + + /* + |-------------------------------------------------------------------------- + | Argon Options + |-------------------------------------------------------------------------- + | + | Here you may specify the configuration options that should be used when + | passwords are hashed using the Argon algorithm. These will allow you + | to control the amount of time it takes to hash the given password. + | + */ + + 'argon' => [ + 'memory' => 65536, + 'threads' => 1, + 'time' => 4, + ], +]; diff --git a/config/http.php b/config/http.php new file mode 100644 index 0000000..ed54e47 --- /dev/null +++ b/config/http.php @@ -0,0 +1,21 @@ + [ + 'client_period' => 1, + 'client' => env('APP_API_CLIENT_RATELIMIT', 720), + + 'application_period' => 1, + 'application' => env('APP_API_APPLICATION_RATELIMIT', 240), + ], +]; diff --git a/config/javascript.php b/config/javascript.php new file mode 100644 index 0000000..5750439 --- /dev/null +++ b/config/javascript.php @@ -0,0 +1,30 @@ + [ + 'layouts.scripts', + ], + + /* + |-------------------------------------------------------------------------- + | JavaScript Namespace + |-------------------------------------------------------------------------- + | + | By default, we'll add variables to the global window object. However, + | it's recommended that you change this to some namespace - anything. + | That way, you can access vars, like "SomeNamespace.someVariable." + | + */ + 'js_namespace' => 'Pterodactyl', +]; diff --git a/config/logging.php b/config/logging.php new file mode 100644 index 0000000..b2cdb82 --- /dev/null +++ b/config/logging.php @@ -0,0 +1,120 @@ + env('LOG_CHANNEL', 'daily'), + + /* + |-------------------------------------------------------------------------- + | Deprecations Log Channel + |-------------------------------------------------------------------------- + | + | This option controls the log channel that should be used to log warnings + | regarding deprecated PHP and library features. This allows you to get + | your application ready for upcoming major versions of dependencies. + | + */ + + 'deprecations' => [ + 'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'), + 'trace' => false, + ], + + /* + |-------------------------------------------------------------------------- + | Log Channels + |-------------------------------------------------------------------------- + | + | Here you may configure the log channels for your application. Out of + | the box, Laravel uses the Monolog PHP logging library. This gives + | you a variety of powerful log handlers / formatters to utilize. + | + | Available Drivers: "single", "daily", "slack", "syslog", + | "errorlog", "monolog", + | "custom", "stack" + | + */ + + 'channels' => [ + 'stack' => [ + 'driver' => 'stack', + 'channels' => ['single'], + 'ignore_exceptions' => false, + ], + + 'single' => [ + 'driver' => 'single', + 'path' => storage_path('logs/laravel.log'), + 'level' => env('LOG_LEVEL', 'debug'), + ], + + 'daily' => [ + 'driver' => 'daily', + 'path' => storage_path('logs/laravel.log'), + 'level' => env('LOG_LEVEL', 'debug'), + 'days' => 7, + ], + + 'slack' => [ + 'driver' => 'slack', + 'url' => env('LOG_SLACK_WEBHOOK_URL'), + 'username' => 'Laravel Log', + 'emoji' => ':boom:', + 'level' => env('LOG_LEVEL', 'critical'), + ], + + 'papertrail' => [ + 'driver' => 'monolog', + 'level' => env('LOG_LEVEL', 'debug'), + 'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class), + 'handler_with' => [ + 'host' => env('PAPERTRAIL_URL'), + 'port' => env('PAPERTRAIL_PORT'), + 'connectionString' => 'tls://' . env('PAPERTRAIL_URL') . ':' . env('PAPERTRAIL_PORT'), + ], + ], + + 'stderr' => [ + 'driver' => 'monolog', + 'level' => env('LOG_LEVEL', 'debug'), + 'handler' => StreamHandler::class, + 'formatter' => env('LOG_STDERR_FORMATTER'), + 'with' => [ + 'stream' => 'php://stderr', + ], + ], + + 'syslog' => [ + 'driver' => 'syslog', + 'level' => env('LOG_LEVEL', 'debug'), + ], + + 'errorlog' => [ + 'driver' => 'errorlog', + 'level' => env('LOG_LEVEL', 'debug'), + ], + + 'null' => [ + 'driver' => 'monolog', + 'handler' => NullHandler::class, + ], + + 'emergency' => [ + 'path' => storage_path('logs/laravel.log'), + ], + ], +]; diff --git a/config/mail.php b/config/mail.php new file mode 100644 index 0000000..71c693c --- /dev/null +++ b/config/mail.php @@ -0,0 +1,116 @@ + env('MAIL_MAILER', env('MAIL_DRIVER', 'smtp')), + + /* + |-------------------------------------------------------------------------- + | Mailer Configurations + |-------------------------------------------------------------------------- + | + | Here you may configure all of the mailers used by your application plus + | their respective settings. Several examples have been configured for + | you and you are free to add your own as your application requires. + | + | Laravel supports a variety of mail "transport" drivers to be used while + | sending an e-mail. You will specify which one you are using for your + | mailers below. You are free to add additional mailers as required. + | + | Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2", + | "postmark", "log", "array", "failover" + | + */ + + 'mailers' => [ + 'smtp' => [ + 'transport' => 'smtp', + 'host' => env('MAIL_HOST', 'smtp.mailgun.org'), + 'port' => env('MAIL_PORT', 587), + 'encryption' => env('MAIL_ENCRYPTION', 'tls'), + 'username' => env('MAIL_USERNAME'), + 'password' => env('MAIL_PASSWORD'), + 'timeout' => null, + 'local_domain' => env('MAIL_EHLO_DOMAIN', env('SERVER_NAME')), + ], + + 'ses' => [ + 'transport' => 'ses', + ], + + 'mailgun' => [ + 'transport' => 'mailgun', + ], + + 'postmark' => [ + 'transport' => 'postmark', + ], + + 'sendmail' => [ + 'transport' => 'sendmail', + 'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'), + ], + + 'log' => [ + 'transport' => 'log', + 'channel' => env('MAIL_LOG_CHANNEL'), + ], + + 'array' => [ + 'transport' => 'array', + ], + + 'failover' => [ + 'transport' => 'failover', + 'mailers' => [ + 'smtp', + 'log', + ], + ], + ], + + /* + |-------------------------------------------------------------------------- + | Global "From" Address + |-------------------------------------------------------------------------- + | + | You may wish for all e-mails sent by your application to be sent from + | the same address. Here, you may specify a name and address that is + | used globally for all e-mails that are sent by your application. + | + */ + + 'from' => [ + 'address' => env('MAIL_FROM_ADDRESS', env('MAIL_FROM', 'hello@example.com')), + 'name' => env('MAIL_FROM_NAME', 'Pterodactyl Panel'), + ], + + /* + |-------------------------------------------------------------------------- + | Markdown Mail Settings + |-------------------------------------------------------------------------- + | + | If you are using Markdown based email rendering, you may configure your + | theme and component paths here, allowing you to customize the design + | of the emails. Or, you may simply stick with the Laravel defaults! + | + */ + + 'markdown' => [ + 'theme' => 'default', + + 'paths' => [ + resource_path('views/vendor/mail'), + ], + ], +]; diff --git a/config/prologue/alerts.php b/config/prologue/alerts.php new file mode 100644 index 0000000..1639770 --- /dev/null +++ b/config/prologue/alerts.php @@ -0,0 +1,37 @@ + [ + 'info', + 'warning', + 'danger', + 'success', + ], + + /* + |-------------------------------------------------------------------------- + | Session Key + |-------------------------------------------------------------------------- + | + | The session key which is used to store flashed messages into the current + | session. This can be changed if it conflicts with another key. + | + */ + + 'session_key' => 'alert_messages', +]; diff --git a/config/pterodactyl.php b/config/pterodactyl.php new file mode 100644 index 0000000..43c7c57 --- /dev/null +++ b/config/pterodactyl.php @@ -0,0 +1,192 @@ + (bool) env('APP_ENVIRONMENT_ONLY', false), + + /* + |-------------------------------------------------------------------------- + | Service Author + |-------------------------------------------------------------------------- + | + | Each panel installation is assigned a unique UUID to identify the + | author of custom services, and make upgrades easier by identifying + | standard Pterodactyl shipped services. + */ + + 'service' => [ + 'author' => env('APP_SERVICE_AUTHOR', 'unknown@unknown.com'), + ], + + /* + |-------------------------------------------------------------------------- + | Authentication + |-------------------------------------------------------------------------- + | + | Should login success and failure events trigger an email to the user? + */ + + 'auth' => [ + '2fa_required' => env('APP_2FA_REQUIRED', 0), + '2fa' => [ + 'bytes' => 32, + 'window' => env('APP_2FA_WINDOW', 4), + 'verify_newer' => true, + ], + ], + + /* + |-------------------------------------------------------------------------- + | Pagination + |-------------------------------------------------------------------------- + | + | Certain pagination result counts can be configured here and will take + | effect globally. + */ + + 'paginate' => [ + 'frontend' => [ + 'servers' => env('APP_PAGINATE_FRONT_SERVERS', 15), + ], + 'admin' => [ + 'servers' => env('APP_PAGINATE_ADMIN_SERVERS', 25), + 'users' => env('APP_PAGINATE_ADMIN_USERS', 25), + ], + 'api' => [ + 'nodes' => env('APP_PAGINATE_API_NODES', 25), + 'servers' => env('APP_PAGINATE_API_SERVERS', 25), + 'users' => env('APP_PAGINATE_API_USERS', 25), + ], + ], + + /* + |-------------------------------------------------------------------------- + | Guzzle Connections + |-------------------------------------------------------------------------- + | + | Configure the timeout to be used for Guzzle connections here. + */ + + 'guzzle' => [ + 'timeout' => env('GUZZLE_TIMEOUT', 15), + 'connect_timeout' => env('GUZZLE_CONNECT_TIMEOUT', 5), + ], + + /* + |-------------------------------------------------------------------------- + | CDN + |-------------------------------------------------------------------------- + | + | Information for the panel to use when contacting the CDN to confirm + | if panel is up to date. + */ + + 'cdn' => [ + 'cache_time' => 60, + 'url' => 'https://cdn.pterodactyl.io/releases/latest.json', + ], + + /* + |-------------------------------------------------------------------------- + | Client Features + |-------------------------------------------------------------------------- + | + | Allow clients to create their own databases. + */ + + 'client_features' => [ + 'databases' => [ + 'enabled' => env('PTERODACTYL_CLIENT_DATABASES_ENABLED', true), + 'allow_random' => env('PTERODACTYL_CLIENT_DATABASES_ALLOW_RANDOM', true), + ], + + 'schedules' => [ + // The total number of tasks that can exist for any given schedule at once. + 'per_schedule_task_limit' => env('PTERODACTYL_PER_SCHEDULE_TASK_LIMIT', 10), + ], + + 'allocations' => [ + 'enabled' => env('PTERODACTYL_CLIENT_ALLOCATIONS_ENABLED', false), + 'range_start' => env('PTERODACTYL_CLIENT_ALLOCATIONS_RANGE_START'), + 'range_end' => env('PTERODACTYL_CLIENT_ALLOCATIONS_RANGE_END'), + ], + ], + + /* + |-------------------------------------------------------------------------- + | File Editor + |-------------------------------------------------------------------------- + | + | This array includes the MIME filetypes that can be edited via the web. + */ + + 'files' => [ + 'max_edit_size' => env('PTERODACTYL_FILES_MAX_EDIT_SIZE', 1024 * 1024 * 4), + ], + + /* + |-------------------------------------------------------------------------- + | Dynamic Environment Variables + |-------------------------------------------------------------------------- + | + | Place dynamic environment variables here that should be auto-appended + | to server environment fields when the server is created or updated. + | + | Items should be in 'key' => 'value' format, where key is the environment + | variable name, and value is the server-object key. For example: + | + | 'P_SERVER_CREATED_AT' => 'created_at' + */ + + 'environment_variables' => [ + 'P_SERVER_ALLOCATION_LIMIT' => 'allocation_limit', + ], + + /* + |-------------------------------------------------------------------------- + | Asset Verification + |-------------------------------------------------------------------------- + | + | This section controls the output format for JS & CSS assets. + */ + + 'assets' => [ + 'use_hash' => env('PTERODACTYL_USE_ASSET_HASH', false), + ], + + /* + |-------------------------------------------------------------------------- + | Email Notification Settings + |-------------------------------------------------------------------------- + | + | This section controls what notifications are sent to users. + */ + + 'email' => [ + // Should an email be sent to a server owner once their server has completed it's first install process? + 'send_install_notification' => env('PTERODACTYL_SEND_INSTALL_NOTIFICATION', true), + // Should an email be sent to a server owner whenever their server is reinstalled? + 'send_reinstall_notification' => env('PTERODACTYL_SEND_REINSTALL_NOTIFICATION', true), + ], + + /* + |-------------------------------------------------------------------------- + | Telemetry Settings + |-------------------------------------------------------------------------- + | + | This section controls the telemetry sent by Pterodactyl. + */ + + 'telemetry' => [ + 'enabled' => env('PTERODACTYL_TELEMETRY_ENABLED', true), + ], +]; diff --git a/config/queue.php b/config/queue.php new file mode 100644 index 0000000..74da2e8 --- /dev/null +++ b/config/queue.php @@ -0,0 +1,78 @@ + env('QUEUE_CONNECTION', env('QUEUE_DRIVER', 'redis')), + + /* + |-------------------------------------------------------------------------- + | Queue Connections + |-------------------------------------------------------------------------- + | + | Here you may configure the connection information for each server that + | is used by your application. A default configuration has been added + | for each back-end shipped with Laravel. You are free to add more. + | + */ + + 'connections' => [ + 'sync' => [ + 'driver' => 'sync', + ], + + 'database' => [ + 'driver' => 'database', + 'table' => 'jobs', + 'queue' => env('QUEUE_STANDARD', 'standard'), + 'retry_after' => 90, + 'after_commit' => false, + ], + + 'sqs' => [ + 'driver' => 'sqs', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'), + 'queue' => env('SQS_QUEUE', env('QUEUE_STANDARD', 'standard')), + 'suffix' => env('SQS_SUFFIX'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + 'after_commit' => false, + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => 'default', + 'queue' => env('REDIS_QUEUE', env('QUEUE_STANDARD', 'standard')), + 'retry_after' => 90, + 'block_for' => null, + 'after_commit' => false, + ], + ], + + /* + |-------------------------------------------------------------------------- + | Failed Queue Jobs + |-------------------------------------------------------------------------- + | + | These options configure the behavior of failed queue job logging so you + | can control which database and table are used to store the jobs that + | have failed. You may change them to any database / table you wish. + | + */ + + 'failed' => [ + 'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'), + 'database' => env('DB_CONNECTION', 'mysql'), + 'table' => 'failed_jobs', + ], +]; diff --git a/config/recaptcha.php b/config/recaptcha.php new file mode 100644 index 0000000..757e184 --- /dev/null +++ b/config/recaptcha.php @@ -0,0 +1,31 @@ + env('RECAPTCHA_ENABLED', true), + + /* + * API endpoint for recaptcha checks. You should not edit this. + */ + 'domain' => env('RECAPTCHA_DOMAIN', 'https://www.google.com/recaptcha/api/siteverify'), + + /* + * Use a custom secret key, we use our public one by default + */ + 'secret_key' => env('RECAPTCHA_SECRET_KEY', '6LcJcjwUAAAAALOcDJqAEYKTDhwELCkzUkNDQ0J5'), + '_shipped_secret_key' => '6LcJcjwUAAAAALOcDJqAEYKTDhwELCkzUkNDQ0J5', + + /* + * Use a custom website key, we use our public one by default + */ + 'website_key' => env('RECAPTCHA_WEBSITE_KEY', '6LcJcjwUAAAAAO_Xqjrtj9wWufUpYRnK6BW8lnfn'), + '_shipped_website_key' => '6LcJcjwUAAAAAO_Xqjrtj9wWufUpYRnK6BW8lnfn', + + /* + * Domain verification is enabled by default and compares the domain used when solving the captcha + * as public keys can't have domain verification on google's side enabled (obviously). + */ + 'verify_domain' => true, +]; diff --git a/config/sanctum.php b/config/sanctum.php new file mode 100644 index 0000000..21129a8 --- /dev/null +++ b/config/sanctum.php @@ -0,0 +1,63 @@ + explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf( + '%s%s', + 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1', + Laravel\Sanctum\Sanctum::currentApplicationUrlWithPort() + ))), + + /* + |-------------------------------------------------------------------------- + | Sanctum Guards + |-------------------------------------------------------------------------- + | + | This array contains the authentication guards that will be checked when + | Sanctum is trying to authenticate a request. If none of these guards + | are able to authenticate the request, Sanctum will use the bearer + | token that's present on an incoming request for authentication. + | + */ + + 'guard' => ['web'], + + /* + |-------------------------------------------------------------------------- + | Expiration Minutes + |-------------------------------------------------------------------------- + | + | This value controls the number of minutes until an issued token will be + | considered expired. If this value is null, personal access tokens do + | not expire. This won't tweak the lifetime of first-party sessions. + | + */ + + 'expiration' => null, + + /* + |-------------------------------------------------------------------------- + | Sanctum Middleware + |-------------------------------------------------------------------------- + | + | When authenticating your first-party SPA with Sanctum you may need to + | customize some of the middleware Sanctum uses while processing the + | request. You may change the middleware listed below as required. + | + */ + + 'middleware' => [ + 'verify_csrf_token' => Pterodactyl\Http\Middleware\VerifyCsrfToken::class, + 'encrypt_cookies' => Pterodactyl\Http\Middleware\EncryptCookies::class, + ], +]; diff --git a/config/services.php b/config/services.php new file mode 100644 index 0000000..5cc6e06 --- /dev/null +++ b/config/services.php @@ -0,0 +1,32 @@ + [ + 'domain' => env('MAILGUN_DOMAIN'), + 'secret' => env('MAILGUN_SECRET'), + 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'), + 'scheme' => 'https', + ], + + 'postmark' => [ + 'token' => env('POSTMARK_TOKEN'), + ], + + 'ses' => [ + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + ], +]; diff --git a/config/session.php b/config/session.php new file mode 100644 index 0000000..d8fb98a --- /dev/null +++ b/config/session.php @@ -0,0 +1,199 @@ + env('SESSION_DRIVER', 'redis'), + + /* + |-------------------------------------------------------------------------- + | Session Lifetime + |-------------------------------------------------------------------------- + | + | Here you may specify the number of minutes that you wish the session + | to be allowed to remain idle before it expires. If you want them + | to immediately expire on the browser closing, set that option. + | + */ + + 'lifetime' => env('SESSION_LIFETIME', 720), + + 'expire_on_close' => false, + + /* + |-------------------------------------------------------------------------- + | Session Encryption + |-------------------------------------------------------------------------- + | + | This option allows you to easily specify that all of your session data + | should be encrypted before it is stored. All encryption will be run + | automatically by Laravel and you can use the Session like normal. + | + */ + + 'encrypt' => true, + + /* + |-------------------------------------------------------------------------- + | Session File Location + |-------------------------------------------------------------------------- + | + | When using the native session driver, we need a location where session + | files may be stored. A default has been set for you but a different + | location may be specified. This is only needed for file sessions. + | + */ + + 'files' => storage_path('framework/sessions'), + + /* + |-------------------------------------------------------------------------- + | Session Database Connection + |-------------------------------------------------------------------------- + | + | When using the "database" or "redis" session drivers, you may specify a + | connection that should be used to manage these sessions. This should + | correspond to a connection in your database configuration options. + | + */ + + 'connection' => env('SESSION_CONNECTION'), + + /* + |-------------------------------------------------------------------------- + | Session Database Table + |-------------------------------------------------------------------------- + | + | When using the "database" session driver, you may specify the table we + | should use to manage the sessions. Of course, a sensible default is + | provided for you; however, you are free to change this as needed. + | + */ + + 'table' => 'sessions', + + /* + |-------------------------------------------------------------------------- + | Session Cache Store + |-------------------------------------------------------------------------- + | + | While using one of the framework's cache driven session backends you may + | list a cache store that should be used for these sessions. This value + | must match with one of the application's configured cache "stores". + | + | Affects: "apc", "dynamodb", "memcached", "redis" + | + */ + + 'store' => env('SESSION_STORE'), + + /* + |-------------------------------------------------------------------------- + | Session Sweeping Lottery + |-------------------------------------------------------------------------- + | + | Some session drivers must manually sweep their storage location to get + | rid of old sessions from storage. Here are the chances that it will + | happen on a given request. By default, the odds are 2 out of 100. + | + */ + + 'lottery' => [2, 100], + + /* + |-------------------------------------------------------------------------- + | Session Cookie Name + |-------------------------------------------------------------------------- + | + | Here you may change the name of the cookie used to identify a session + | instance by ID. The name specified here will get used every time a + | new session cookie is created by the framework for every driver. + | + */ + + 'cookie' => env( + 'SESSION_COOKIE', + Str::slug(env('APP_NAME', 'pterodactyl'), '_') . '_session' + ), + + /* + |-------------------------------------------------------------------------- + | Session Cookie Path + |-------------------------------------------------------------------------- + | + | The session cookie path determines the path for which the cookie will + | be regarded as available. Typically, this will be the root path of + | your application but you are free to change this when necessary. + | + */ + + 'path' => '/', + + /* + |-------------------------------------------------------------------------- + | Session Cookie Domain + |-------------------------------------------------------------------------- + | + | Here you may change the domain of the cookie used to identify a session + | in your application. This will determine which domains the cookie is + | available to in your application. A sensible default has been set. + | + */ + + 'domain' => env('SESSION_DOMAIN'), + + /* + |-------------------------------------------------------------------------- + | HTTPS Only Cookies + |-------------------------------------------------------------------------- + | + | By setting this option to true, session cookies will only be sent back + | to the server if the browser has a HTTPS connection. This will keep + | the cookie from being sent to you when it can't be done securely. + | + */ + + 'secure' => env('SESSION_SECURE_COOKIE'), + + /* + |-------------------------------------------------------------------------- + | HTTP Access Only + |-------------------------------------------------------------------------- + | + | Setting this value to true will prevent JavaScript from accessing the + | value of the cookie and the cookie will only be accessible through + | the HTTP protocol. You are free to modify this option if needed. + | + */ + + 'http_only' => true, + + /* + |-------------------------------------------------------------------------- + | Same-Site Cookies + |-------------------------------------------------------------------------- + | + | This option determines how your cookies behave when cross-site requests + | take place, and can be used to mitigate CSRF attacks. By default, we + | will set this value to "lax" since this is a secure default value. + | + | Supported: "lax", "strict", "none", null + | + */ + + 'same_site' => env('SESSION_SAMESITE_COOKIE', 'lax'), +]; diff --git a/config/trustedproxy.php b/config/trustedproxy.php new file mode 100644 index 0000000..7e0166a --- /dev/null +++ b/config/trustedproxy.php @@ -0,0 +1,28 @@ +getClientIp() + * always gets the originating client IP, no matter + * how many proxies that client's request has + * subsequently passed through. + */ + 'proxies' => in_array(env('TRUSTED_PROXIES', []), ['*', '**']) ? + env('TRUSTED_PROXIES') : explode(',', env('TRUSTED_PROXIES') ?? ''), +]; diff --git a/config/view.php b/config/view.php new file mode 100644 index 0000000..24dea7a --- /dev/null +++ b/config/view.php @@ -0,0 +1,34 @@ + [ + resource_path('views'), + ], + + /* + |-------------------------------------------------------------------------- + | Compiled View Path + |-------------------------------------------------------------------------- + | + | This option determines where all the compiled Blade templates will be + | stored for your application. Typically, this is within the storage + | directory. However, as usual, you are free to change this value. + | + */ + + 'compiled' => env( + 'VIEW_COMPILED_PATH', + realpath(storage_path('framework/views')) + ), +]; diff --git a/database/.gitignore b/database/.gitignore new file mode 100644 index 0000000..9b1dffd --- /dev/null +++ b/database/.gitignore @@ -0,0 +1 @@ +*.sqlite diff --git a/database/Factories/AllocationFactory.php b/database/Factories/AllocationFactory.php new file mode 100644 index 0000000..4a5eb70 --- /dev/null +++ b/database/Factories/AllocationFactory.php @@ -0,0 +1,36 @@ + $this->faker->unique()->ipv4, + 'port' => $this->faker->unique()->numberBetween(1024, 65535), + ]; + } + + /** + * Attaches the allocation to a specific server model. + */ + public function forServer(Server $server): self + { + return $this->for($server)->for($server->node); + } +} diff --git a/database/Factories/ApiKeyFactory.php b/database/Factories/ApiKeyFactory.php new file mode 100644 index 0000000..40c78ce --- /dev/null +++ b/database/Factories/ApiKeyFactory.php @@ -0,0 +1,36 @@ + ApiKey::TYPE_APPLICATION, + 'identifier' => ApiKey::generateTokenIdentifier(ApiKey::TYPE_APPLICATION), + 'token' => $token ?: $token = encrypt(Str::random(ApiKey::KEY_LENGTH)), + 'allowed_ips' => null, + 'memo' => 'Test Function Key', + 'created_at' => Carbon::now(), + 'updated_at' => Carbon::now(), + ]; + } +} diff --git a/database/Factories/BackupFactory.php b/database/Factories/BackupFactory.php new file mode 100644 index 0000000..4333ee3 --- /dev/null +++ b/database/Factories/BackupFactory.php @@ -0,0 +1,33 @@ + Uuid::uuid4()->toString(), + 'name' => $this->faker->sentence, + 'disk' => Backup::ADAPTER_WINGS, + 'is_successful' => true, + 'created_at' => CarbonImmutable::now(), + 'completed_at' => CarbonImmutable::now(), + ]; + } +} diff --git a/database/Factories/DatabaseFactory.php b/database/Factories/DatabaseFactory.php new file mode 100644 index 0000000..d3c6f2a --- /dev/null +++ b/database/Factories/DatabaseFactory.php @@ -0,0 +1,35 @@ + Str::random(10), + 'username' => Str::random(10), + 'remote' => '%', + 'password' => $password ?: encrypt('test123'), + 'created_at' => Carbon::now(), + 'updated_at' => Carbon::now(), + ]; + } +} diff --git a/database/Factories/DatabaseHostFactory.php b/database/Factories/DatabaseHostFactory.php new file mode 100644 index 0000000..b65e856 --- /dev/null +++ b/database/Factories/DatabaseHostFactory.php @@ -0,0 +1,31 @@ + $this->faker->colorName, + 'host' => $this->faker->unique()->ipv4, + 'port' => 3306, + 'username' => $this->faker->colorName, + 'password' => Crypt::encrypt($this->faker->word), + ]; + } +} diff --git a/database/Factories/EggFactory.php b/database/Factories/EggFactory.php new file mode 100644 index 0000000..4085b70 --- /dev/null +++ b/database/Factories/EggFactory.php @@ -0,0 +1,30 @@ + Uuid::uuid4()->toString(), + 'name' => $this->faker->name, + 'description' => implode(' ', $this->faker->sentences()), + 'startup' => 'java -jar test.jar', + ]; + } +} diff --git a/database/Factories/EggVariableFactory.php b/database/Factories/EggVariableFactory.php new file mode 100644 index 0000000..c2bce81 --- /dev/null +++ b/database/Factories/EggVariableFactory.php @@ -0,0 +1,57 @@ + $this->faker->unique()->firstName, + 'description' => $this->faker->sentence(), + 'env_variable' => Str::upper(Str::replaceArray(' ', ['_'], $this->faker->words(2, true))), + 'default_value' => $this->faker->colorName, + 'user_viewable' => 0, + 'user_editable' => 0, + 'rules' => 'required|string', + ]; + } + + /** + * Indicate that the egg variable is viewable. + */ + public function viewable(): static + { + return $this->state(function (array $attributes) { + return [ + 'user_viewable' => 1, + ]; + }); + } + + /** + * Indicate that the egg variable is editable. + */ + public function editable(): static + { + return $this->state(function (array $attributes) { + return [ + 'user_editable' => 1, + ]; + }); + } +} diff --git a/database/Factories/LocationFactory.php b/database/Factories/LocationFactory.php new file mode 100644 index 0000000..9e0af5d --- /dev/null +++ b/database/Factories/LocationFactory.php @@ -0,0 +1,28 @@ + Str::random(8), + 'long' => Str::random(32), + ]; + } +} diff --git a/database/Factories/NestFactory.php b/database/Factories/NestFactory.php new file mode 100644 index 0000000..9a5755b --- /dev/null +++ b/database/Factories/NestFactory.php @@ -0,0 +1,30 @@ + Uuid::uuid4()->toString(), + 'author' => 'testauthor@example.com', + 'name' => $this->faker->word, + 'description' => null, + ]; + } +} diff --git a/database/Factories/NodeFactory.php b/database/Factories/NodeFactory.php new file mode 100644 index 0000000..1dbd5d9 --- /dev/null +++ b/database/Factories/NodeFactory.php @@ -0,0 +1,44 @@ + Uuid::uuid4()->toString(), + 'public' => true, + 'name' => 'FactoryNode_' . Str::random(10), + 'fqdn' => $this->faker->unique()->ipv4, + 'scheme' => 'http', + 'behind_proxy' => false, + 'memory' => 1024, + 'memory_overallocate' => 0, + 'disk' => 10240, + 'disk_overallocate' => 0, + 'upload_size' => 100, + 'daemon_token_id' => Str::random(Node::DAEMON_TOKEN_ID_LENGTH), + 'daemon_token' => Crypt::encrypt(Str::random(Node::DAEMON_TOKEN_LENGTH)), + 'daemonListen' => 8080, + 'daemonSFTP' => 2022, + 'daemonBase' => '/var/lib/pterodactyl/volumes', + ]; + } +} diff --git a/database/Factories/ScheduleFactory.php b/database/Factories/ScheduleFactory.php new file mode 100644 index 0000000..0a28f4c --- /dev/null +++ b/database/Factories/ScheduleFactory.php @@ -0,0 +1,26 @@ + $this->faker->firstName(), + ]; + } +} diff --git a/database/Factories/ServerFactory.php b/database/Factories/ServerFactory.php new file mode 100644 index 0000000..d89bd6a --- /dev/null +++ b/database/Factories/ServerFactory.php @@ -0,0 +1,50 @@ + Uuid::uuid4()->toString(), + 'uuidShort' => Str::lower(Str::random(8)), + 'name' => $this->faker->firstName, + 'description' => implode(' ', $this->faker->sentences()), + 'skip_scripts' => 0, + 'status' => null, + 'memory' => 512, + 'swap' => 0, + 'disk' => 512, + 'io' => 500, + 'cpu' => 0, + 'threads' => null, + 'oom_disabled' => 0, + 'startup' => '/bin/bash echo "hello world"', + 'image' => 'foo/bar:latest', + 'allocation_limit' => null, + 'database_limit' => null, + 'backup_limit' => 0, + 'created_at' => Carbon::now(), + 'updated_at' => Carbon::now(), + ]; + } +} diff --git a/database/Factories/SubuserFactory.php b/database/Factories/SubuserFactory.php new file mode 100644 index 0000000..7db93d4 --- /dev/null +++ b/database/Factories/SubuserFactory.php @@ -0,0 +1,29 @@ + [ + Permission::ACTION_WEBSOCKET_CONNECT, + ], + ]; + } +} diff --git a/database/Factories/TaskFactory.php b/database/Factories/TaskFactory.php new file mode 100644 index 0000000..32bf950 --- /dev/null +++ b/database/Factories/TaskFactory.php @@ -0,0 +1,22 @@ + $this->faker->numberBetween(1, 10), + 'action' => 'command', + 'payload' => 'test command', + 'time_offset' => 120, + 'is_queued' => false, + ]; + } +} diff --git a/database/Factories/UserFactory.php b/database/Factories/UserFactory.php new file mode 100644 index 0000000..977b032 --- /dev/null +++ b/database/Factories/UserFactory.php @@ -0,0 +1,50 @@ + null, + 'uuid' => Uuid::uuid4()->toString(), + 'username' => $this->faker->userName . '_' . Str::random(10), + 'email' => Str::random(32) . '@example.com', + 'name_first' => $this->faker->firstName, + 'name_last' => $this->faker->lastName, + 'password' => $password ?: $password = bcrypt('password'), + 'language' => 'en', + 'root_admin' => false, + 'use_totp' => false, + 'created_at' => Carbon::now(), + 'updated_at' => Carbon::now(), + ]; + } + + /** + * Indicate that the user is an admin. + */ + public function admin(): static + { + return $this->state(['root_admin' => true]); + } +} diff --git a/database/Factories/UserSSHKeyFactory.php b/database/Factories/UserSSHKeyFactory.php new file mode 100644 index 0000000..ab20b25 --- /dev/null +++ b/database/Factories/UserSSHKeyFactory.php @@ -0,0 +1,64 @@ + 'ssh-dss AAAAB3NzaC1kc3MAAACBAPfiWwEFvBOafdUmHDPjXsUttt+65FHSZSCVVeEFOTaL7Y3d0CJyrtck8KS1vmXHSb8QFBY2B1yVSb/reaQvNreWZN3KDYfLbF57/zimBn+IrHrJR+ZglhOxDRHoGPWK7q9jYIrOLwoOjkNKXxz1eOHKUgufFfSNtIRLycEXczLrAAAAFQC6LnBErezotG52jN4JostfC/TfEwAAAIACuTxRzYFDXHAxFICeqqY9w+y+v2yQfdeQ1BgCq2GMagUYfOdqnjizTO9M614r/nXZK1SV10TqhUcQtkJzDQIUtBqzBF5cIC/1cIFKzXi5rNHs8Y4bz/PBD+EbQJdiy+1so1oi790r710bqnkzTravAOJ5rGyfuQRLt+f+kuS9NAAAAIEA7tjGtJuXGUtPIXfnrMYS1iOWryO4irqnvaWfel002/DaGaNjRghNe/cUBYlAsjPhGJ1F7BQlLAY1koliTY6l0svs7ZPBM5QOumrr8OaNXGGVIq/RkkxuZHmRoUL2qH3DGYaktPUn4vFPliiAmGWOHAEu1K6B4g4vG/SKgMRpIvc=', + 'rsa_2048' => 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC4VVsHFO5MxvCtAPyoKGANWyuwZ4fvllvFog5RJbfpDpw8etDFVGEXl+uRR8p79g9oV7MscoFo6HiWrJc4/4vlP665msjosILdIcbnuzMhvXnKisaGh9zflkpyR3KhUxoHxqYp2q8XtffjKKAHz1a8o7OUG6fwaKIqu+d0PoICZQ==', + 'rsa_4096' => 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCo/YLm2SPSlOIG7AagBSmEe5c0b2PLPzUGFp3gARhD6n6ydBS40TlWzeg2qV95lh6fWBd8LsNgPOFmmuKuNZdBjAGeTY4gxKfHY1vK5/zOI4jPPqAMcCMNfd82aM97kx6dO8Hw1R79OyVpOZylpXLHayVPGHUK37Tpih4W7TeVSMrOqQF9F72lzhwgEtkdjm4gLBL6RpdNXrdnjIaNVnuade0Sb3w384vecZPe+S/997WirOMNy2JU4NdMHEnSjd1/i463RpN96AsXFAu1zl9nrXVhA7DVfSHoigXAqbs/xav8PRpLgAKjYpPohxQ9Nu6tP5jRUhfWdYwNFFp/aWloD/0JdP9LqcBBc9sO9TLkz3fBiUf11VM/QT1UhO84G+ahMxVn95jA472VPUe8uKff69lzbvSavEE6qcQX2TzVKOSi1E26Fzc6IZ/tHEuGEbGFxTsiQ1GysVZ0wr1p6ftd1SVqH5F/oaEK7UO8+xn/syEqaPf6A0eJWRNc0+lHA1sIRjmo9MOBvbkKExkx5JLHgGG81DYDFdZUuHY1BgSxJJcmNWV5BKRm350EbgRngoYI5tB3tCiZVW1PI8qyff9mBae11LY5GPlUeDnPrMvSdCKMIWrg7nC8SbndBCO3Fx4z7G2dTQy4ZmY7Ae9jR4pyg7tTOI3qgl8Z462GZi/jzw==', + 'ed25519' => 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOaXIq09NH4a93EVdrvHYiZ67Wj+GBEBQ9ou4W0qSYm2', + ]; + + /** + * Returns a fake public key for use on the system. + * + * @return array + */ + public function definition() + { + $key = PublicKeyLoader::loadPublicKey(static::$keys['ed25519']); + + return [ + 'name' => $this->faker->name(), + 'public_key' => $key->toString('PKCS8'), + 'fingerprint' => $key->getFingerprint('sha256'), + ]; + } + + /** + * Returns a DSA public key. + */ + public function dsa(): self + { + $key = PublicKeyLoader::loadPublicKey(static::$keys['dsa']); + + return $this->state([ + 'public_key' => $key->toString('PKCS8'), + 'fingerprint' => $key->getFingerprint('sha256'), + ]); + } + + /** + * Returns an RSA public key, if "weak" is specified a 1024-bit RSA key is returned + * which should fail validation when being stored. + */ + public function rsa(bool $weak = false): self + { + $key = PublicKeyLoader::loadPublicKey(static::$keys[$weak ? 'rsa_2048' : 'rsa_4096']); + + return $this->state([ + 'public_key' => $key->toString('PKCS8'), + 'fingerprint' => $key->getFingerprint('sha256'), + ]); + } +} diff --git a/database/Seeders/.gitkeep b/database/Seeders/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/database/Seeders/DatabaseSeeder.php b/database/Seeders/DatabaseSeeder.php new file mode 100644 index 0000000..ae534fe --- /dev/null +++ b/database/Seeders/DatabaseSeeder.php @@ -0,0 +1,17 @@ +call(NestSeeder::class); + $this->call(EggSeeder::class); + } +} diff --git a/database/Seeders/EggSeeder.php b/database/Seeders/EggSeeder.php new file mode 100644 index 0000000..234e7b5 --- /dev/null +++ b/database/Seeders/EggSeeder.php @@ -0,0 +1,85 @@ +importerService = $importerService; + $this->updateImporterService = $updateImporterService; + } + + /** + * Run the egg seeder. + */ + public function run() + { + foreach (static::$import as $nest) { + /* @noinspection PhpParamsInspection */ + $this->parseEggFiles( + Nest::query()->where('author', 'support@pterodactyl.io')->where('name', $nest)->firstOrFail() + ); + } + } + + /** + * Loop through the list of egg files and import them. + */ + protected function parseEggFiles(Nest $nest) + { + $files = new \DirectoryIterator(database_path('Seeders/eggs/' . kebab_case($nest->name))); + + $this->command->alert('Updating Eggs for Nest: ' . $nest->name); + /** @var \DirectoryIterator $file */ + foreach ($files as $file) { + if (!$file->isFile() || !$file->isReadable()) { + continue; + } + + $decoded = json_decode(file_get_contents($file->getRealPath()), true, 512, JSON_THROW_ON_ERROR); + $file = new UploadedFile($file->getPathname(), $file->getFilename(), 'application/json'); + + $egg = $nest->eggs() + ->where('author', $decoded['author']) + ->where('name', $decoded['name']) + ->first(); + + if ($egg instanceof Egg) { + $this->updateImporterService->handle($egg, $file); + $this->command->info('Updated ' . $decoded['name']); + } else { + $this->importerService->handle($file, $nest->id); + $this->command->comment('Created ' . $decoded['name']); + } + } + + $this->command->line(''); + } +} diff --git a/database/Seeders/NestSeeder.php b/database/Seeders/NestSeeder.php new file mode 100644 index 0000000..eae7ae2 --- /dev/null +++ b/database/Seeders/NestSeeder.php @@ -0,0 +1,108 @@ +creationService = $creationService; + $this->repository = $repository; + } + + /** + * Run the seeder to add missing nests to the Panel. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + */ + public function run() + { + $items = $this->repository->findWhere([ + 'author' => 'support@pterodactyl.io', + ])->keyBy('name')->toArray(); + + $this->createMinecraftNest(array_get($items, 'Minecraft')); + $this->createSourceEngineNest(array_get($items, 'Source Engine')); + $this->createVoiceServersNest(array_get($items, 'Voice Servers')); + $this->createRustNest(array_get($items, 'Rust')); + } + + /** + * Create the Minecraft nest to be used later on. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + */ + private function createMinecraftNest(array $nest = null) + { + if (is_null($nest)) { + $this->creationService->handle([ + 'name' => 'Minecraft', + 'description' => 'Minecraft - the classic game from Mojang. With support for Vanilla MC, Spigot, and many others!', + ], 'support@pterodactyl.io'); + } + } + + /** + * Create the Source Engine Games nest to be used later on. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + */ + private function createSourceEngineNest(array $nest = null) + { + if (is_null($nest)) { + $this->creationService->handle([ + 'name' => 'Source Engine', + 'description' => 'Includes support for most Source Dedicated Server games.', + ], 'support@pterodactyl.io'); + } + } + + /** + * Create the Voice Servers nest to be used later on. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + */ + private function createVoiceServersNest(array $nest = null) + { + if (is_null($nest)) { + $this->creationService->handle([ + 'name' => 'Voice Servers', + 'description' => 'Voice servers such as Mumble and Teamspeak 3.', + ], 'support@pterodactyl.io'); + } + } + + /** + * Create the Rust nest to be used later on. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + */ + private function createRustNest(array $nest = null) + { + if (is_null($nest)) { + $this->creationService->handle([ + 'name' => 'Rust', + 'description' => 'Rust - A game where you must fight to survive.', + ], 'support@pterodactyl.io'); + } + } +} diff --git a/database/Seeders/eggs/minecraft/egg-bungeecord.json b/database/Seeders/eggs/minecraft/egg-bungeecord.json new file mode 100644 index 0000000..0ecf03a --- /dev/null +++ b/database/Seeders/eggs/minecraft/egg-bungeecord.json @@ -0,0 +1,60 @@ +{ + "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", + "meta": { + "version": "PTDL_v2", + "update_url": null + }, + "exported_at": "2024-05-07T12:55:57+00:00", + "name": "Bungeecord", + "author": "support@pterodactyl.io", + "description": "For a long time, Minecraft server owners have had a dream that encompasses a free, easy, and reliable way to connect multiple Minecraft servers together. BungeeCord is the answer to said dream. Whether you are a small server wishing to string multiple game-modes together, or the owner of the ShotBow Network, BungeeCord is the ideal solution for you. With the help of BungeeCord, you will be able to unlock your community's full potential.", + "features": [ + "eula", + "java_version", + "pid_limit" + ], + "docker_images": { + "Java 21": "ghcr.io\/pterodactyl\/yolks:java_21", + "Java 17": "ghcr.io\/pterodactyl\/yolks:java_17", + "Java 16": "ghcr.io\/pterodactyl\/yolks:java_16", + "Java 11": "ghcr.io\/pterodactyl\/yolks:java_11", + "Java 8": "ghcr.io\/pterodactyl\/yolks:java_8" + }, + "file_denylist": [], + "startup": "java -Xms128M -XX:MaxRAMPercentage=95.0 -jar {{SERVER_JARFILE}}", + "config": { + "files": "{\r\n \"config.yml\": {\r\n \"parser\": \"yaml\",\r\n \"find\": {\r\n \"listeners[0].query_port\": \"{{server.build.default.port}}\",\r\n \"listeners[0].host\": \"0.0.0.0:{{server.build.default.port}}\",\r\n \"servers.*.address\": {\r\n \"regex:^(127\\\\.0\\\\.0\\\\.1|localhost)(:\\\\d{1,5})?$\": \"{{config.docker.interface}}$2\"\r\n }\r\n }\r\n }\r\n}", + "startup": "{\r\n \"done\": \"Listening on \"\r\n}", + "logs": "{}", + "stop": "end" + }, + "scripts": { + "installation": { + "script": "#!\/bin\/ash\r\n# Bungeecord Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n\r\ncd \/mnt\/server\r\n\r\nif [ -z \"${BUNGEE_VERSION}\" ] || [ \"${BUNGEE_VERSION}\" == \"latest\" ]; then\r\n BUNGEE_VERSION=\"lastStableBuild\"\r\nfi\r\n\r\ncurl -o ${SERVER_JARFILE} https:\/\/ci.md-5.net\/job\/BungeeCord\/${BUNGEE_VERSION}\/artifact\/bootstrap\/target\/BungeeCord.jar", + "container": "ghcr.io\/pterodactyl\/installers:alpine", + "entrypoint": "ash" + } + }, + "variables": [ + { + "name": "Bungeecord Version", + "description": "The version of Bungeecord to download and use.", + "env_variable": "BUNGEE_VERSION", + "default_value": "latest", + "user_viewable": true, + "user_editable": true, + "rules": "required|alpha_num|between:1,6", + "field_type": "text" + }, + { + "name": "Bungeecord Jar File", + "description": "The name of the Jarfile to use when running Bungeecord.", + "env_variable": "SERVER_JARFILE", + "default_value": "bungeecord.jar", + "user_viewable": true, + "user_editable": true, + "rules": "required|regex:\/^([\\w\\d._-]+)(\\.jar)$\/", + "field_type": "text" + } + ] +} \ No newline at end of file diff --git a/database/Seeders/eggs/minecraft/egg-forge-minecraft.json b/database/Seeders/eggs/minecraft/egg-forge-minecraft.json new file mode 100644 index 0000000..a30a308 --- /dev/null +++ b/database/Seeders/eggs/minecraft/egg-forge-minecraft.json @@ -0,0 +1,80 @@ +{ + "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", + "meta": { + "version": "PTDL_v2", + "update_url": null + }, + "exported_at": "2024-05-07T12:55:56+00:00", + "name": "Forge Minecraft", + "author": "support@pterodactyl.io", + "description": "Minecraft Forge Server. Minecraft Forge is a modding API (Application Programming Interface), which makes it easier to create mods, and also make sure mods are compatible with each other.", + "features": [ + "eula", + "java_version", + "pid_limit" + ], + "docker_images": { + "Java 21": "ghcr.io\/pterodactyl\/yolks:java_21", + "Java 17": "ghcr.io\/pterodactyl\/yolks:java_17", + "Java 16": "ghcr.io\/pterodactyl\/yolks:java_16", + "Java 11": "ghcr.io\/pterodactyl\/yolks:java_11", + "Java 8": "ghcr.io\/pterodactyl\/yolks:java_8" + }, + "file_denylist": [], + "startup": "java -Xms128M -XX:MaxRAMPercentage=95.0 -Dterminal.jline=false -Dterminal.ansi=true $( [[ ! -f unix_args.txt ]] && printf %s \"-jar {{SERVER_JARFILE}}\" || printf %s \"@unix_args.txt\" )", + "config": { + "files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"server-port\": \"{{server.build.default.port}}\",\r\n \"query.port\": \"{{server.build.default.port}}\"\r\n }\r\n }\r\n}", + "startup": "{\r\n \"done\": \")! For help, type \"\r\n}", + "logs": "{}", + "stop": "stop" + }, + "scripts": { + "installation": { + "script": "#!\/bin\/bash\r\n# Forge Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napt update\r\napt install -y curl jq\r\n\r\nif [[ ! -d \/mnt\/server ]]; then\r\n mkdir \/mnt\/server\r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\n# Remove spaces from the version number to avoid issues with curl\r\nFORGE_VERSION=\"$(echo \"$FORGE_VERSION\" | tr -d ' ')\"\r\nMC_VERSION=\"$(echo \"$MC_VERSION\" | tr -d ' ')\"\r\n\r\nif [[ ! -z ${FORGE_VERSION} ]]; then\r\n DOWNLOAD_LINK=https:\/\/maven.minecraftforge.net\/net\/minecraftforge\/forge\/${FORGE_VERSION}\/forge-${FORGE_VERSION}\r\n FORGE_JAR=forge-${FORGE_VERSION}*.jar\r\nelse\r\n JSON_DATA=$(curl -sSL https:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/promotions_slim.json)\r\n\r\n if [[ \"${MC_VERSION}\" == \"latest\" ]] || [[ \"${MC_VERSION}\" == \"\" ]]; then\r\n echo -e \"getting latest version of forge.\"\r\n MC_VERSION=$(echo -e ${JSON_DATA} | jq -r '.promos | del(.\"latest-1.7.10\") | del(.\"1.7.10-latest-1.7.10\") | to_entries[] | .key | select(contains(\"latest\")) | split(\"-\")[0]' | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n | tail -1)\r\n BUILD_TYPE=latest\r\n fi\r\n\r\n if [[ \"${BUILD_TYPE}\" != \"recommended\" ]] && [[ \"${BUILD_TYPE}\" != \"latest\" ]]; then\r\n BUILD_TYPE=recommended\r\n fi\r\n\r\n echo -e \"minecraft version: ${MC_VERSION}\"\r\n echo -e \"build type: ${BUILD_TYPE}\"\r\n\r\n ## some variables for getting versions and things\r\n FILE_SITE=https:\/\/maven.minecraftforge.net\/net\/minecraftforge\/forge\/\r\n VERSION_KEY=$(echo -e ${JSON_DATA} | jq -r --arg MC_VERSION \"${MC_VERSION}\" --arg BUILD_TYPE \"${BUILD_TYPE}\" '.promos | del(.\"latest-1.7.10\") | del(.\"1.7.10-latest-1.7.10\") | to_entries[] | .key | select(contains($MC_VERSION)) | select(contains($BUILD_TYPE))')\r\n\r\n ## locating the forge version\r\n if [[ \"${VERSION_KEY}\" == \"\" ]] && [[ \"${BUILD_TYPE}\" == \"recommended\" ]]; then\r\n echo -e \"dropping back to latest from recommended due to there not being a recommended version of forge for the mc version requested.\"\r\n VERSION_KEY=$(echo -e ${JSON_DATA} | jq -r --arg MC_VERSION \"${MC_VERSION}\" '.promos | del(.\"latest-1.7.10\") | del(.\"1.7.10-latest-1.7.10\") | to_entries[] | .key | select(contains($MC_VERSION)) | select(contains(\"latest\"))')\r\n fi\r\n\r\n ## Error if the mc version set wasn't valid.\r\n if [ \"${VERSION_KEY}\" == \"\" ] || [ \"${VERSION_KEY}\" == \"null\" ]; then\r\n echo -e \"The install failed because there is no valid version of forge for the version of minecraft selected.\"\r\n exit 1\r\n fi\r\n\r\n FORGE_VERSION=$(echo -e ${JSON_DATA} | jq -r --arg VERSION_KEY \"$VERSION_KEY\" '.promos | .[$VERSION_KEY]')\r\n\r\n if [[ \"${MC_VERSION}\" == \"1.7.10\" ]] || [[ \"${MC_VERSION}\" == \"1.8.9\" ]]; then\r\n DOWNLOAD_LINK=${FILE_SITE}${MC_VERSION}-${FORGE_VERSION}-${MC_VERSION}\/forge-${MC_VERSION}-${FORGE_VERSION}-${MC_VERSION}\r\n FORGE_JAR=forge-${MC_VERSION}-${FORGE_VERSION}-${MC_VERSION}.jar\r\n if [[ \"${MC_VERSION}\" == \"1.7.10\" ]]; then\r\n FORGE_JAR=forge-${MC_VERSION}-${FORGE_VERSION}-${MC_VERSION}-universal.jar\r\n fi\r\n else\r\n DOWNLOAD_LINK=${FILE_SITE}${MC_VERSION}-${FORGE_VERSION}\/forge-${MC_VERSION}-${FORGE_VERSION}\r\n FORGE_JAR=forge-${MC_VERSION}-${FORGE_VERSION}.jar\r\n fi\r\nfi\r\n\r\n#Adding .jar when not eding by SERVER_JARFILE\r\nif [[ ! $SERVER_JARFILE = *\\.jar ]]; then\r\n SERVER_JARFILE=\"$SERVER_JARFILE.jar\"\r\nfi\r\n\r\n#Downloading jars\r\necho -e \"Downloading forge version ${FORGE_VERSION}\"\r\necho -e \"Download link is ${DOWNLOAD_LINK}\"\r\n\r\nif [[ ! -z \"${DOWNLOAD_LINK}\" ]]; then\r\n if curl --output \/dev\/null --silent --head --fail ${DOWNLOAD_LINK}-installer.jar; then\r\n echo -e \"installer jar download link is valid.\"\r\n else\r\n echo -e \"link is invalid. Exiting now\"\r\n exit 2\r\n fi\r\nelse\r\n echo -e \"no download link provided. Exiting now\"\r\n exit 3\r\nfi\r\n\r\ncurl -s -o installer.jar -sS ${DOWNLOAD_LINK}-installer.jar\r\n\r\n#Checking if downloaded jars exist\r\nif [[ ! -f .\/installer.jar ]]; then\r\n echo \"!!! Error downloading forge version ${FORGE_VERSION} !!!\"\r\n exit\r\nfi\r\n\r\nfunction unix_args {\r\n echo -e \"Detected Forge 1.17 or newer version. Setting up forge unix args.\"\r\n ln -sf libraries\/net\/minecraftforge\/forge\/*\/unix_args.txt unix_args.txt\r\n}\r\n\r\n# Delete args to support downgrading\/upgrading\r\nrm -rf libraries\/net\/minecraftforge\/forge\r\nrm unix_args.txt\r\n\r\n#Installing server\r\necho -e \"Installing forge server.\\n\"\r\njava -jar installer.jar --installServer || { echo -e \"\\nInstall failed using Forge version ${FORGE_VERSION} and Minecraft version ${MINECRAFT_VERSION}.\\nShould you be using unlimited memory value of 0, make sure to increase the default install resource limits in the Wings config or specify exact allocated memory in the server Build Configuration instead of 0! \\nOtherwise, the Forge installer will not have enough memory.\"; exit 4; }\r\n\r\n# Check if we need a symlink for 1.17+ Forge JPMS args\r\nif [[ $MC_VERSION =~ ^1\\.(17|18|19|20|21|22|23) || $FORGE_VERSION =~ ^1\\.(17|18|19|20|21|22|23) ]]; then\r\n unix_args\r\n\r\n# Check if someone has set MC to latest but overwrote it with older Forge version, otherwise we would have false positives\r\nelif [[ $MC_VERSION == \"latest\" && $FORGE_VERSION =~ ^1\\.(17|18|19|20|21|22|23) ]]; then\r\n unix_args\r\nelse\r\n # For versions below 1.17 that ship with jar\r\n mv $FORGE_JAR $SERVER_JARFILE\r\nfi\r\n\r\necho -e \"Deleting installer.jar file.\\n\"\r\nrm -rf installer.jar\r\necho -e \"Installation process is completed\"", + "container": "openjdk:8-jdk-slim", + "entrypoint": "bash" + } + }, + "variables": [ + { + "name": "Server Jar File", + "description": "The name of the Jarfile to use when running Forge version below 1.17.", + "env_variable": "SERVER_JARFILE", + "default_value": "server.jar", + "user_viewable": true, + "user_editable": true, + "rules": "required|regex:\/^([\\w\\d._-]+)(\\.jar)$\/", + "field_type": "text" + }, + { + "name": "Minecraft Version", + "description": "The version of minecraft you want to install for.\r\n\r\nLeaving latest will install the latest recommended version.", + "env_variable": "MC_VERSION", + "default_value": "latest", + "user_viewable": true, + "user_editable": true, + "rules": "required|string|max:9", + "field_type": "text" + }, + { + "name": "Build Type", + "description": "The type of server jar to download from forge.\r\n\r\nValid types are \"recommended\" and \"latest\".", + "env_variable": "BUILD_TYPE", + "default_value": "recommended", + "user_viewable": true, + "user_editable": true, + "rules": "required|string|in:recommended,latest", + "field_type": "text" + }, + { + "name": "Forge Version", + "description": "The full exact version.\r\n\r\nEx. 1.15.2-31.2.4\r\n\r\nOverrides MC_VERSION and BUILD_TYPE. If it fails to download the server files it will fail to install.", + "env_variable": "FORGE_VERSION", + "default_value": "", + "user_viewable": true, + "user_editable": true, + "rules": "nullable|regex:\/^[0-9\\.\\-]+$\/", + "field_type": "text" + } + ] +} \ No newline at end of file diff --git a/database/Seeders/eggs/minecraft/egg-paper.json b/database/Seeders/eggs/minecraft/egg-paper.json new file mode 100644 index 0000000..cb78f78 --- /dev/null +++ b/database/Seeders/eggs/minecraft/egg-paper.json @@ -0,0 +1,80 @@ +{ + "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", + "meta": { + "version": "PTDL_v2", + "update_url": null + }, + "exported_at": "2024-05-07T12:55:55+00:00", + "name": "Paper", + "author": "parker@pterodactyl.io", + "description": "High performance Spigot fork that aims to fix gameplay and mechanics inconsistencies.", + "features": [ + "eula", + "java_version", + "pid_limit" + ], + "docker_images": { + "Java 21": "ghcr.io\/pterodactyl\/yolks:java_21", + "Java 17": "ghcr.io\/pterodactyl\/yolks:java_17", + "Java 16": "ghcr.io\/pterodactyl\/yolks:java_16", + "Java 11": "ghcr.io\/pterodactyl\/yolks:java_11", + "Java 8": "ghcr.io\/pterodactyl\/yolks:java_8" + }, + "file_denylist": [], + "startup": "java -Xms128M -XX:MaxRAMPercentage=95.0 -Dterminal.jline=false -Dterminal.ansi=true -jar {{SERVER_JARFILE}}", + "config": { + "files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"server-port\": \"{{server.build.default.port}}\",\r\n \"query.port\": \"{{server.build.default.port}}\"\r\n }\r\n }\r\n}", + "startup": "{\r\n \"done\": \")! For help, type \"\r\n}", + "logs": "{}", + "stop": "stop" + }, + "scripts": { + "installation": { + "script": "#!\/bin\/ash\r\n# Paper Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\nPROJECT=paper\r\n\r\nif [ -n \"${DL_PATH}\" ]; then\r\n\techo -e \"Using supplied download url: ${DL_PATH}\"\r\n\tDOWNLOAD_URL=`eval echo $(echo ${DL_PATH} | sed -e 's\/{{\/${\/g' -e 's\/}}\/}\/g')`\r\nelse\r\n\tVER_EXISTS=`curl -s https:\/\/api.papermc.io\/v2\/projects\/${PROJECT} | jq -r --arg VERSION $MINECRAFT_VERSION '.versions[] | contains($VERSION)' | grep -m1 true`\r\n\tLATEST_VERSION=`curl -s https:\/\/api.papermc.io\/v2\/projects\/${PROJECT} | jq -r '.versions' | jq -r '.[-1]'`\r\n\r\n\tif [ \"${VER_EXISTS}\" == \"true\" ]; then\r\n\t\techo -e \"Version is valid. Using version ${MINECRAFT_VERSION}\"\r\n\telse\r\n\t\techo -e \"Specified version not found. Defaulting to the latest ${PROJECT} version\"\r\n\t\tMINECRAFT_VERSION=${LATEST_VERSION}\r\n\tfi\r\n\r\n\tBUILD_EXISTS=`curl -s https:\/\/api.papermc.io\/v2\/projects\/${PROJECT}\/versions\/${MINECRAFT_VERSION} | jq -r --arg BUILD ${BUILD_NUMBER} '.builds[] | tostring | contains($BUILD)' | grep -m1 true`\r\n\tLATEST_BUILD=`curl -s https:\/\/api.papermc.io\/v2\/projects\/${PROJECT}\/versions\/${MINECRAFT_VERSION} | jq -r '.builds' | jq -r '.[-1]'`\r\n\r\n\tif [ \"${BUILD_EXISTS}\" == \"true\" ]; then\r\n\t\techo -e \"Build is valid for version ${MINECRAFT_VERSION}. Using build ${BUILD_NUMBER}\"\r\n\telse\r\n\t\techo -e \"Using the latest ${PROJECT} build for version ${MINECRAFT_VERSION}\"\r\n\t\tBUILD_NUMBER=${LATEST_BUILD}\r\n\tfi\r\n\r\n\tJAR_NAME=${PROJECT}-${MINECRAFT_VERSION}-${BUILD_NUMBER}.jar\r\n\r\n\techo \"Version being downloaded\"\r\n\techo -e \"MC Version: ${MINECRAFT_VERSION}\"\r\n\techo -e \"Build: ${BUILD_NUMBER}\"\r\n\techo -e \"JAR Name of Build: ${JAR_NAME}\"\r\n\tDOWNLOAD_URL=https:\/\/api.papermc.io\/v2\/projects\/${PROJECT}\/versions\/${MINECRAFT_VERSION}\/builds\/${BUILD_NUMBER}\/downloads\/${JAR_NAME}\r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\necho -e \"Running curl -o ${SERVER_JARFILE} ${DOWNLOAD_URL}\"\r\n\r\nif [ -f ${SERVER_JARFILE} ]; then\r\n\tmv ${SERVER_JARFILE} ${SERVER_JARFILE}.old\r\nfi\r\n\r\ncurl -o ${SERVER_JARFILE} ${DOWNLOAD_URL}\r\n\r\nif [ ! -f server.properties ]; then\r\n echo -e \"Downloading MC server.properties\"\r\n curl -o server.properties https:\/\/raw.githubusercontent.com\/parkervcp\/eggs\/master\/minecraft\/java\/server.properties\r\nfi", + "container": "ghcr.io\/pterodactyl\/installers:alpine", + "entrypoint": "ash" + } + }, + "variables": [ + { + "name": "Minecraft Version", + "description": "The version of minecraft to download. \r\n\r\nLeave at latest to always get the latest version. Invalid versions will default to latest.", + "env_variable": "MINECRAFT_VERSION", + "default_value": "latest", + "user_viewable": true, + "user_editable": true, + "rules": "nullable|string|max:20", + "field_type": "text" + }, + { + "name": "Server Jar File", + "description": "The name of the server jarfile to run the server with.", + "env_variable": "SERVER_JARFILE", + "default_value": "server.jar", + "user_viewable": true, + "user_editable": true, + "rules": "required|regex:\/^([\\w\\d._-]+)(\\.jar)$\/", + "field_type": "text" + }, + { + "name": "Download Path", + "description": "A URL to use to download a server.jar rather than the ones in the install script. This is not user viewable.", + "env_variable": "DL_PATH", + "default_value": "", + "user_viewable": false, + "user_editable": false, + "rules": "nullable|string", + "field_type": "text" + }, + { + "name": "Build Number", + "description": "The build number for the paper release.\r\n\r\nLeave at latest to always get the latest version. Invalid versions will default to latest.", + "env_variable": "BUILD_NUMBER", + "default_value": "latest", + "user_viewable": true, + "user_editable": true, + "rules": "required|string|max:20", + "field_type": "text" + } + ] +} \ No newline at end of file diff --git a/database/Seeders/eggs/minecraft/egg-sponge--sponge-vanilla.json b/database/Seeders/eggs/minecraft/egg-sponge--sponge-vanilla.json new file mode 100644 index 0000000..51575f8 --- /dev/null +++ b/database/Seeders/eggs/minecraft/egg-sponge--sponge-vanilla.json @@ -0,0 +1,59 @@ +{ + "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", + "meta": { + "version": "PTDL_v2", + "update_url": null + }, + "exported_at": "2024-05-07T12:55:54+00:00", + "name": "Sponge (SpongeVanilla)", + "author": "support@pterodactyl.io", + "description": "SpongeVanilla is the SpongeAPI implementation for Vanilla Minecraft.", + "features": [ + "eula", + "java_version", + "pid_limit" + ], + "docker_images": { + "Java 21": "ghcr.io\/pterodactyl\/yolks:java_21", + "Java 16": "ghcr.io\/pterodactyl\/yolks:java_16", + "Java 11": "ghcr.io\/pterodactyl\/yolks:java_11", + "Java 8": "ghcr.io\/pterodactyl\/yolks:java_8" + }, + "file_denylist": [], + "startup": "java -Xms128M -XX:MaxRAMPercentage=95.0 -jar {{SERVER_JARFILE}}", + "config": { + "files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"server-port\": \"{{server.build.default.port}}\",\r\n \"query.port\": \"{{server.build.default.port}}\"\r\n }\r\n }\r\n}", + "startup": "{\r\n \"done\": \")! For help, type \"\r\n}", + "logs": "{}", + "stop": "stop" + }, + "scripts": { + "installation": { + "script": "#!\/bin\/ash\r\n# Sponge Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n\r\ncd \/mnt\/server\r\n\r\ncurl -sSL \"https:\/\/repo.spongepowered.org\/maven\/org\/spongepowered\/spongevanilla\/${SPONGE_VERSION}\/spongevanilla-${SPONGE_VERSION}.jar\" -o ${SERVER_JARFILE}", + "container": "ghcr.io\/pterodactyl\/installers:alpine", + "entrypoint": "ash" + } + }, + "variables": [ + { + "name": "Sponge Version", + "description": "The version of SpongeVanilla to download and use.", + "env_variable": "SPONGE_VERSION", + "default_value": "1.12.2-7.3.0", + "user_viewable": true, + "user_editable": true, + "rules": "required|regex:\/^([a-zA-Z0-9.\\-_]+)$\/", + "field_type": "text" + }, + { + "name": "Server Jar File", + "description": "The name of the Jarfile to use when running SpongeVanilla.", + "env_variable": "SERVER_JARFILE", + "default_value": "server.jar", + "user_viewable": true, + "user_editable": true, + "rules": "required|regex:\/^([\\w\\d._-]+)(\\.jar)$\/", + "field_type": "text" + } + ] +} \ No newline at end of file diff --git a/database/Seeders/eggs/minecraft/egg-vanilla-minecraft.json b/database/Seeders/eggs/minecraft/egg-vanilla-minecraft.json new file mode 100644 index 0000000..71fd165 --- /dev/null +++ b/database/Seeders/eggs/minecraft/egg-vanilla-minecraft.json @@ -0,0 +1,60 @@ +{ + "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", + "meta": { + "version": "PTDL_v2", + "update_url": null + }, + "exported_at": "2024-05-07T12:55:58+00:00", + "name": "Vanilla Minecraft", + "author": "support@pterodactyl.io", + "description": "Minecraft is a game about placing blocks and going on adventures. Explore randomly generated worlds and build amazing things from the simplest of homes to the grandest of castles. Play in Creative Mode with unlimited resources or mine deep in Survival Mode, crafting weapons and armor to fend off dangerous mobs. Do all this alone or with friends.", + "features": [ + "eula", + "java_version", + "pid_limit" + ], + "docker_images": { + "Java 21": "ghcr.io\/pterodactyl\/yolks:java_21", + "Java 17": "ghcr.io\/pterodactyl\/yolks:java_17", + "Java 16": "ghcr.io\/pterodactyl\/yolks:java_16", + "Java 11": "ghcr.io\/pterodactyl\/yolks:java_11", + "Java 8": "ghcr.io\/pterodactyl\/yolks:java_8" + }, + "file_denylist": [], + "startup": "java -Xms128M -XX:MaxRAMPercentage=95.0 -jar {{SERVER_JARFILE}}", + "config": { + "files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"server-port\": \"{{server.build.default.port}}\",\r\n \"query.port\": \"{{server.build.default.port}}\"\r\n }\r\n }\r\n}", + "startup": "{\r\n \"done\": \")! For help, type \"\r\n}", + "logs": "{}", + "stop": "stop" + }, + "scripts": { + "installation": { + "script": "#!\/bin\/ash\r\n# Vanilla MC Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\nmkdir -p \/mnt\/server\r\ncd \/mnt\/server\r\n\r\nLATEST_VERSION=`curl https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq -r '.latest.release'`\r\nLATEST_SNAPSHOT_VERSION=`curl https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq -r '.latest.snapshot'`\r\n\r\necho -e \"latest version is $LATEST_VERSION\"\r\necho -e \"latest snapshot is $LATEST_SNAPSHOT_VERSION\"\r\n\r\nif [ -z \"$VANILLA_VERSION\" ] || [ \"$VANILLA_VERSION\" == \"latest\" ]; then\r\n MANIFEST_URL=$(curl -sSL https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq --arg VERSION $LATEST_VERSION -r '.versions | .[] | select(.id== $VERSION )|.url')\r\nelif [ \"$VANILLA_VERSION\" == \"snapshot\" ]; then\r\n MANIFEST_URL=$(curl -sSL https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq --arg VERSION $LATEST_SNAPSHOT_VERSION -r '.versions | .[] | select(.id== $VERSION )|.url')\r\nelse\r\n MANIFEST_URL=$(curl -sSL https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq --arg VERSION $VANILLA_VERSION -r '.versions | .[] | select(.id== $VERSION )|.url')\r\nfi\r\n\r\nDOWNLOAD_URL=$(curl ${MANIFEST_URL} | jq .downloads.server | jq -r '. | .url')\r\n\r\necho -e \"running: curl -o ${SERVER_JARFILE} $DOWNLOAD_URL\"\r\ncurl -o ${SERVER_JARFILE} $DOWNLOAD_URL\r\n\r\necho -e \"Install Complete\"", + "container": "ghcr.io\/pterodactyl\/installers:alpine", + "entrypoint": "ash" + } + }, + "variables": [ + { + "name": "Server Jar File", + "description": "The name of the server jarfile to run the server with.", + "env_variable": "SERVER_JARFILE", + "default_value": "server.jar", + "user_viewable": true, + "user_editable": true, + "rules": "required|regex:\/^([\\w\\d._-]+)(\\.jar)$\/", + "field_type": "text" + }, + { + "name": "Server Version", + "description": "The version of Minecraft Vanilla to install. Use \"latest\" to install the latest version, or use \"snapshot\" to install the latest snapshot. Go to Settings > Reinstall Server to apply.", + "env_variable": "VANILLA_VERSION", + "default_value": "latest", + "user_viewable": true, + "user_editable": true, + "rules": "required|string|between:3,15", + "field_type": "text" + } + ] +} \ No newline at end of file diff --git a/database/Seeders/eggs/rust/egg-rust.json b/database/Seeders/eggs/rust/egg-rust.json new file mode 100644 index 0000000..35f543c --- /dev/null +++ b/database/Seeders/eggs/rust/egg-rust.json @@ -0,0 +1,204 @@ +{ + "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", + "meta": { + "version": "PTDL_v2", + "update_url": null + }, + "exported_at": "2023-03-25T13:37:00+00:00", + "name": "Rust", + "author": "support@pterodactyl.io", + "description": "The only aim in Rust is to survive. To do this you will need to overcome struggles such as hunger, thirst and cold. Build a fire. Build a shelter. Kill animals for meat. Protect yourself from other players, and kill them for meat. Create alliances with other players and form a town. Do whatever it takes to survive.", + "features": [ + "steam_disk_space" + ], + "docker_images": { + "ghcr.io\/pterodactyl\/games:rust": "ghcr.io\/pterodactyl\/games:rust" + }, + "file_denylist": [], + "startup": ".\/RustDedicated -batchmode +server.port {{SERVER_PORT}} +server.queryport {{QUERY_PORT}} +server.identity \"rust\" +rcon.port {{RCON_PORT}} +rcon.web true +server.hostname \\\"{{HOSTNAME}}\\\" +server.level \\\"{{LEVEL}}\\\" +server.description \\\"{{DESCRIPTION}}\\\" +server.url \\\"{{SERVER_URL}}\\\" +server.headerimage \\\"{{SERVER_IMG}}\\\" +server.logoimage \\\"{{SERVER_LOGO}}\\\" +server.maxplayers {{MAX_PLAYERS}} +rcon.password \\\"{{RCON_PASS}}\\\" +server.saveinterval {{SAVEINTERVAL}} +app.port {{APP_PORT}} $( [ -z ${MAP_URL} ] && printf %s \"+server.worldsize \\\"{{WORLD_SIZE}}\\\" +server.seed \\\"{{WORLD_SEED}}\\\"\" || printf %s \"+server.levelurl {{MAP_URL}}\" ) {{ADDITIONAL_ARGS}}", + "config": { + "files": "{}", + "startup": "{\r\n \"done\": \"Server startup complete\"\r\n}", + "logs": "{}", + "stop": "quit" + }, + "scripts": { + "installation": { + "script": "#!\/bin\/bash\r\n# steamcmd Base Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n\r\nSRCDS_APPID=258550\r\n\r\n## just in case someone removed the defaults.\r\nif [ \"${STEAM_USER}\" == \"\" ]; then\r\n echo -e \"steam user is not set.\\n\"\r\n echo -e \"Using anonymous user.\\n\"\r\n STEAM_USER=anonymous\r\n STEAM_PASS=\"\"\r\n STEAM_AUTH=\"\"\r\nelse\r\n echo -e \"user set to ${STEAM_USER}\"\r\nfi\r\n\r\n## download and install steamcmd\r\ncd \/tmp\r\nmkdir -p \/mnt\/server\/steamcmd\r\ncurl -sSL -o steamcmd.tar.gz https:\/\/steamcdn-a.akamaihd.net\/client\/installer\/steamcmd_linux.tar.gz\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\r\nmkdir -p \/mnt\/server\/steamapps # Fix steamcmd disk write error when this folder is missing\r\ncd \/mnt\/server\/steamcmd\r\n\r\n# SteamCMD fails otherwise for some reason, even running as root.\r\n# This is changed at the end of the install process anyways.\r\nchown -R root:root \/mnt\r\nexport HOME=\/mnt\/server\r\n\r\n## install game using steamcmd\r\n.\/steamcmd.sh +force_install_dir \/mnt\/server +login ${STEAM_USER} ${STEAM_PASS} ${STEAM_AUTH} +app_update ${SRCDS_APPID} ${EXTRA_FLAGS} validate +quit ## other flags may be needed depending on install. looking at you cs 1.6\r\n\r\n## set up 32 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk32\r\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so\r\n\r\n## set up 64 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk64\r\ncp -v linux64\/steamclient.so ..\/.steam\/sdk64\/steamclient.so", + "container": "ghcr.io\/pterodactyl\/installers:debian", + "entrypoint": "bash" + } + }, + "variables": [ + { + "name": "Server Name", + "description": "The name of your server in the public server list.", + "env_variable": "HOSTNAME", + "default_value": "A Rust Server", + "user_viewable": true, + "user_editable": true, + "rules": "required|string|max:60", + "field_type": "text" + }, + { + "name": "Modding Framework", + "description": "The modding framework to be used: carbon, oxide, vanilla.\r\nDefaults to \"vanilla\" for a non-modded server installation.", + "env_variable": "FRAMEWORK", + "default_value": "vanilla", + "user_viewable": true, + "user_editable": true, + "rules": "required|in:vanilla,oxide,carbon", + "field_type": "text" + }, + { + "name": "Level", + "description": "The world file for Rust to use.", + "env_variable": "LEVEL", + "default_value": "Procedural Map", + "user_viewable": true, + "user_editable": true, + "rules": "required|string|max:20", + "field_type": "text" + }, + { + "name": "Description", + "description": "The description under your server title. Commonly used for rules & info. Use \\n for newlines.", + "env_variable": "DESCRIPTION", + "default_value": "Powered by Pterodactyl", + "user_viewable": true, + "user_editable": true, + "rules": "required|string", + "field_type": "text" + }, + { + "name": "URL", + "description": "The URL for your server. This is what comes up when clicking the \"Visit Website\" button.", + "env_variable": "SERVER_URL", + "default_value": "http:\/\/pterodactyl.io", + "user_viewable": true, + "user_editable": true, + "rules": "nullable|url", + "field_type": "text" + }, + { + "name": "World Size", + "description": "The world size for a procedural map.", + "env_variable": "WORLD_SIZE", + "default_value": "3000", + "user_viewable": true, + "user_editable": true, + "rules": "required|integer", + "field_type": "text" + }, + { + "name": "World Seed", + "description": "The seed for a procedural map.", + "env_variable": "WORLD_SEED", + "default_value": "", + "user_viewable": true, + "user_editable": true, + "rules": "nullable|string", + "field_type": "text" + }, + { + "name": "Max Players", + "description": "The maximum amount of players allowed in the server at once.", + "env_variable": "MAX_PLAYERS", + "default_value": "40", + "user_viewable": true, + "user_editable": true, + "rules": "required|integer", + "field_type": "text" + }, + { + "name": "Server Image", + "description": "The header image for the top of your server listing.", + "env_variable": "SERVER_IMG", + "default_value": "", + "user_viewable": true, + "user_editable": true, + "rules": "nullable|url", + "field_type": "text" + }, + { + "name": "Query Port", + "description": "Server Query Port. Can't be the same as Game's primary port.", + "env_variable": "QUERY_PORT", + "default_value": "27017", + "user_viewable": true, + "user_editable": false, + "rules": "required|integer", + "field_type": "text" + }, + { + "name": "RCON Port", + "description": "Port for RCON connections.", + "env_variable": "RCON_PORT", + "default_value": "28016", + "user_viewable": true, + "user_editable": false, + "rules": "required|integer", + "field_type": "text" + }, + { + "name": "RCON Password", + "description": "RCON access password.", + "env_variable": "RCON_PASS", + "default_value": "", + "user_viewable": true, + "user_editable": true, + "rules": "required|regex:\/^[\\w.-]*$\/|max:64", + "field_type": "text" + }, + { + "name": "Save Interval", + "description": "Sets the server\u2019s auto-save interval in seconds.", + "env_variable": "SAVEINTERVAL", + "default_value": "60", + "user_viewable": true, + "user_editable": true, + "rules": "required|integer", + "field_type": "text" + }, + { + "name": "Additional Arguments", + "description": "Add additional startup parameters to the server.", + "env_variable": "ADDITIONAL_ARGS", + "default_value": "", + "user_viewable": true, + "user_editable": true, + "rules": "nullable|string", + "field_type": "text" + }, + { + "name": "App Port", + "description": "Port for the Rust+ App. -1 to disable.", + "env_variable": "APP_PORT", + "default_value": "28082", + "user_viewable": true, + "user_editable": false, + "rules": "required|integer", + "field_type": "text" + }, + { + "name": "Server Logo", + "description": "The circular server logo for the Rust+ app.", + "env_variable": "SERVER_LOGO", + "default_value": "", + "user_viewable": true, + "user_editable": true, + "rules": "nullable|url", + "field_type": "text" + }, + { + "name": "Custom Map URL", + "description": "Overwrites the map with the one from the direct download URL. Invalid URLs will cause the server to crash.", + "env_variable": "MAP_URL", + "default_value": "", + "user_viewable": true, + "user_editable": true, + "rules": "nullable|url", + "field_type": "text" + } + ] +} diff --git a/database/Seeders/eggs/source-engine/egg-ark--survival-evolved.json b/database/Seeders/eggs/source-engine/egg-ark--survival-evolved.json new file mode 100644 index 0000000..660ebd1 --- /dev/null +++ b/database/Seeders/eggs/source-engine/egg-ark--survival-evolved.json @@ -0,0 +1,124 @@ +{ + "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", + "meta": { + "version": "PTDL_v1", + "update_url": null + }, + "exported_at": "2022-01-18T07:01:38-05:00", + "name": "Ark: Survival Evolved", + "author": "dev@shepper.fr", + "description": "As a man or woman stranded, naked, freezing, and starving on the unforgiving shores of a mysterious island called ARK, use your skill and cunning to kill or tame and ride the plethora of leviathan dinosaurs and other primeval creatures roaming the land. Hunt, harvest resources, craft items, grow crops, research technologies, and build shelters to withstand the elements and store valuables, all while teaming up with (or preying upon) hundreds of other players to survive, dominate... and escape! \u2014 Gamepedia: ARK", + "features": [ + "steam_disk_space" + ], + "images": [ + "quay.io\/parkervcp\/pterodactyl-images:debian_source" + ], + "file_denylist": [], + "startup": "rmv() { echo -e \"stopping server\"; rcon -t rcon -a 127.0.0.1:${RCON_PORT} -p ${ARK_ADMIN_PASSWORD} -c saveworld && rcon -a 127.0.0.1:${RCON_PORT} -p ${ARK_ADMIN_PASSWORD} -c DoExit; }; trap rmv 15; cd ShooterGame\/Binaries\/Linux && .\/ShooterGameServer {{SERVER_MAP}}?listen?SessionName=\"{{SESSION_NAME}}\"?ServerPassword={{ARK_PASSWORD}}?ServerAdminPassword={{ARK_ADMIN_PASSWORD}}?Port={{SERVER_PORT}}?RCONPort={{RCON_PORT}}?QueryPort={{QUERY_PORT}}?RCONEnabled=True$( [ \"$BATTLE_EYE\" == \"1\" ] || printf %s ' -NoBattlEye' ) -server {{ARGS}} -log & until echo \"waiting for rcon connection...\"; rcon -t rcon -a 127.0.0.1:${RCON_PORT} -p ${ARK_ADMIN_PASSWORD}; do sleep 5; done", + "config": { + "files": "{}", + "startup": "{\r\n \"done\": \"Waiting commands for 127.0.0.1:\"\r\n}", + "logs": "{}", + "stop": "^C" + }, + "scripts": { + "installation": { + "script": "#!\/bin\/bash\r\n# steamcmd Base Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n# Image to install with is 'ubuntu:18.04'\r\n\r\n## just in case someone removed the defaults.\r\nif [ \"${STEAM_USER}\" == \"\" ]; then\r\n STEAM_USER=anonymous\r\n STEAM_PASS=\"\"\r\n STEAM_AUTH=\"\"\r\nfi\r\n\r\n## download and install steamcmd\r\ncd \/tmp\r\nmkdir -p \/mnt\/server\/steamcmd\r\ncurl -sSL -o steamcmd.tar.gz https:\/\/steamcdn-a.akamaihd.net\/client\/installer\/steamcmd_linux.tar.gz\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\r\n\r\nmkdir -p \/mnt\/server\/Engine\/Binaries\/ThirdParty\/SteamCMD\/Linux\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/Engine\/Binaries\/ThirdParty\/SteamCMD\/Linux\r\nmkdir -p \/mnt\/server\/steamapps # Fix steamcmd disk write error when this folder is missing\r\ncd \/mnt\/server\/steamcmd\r\n\r\n# SteamCMD fails otherwise for some reason, even running as root.\r\n# This is changed at the end of the install process anyways.\r\nchown -R root:root \/mnt\r\nexport HOME=\/mnt\/server\r\n\r\n## install game using steamcmd\r\n.\/steamcmd.sh +force_install_dir \/mnt\/server +login ${STEAM_USER} ${STEAM_PASS} ${STEAM_AUTH} +app_update ${SRCDS_APPID} ${EXTRA_FLAGS} +quit ## other flags may be needed depending on install. looking at you cs 1.6\r\n\r\n## set up 32 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk32\r\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so\r\n\r\n## set up 64 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk64\r\ncp -v linux64\/steamclient.so ..\/.steam\/sdk64\/steamclient.so\r\n\r\n## create a symbolic link for loading mods\r\ncd \/mnt\/server\/Engine\/Binaries\/ThirdParty\/SteamCMD\/Linux\r\nln -sf ..\/..\/..\/..\/..\/Steam\/steamapps steamapps\r\ncd \/mnt\/server", + "container": "ghcr.io\/pterodactyl\/installers:debian", + "entrypoint": "bash" + } + }, + "variables": [ + { + "name": "Server Password", + "description": "If specified, players must provide this password to join the server.", + "env_variable": "ARK_PASSWORD", + "default_value": "", + "user_viewable": true, + "user_editable": true, + "rules": "nullable|alpha_dash|between:1,100" + }, + { + "name": "Admin Password", + "description": "If specified, players must provide this password (via the in-game console) to gain access to administrator commands on the server.", + "env_variable": "ARK_ADMIN_PASSWORD", + "default_value": "PleaseChangeMe", + "user_viewable": true, + "user_editable": true, + "rules": "required|alpha_dash|between:1,100" + }, + { + "name": "Server Map", + "description": "Available Maps: TheIsland, TheCenter, Ragnarok, ScorchedEarth_P, Aberration_P, Extinction, Valguero_P, Genesis, CrystalIsles, Gen2, LostIsland, Fjordur", + "env_variable": "SERVER_MAP", + "default_value": "TheIsland", + "user_viewable": true, + "user_editable": true, + "rules": "required|string|max:20" + }, + { + "name": "Server Name", + "description": "ARK server name", + "env_variable": "SESSION_NAME", + "default_value": "A Pterodactyl Hosted ARK Server", + "user_viewable": true, + "user_editable": true, + "rules": "required|string|max:128" + }, + { + "name": "Rcon Port", + "description": "ARK rcon port used by rcon tools.", + "env_variable": "RCON_PORT", + "default_value": "27020", + "user_viewable": true, + "user_editable": true, + "rules": "required|numeric" + }, + { + "name": "Query Port", + "description": "ARK query port used by steam server browser and ark client server browser.", + "env_variable": "QUERY_PORT", + "default_value": "27015", + "user_viewable": true, + "user_editable": true, + "rules": "required|numeric" + }, + { + "name": "Auto-update server", + "description": "This is to enable auto-updating for servers.\r\n\r\nDefault is 0. Set to 1 to update", + "env_variable": "AUTO_UPDATE", + "default_value": "0", + "user_viewable": true, + "user_editable": true, + "rules": "required|boolean" + }, + { + "name": "Battle Eye", + "description": "Enable BattleEye\r\n\r\n0 to disable\r\n1 to enable\r\n\r\ndefault=\"1\"", + "env_variable": "BATTLE_EYE", + "default_value": "1", + "user_viewable": true, + "user_editable": true, + "rules": "required|boolean" + }, + { + "name": "App ID", + "description": "ARK steam app id for auto updates. Leave blank to avoid auto update.", + "env_variable": "SRCDS_APPID", + "default_value": "376030", + "user_viewable": true, + "user_editable": false, + "rules": "nullable|numeric" + }, + { + "name": "Additional Arguments", + "description": "Specify additional launch parameters such as -crossplay. You must include a dash - and separate each parameter with space: -crossplay -exclusivejoin", + "env_variable": "ARGS", + "default_value": "", + "user_viewable": true, + "user_editable": true, + "rules": "nullable|string" + } + ] +} diff --git a/database/Seeders/eggs/source-engine/egg-counter--strike--global-offensive.json b/database/Seeders/eggs/source-engine/egg-counter--strike--global-offensive.json new file mode 100644 index 0000000..7c9a977 --- /dev/null +++ b/database/Seeders/eggs/source-engine/egg-counter--strike--global-offensive.json @@ -0,0 +1,62 @@ +{ + "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", + "meta": { + "version": "PTDL_v1", + "update_url": null + }, + "exported_at": "2022-01-18T07:01:54-05:00", + "name": "Counter-Strike: Global Offensive", + "author": "support@pterodactyl.io", + "description": "Counter-Strike: Global Offensive is a multiplayer first-person shooter video game developed by Hidden Path Entertainment and Valve Corporation.", + "features": [ + "gsl_token", + "steam_disk_space" + ], + "images": [ + "ghcr.io\/pterodactyl\/games:source" + ], + "file_denylist": [], + "startup": ".\/srcds_run -game csgo -console -port {{SERVER_PORT}} +ip 0.0.0.0 +map {{SRCDS_MAP}} -strictportbind -norestart +sv_setsteamaccount {{STEAM_ACC}}", + "config": { + "files": "{}", + "startup": "{\r\n \"done\": \"Connection to Steam servers successful\"\r\n}", + "logs": "{}", + "stop": "quit" + }, + "scripts": { + "installation": { + "script": "#!\/bin\/bash\r\n# steamcmd Base Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n\r\n## just in case someone removed the defaults.\r\nif [ \"${STEAM_USER}\" == \"\" ]; then\r\n STEAM_USER=anonymous\r\n STEAM_PASS=\"\"\r\n STEAM_AUTH=\"\"\r\nfi\r\n\r\n## download and install steamcmd\r\ncd \/tmp\r\nmkdir -p \/mnt\/server\/steamcmd\r\ncurl -sSL -o steamcmd.tar.gz https:\/\/steamcdn-a.akamaihd.net\/client\/installer\/steamcmd_linux.tar.gz\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\r\nmkdir -p \/mnt\/server\/steamapps # Fix steamcmd disk write error when this folder is missing\r\ncd \/mnt\/server\/steamcmd\r\n\r\n# SteamCMD fails otherwise for some reason, even running as root.\r\n# This is changed at the end of the install process anyways.\r\nchown -R root:root \/mnt\r\nexport HOME=\/mnt\/server\r\n\r\n## install game using steamcmd\r\n.\/steamcmd.sh +force_install_dir \/mnt\/server +login ${STEAM_USER} ${STEAM_PASS} ${STEAM_AUTH} +app_update ${SRCDS_APPID} ${EXTRA_FLAGS} +quit ## other flags may be needed depending on install. looking at you cs 1.6\r\n\r\n## set up 32 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk32\r\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so\r\n\r\n## set up 64 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk64\r\ncp -v linux64\/steamclient.so ..\/.steam\/sdk64\/steamclient.so", + "container": "ghcr.io\/pterodactyl\/installers:debian", + "entrypoint": "bash" + } + }, + "variables": [ + { + "name": "Map", + "description": "The default map for the server.", + "env_variable": "SRCDS_MAP", + "default_value": "de_dust2", + "user_viewable": true, + "user_editable": true, + "rules": "required|string|alpha_dash" + }, + { + "name": "Steam Account Token", + "description": "The Steam Account Token required for the server to be displayed publicly.", + "env_variable": "STEAM_ACC", + "default_value": "", + "user_viewable": true, + "user_editable": true, + "rules": "required|string|alpha_num|size:32" + }, + { + "name": "Source AppID", + "description": "Required for game to update on server restart. Do not modify this.", + "env_variable": "SRCDS_APPID", + "default_value": "740", + "user_viewable": false, + "user_editable": false, + "rules": "required|string|max:20" + } + ] +} diff --git a/database/Seeders/eggs/source-engine/egg-custom-source-engine-game.json b/database/Seeders/eggs/source-engine/egg-custom-source-engine-game.json new file mode 100644 index 0000000..6c93175 --- /dev/null +++ b/database/Seeders/eggs/source-engine/egg-custom-source-engine-game.json @@ -0,0 +1,88 @@ +{ + "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", + "meta": { + "version": "PTDL_v1", + "update_url": null + }, + "exported_at": "2022-01-18T07:03:08-05:00", + "name": "Custom Source Engine Game", + "author": "support@pterodactyl.io", + "description": "This option allows modifying the startup arguments and other details to run a custom SRCDS based game on the panel.", + "features": [ + "steam_disk_space" + ], + "images": [ + "ghcr.io\/pterodactyl\/games:source" + ], + "file_denylist": [], + "startup": ".\/srcds_run -game {{SRCDS_GAME}} -console -port {{SERVER_PORT}} +map {{SRCDS_MAP}} +ip 0.0.0.0 -strictportbind -norestart", + "config": { + "files": "{}", + "startup": "{\r\n \"done\": \"gameserver Steam ID\"\r\n}", + "logs": "{}", + "stop": "quit" + }, + "scripts": { + "installation": { + "script": "#!\/bin\/bash\r\n# steamcmd Base Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n\r\n##\r\n#\r\n# Variables\r\n# STEAM_USER, STEAM_PASS, STEAM_AUTH - Steam user setup. If a user has 2fa enabled it will most likely fail due to timeout. Leave blank for anon install.\r\n# WINDOWS_INSTALL - if it's a windows server you want to install set to 1\r\n# SRCDS_APPID - steam app id ffound here - https:\/\/developer.valvesoftware.com\/wiki\/Dedicated_Servers_List\r\n# EXTRA_FLAGS - when a server has extra glas for things like beta installs or updates.\r\n#\r\n##\r\n\r\n\r\n## just in case someone removed the defaults.\r\nif [ \"${STEAM_USER}\" == \"\" ]; then\r\n echo -e \"steam user is not set.\\n\"\r\n echo -e \"Using anonymous user.\\n\"\r\n STEAM_USER=anonymous\r\n STEAM_PASS=\"\"\r\n STEAM_AUTH=\"\"\r\nelse\r\n echo -e \"user set to ${STEAM_USER}\"\r\nfi\r\n\r\n## download and install steamcmd\r\ncd \/tmp\r\nmkdir -p \/mnt\/server\/steamcmd\r\ncurl -sSL -o steamcmd.tar.gz https:\/\/steamcdn-a.akamaihd.net\/client\/installer\/steamcmd_linux.tar.gz\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\r\nmkdir -p \/mnt\/server\/steamapps # Fix steamcmd disk write error when this folder is missing\r\ncd \/mnt\/server\/steamcmd\r\n\r\n# SteamCMD fails otherwise for some reason, even running as root.\r\n# This is changed at the end of the install process anyways.\r\nchown -R root:root \/mnt\r\nexport HOME=\/mnt\/server\r\n\r\n## install game using steamcmd\r\n.\/steamcmd.sh +force_install_dir \/mnt\/server +login ${STEAM_USER} ${STEAM_PASS} ${STEAM_AUTH} $( [[ \"${WINDOWS_INSTALL}\" == \"1\" ]] && printf %s '+@sSteamCmdForcePlatformType windows' ) +app_update ${SRCDS_APPID} ${EXTRA_FLAGS} validate +quit ## other flags may be needed depending on install. looking at you cs 1.6\r\n\r\n## set up 32 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk32\r\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so\r\n\r\n## set up 64 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk64\r\ncp -v linux64\/steamclient.so ..\/.steam\/sdk64\/steamclient.so", + "container": "ghcr.io\/pterodactyl\/installers:debian", + "entrypoint": "bash" + } + }, + "variables": [ + { + "name": "Game ID", + "description": "The ID corresponding to the game to download and run using SRCDS.", + "env_variable": "SRCDS_APPID", + "default_value": "", + "user_viewable": true, + "user_editable": false, + "rules": "required|numeric|digits_between:1,6" + }, + { + "name": "Game Name", + "description": "The name corresponding to the game to download and run using SRCDS.", + "env_variable": "SRCDS_GAME", + "default_value": "", + "user_viewable": true, + "user_editable": false, + "rules": "required|alpha_dash|between:1,100" + }, + { + "name": "Map", + "description": "The default map for the server.", + "env_variable": "SRCDS_MAP", + "default_value": "", + "user_viewable": true, + "user_editable": true, + "rules": "required|string|alpha_dash" + }, + { + "name": "Steam Username", + "description": "", + "env_variable": "STEAM_USER", + "default_value": "", + "user_viewable": true, + "user_editable": true, + "rules": "nullable|string" + }, + { + "name": "Steam Password", + "description": "", + "env_variable": "STEAM_PASS", + "default_value": "", + "user_viewable": true, + "user_editable": true, + "rules": "nullable|string" + }, + { + "name": "Steam Auth", + "description": "", + "env_variable": "STEAM_AUTH", + "default_value": "", + "user_viewable": true, + "user_editable": true, + "rules": "nullable|string" + } + ] +} diff --git a/database/Seeders/eggs/source-engine/egg-garrys-mod.json b/database/Seeders/eggs/source-engine/egg-garrys-mod.json new file mode 100644 index 0000000..3282917 --- /dev/null +++ b/database/Seeders/eggs/source-engine/egg-garrys-mod.json @@ -0,0 +1,107 @@ +{ + "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", + "meta": { + "version": "PTDL_v1", + "update_url": null + }, + "exported_at": "2022-01-18T07:04:20-05:00", + "name": "Garrys Mod", + "author": "support@pterodactyl.io", + "description": "Garrys Mod, is a sandbox physics game created by Garry Newman, and developed by his company, Facepunch Studios.", + "features": [ + "gsl_token", + "steam_disk_space" + ], + "images": [ + "ghcr.io\/pterodactyl\/games:source" + ], + "file_denylist": [], + "startup": ".\/srcds_run -game garrysmod -console -port {{SERVER_PORT}} +ip 0.0.0.0 +host_workshop_collection {{WORKSHOP_ID}} +map {{SRCDS_MAP}} +gamemode {{GAMEMODE}} -strictportbind -norestart +sv_setsteamaccount {{STEAM_ACC}} +maxplayers {{MAX_PLAYERS}} -tickrate {{TICKRATE}} $( [ \"$LUA_REFRESH\" == \"1\" ] || printf %s '-disableluarefresh' )", + "config": { + "files": "{}", + "startup": "{\r\n \"done\": \"gameserver Steam ID\"\r\n}", + "logs": "{}", + "stop": "quit" + }, + "scripts": { + "installation": { + "script": "#!\/bin\/bash\r\n# steamcmd Base Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n\r\n## just in case someone removed the defaults.\r\nif [ \"${STEAM_USER}\" == \"\" ]; then\r\n echo -e \"steam user is not set.\\n\"\r\n echo -e \"Using anonymous user.\\n\"\r\n STEAM_USER=anonymous\r\n STEAM_PASS=\"\"\r\n STEAM_AUTH=\"\"\r\nelse\r\n echo -e \"user set to ${STEAM_USER}\"\r\nfi\r\n\r\n## download and install steamcmd\r\ncd \/tmp\r\nmkdir -p \/mnt\/server\/steamcmd\r\ncurl -sSL -o steamcmd.tar.gz https:\/\/steamcdn-a.akamaihd.net\/client\/installer\/steamcmd_linux.tar.gz\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\r\nmkdir -p \/mnt\/server\/steamapps # Fix steamcmd disk write error when this folder is missing\r\ncd \/mnt\/server\/steamcmd\r\n\r\n# SteamCMD fails otherwise for some reason, even running as root.\r\n# This is changed at the end of the install process anyways.\r\nchown -R root:root \/mnt\r\nexport HOME=\/mnt\/server\r\n\r\n## install game using steamcmd\r\n.\/steamcmd.sh +force_install_dir \/mnt\/server +login ${STEAM_USER} ${STEAM_PASS} ${STEAM_AUTH} $( [[ \"${WINDOWS_INSTALL}\" == \"1\" ]] && printf %s '+@sSteamCmdForcePlatformType windows' ) +app_update ${SRCDS_APPID} ${EXTRA_FLAGS} validate +quit ## other flags may be needed depending on install. looking at you cs 1.6\r\n\r\n## set up 32 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk32\r\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so\r\n\r\n## set up 64 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk64\r\ncp -v linux64\/steamclient.so ..\/.steam\/sdk64\/steamclient.so\r\n\r\n# Creating needed default files for the game\r\ncd \/mnt\/server\/garrysmod\/lua\/autorun\/server\r\necho '\r\n-- Docs: https:\/\/wiki.garrysmod.com\/page\/resource\/AddWorkshop\r\n-- Place the ID of the workshop addon you want to be downloaded to people who join your server, not the collection ID\r\n-- Use https:\/\/beta.configcreator.com\/create\/gmod\/resources.lua to easily create a list based on your collection ID\r\n\r\nresource.AddWorkshop( \"\" )\r\n' > workshop.lua\r\n\r\ncd \/mnt\/server\/garrysmod\/cfg\r\necho '\r\n\/\/ Please do not set RCon in here, use the startup parameters.\r\n\r\nhostname\t\t\"New Gmod Server\"\r\nsv_password\t\t\"\"\r\nsv_loadingurl \"\"\r\nsv_downloadurl \"\"\r\n\r\n\/\/ Steam Server List Settings\r\n\/\/ sv_location \"eu\"\r\nsv_region \"255\"\r\nsv_lan \"0\"\r\nsv_max_queries_sec_global \"30000\"\r\nsv_max_queries_window \"45\"\r\nsv_max_queries_sec \"5\"\r\n\r\n\/\/ Server Limits\r\nsbox_maxprops\t\t100\r\nsbox_maxragdolls\t5\r\nsbox_maxnpcs\t\t10\r\nsbox_maxballoons\t10\r\nsbox_maxeffects\t\t10\r\nsbox_maxdynamite\t10\r\nsbox_maxlamps\t\t10\r\nsbox_maxthrusters\t10\r\nsbox_maxwheels\t\t10\r\nsbox_maxhoverballs\t10\r\nsbox_maxvehicles\t20\r\nsbox_maxbuttons\t\t10\r\nsbox_maxsents\t\t20\r\nsbox_maxemitters\t5\r\nsbox_godmode\t\t0\r\nsbox_noclip\t\t 0\r\n\r\n\/\/ Network Settings - Please keep these set to default.\r\n\r\nsv_minrate\t\t75000\r\nsv_maxrate\t\t0\r\ngmod_physiterations\t2\r\nnet_splitpacket_maxrate\t45000\r\ndecalfrequency\t\t12 \r\n\r\n\/\/ Execute Ban Files - Please do not edit\r\nexec banned_ip.cfg \r\nexec banned_user.cfg \r\n\r\n\/\/ Add custom lines under here\r\n' > server.cfg", + "container": "ghcr.io\/pterodactyl\/installers:debian", + "entrypoint": "bash" + } + }, + "variables": [ + { + "name": "Map", + "description": "The default map for the server.", + "env_variable": "SRCDS_MAP", + "default_value": "gm_flatgrass", + "user_viewable": true, + "user_editable": true, + "rules": "required|string|alpha_dash" + }, + { + "name": "Steam Account Token", + "description": "The Steam Account Token required for the server to be displayed publicly.", + "env_variable": "STEAM_ACC", + "default_value": "", + "user_viewable": true, + "user_editable": true, + "rules": "nullable|string|alpha_num|size:32" + }, + { + "name": "Source AppID", + "description": "Required for game to update on server restart. Do not modify this.", + "env_variable": "SRCDS_APPID", + "default_value": "4020", + "user_viewable": false, + "user_editable": false, + "rules": "required|string|max:20" + }, + { + "name": "Workshop ID", + "description": "The ID of your workshop collection (the numbers at the end of the URL)", + "env_variable": "WORKSHOP_ID", + "default_value": "", + "user_viewable": true, + "user_editable": true, + "rules": "nullable|integer" + }, + { + "name": "Gamemode", + "description": "The gamemode of your server.", + "env_variable": "GAMEMODE", + "default_value": "sandbox", + "user_viewable": true, + "user_editable": true, + "rules": "required|string" + }, + { + "name": "Max Players", + "description": "The maximum amount of players allowed on your game server.", + "env_variable": "MAX_PLAYERS", + "default_value": "32", + "user_viewable": true, + "user_editable": true, + "rules": "required|integer|max:128" + }, + { + "name": "Tickrate", + "description": "The tickrate defines how fast the server will update each entity's location.", + "env_variable": "TICKRATE", + "default_value": "22", + "user_viewable": true, + "user_editable": true, + "rules": "required|integer|max:100" + }, + { + "name": "Lua Refresh", + "description": "0 = disable Lua refresh,\r\n1 = enable Lua refresh", + "env_variable": "LUA_REFRESH", + "default_value": "0", + "user_viewable": true, + "user_editable": true, + "rules": "required|boolean" + } + ] +} diff --git a/database/Seeders/eggs/source-engine/egg-insurgency.json b/database/Seeders/eggs/source-engine/egg-insurgency.json new file mode 100644 index 0000000..69d182f --- /dev/null +++ b/database/Seeders/eggs/source-engine/egg-insurgency.json @@ -0,0 +1,52 @@ +{ + "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", + "meta": { + "version": "PTDL_v1", + "update_url": null + }, + "exported_at": "2022-01-18T07:07:27-05:00", + "name": "Insurgency", + "author": "support@pterodactyl.io", + "description": "Take to the streets for intense close quarters combat, where a team's survival depends upon securing crucial strongholds and destroying enemy supply in this multiplayer and cooperative Source Engine based experience.", + "features": [ + "steam_disk_space" + ], + "images": [ + "ghcr.io\/pterodactyl\/games:source" + ], + "file_denylist": [], + "startup": ".\/srcds_run -game insurgency -console -port {{SERVER_PORT}} +map {{SRCDS_MAP}} +ip 0.0.0.0 -strictportbind -norestart", + "config": { + "files": "{}", + "startup": "{\r\n \"done\": \"gameserver Steam ID\"\r\n}", + "logs": "{}", + "stop": "quit" + }, + "scripts": { + "installation": { + "script": "#!\/bin\/bash\r\n# steamcmd Base Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n\r\n## download and install steamcmd\r\ncd \/tmp\r\nmkdir -p \/mnt\/server\/steamcmd\r\ncurl -sSL -o steamcmd.tar.gz https:\/\/steamcdn-a.akamaihd.net\/client\/installer\/steamcmd_linux.tar.gz\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\r\ncd \/mnt\/server\/steamcmd\r\n\r\n# SteamCMD fails otherwise for some reason, even running as root.\r\n# This is changed at the end of the install process anyways.\r\nchown -R root:root \/mnt\r\nexport HOME=\/mnt\/server\r\n\r\n## install game using steamcmd\r\n.\/steamcmd.sh +force_install_dir \/mnt\/server +login anonymous +app_update ${SRCDS_APPID} ${EXTRA_FLAGS} +quit\r\n\r\n## set up 32 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk32\r\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so\r\n\r\n## set up 64 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk64\r\ncp -v linux64\/steamclient.so ..\/.steam\/sdk64\/steamclient.so", + "container": "ghcr.io\/pterodactyl\/installers:debian", + "entrypoint": "bash" + } + }, + "variables": [ + { + "name": "Game ID", + "description": "The ID corresponding to the game to download and run using SRCDS.", + "env_variable": "SRCDS_APPID", + "default_value": "237410", + "user_viewable": true, + "user_editable": false, + "rules": "required|regex:\/^(237410)$\/" + }, + { + "name": "Default Map", + "description": "The default map to use when starting the server.", + "env_variable": "SRCDS_MAP", + "default_value": "sinjar", + "user_viewable": true, + "user_editable": true, + "rules": "required|regex:\/^(\\w{1,20})$\/" + } + ] +} diff --git a/database/Seeders/eggs/source-engine/egg-team-fortress2.json b/database/Seeders/eggs/source-engine/egg-team-fortress2.json new file mode 100644 index 0000000..6785984 --- /dev/null +++ b/database/Seeders/eggs/source-engine/egg-team-fortress2.json @@ -0,0 +1,62 @@ +{ + "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", + "meta": { + "version": "PTDL_v1", + "update_url": null + }, + "exported_at": "2022-01-30T14:09:22-05:00", + "name": "Team Fortress 2", + "author": "support@pterodactyl.io", + "description": "Team Fortress 2 is a team-based first-person shooter multiplayer video game developed and published by Valve Corporation. It is the sequel to the 1996 mod Team Fortress for Quake and its 1999 remake.", + "features": [ + "gsl_token", + "steam_disk_space" + ], + "images": [ + "ghcr.io\/pterodactyl\/games:source" + ], + "file_denylist": [], + "startup": ".\/srcds_run -game tf -console -port {{SERVER_PORT}} +map {{SRCDS_MAP}} +ip 0.0.0.0 -strictportbind -norestart +sv_setsteamaccount {{STEAM_ACC}}", + "config": { + "files": "{}", + "startup": "{\r\n \"done\": \"gameserver Steam ID\"\r\n}", + "logs": "{}", + "stop": "quit" + }, + "scripts": { + "installation": { + "script": "#!\/bin\/bash\r\n# steamcmd Base Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n# Image to install with is 'debian:buster-slim'\r\n\r\n##\r\n#\r\n# Variables\r\n# STEAM_USER, STEAM_PASS, STEAM_AUTH - Steam user setup. If a user has 2fa enabled it will most likely fail due to timeout. Leave blank for anon install.\r\n# WINDOWS_INSTALL - if it's a windows server you want to install set to 1\r\n# SRCDS_APPID - steam app id ffound here - https:\/\/developer.valvesoftware.com\/wiki\/Dedicated_Servers_List\r\n# EXTRA_FLAGS - when a server has extra glas for things like beta installs or updates.\r\n#\r\n##\r\n\r\n## just in case someone removed the defaults.\r\nif [ \"${STEAM_USER}\" == \"\" ]; then\r\n echo -e \"steam user is not set.\\n\"\r\n echo -e \"Using anonymous user.\\n\"\r\n STEAM_USER=anonymous\r\n STEAM_PASS=\"\"\r\n STEAM_AUTH=\"\"\r\nelse\r\n echo -e \"user set to ${STEAM_USER}\"\r\nfi\r\n\r\n## download and install steamcmd\r\ncd \/tmp\r\nmkdir -p \/mnt\/server\/steamcmd\r\ncurl -sSL -o steamcmd.tar.gz https:\/\/steamcdn-a.akamaihd.net\/client\/installer\/steamcmd_linux.tar.gz\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\r\nmkdir -p \/mnt\/server\/steamapps # Fix steamcmd disk write error when this folder is missing\r\ncd \/mnt\/server\/steamcmd\r\n\r\n# SteamCMD fails otherwise for some reason, even running as root.\r\n# This is changed at the end of the install process anyways.\r\nchown -R root:root \/mnt\r\nexport HOME=\/mnt\/server\r\n\r\n## install game using steamcmd\r\n.\/steamcmd.sh +force_install_dir \/mnt\/server +login ${STEAM_USER} ${STEAM_PASS} ${STEAM_AUTH} $( [[ \"${WINDOWS_INSTALL}\" == \"1\" ]] && printf %s '+@sSteamCmdForcePlatformType windows' ) +app_update ${SRCDS_APPID} ${EXTRA_FLAGS} validate +quit ## other flags may be needed depending on install. looking at you cs 1.6\r\n\r\n## set up 32 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk32\r\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so\r\n\r\n## set up 64 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk64\r\ncp -v linux64\/steamclient.so ..\/.steam\/sdk64\/steamclient.so", + "container": "ghcr.io\/pterodactyl\/installers:debian", + "entrypoint": "bash" + } + }, + "variables": [ + { + "name": "Game ID", + "description": "The ID corresponding to the game to download and run using SRCDS.", + "env_variable": "SRCDS_APPID", + "default_value": "232250", + "user_viewable": true, + "user_editable": false, + "rules": "required|regex:\/^(232250)$\/" + }, + { + "name": "Default Map", + "description": "The default map to use when starting the server.", + "env_variable": "SRCDS_MAP", + "default_value": "cp_dustbowl", + "user_viewable": true, + "user_editable": true, + "rules": "required|regex:\/^(\\w{1,20})$\/" + }, + { + "name": "Steam", + "description": "The Steam Game Server Login Token to display servers publicly. Generate one at https:\/\/steamcommunity.com\/dev\/managegameservers", + "env_variable": "STEAM_ACC", + "default_value": "", + "user_viewable": true, + "user_editable": true, + "rules": "required|string|alpha_num|size:32" + } + ] +} diff --git a/database/Seeders/eggs/voice-servers/egg-mumble-server.json b/database/Seeders/eggs/voice-servers/egg-mumble-server.json new file mode 100644 index 0000000..feac4dc --- /dev/null +++ b/database/Seeders/eggs/voice-servers/egg-mumble-server.json @@ -0,0 +1,42 @@ +{ + "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", + "meta": { + "version": "PTDL_v2", + "update_url": null + }, + "exported_at": "2022-10-15T12:38:18+02:00", + "name": "Mumble Server", + "author": "support@pterodactyl.io", + "description": "Mumble is an open source, low-latency, high quality voice chat software primarily intended for use while gaming.", + "features": null, + "docker_images": { + "Mumble": "ghcr.io\/parkervcp\/yolks:voice_mumble" + }, + "file_denylist": [], + "startup": "mumble-server -fg -ini murmur.ini", + "config": { + "files": "{\r\n \"murmur.ini\": {\r\n \"parser\": \"ini\",\r\n \"find\": {\r\n \"database\": \"\/home\/container\/murmur.sqlite\",\r\n \"logfile\": \"\/home\/container\/murmur.log\",\r\n \"port\": \"{{server.build.default.port}}\",\r\n \"host\": \"0.0.0.0\",\r\n \"users\": \"{{server.build.env.MAX_USERS}}\"\r\n }\r\n }\r\n}", + "startup": "{\r\n \"done\": \"Server listening on\"\r\n}", + "logs": "{}", + "stop": "^C" + }, + "scripts": { + "installation": { + "script": "#!\/bin\/ash\r\n\r\nif [ ! -d \/mnt\/server\/ ]; then\r\n mkdir \/mnt\/server\/\r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\nFILE=\/mnt\/server\/murmur.ini\r\nif [ -f \"$FILE\" ]; then\r\n echo \"Config file already exists.\"\r\nelse \r\n echo \"Downloading the config file.\"\r\n apk add --no-cache murmur\r\n cp \/etc\/murmur.ini \/mnt\/server\/murmur.ini\r\n apk del murmur\r\nfi\r\necho \"done\"", + "container": "ghcr.io\/pterodactyl\/installers:alpine", + "entrypoint": "ash" + } + }, + "variables": [ + { + "name": "Maximum Users", + "description": "Maximum concurrent users on the mumble server.", + "env_variable": "MAX_USERS", + "default_value": "100", + "user_viewable": true, + "user_editable": false, + "rules": "required|numeric|digits_between:1,5", + "field_type": "text" + } + ] +} diff --git a/database/Seeders/eggs/voice-servers/egg-teamspeak3-server.json b/database/Seeders/eggs/voice-servers/egg-teamspeak3-server.json new file mode 100644 index 0000000..ef15f1c --- /dev/null +++ b/database/Seeders/eggs/voice-servers/egg-teamspeak3-server.json @@ -0,0 +1,92 @@ +{ + "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", + "meta": { + "version": "PTDL_v1", + "update_url": null + }, + "exported_at": "2021-06-15T17:24:18-04:00", + "name": "Teamspeak3 Server", + "author": "support@pterodactyl.io", + "description": "VoIP software designed with security in mind, featuring crystal clear voice quality, endless customization options, and scalabilty up to thousands of simultaneous users.", + "features": null, + "images": [ + "ghcr.io\/pterodactyl\/yolks:debian" + ], + "file_denylist": [], + "startup": ".\/ts3server default_voice_port={{SERVER_PORT}} query_port={{QUERY_PORT}} filetransfer_ip=0.0.0.0 filetransfer_port={{FILE_TRANSFER}} query_http_port={{QUERY_HTTP}} query_ssh_port={{QUERY_SSH}} query_protocols={{QUERY_PROTOCOLS_VAR}} license_accepted=1", + "config": { + "files": "{}", + "startup": "{\r\n \"done\": \"listening on 0.0.0.0:\"\r\n}", + "logs": "{\r\n \"custom\": true,\r\n \"location\": \"logs\/ts3.log\"\r\n}", + "stop": "^C" + }, + "scripts": { + "installation": { + "script": "#!\/bin\/ash\r\n# TS3 Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n\r\nif [ -z ${TS_VERSION} ] || [ ${TS_VERSION} == latest ]; then\r\n TS_VERSION=$(curl -sSL https:\/\/teamspeak.com\/versions\/server.json | jq -r '.linux.x86_64.version')\r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\necho -e \"getting files from http:\/\/files.teamspeak-services.com\/releases\/server\/${TS_VERSION}\/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2\" \r\ncurl -L http:\/\/files.teamspeak-services.com\/releases\/server\/${TS_VERSION}\/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2 | tar -xvj --strip-components=1\r\ncp .\/redist\/libmariadb.so.2 .\/", + "container": "ghcr.io\/pterodactyl\/installers:alpine", + "entrypoint": "ash" + } + }, + "variables": [ + { + "name": "Server Version", + "description": "The version of Teamspeak 3 to use when running the server.", + "env_variable": "TS_VERSION", + "default_value": "latest", + "user_viewable": true, + "user_editable": true, + "rules": "required|string|max:6", + "field_type": "text" + }, + { + "name": "File Transfer Port", + "description": "The Teamspeak file transfer port", + "env_variable": "FILE_TRANSFER", + "default_value": "30033", + "user_viewable": true, + "user_editable": false, + "rules": "required|integer|between:1025,65535", + "field_type": "text" + }, + { + "name": "Query Port", + "description": "The Teamspeak Query Port", + "env_variable": "QUERY_PORT", + "default_value": "10011", + "user_viewable": true, + "user_editable": false, + "rules": "required|integer|between:1025,65535", + "field_type": "text" + }, + { + "name": "Query Protocols", + "description": "Comma separated list of protocols that can be used to connect to the ServerQuery | \r\nPossible values are raw, ssh and http | \r\nE.g.: raw,ssh,http", + "env_variable": "QUERY_PROTOCOLS_VAR", + "default_value": "raw,http,ssh", + "user_viewable": true, + "user_editable": true, + "rules": "required|string|max:12", + "field_type": "text" + }, + { + "name": "Query SSH Port", + "description": "TCP Port opened for ServerQuery connections using SSH", + "env_variable": "QUERY_SSH", + "default_value": "10022", + "user_viewable": true, + "user_editable": false, + "rules": "required|integer|between:1025,65535", + "field_type": "text" + }, + { + "name": "Query HTTP Port", + "description": "TCP Port opened for ServerQuery connections using http", + "env_variable": "QUERY_HTTP", + "default_value": "10080", + "user_viewable": true, + "user_editable": false, + "rules": "required|integer|between:1025,65535", + "field_type": "text" + } + ] +} diff --git a/database/migrations/2016_01_23_195641_add_allocations_table.php b/database/migrations/2016_01_23_195641_add_allocations_table.php new file mode 100644 index 0000000..cfff2b3 --- /dev/null +++ b/database/migrations/2016_01_23_195641_add_allocations_table.php @@ -0,0 +1,30 @@ +increments('id'); + $table->mediumInteger('node')->unsigned(); + $table->string('ip'); + $table->mediumInteger('port')->unsigned(); + $table->mediumInteger('assigned_to')->unsigned()->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::dropIfExists('allocations'); + } +} diff --git a/database/migrations/2016_01_23_195851_add_api_keys.php b/database/migrations/2016_01_23_195851_add_api_keys.php new file mode 100644 index 0000000..af7deb6 --- /dev/null +++ b/database/migrations/2016_01_23_195851_add_api_keys.php @@ -0,0 +1,29 @@ +increments('id'); + $table->char('public', 16); + $table->text('secret'); + $table->text('allowed_ips')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::dropIfExists('api_keys'); + } +} diff --git a/database/migrations/2016_01_23_200044_add_api_permissions.php b/database/migrations/2016_01_23_200044_add_api_permissions.php new file mode 100644 index 0000000..e6f6bcb --- /dev/null +++ b/database/migrations/2016_01_23_200044_add_api_permissions.php @@ -0,0 +1,27 @@ +increments('id'); + $table->mediumInteger('key_id')->unsigned(); + $table->string('permission'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::dropIfExists('api_permissions'); + } +} diff --git a/database/migrations/2016_01_23_200159_add_downloads.php b/database/migrations/2016_01_23_200159_add_downloads.php new file mode 100644 index 0000000..b1771c5 --- /dev/null +++ b/database/migrations/2016_01_23_200159_add_downloads.php @@ -0,0 +1,29 @@ +increments('id'); + $table->char('token', 36)->unique(); + $table->char('server', 36); + $table->text('path'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::dropIfExists('downloads'); + } +} diff --git a/database/migrations/2016_01_23_200421_create_failed_jobs_table.php b/database/migrations/2016_01_23_200421_create_failed_jobs_table.php new file mode 100644 index 0000000..83923e7 --- /dev/null +++ b/database/migrations/2016_01_23_200421_create_failed_jobs_table.php @@ -0,0 +1,29 @@ +increments('id'); + $table->text('connection'); + $table->text('queue'); + $table->longText('payload'); + $table->timestamp('failed_at'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::dropIfExists('failed_jobs'); + } +} diff --git a/database/migrations/2016_01_23_200440_create_jobs_table.php b/database/migrations/2016_01_23_200440_create_jobs_table.php new file mode 100644 index 0000000..277acae --- /dev/null +++ b/database/migrations/2016_01_23_200440_create_jobs_table.php @@ -0,0 +1,33 @@ +bigIncrements('id'); + $table->string('queue'); + $table->longText('payload'); + $table->tinyInteger('attempts')->unsigned(); + $table->tinyInteger('reserved')->unsigned(); + $table->unsignedInteger('reserved_at')->nullable(); + $table->unsignedInteger('available_at'); + $table->unsignedInteger('created_at'); + $table->index(['queue', 'reserved', 'reserved_at']); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::dropIfExists('jobs'); + } +} diff --git a/database/migrations/2016_01_23_200528_add_locations.php b/database/migrations/2016_01_23_200528_add_locations.php new file mode 100644 index 0000000..b34a5fb --- /dev/null +++ b/database/migrations/2016_01_23_200528_add_locations.php @@ -0,0 +1,28 @@ +increments('id'); + $table->string('short')->unique(); + $table->string('long'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::dropIfExists('locations'); + } +} diff --git a/database/migrations/2016_01_23_200648_add_nodes.php b/database/migrations/2016_01_23_200648_add_nodes.php new file mode 100644 index 0000000..52c0a29 --- /dev/null +++ b/database/migrations/2016_01_23_200648_add_nodes.php @@ -0,0 +1,39 @@ +increments('id'); + $table->smallInteger('public')->unsigned(); + $table->string('name'); + $table->mediumInteger('location')->unsigned(); + $table->string('fqdn'); + $table->string('scheme')->default('https'); + $table->integer('memory')->unsigned(); + $table->mediumInteger('memory_overallocate')->unsigned()->nullable(); + $table->integer('disk')->unsigned(); + $table->mediumInteger('disk_overallocate')->unsigned()->nullable(); + $table->char('daemonSecret', 36)->unique(); + $table->smallInteger('daemonListen')->unsigned()->default(8080); + $table->smallInteger('daemonSFTP')->unsgined()->default(2022); + $table->string('daemonBase')->default('/home/daemon-files'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::dropIfExists('nodes'); + } +} diff --git a/database/migrations/2016_01_23_201433_add_password_resets.php b/database/migrations/2016_01_23_201433_add_password_resets.php new file mode 100644 index 0000000..0584e36 --- /dev/null +++ b/database/migrations/2016_01_23_201433_add_password_resets.php @@ -0,0 +1,27 @@ +string('email')->index(); + $table->string('token')->index(); + $table->timestamp('created_at'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::dropIfExists('password_resets'); + } +} diff --git a/database/migrations/2016_01_23_201531_add_permissions.php b/database/migrations/2016_01_23_201531_add_permissions.php new file mode 100644 index 0000000..12c9bbe --- /dev/null +++ b/database/migrations/2016_01_23_201531_add_permissions.php @@ -0,0 +1,29 @@ +increments('id'); + $table->integer('user_id')->unsigned(); + $table->integer('server_id')->unsigned(); + $table->string('permissions'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::dropIfExists('permissions'); + } +} diff --git a/database/migrations/2016_01_23_201649_add_server_variables.php b/database/migrations/2016_01_23_201649_add_server_variables.php new file mode 100644 index 0000000..d9a436e --- /dev/null +++ b/database/migrations/2016_01_23_201649_add_server_variables.php @@ -0,0 +1,29 @@ +increments('id'); + $table->mediumInteger('server_id')->unsigned(); + $table->mediumInteger('variable_id')->unsigned(); + $table->string('variable_value'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::dropIfExists('server_variables'); + } +} diff --git a/database/migrations/2016_01_23_201748_add_servers.php b/database/migrations/2016_01_23_201748_add_servers.php new file mode 100644 index 0000000..5e10610 --- /dev/null +++ b/database/migrations/2016_01_23_201748_add_servers.php @@ -0,0 +1,46 @@ +increments('id'); + $table->char('uuid', 36)->unique(); + $table->char('uuidShort', 8)->unique(); + $table->mediumInteger('node')->unsigned(); + $table->string('name'); + $table->tinyInteger('active')->unsigned(); + $table->mediumInteger('owner')->unsigned(); + $table->integer('memory')->unsigned(); + $table->integer('swap')->unsigned(); + $table->integer('disk')->unsigned(); + $table->integer('io')->unsigned(); + $table->integer('cpu')->unsigned(); + $table->tinyInteger('oom_disabled')->unsigned()->default(0); + $table->string('ip'); + $table->integer('port')->unsigned(); + $table->mediumInteger('service')->unsigned(); + $table->mediumInteger('option')->unsigned(); + $table->text('startup'); + $table->char('daemonSecret', 36)->unique(); + $table->string('username')->unique(); + $table->tinyInteger('installed')->unsigned()->default(0); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::dropIfExists('servers'); + } +} diff --git a/database/migrations/2016_01_23_202544_add_service_options.php b/database/migrations/2016_01_23_202544_add_service_options.php new file mode 100644 index 0000000..7b0a336 --- /dev/null +++ b/database/migrations/2016_01_23_202544_add_service_options.php @@ -0,0 +1,31 @@ +increments('id'); + $table->mediumInteger('parent_service')->unsigned(); + $table->string('name'); + $table->text('description'); + $table->string('tag'); + $table->text('docker_image'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::dropIfExists('service_options'); + } +} diff --git a/database/migrations/2016_01_23_202731_add_service_varibles.php b/database/migrations/2016_01_23_202731_add_service_varibles.php new file mode 100644 index 0000000..e79fa1f --- /dev/null +++ b/database/migrations/2016_01_23_202731_add_service_varibles.php @@ -0,0 +1,35 @@ +increments('id'); + $table->mediumInteger('option_id')->unsigned(); + $table->string('name'); + $table->text('description'); + $table->string('env_variable'); + $table->string('default_value'); + $table->tinyInteger('user_viewable')->unsigned(); + $table->tinyInteger('user_editable')->unsigned(); + $table->tinyInteger('required')->unsigned(); + $table->string('regex')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::dropIfExists('service_variables'); + } +} diff --git a/database/migrations/2016_01_23_202943_add_services.php b/database/migrations/2016_01_23_202943_add_services.php new file mode 100644 index 0000000..31f7234 --- /dev/null +++ b/database/migrations/2016_01_23_202943_add_services.php @@ -0,0 +1,31 @@ +increments('id'); + $table->string('name'); + $table->text('description'); + $table->string('file'); + $table->string('executable'); + $table->text('startup'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::dropIfExists('services'); + } +} diff --git a/database/migrations/2016_01_23_203119_create_settings_table.php b/database/migrations/2016_01_23_203119_create_settings_table.php new file mode 100644 index 0000000..2cd6922 --- /dev/null +++ b/database/migrations/2016_01_23_203119_create_settings_table.php @@ -0,0 +1,26 @@ +string('key')->unique(); + $table->text('value'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::dropIfExists('settings'); + } +} diff --git a/database/migrations/2016_01_23_203150_add_subusers.php b/database/migrations/2016_01_23_203150_add_subusers.php new file mode 100644 index 0000000..2f0e463 --- /dev/null +++ b/database/migrations/2016_01_23_203150_add_subusers.php @@ -0,0 +1,29 @@ +increments('id'); + $table->integer('user_id')->unsigned(); + $table->integer('server_id')->unsigned(); + $table->char('daemonSecret', 36)->unique(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::dropIfExists('subusers'); + } +} diff --git a/database/migrations/2016_01_23_203159_add_users.php b/database/migrations/2016_01_23_203159_add_users.php new file mode 100644 index 0000000..05ace7e --- /dev/null +++ b/database/migrations/2016_01_23_203159_add_users.php @@ -0,0 +1,34 @@ +increments('id'); + $table->char('uuid', 36)->unique(); + $table->string('email')->unique(); + $table->text('password'); + $table->string('remember_token')->nullable(); + $table->char('language', 5)->default('en'); + $table->tinyInteger('root_admin')->unsigned()->default(0); + $table->tinyInteger('use_totp')->unsigned(); + $table->char('totp_secret', 16)->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::dropIfExists('users'); + } +} diff --git a/database/migrations/2016_01_23_203947_create_sessions_table.php b/database/migrations/2016_01_23_203947_create_sessions_table.php new file mode 100644 index 0000000..533fa8a --- /dev/null +++ b/database/migrations/2016_01_23_203947_create_sessions_table.php @@ -0,0 +1,30 @@ +string('id')->unique(); + $table->integer('user_id')->nullable(); + $table->string('ip_address', 45)->nullable(); + $table->text('user_agent')->nullable(); + $table->text('payload'); + $table->integer('last_activity'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::drop('sessions'); + } +} diff --git a/database/migrations/2016_01_25_234418_rename_permissions_column.php b/database/migrations/2016_01_25_234418_rename_permissions_column.php new file mode 100644 index 0000000..ae46dce --- /dev/null +++ b/database/migrations/2016_01_25_234418_rename_permissions_column.php @@ -0,0 +1,26 @@ +renameColumn('permissions', 'permission'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('permissions', function (Blueprint $table) { + }); + } +} diff --git a/database/migrations/2016_02_07_172148_add_databases_tables.php b/database/migrations/2016_02_07_172148_add_databases_tables.php new file mode 100644 index 0000000..7b1048b --- /dev/null +++ b/database/migrations/2016_02_07_172148_add_databases_tables.php @@ -0,0 +1,32 @@ +increments('id'); + $table->integer('server')->unsigned(); + $table->integer('db_server')->unsigned(); + $table->string('database')->unique(); + $table->string('username')->unique(); + $table->string('remote')->default('%'); + $table->text('password'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::drop('databases'); + } +} diff --git a/database/migrations/2016_02_07_181319_add_database_servers_table.php b/database/migrations/2016_02_07_181319_add_database_servers_table.php new file mode 100644 index 0000000..5a6740a --- /dev/null +++ b/database/migrations/2016_02_07_181319_add_database_servers_table.php @@ -0,0 +1,33 @@ +increments('id'); + $table->string('name'); + $table->string('host'); + $table->integer('port')->unsigned(); + $table->string('username'); + $table->text('password'); + $table->integer('max_databases')->unsigned()->nullable(); + $table->integer('linked_node')->unsigned()->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::drop('database_servers'); + } +} diff --git a/database/migrations/2016_02_13_154306_add_service_option_default_startup.php b/database/migrations/2016_02_13_154306_add_service_option_default_startup.php new file mode 100644 index 0000000..c8255ff --- /dev/null +++ b/database/migrations/2016_02_13_154306_add_service_option_default_startup.php @@ -0,0 +1,29 @@ +text('executable')->after('docker_image')->nullable()->default(null); + $table->text('startup')->after('executable')->nullable()->default(null); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('service_options', function (Blueprint $table) { + $table->dropColumn('executable'); + $table->dropColumn('startup'); + }); + } +} diff --git a/database/migrations/2016_02_20_155318_add_unique_service_field.php b/database/migrations/2016_02_20_155318_add_unique_service_field.php new file mode 100644 index 0000000..01ff913 --- /dev/null +++ b/database/migrations/2016_02_20_155318_add_unique_service_field.php @@ -0,0 +1,27 @@ +string('file')->unique()->change(); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('services', function (Blueprint $table) { + $table->dropUnique('services_file_unique'); + }); + } +} diff --git a/database/migrations/2016_02_27_163411_add_tasks_table.php b/database/migrations/2016_02_27_163411_add_tasks_table.php new file mode 100644 index 0000000..f4cb7b1 --- /dev/null +++ b/database/migrations/2016_02_27_163411_add_tasks_table.php @@ -0,0 +1,39 @@ +increments('id'); + $table->integer('server')->unsigned(); + $table->tinyInteger('active')->default(1); + $table->string('action'); + $table->text('data'); + $table->tinyInteger('queued')->unsigned()->default(0); + $table->string('year')->default('*'); + $table->string('day_of_week')->default('*'); + $table->string('month')->default('*'); + $table->string('day_of_month')->default('*'); + $table->string('hour')->default('*'); + $table->string('minute')->default('*'); + $table->timestamp('last_run')->nullable(); + $table->timestamp('next_run')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::drop('tasks'); + } +} diff --git a/database/migrations/2016_02_27_163447_add_tasks_log_table.php b/database/migrations/2016_02_27_163447_add_tasks_log_table.php new file mode 100644 index 0000000..265e7fd --- /dev/null +++ b/database/migrations/2016_02_27_163447_add_tasks_log_table.php @@ -0,0 +1,30 @@ +increments('id'); + $table->integer('task_id')->unsigned(); + $table->timestamp('run_time'); + $table->integer('run_status')->unsigned(); + $table->text('response'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::drop('tasks_log'); + } +} diff --git a/database/migrations/2016_03_18_155649_add_nullable_field_lastrun.php b/database/migrations/2016_03_18_155649_add_nullable_field_lastrun.php new file mode 100644 index 0000000..9d4752e --- /dev/null +++ b/database/migrations/2016_03_18_155649_add_nullable_field_lastrun.php @@ -0,0 +1,24 @@ +wrapTable('tasks'); + DB::statement('ALTER TABLE ' . $table . ' CHANGE `last_run` `last_run` TIMESTAMP NULL;'); + } + + /** + * Reverse the migrations. + */ + public function down() + { + $table = DB::getQueryGrammar()->wrapTable('tasks'); + DB::statement('ALTER TABLE ' . $table . ' CHANGE `last_run` `last_run` TIMESTAMP;'); + } +} diff --git a/database/migrations/2016_08_30_212718_add_ip_alias.php b/database/migrations/2016_08_30_212718_add_ip_alias.php new file mode 100644 index 0000000..26aa5ea --- /dev/null +++ b/database/migrations/2016_08_30_212718_add_ip_alias.php @@ -0,0 +1,38 @@ +text('ip_alias')->nullable()->after('ip'); + }); + + $allocations = DB::select('SELECT id, ip FROM allocations'); + foreach ($allocations as $allocation) { + DB::update( + 'UPDATE allocations SET ip_alias = :ip WHERE id = :id', + [ + 'ip' => $allocation->ip, + 'id' => $allocation->id, + ] + ); + } + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('allocations', function (Blueprint $table) { + $table->dropColumn('ip_alias'); + }); + } +} diff --git a/database/migrations/2016_08_30_213301_modify_ip_storage_method.php b/database/migrations/2016_08_30_213301_modify_ip_storage_method.php new file mode 100644 index 0000000..ee7e704 --- /dev/null +++ b/database/migrations/2016_08_30_213301_modify_ip_storage_method.php @@ -0,0 +1,78 @@ +mediumInteger('allocation')->unsigned()->after('oom_disabled'); + }); + + // Parse All Servers + $servers = DB::select('SELECT id, ip, port, node FROM servers'); + foreach ($servers as $server) { + $allocation = DB::select( + 'SELECT id FROM allocations WHERE ip = :ip AND port = :port AND node = :node', + [ + 'ip' => $server->ip, + 'port' => $server->port, + 'node' => $server->node, + ] + ); + + if (isset($allocation[0])) { + DB::update( + 'UPDATE servers SET allocation = :alocid WHERE id = :id', + [ + 'alocid' => $allocation[0]->id, + 'id' => $server->id, + ] + ); + } + } + + // Updated the server allocations, remove old fields + Schema::table('servers', function (Blueprint $table) { + $table->dropColumn('ip'); + $table->dropColumn('port'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('servers', function (Blueprint $table) { + $table->text('ip')->after('allocation'); + $table->integer('port')->unsigned()->after('ip'); + }); + + // Find the allocations and reset the servers... + $servers = DB::select('SELECT id, allocation FROM servers'); + foreach ($servers as $server) { + $allocation = DB::select('SELECT * FROM allocations WHERE id = :alocid', ['alocid' => $server->allocation]); + + if (isset($allocation[0])) { + DB::update( + 'UPDATE servers SET ip = :ip, port = :port WHERE id = :id', + [ + 'ip' => $allocation[0]->ip, + 'port' => $allocation[0]->port, + 'id' => $server->id, + ] + ); + } + } + + Schema::table('servers', function (Blueprint $table) { + $table->dropColumn('allocation'); + }); + } +} diff --git a/database/migrations/2016_09_01_193520_add_suspension_for_servers.php b/database/migrations/2016_09_01_193520_add_suspension_for_servers.php new file mode 100644 index 0000000..7bfb75b --- /dev/null +++ b/database/migrations/2016_09_01_193520_add_suspension_for_servers.php @@ -0,0 +1,27 @@ +tinyInteger('suspended')->unsigned()->default(0)->after('active'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('servers', function (Blueprint $table) { + $table->dropColumn('suspended'); + }); + } +} diff --git a/database/migrations/2016_09_01_211924_remove_active_column.php b/database/migrations/2016_09_01_211924_remove_active_column.php new file mode 100644 index 0000000..22a2bde --- /dev/null +++ b/database/migrations/2016_09_01_211924_remove_active_column.php @@ -0,0 +1,27 @@ +dropColumn('active'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('servers', function (Blueprint $table) { + $table->tinyInteger('active')->after('name')->unsigned()->default(0); + }); + } +} diff --git a/database/migrations/2016_09_02_190647_add_sftp_password_storage.php b/database/migrations/2016_09_02_190647_add_sftp_password_storage.php new file mode 100644 index 0000000..565957d --- /dev/null +++ b/database/migrations/2016_09_02_190647_add_sftp_password_storage.php @@ -0,0 +1,27 @@ +text('sftp_password')->after('username')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('servers', function (Blueprint $table) { + $table->dropColumn('sftp_password'); + }); + } +} diff --git a/database/migrations/2016_09_04_171338_update_jobs_tables.php b/database/migrations/2016_09_04_171338_update_jobs_tables.php new file mode 100644 index 0000000..840ecac --- /dev/null +++ b/database/migrations/2016_09_04_171338_update_jobs_tables.php @@ -0,0 +1,32 @@ +dropIndex('jobs_queue_reserved_reserved_at_index'); + $table->dropColumn('reserved'); + $table->index(['queue', 'reserved_at']); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('jobs', function (Blueprint $table) { + $table->dropIndex('jobs_queue_reserved_at_index'); + $table->tinyInteger('reserved')->unsigned(); + $table->index(['queue', 'reserved', 'reserved_at']); + }); + } +} diff --git a/database/migrations/2016_09_04_172028_update_failed_jobs_table.php b/database/migrations/2016_09_04_172028_update_failed_jobs_table.php new file mode 100644 index 0000000..a00f5f1 --- /dev/null +++ b/database/migrations/2016_09_04_172028_update_failed_jobs_table.php @@ -0,0 +1,28 @@ +text('exception'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('failed_jobs', function (Blueprint $table) { + $table->dropColumn('exception'); + }); + } +} diff --git a/database/migrations/2016_09_04_182835_create_notifications_table.php b/database/migrations/2016_09_04_182835_create_notifications_table.php new file mode 100644 index 0000000..30fc23a --- /dev/null +++ b/database/migrations/2016_09_04_182835_create_notifications_table.php @@ -0,0 +1,30 @@ +string('id')->primary(); + $table->string('type'); + $table->morphs('notifiable'); + $table->text('data'); + $table->timestamp('read_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::drop('notifications'); + } +} diff --git a/database/migrations/2016_09_07_163017_add_unique_identifier.php b/database/migrations/2016_09_07_163017_add_unique_identifier.php new file mode 100644 index 0000000..e1bab9c --- /dev/null +++ b/database/migrations/2016_09_07_163017_add_unique_identifier.php @@ -0,0 +1,28 @@ +char('author', 36)->after('id'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('services', function (Blueprint $table) { + $table->dropColumn('author'); + }); + } +} diff --git a/database/migrations/2016_09_14_145945_allow_longer_regex_field.php b/database/migrations/2016_09_14_145945_allow_longer_regex_field.php new file mode 100644 index 0000000..a7df1ca --- /dev/null +++ b/database/migrations/2016_09_14_145945_allow_longer_regex_field.php @@ -0,0 +1,28 @@ +text('regex')->change(); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('service_variables', function (Blueprint $table) { + $table->string('regex')->change(); + }); + } +} diff --git a/database/migrations/2016_09_17_194246_add_docker_image_column.php b/database/migrations/2016_09_17_194246_add_docker_image_column.php new file mode 100644 index 0000000..05d2611 --- /dev/null +++ b/database/migrations/2016_09_17_194246_add_docker_image_column.php @@ -0,0 +1,41 @@ +string('image')->after('daemonSecret'); + }); + + // Populate the column + DB::transaction(function () { + $servers = DB::table('servers')->select( + 'servers.id', + 'service_options.docker_image as s_optionImage' + )->join('service_options', 'service_options.id', '=', 'servers.option')->get(); + + foreach ($servers as $server) { + $server->image = $server->s_optionImage; + $server->save(); + } + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('servers', function (Blueprint $table) { + $table->dropColumn('image'); + }); + } +} diff --git a/database/migrations/2016_09_21_165554_update_servers_column_name.php b/database/migrations/2016_09_21_165554_update_servers_column_name.php new file mode 100644 index 0000000..14ae07c --- /dev/null +++ b/database/migrations/2016_09_21_165554_update_servers_column_name.php @@ -0,0 +1,28 @@ +renameColumn('server', 'server_id'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('databases', function (Blueprint $table) { + $table->renameColumn('server_id', 'server'); + }); + } +} diff --git a/database/migrations/2016_09_29_213518_rename_double_insurgency.php b/database/migrations/2016_09_29_213518_rename_double_insurgency.php new file mode 100644 index 0000000..adb5777 --- /dev/null +++ b/database/migrations/2016_09_29_213518_rename_double_insurgency.php @@ -0,0 +1,27 @@ +where('parent_service', 2)->where('id', 3)->where('name', 'Insurgency')->first(); + if ($model) { + $model->name = 'Team Fortress 2'; + $model->save(); + } + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + } +} diff --git a/database/migrations/2016_10_07_152117_build_api_log_table.php b/database/migrations/2016_10_07_152117_build_api_log_table.php new file mode 100644 index 0000000..08ea312 --- /dev/null +++ b/database/migrations/2016_10_07_152117_build_api_log_table.php @@ -0,0 +1,35 @@ +increments('id'); + $table->boolean('authorized'); + $table->text('error')->nullable(); + $table->char('key', 16)->nullable(); + $table->char('method', 6); + $table->text('route'); + $table->text('content')->nullable(); + $table->text('user_agent'); + $table->ipAddress('request_ip'); + $table->timestampsTz(); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::drop('api_logs'); + } +} diff --git a/database/migrations/2016_10_14_164802_update_api_keys.php b/database/migrations/2016_10_14_164802_update_api_keys.php new file mode 100644 index 0000000..56c3e80 --- /dev/null +++ b/database/migrations/2016_10_14_164802_update_api_keys.php @@ -0,0 +1,32 @@ +unsignedInteger('user')->after('id'); + $table->text('memo')->after('allowed_ips')->nullable(); + $table->timestamp('expires_at')->after('memo')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('api_keys', function (Blueprint $table) { + $table->dropColumn('user'); + $table->dropColumn('memo'); + $table->dropColumn('expires_at'); + }); + } +} diff --git a/database/migrations/2016_10_23_181719_update_misnamed_bungee.php b/database/migrations/2016_10_23_181719_update_misnamed_bungee.php new file mode 100644 index 0000000..70ec18b --- /dev/null +++ b/database/migrations/2016_10_23_181719_update_misnamed_bungee.php @@ -0,0 +1,23 @@ +select('env_variable')->where('env_variable', 'BUNGE_VERSION')->update([ + 'env_variable' => 'BUNGEE_VERSION', + ]); + } + + /** + * Reverse the migrations. + */ + public function down() + { + } +} diff --git a/database/migrations/2016_10_23_193810_add_foreign_keys_servers.php b/database/migrations/2016_10_23_193810_add_foreign_keys_servers.php new file mode 100644 index 0000000..1412720 --- /dev/null +++ b/database/migrations/2016_10_23_193810_add_foreign_keys_servers.php @@ -0,0 +1,61 @@ +foreign('node')->references('id')->on('nodes'); + $table->foreign('owner')->references('id')->on('users'); + $table->foreign('allocation')->references('id')->on('allocations'); + $table->foreign('service')->references('id')->on('services'); + $table->foreign('option')->references('id')->on('service_options'); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('servers', function (Blueprint $table) { + $table->dropForeign('servers_node_foreign'); + $table->dropForeign('servers_owner_foreign'); + $table->dropForeign('servers_allocation_foreign'); + $table->dropForeign('servers_service_foreign'); + $table->dropForeign('servers_option_foreign'); + + $table->dropIndex('servers_node_foreign'); + $table->dropIndex('servers_owner_foreign'); + $table->dropIndex('servers_allocation_foreign'); + $table->dropIndex('servers_service_foreign'); + $table->dropIndex('servers_option_foreign'); + + $table->dropColumn('deleted_at'); + }); + + DB::statement('ALTER TABLE servers + MODIFY COLUMN node MEDIUMINT(8) UNSIGNED NOT NULL, + MODIFY COLUMN owner MEDIUMINT(8) UNSIGNED NOT NULL, + MODIFY COLUMN allocation MEDIUMINT(8) UNSIGNED NOT NULL, + MODIFY COLUMN service MEDIUMINT(8) UNSIGNED NOT NULL, + MODIFY COLUMN `option` MEDIUMINT(8) UNSIGNED NOT NULL + '); + } +} diff --git a/database/migrations/2016_10_23_201624_add_foreign_allocations.php b/database/migrations/2016_10_23_201624_add_foreign_allocations.php new file mode 100644 index 0000000..0660081 --- /dev/null +++ b/database/migrations/2016_10_23_201624_add_foreign_allocations.php @@ -0,0 +1,43 @@ +foreign('assigned_to')->references('id')->on('servers'); + $table->foreign('node')->references('id')->on('nodes'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('allocations', function (Blueprint $table) { + $table->dropForeign('allocations_assigned_to_foreign'); + $table->dropForeign('allocations_node_foreign'); + + $table->dropIndex('allocations_assigned_to_foreign'); + $table->dropIndex('allocations_node_foreign'); + }); + + DB::statement('ALTER TABLE allocations + MODIFY COLUMN assigned_to MEDIUMINT(8) UNSIGNED NULL, + MODIFY COLUMN node MEDIUMINT(8) UNSIGNED NOT NULL + '); + } +} diff --git a/database/migrations/2016_10_23_202222_add_foreign_api_keys.php b/database/migrations/2016_10_23_202222_add_foreign_api_keys.php new file mode 100644 index 0000000..700342d --- /dev/null +++ b/database/migrations/2016_10_23_202222_add_foreign_api_keys.php @@ -0,0 +1,29 @@ +foreign('user')->references('id')->on('users'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('api_keys', function (Blueprint $table) { + $table->dropForeign('api_keys_user_foreign'); + $table->dropIndex('api_keys_user_foreign'); + }); + } +} diff --git a/database/migrations/2016_10_23_202703_add_foreign_api_permissions.php b/database/migrations/2016_10_23_202703_add_foreign_api_permissions.php new file mode 100644 index 0000000..d8eb350 --- /dev/null +++ b/database/migrations/2016_10_23_202703_add_foreign_api_permissions.php @@ -0,0 +1,33 @@ +foreign('key_id')->references('id')->on('api_keys'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('api_permissions', function (Blueprint $table) { + $table->dropForeign('api_permissions_key_id_foreign'); + $table->dropIndex('api_permissions_key_id_foreign'); + }); + + DB::statement('ALTER TABLE api_permissions MODIFY key_id MEDIUMINT(8) UNSIGNED NOT NULL'); + } +} diff --git a/database/migrations/2016_10_23_202953_add_foreign_database_servers.php b/database/migrations/2016_10_23_202953_add_foreign_database_servers.php new file mode 100644 index 0000000..769b7da --- /dev/null +++ b/database/migrations/2016_10_23_202953_add_foreign_database_servers.php @@ -0,0 +1,29 @@ +foreign('linked_node')->references('id')->on('nodes'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('database_servers', function (Blueprint $table) { + $table->dropForeign('database_servers_linked_node_foreign'); + $table->dropIndex('database_servers_linked_node_foreign'); + }); + } +} diff --git a/database/migrations/2016_10_23_203105_add_foreign_databases.php b/database/migrations/2016_10_23_203105_add_foreign_databases.php new file mode 100644 index 0000000..be26e3c --- /dev/null +++ b/database/migrations/2016_10_23_203105_add_foreign_databases.php @@ -0,0 +1,33 @@ +foreign('server_id')->references('id')->on('servers'); + $table->foreign('db_server')->references('id')->on('database_servers'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('databases', function (Blueprint $table) { + $table->dropForeign('databases_server_id_foreign'); + $table->dropForeign('databases_db_server_foreign'); + + $table->dropIndex('databases_server_id_foreign'); + $table->dropIndex('databases_db_server_foreign'); + }); + } +} diff --git a/database/migrations/2016_10_23_203335_add_foreign_nodes.php b/database/migrations/2016_10_23_203335_add_foreign_nodes.php new file mode 100644 index 0000000..f861e0a --- /dev/null +++ b/database/migrations/2016_10_23_203335_add_foreign_nodes.php @@ -0,0 +1,33 @@ +foreign('location')->references('id')->on('locations'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('nodes', function (Blueprint $table) { + $table->dropForeign('nodes_location_foreign'); + $table->dropIndex('nodes_location_foreign'); + }); + + DB::statement('ALTER TABLE nodes MODIFY location MEDIUMINT(10) UNSIGNED NOT NULL'); + } +} diff --git a/database/migrations/2016_10_23_203522_add_foreign_permissions.php b/database/migrations/2016_10_23_203522_add_foreign_permissions.php new file mode 100644 index 0000000..a43f0ea --- /dev/null +++ b/database/migrations/2016_10_23_203522_add_foreign_permissions.php @@ -0,0 +1,33 @@ +foreign('user_id')->references('id')->on('users'); + $table->foreign('server_id')->references('id')->on('servers'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('permissions', function (Blueprint $table) { + $table->dropForeign('permissions_user_id_foreign'); + $table->dropForeign('permissions_server_id_foreign'); + + $table->dropIndex('permissions_user_id_foreign'); + $table->dropIndex('permissions_server_id_foreign'); + }); + } +} diff --git a/database/migrations/2016_10_23_203857_add_foreign_server_variables.php b/database/migrations/2016_10_23_203857_add_foreign_server_variables.php new file mode 100644 index 0000000..b472049 --- /dev/null +++ b/database/migrations/2016_10_23_203857_add_foreign_server_variables.php @@ -0,0 +1,40 @@ +foreign('server_id')->references('id')->on('servers'); + $table->foreign('variable_id')->references('id')->on('service_variables'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('server_variables', function (Blueprint $table) { + $table->dropForeign(['server_id']); + $table->dropForeign(['variable_id']); + }); + + DB::statement('ALTER TABLE server_variables + MODIFY COLUMN server_id MEDIUMINT(8) UNSIGNED NULL, + MODIFY COLUMN variable_id MEDIUMINT(8) UNSIGNED NOT NULL + '); + } +} diff --git a/database/migrations/2016_10_23_204157_add_foreign_service_options.php b/database/migrations/2016_10_23_204157_add_foreign_service_options.php new file mode 100644 index 0000000..cb8c0e2 --- /dev/null +++ b/database/migrations/2016_10_23_204157_add_foreign_service_options.php @@ -0,0 +1,33 @@ +foreign('parent_service')->references('id')->on('services'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('service_options', function (Blueprint $table) { + $table->dropForeign('service_options_parent_service_foreign'); + $table->dropIndex('service_options_parent_service_foreign'); + }); + + DB::statement('ALTER TABLE service_options MODIFY parent_service MEDIUMINT(8) UNSIGNED NOT NULL'); + } +} diff --git a/database/migrations/2016_10_23_204321_add_foreign_service_variables.php b/database/migrations/2016_10_23_204321_add_foreign_service_variables.php new file mode 100644 index 0000000..02bbc46 --- /dev/null +++ b/database/migrations/2016_10_23_204321_add_foreign_service_variables.php @@ -0,0 +1,33 @@ +foreign('option_id')->references('id')->on('service_options'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('service_variables', function (Blueprint $table) { + $table->dropForeign('service_variables_option_id_foreign'); + $table->dropIndex('service_variables_option_id_foreign'); + }); + + DB::statement('ALTER TABLE service_variables MODIFY option_id MEDIUMINT(8) UNSIGNED NOT NULL'); + } +} diff --git a/database/migrations/2016_10_23_204454_add_foreign_subusers.php b/database/migrations/2016_10_23_204454_add_foreign_subusers.php new file mode 100644 index 0000000..b637c80 --- /dev/null +++ b/database/migrations/2016_10_23_204454_add_foreign_subusers.php @@ -0,0 +1,33 @@ +foreign('user_id')->references('id')->on('users'); + $table->foreign('server_id')->references('id')->on('servers'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('subusers', function (Blueprint $table) { + $table->dropForeign('subusers_user_id_foreign'); + $table->dropForeign('subusers_server_id_foreign'); + + $table->dropIndex('subusers_user_id_foreign'); + $table->dropIndex('subusers_server_id_foreign'); + }); + } +} diff --git a/database/migrations/2016_10_23_204610_add_foreign_tasks.php b/database/migrations/2016_10_23_204610_add_foreign_tasks.php new file mode 100644 index 0000000..18ea297 --- /dev/null +++ b/database/migrations/2016_10_23_204610_add_foreign_tasks.php @@ -0,0 +1,28 @@ +foreign('server')->references('id')->on('servers'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('tasks', function (Blueprint $table) { + $table->dropForeign(['server']); + }); + } +} diff --git a/database/migrations/2016_11_04_000949_add_ark_service_option_fixed.php b/database/migrations/2016_11_04_000949_add_ark_service_option_fixed.php new file mode 100644 index 0000000..1547e32 --- /dev/null +++ b/database/migrations/2016_11_04_000949_add_ark_service_option_fixed.php @@ -0,0 +1,90 @@ +select('id')->where('author', 'ptrdctyl-v040-11e6-8b77-86f30ca893d3')->where('name', 'Source Engine')->first(); + + // No SRCDS Service, Skipping + if (!$service) { + return; + } + + // Already have this service option installed. + if (DB::table('service_options')->select('id')->where('name', 'Ark: Survival Evolved')->where('parent_service', $service->id)->first()) { + return; + } + + $oid = DB::table('service_options')->insertGetId([ + 'parent_service' => $service->id, + 'name' => 'Ark: Survival Evolved', + 'description' => 'As a man or woman stranded, naked, freezing, and starving on the unforgiving shores of a mysterious island called ARK, use your skill and cunning to kill or tame and ride the plethora of leviathan dinosaurs and other primeval creatures roaming the land. Hunt, harvest resources, craft items, grow crops, research technologies, and build shelters to withstand the elements and store valuables, all while teaming up with (or preying upon) hundreds of other players to survive, dominate... and escape! — Gamepedia: ARK', + 'tag' => 'ark', + 'docker_image' => 'quay.io/pterodactyl/srcds:ark', + 'executable' => './ShooterGameServer', + 'startup' => 'TheIsland?listen?ServerPassword={{ARK_PASSWORD}}?ServerAdminPassword={{ARK_ADMIN_PASSWORD}}?Port={{SERVER_PORT}}?MaxPlayers={{SERVER_MAX_PLAYERS}}', + ]); + + DB::table('service_variables')->insert([ + 'option_id' => $oid, + 'name' => 'Server Password', + 'description' => 'If specified, players must provide this password to join the server.', + 'env_variable' => 'ARK_PASSWORD', + 'default_value' => '', + 'user_viewable' => 1, + 'user_editable' => 1, + 'required' => 0, + 'regex' => '/^(\w\.*)$/', + ]); + + DB::table('service_variables')->insert([ + 'option_id' => $oid, + 'name' => 'Admin Password', + 'description' => 'If specified, players must provide this password (via the in-game console) to gain access to administrator commands on the server.', + 'env_variable' => 'ARK_ADMIN_PASSWORD', + 'default_value' => '', + 'user_viewable' => 1, + 'user_editable' => 1, + 'required' => 0, + 'regex' => '/^(\w\.*)$/', + ]); + + DB::table('service_variables')->insert([ + 'option_id' => $oid, + 'name' => 'Maximum Players', + 'description' => 'Specifies the maximum number of players that can play on the server simultaneously.', + 'env_variable' => 'SERVER_MAX_PLAYERS', + 'default_value' => 20, + 'user_viewable' => 1, + 'user_editable' => 1, + 'required' => 1, + 'regex' => '/^(\d{1,4})$/', + ]); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + DB::transaction(function () { + $service = DB::table('services')->select('id')->where('author', 'ptrdctyl-v040-11e6-8b77-86f30ca893d3')->where('name', 'Source Engine')->first(); + + if ($service) { + $option = DB::table('service_options')->where('parent_service', $service->id)->where('tag', 'ark')->first(); + + if ($option) { + $variables = DB::table('service_variables')->where('option_id', $option->id)->delete(); + } + } + }); + } +} diff --git a/database/migrations/2016_11_11_220649_add_pack_support.php b/database/migrations/2016_11_11_220649_add_pack_support.php new file mode 100644 index 0000000..b6fa097 --- /dev/null +++ b/database/migrations/2016_11_11_220649_add_pack_support.php @@ -0,0 +1,36 @@ +increments('id'); + $table->unsignedInteger('option'); + $table->char('uuid', 36)->unique(); + $table->string('name'); + $table->string('version'); + $table->text('description')->nullable(); + $table->boolean('selectable')->default(true); + $table->boolean('visible')->default(true); + $table->timestamps(); + + $table->foreign('option')->references('id')->on('service_options'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::drop('service_packs'); + } +} diff --git a/database/migrations/2016_11_11_231731_set_service_name_unique.php b/database/migrations/2016_11_11_231731_set_service_name_unique.php new file mode 100644 index 0000000..42b0f69 --- /dev/null +++ b/database/migrations/2016_11_11_231731_set_service_name_unique.php @@ -0,0 +1,28 @@ +unique('name'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('services', function (Blueprint $table) { + $table->dropUnique('services_name_unique'); + }); + } +} diff --git a/database/migrations/2016_11_27_142519_add_pack_column.php b/database/migrations/2016_11_27_142519_add_pack_column.php new file mode 100644 index 0000000..d520466 --- /dev/null +++ b/database/migrations/2016_11_27_142519_add_pack_column.php @@ -0,0 +1,31 @@ +unsignedInteger('pack')->nullable()->after('option'); + + $table->foreign('pack')->references('id')->on('service_packs'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('servers', function (Blueprint $table) { + $table->dropForeign(['pack']); + $table->dropColumn('pack'); + }); + } +} diff --git a/database/migrations/2016_12_01_173018_add_configurable_upload_limit.php b/database/migrations/2016_12_01_173018_add_configurable_upload_limit.php new file mode 100644 index 0000000..d2d14f4 --- /dev/null +++ b/database/migrations/2016_12_01_173018_add_configurable_upload_limit.php @@ -0,0 +1,28 @@ +unsignedInteger('upload_size')->after('disk_overallocate')->default(100); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('nodes', function (Blueprint $table) { + $table->dropColumn('upload_size'); + }); + } +} diff --git a/database/migrations/2016_12_02_185206_correct_service_variables.php b/database/migrations/2016_12_02_185206_correct_service_variables.php new file mode 100644 index 0000000..e9c8798 --- /dev/null +++ b/database/migrations/2016_12_02_185206_correct_service_variables.php @@ -0,0 +1,73 @@ +where([ + ['name', 'Spigot'], + ['tag', 'spigot'], + ['startup', '-Xms128M -Xmx{{SERVER_MEMORY}}M -Djline.terminal=jline.UnsupportedTerminal -jar {{SERVER_JARFILE}}'], + ])->update([ + 'startup' => null, + ]); + + // Correct Spigot Version Checking + DB::table('service_variables')->where([ + ['name', 'Spigot Version'], + ['env_variable', 'DL_VERSION'], + ['default_value', 'latest'], + ['regex', '/^(latest|[a-zA-Z0-9_\.-]{5,6})$/'], + ])->update([ + 'regex' => '/^(latest|[a-zA-Z0-9_\.-]{3,7})$/', + ]); + + // Correct Vanilla Version Checking (as well as naming) + DB::table('service_variables')->where([ + ['name', 'Server Jar File'], + ['env_variable', 'VANILLA_VERSION'], + ['default_value', 'latest'], + ['regex', '/^(latest|[a-zA-Z0-9_\.-]{5,6})$/'], + ])->update([ + 'name' => 'Server Version', + 'regex' => '/^(latest|[a-zA-Z0-9_\.-]{3,7})$/', + ]); + + // Update Sponge Version Checking and Update Default Version + DB::table('service_variables')->where([ + ['name', 'Sponge Version'], + ['env_variable', 'SPONGE_VERSION'], + ['default_value', '1.8.9-4.2.0-BETA-351'], + ['regex', '/^(.*)$/'], + ])->update([ + 'default_value' => '1.10.2-5.1.0-BETA-359', + 'regex' => '/^([a-zA-Z0-9.\-_]+)$/', + ]); + + // Update Bungeecord Version Checking + DB::table('service_variables')->where([ + ['name', 'Bungeecord Version'], + ['env_variable', 'BUNGEE_VERSION'], + ['default_value', 'latest'], + ['regex', '/^(latest|[\d]{3,5})$/'], + ])->update([ + 'regex' => '/^(latest|[\d]{1,6})$/', + ]); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + // do nothing + } +} diff --git a/database/migrations/2017_01_03_150436_fix_misnamed_option_tag.php b/database/migrations/2017_01_03_150436_fix_misnamed_option_tag.php new file mode 100644 index 0000000..7cdf968 --- /dev/null +++ b/database/migrations/2017_01_03_150436_fix_misnamed_option_tag.php @@ -0,0 +1,36 @@ +where([ + ['name', 'Sponge (SpongeVanilla)'], + ['tag', 'spigot'], + ['docker_image', 'quay.io/pterodactyl/minecraft:sponge'], + ])->update([ + 'tag' => 'sponge', + ]); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + DB::table('service_options')->where([ + ['name', 'Sponge (SpongeVanilla)'], + ['tag', 'sponge'], + ['docker_image', 'quay.io/pterodactyl/minecraft:sponge'], + ])->update([ + 'tag' => 'spigot', + ]); + } +} diff --git a/database/migrations/2017_01_07_154228_create_node_configuration_tokens_table.php b/database/migrations/2017_01_07_154228_create_node_configuration_tokens_table.php new file mode 100644 index 0000000..77693c2 --- /dev/null +++ b/database/migrations/2017_01_07_154228_create_node_configuration_tokens_table.php @@ -0,0 +1,31 @@ +increments('id'); + $table->char('token', 32); + $table->timestamp('expires_at'); + $table->integer('node')->unsigned(); + $table->foreign('node')->references('id')->on('nodes'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::dropIfExists('node_configuration_tokens'); + } +} diff --git a/database/migrations/2017_01_12_135449_add_more_user_data.php b/database/migrations/2017_01_12_135449_add_more_user_data.php new file mode 100644 index 0000000..0206040 --- /dev/null +++ b/database/migrations/2017_01_12_135449_add_more_user_data.php @@ -0,0 +1,46 @@ +string('name_first')->after('email')->nullable(); + $table->string('name_last')->after('name_first')->nullable(); + $table->string('username')->after('uuid'); + $table->boolean('gravatar')->after('totp_secret')->default(true); + }); + + DB::transaction(function () { + foreach (User::all() as &$user) { + $user->username = $user->email; + $user->save(); + } + }); + + Schema::table('users', function (Blueprint $table) { + $table->string('username')->unique()->change(); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('name_first'); + $table->dropColumn('name_last'); + $table->dropColumn('username'); + $table->dropColumn('gravatar'); + }); + } +} diff --git a/database/migrations/2017_02_02_175548_UpdateColumnNames.php b/database/migrations/2017_02_02_175548_UpdateColumnNames.php new file mode 100644 index 0000000..c88aa8d --- /dev/null +++ b/database/migrations/2017_02_02_175548_UpdateColumnNames.php @@ -0,0 +1,74 @@ +dropForeign('servers_node_foreign'); + $table->dropForeign('servers_owner_foreign'); + $table->dropForeign('servers_allocation_foreign'); + $table->dropForeign('servers_service_foreign'); + $table->dropForeign('servers_option_foreign'); + $table->dropForeign('servers_pack_foreign'); + + $table->dropIndex('servers_node_foreign'); + $table->dropIndex('servers_owner_foreign'); + $table->dropIndex('servers_allocation_foreign'); + $table->dropIndex('servers_service_foreign'); + $table->dropIndex('servers_option_foreign'); + $table->dropIndex('servers_pack_foreign'); + + $table->renameColumn('node', 'node_id'); + $table->renameColumn('owner', 'owner_id'); + $table->renameColumn('allocation', 'allocation_id'); + $table->renameColumn('service', 'service_id'); + $table->renameColumn('option', 'option_id'); + $table->renameColumn('pack', 'pack_id'); + + $table->foreign('node_id')->references('id')->on('nodes'); + $table->foreign('owner_id')->references('id')->on('users'); + $table->foreign('allocation_id')->references('id')->on('allocations'); + $table->foreign('service_id')->references('id')->on('services'); + $table->foreign('option_id')->references('id')->on('service_options'); + + // Pack ID was forgotten until multiple releases later, therefore it is + // contained in '2017_03_18_204953_AddForeignKeyToPacks' + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('servers', function (Blueprint $table) { + $table->dropForeign(['node_id']); + $table->dropForeign(['owner_id']); + $table->dropForeign(['allocation_id']); + $table->dropForeign(['service_id']); + $table->dropForeign(['option_id']); + + $table->renameColumn('node_id', 'node'); + $table->renameColumn('owner_id', 'owner'); + $table->renameColumn('allocation_id', 'allocation'); + $table->renameColumn('service_id', 'service'); + $table->renameColumn('option_id', 'option'); + $table->renameColumn('pack_id', 'pack'); + + $table->foreign('node')->references('id')->on('nodes'); + $table->foreign('owner')->references('id')->on('users'); + $table->foreign('allocation')->references('id')->on('allocations'); + $table->foreign('service')->references('id')->on('services'); + $table->foreign('option')->references('id')->on('service_options'); + $table->foreign('pack')->references('id')->on('service_packs'); + }); + } +} diff --git a/database/migrations/2017_02_03_140948_UpdateNodesTable.php b/database/migrations/2017_02_03_140948_UpdateNodesTable.php new file mode 100644 index 0000000..58ec63e --- /dev/null +++ b/database/migrations/2017_02_03_140948_UpdateNodesTable.php @@ -0,0 +1,36 @@ +dropForeign('nodes_location_foreign'); + $table->dropIndex('nodes_location_foreign'); + + $table->renameColumn('location', 'location_id'); + $table->foreign('location_id')->references('id')->on('locations'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('nodes', function (Blueprint $table) { + $table->dropForeign('nodes_location_id_foreign'); + $table->dropIndex('nodes_location_id_foreign'); + + $table->renameColumn('location_id', 'location'); + $table->foreign('location')->references('id')->on('locations'); + }); + } +} diff --git a/database/migrations/2017_02_03_155554_RenameColumns.php b/database/migrations/2017_02_03_155554_RenameColumns.php new file mode 100644 index 0000000..5f617ab --- /dev/null +++ b/database/migrations/2017_02_03_155554_RenameColumns.php @@ -0,0 +1,44 @@ +dropForeign('allocations_node_foreign'); + $table->dropForeign('allocations_assigned_to_foreign'); + $table->dropIndex('allocations_node_foreign'); + $table->dropIndex('allocations_assigned_to_foreign'); + + $table->renameColumn('node', 'node_id'); + $table->renameColumn('assigned_to', 'server_id'); + $table->foreign('node_id')->references('id')->on('nodes'); + $table->foreign('server_id')->references('id')->on('servers'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('allocations', function (Blueprint $table) { + $table->dropForeign('allocations_node_id_foreign'); + $table->dropForeign('allocations_server_id_foreign'); + $table->dropIndex('allocations_node_id_foreign'); + $table->dropIndex('allocations_server_id_foreign'); + + $table->renameColumn('node_id', 'node'); + $table->renameColumn('server_id', 'assigned_to'); + $table->foreign('node')->references('id')->on('nodes'); + $table->foreign('assigned_to')->references('id')->on('servers'); + }); + } +} diff --git a/database/migrations/2017_02_05_164123_AdjustColumnNames.php b/database/migrations/2017_02_05_164123_AdjustColumnNames.php new file mode 100644 index 0000000..c7688f0 --- /dev/null +++ b/database/migrations/2017_02_05_164123_AdjustColumnNames.php @@ -0,0 +1,36 @@ +dropForeign('service_options_parent_service_foreign'); + $table->dropIndex('service_options_parent_service_foreign'); + + $table->renameColumn('parent_service', 'service_id'); + $table->foreign('service_id')->references('id')->on('services'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('service_options', function (Blueprint $table) { + $table->dropForeign('service_options_service_id_foreign'); + $table->dropIndex('service_options_service_id_foreign'); + + $table->renameColumn('service_id', 'parent_service'); + $table->foreign('parent_service')->references('id')->on('services'); + }); + } +} diff --git a/database/migrations/2017_02_05_164516_AdjustColumnNamesForServicePacks.php b/database/migrations/2017_02_05_164516_AdjustColumnNamesForServicePacks.php new file mode 100644 index 0000000..6f86b3b --- /dev/null +++ b/database/migrations/2017_02_05_164516_AdjustColumnNamesForServicePacks.php @@ -0,0 +1,36 @@ +dropForeign('service_packs_option_foreign'); + $table->dropIndex('service_packs_option_foreign'); + + $table->renameColumn('option', 'option_id'); + $table->foreign('option_id')->references('id')->on('service_options'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('service_packs', function (Blueprint $table) { + $table->dropForeign('service_packs_option_id_foreign'); + $table->dropIndex('service_packs_option_id_foreign'); + + $table->renameColumn('option_id', 'option'); + $table->foreign('option')->references('id')->on('service_options'); + }); + } +} diff --git a/database/migrations/2017_02_09_174834_SetupPermissionsPivotTable.php b/database/migrations/2017_02_09_174834_SetupPermissionsPivotTable.php new file mode 100644 index 0000000..45efce8 --- /dev/null +++ b/database/migrations/2017_02_09_174834_SetupPermissionsPivotTable.php @@ -0,0 +1,71 @@ +unsignedInteger('subuser_id')->after('id'); + }); + + DB::transaction(function () { + foreach (Subuser::all() as &$subuser) { + Permission::where('user_id', $subuser->user_id)->where('server_id', $subuser->server_id)->update([ + 'subuser_id' => $subuser->id, + ]); + } + }); + + Schema::table('permissions', function (Blueprint $table) { + $table->dropForeign('permissions_server_id_foreign'); + $table->dropIndex('permissions_server_id_foreign'); + $table->dropForeign('permissions_user_id_foreign'); + $table->dropIndex('permissions_user_id_foreign'); + + $table->dropColumn('server_id'); + $table->dropColumn('user_id'); + $table->dropColumn('created_at'); + $table->dropColumn('updated_at'); + $table->foreign('subuser_id')->references('id')->on('subusers'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('permissions', function (Blueprint $table) { + $table->unsignedInteger('server_id')->after('subuser_id'); + $table->unsignedInteger('user_id')->after('server_id'); + $table->timestamps(); + }); + + DB::transaction(function () { + foreach (Subuser::all() as &$subuser) { + Permission::where('subuser_id', $subuser->id)->update([ + 'user_id' => $subuser->user_id, + 'server_id' => $subuser->server_id, + ]); + } + }); + + Schema::table('permissions', function (Blueprint $table) { + $table->dropForeign('permissions_subuser_id_foreign'); + $table->dropIndex('permissions_subuser_id_foreign'); + $table->dropColumn('subuser_id'); + + $table->foreign('server_id')->references('id')->on('servers'); + $table->foreign('user_id')->references('id')->on('users'); + }); + } +} diff --git a/database/migrations/2017_02_10_171858_UpdateAPIKeyColumnNames.php b/database/migrations/2017_02_10_171858_UpdateAPIKeyColumnNames.php new file mode 100644 index 0000000..8b541d9 --- /dev/null +++ b/database/migrations/2017_02_10_171858_UpdateAPIKeyColumnNames.php @@ -0,0 +1,34 @@ +dropForeign('api_keys_user_foreign')->dropIndex('api_keys_user_foreign'); + + $table->renameColumn('user', 'user_id'); + $table->foreign('user_id')->references('id')->on('users'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('api_keys', function (Blueprint $table) { + $table->dropForeign('api_keys_user_id_foreign')->dropIndex('api_keys_user_id_foreign'); + + $table->renameColumn('user_id', 'user'); + $table->foreign('user')->references('id')->on('users'); + }); + } +} diff --git a/database/migrations/2017_03_03_224254_UpdateNodeConfigTokensColumns.php b/database/migrations/2017_03_03_224254_UpdateNodeConfigTokensColumns.php new file mode 100644 index 0000000..4f27346 --- /dev/null +++ b/database/migrations/2017_03_03_224254_UpdateNodeConfigTokensColumns.php @@ -0,0 +1,36 @@ +dropForeign(['node']); + $table->dropColumn('expires_at'); + $table->renameColumn('node', 'node_id'); + + $table->foreign('node_id')->references('id')->on('nodes'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('node_configuration_tokens', function (Blueprint $table) { + $table->dropForeign(['node_id']); + $table->renameColumn('node_id', 'node'); + $table->timestamp('expires_at')->after('token'); + + $table->foreign('node')->references('id')->on('nodes'); + }); + } +} diff --git a/database/migrations/2017_03_05_212803_DeleteServiceExecutableOption.php b/database/migrations/2017_03_05_212803_DeleteServiceExecutableOption.php new file mode 100644 index 0000000..6792f26 --- /dev/null +++ b/database/migrations/2017_03_05_212803_DeleteServiceExecutableOption.php @@ -0,0 +1,34 @@ +renameColumn('file', 'folder'); + $table->dropColumn('executable'); + $table->text('description')->nullable()->change(); + $table->text('startup')->nullable()->change(); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('services', function (Blueprint $table) { + $table->string('executable')->after('folder'); + $table->renameColumn('folder', 'file'); + $table->text('description')->nullable(false)->change(); + $table->text('startup')->nullable(false)->change(); + }); + } +} diff --git a/database/migrations/2017_03_10_162934_AddNewServiceOptionsColumns.php b/database/migrations/2017_03_10_162934_AddNewServiceOptionsColumns.php new file mode 100644 index 0000000..385004f --- /dev/null +++ b/database/migrations/2017_03_10_162934_AddNewServiceOptionsColumns.php @@ -0,0 +1,44 @@ +dropColumn('executable'); + + $table->unsignedInteger('config_from')->nullable()->after('docker_image'); + $table->string('config_stop')->nullable()->after('docker_image'); + $table->text('config_logs')->nullable()->after('docker_image'); + $table->text('config_startup')->nullable()->after('docker_image'); + $table->text('config_files')->nullable()->after('docker_image'); + + $table->foreign('config_from')->references('id')->on('service_options'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('service_options', function (Blueprint $table) { + $table->dropForeign(['config_from']); + + $table->dropColumn('config_from'); + $table->dropColumn('config_stop'); + $table->dropColumn('config_logs'); + $table->dropColumn('config_startup'); + $table->dropColumn('config_files'); + + $table->string('executable')->after('docker_image')->nullable(); + }); + } +} diff --git a/database/migrations/2017_03_10_173607_MigrateToNewServiceSystem.php b/database/migrations/2017_03_10_173607_MigrateToNewServiceSystem.php new file mode 100644 index 0000000..7cf5707 --- /dev/null +++ b/database/migrations/2017_03_10_173607_MigrateToNewServiceSystem.php @@ -0,0 +1,39 @@ +where('author', config('pterodactyl.service.core'))->where('folder', 'srcds')->first(); + if (!$service) { + return; + } + + $options = DB::table('service_options')->where('service_id', $service->id)->get(); + $options->each(function ($item) { + if ($item->tag === 'srcds' && $item->name === 'Insurgency') { + $item->tag = 'insurgency'; + } elseif ($item->tag === 'srcds' && $item->name === 'Team Fortress 2') { + $item->tag = 'tf2'; + } elseif ($item->tag === 'srcds' && $item->name === 'Custom Source Engine Game') { + $item->tag = 'source'; + } + $item->save(); + }); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + // Not doing reversals right now... + } +} diff --git a/database/migrations/2017_03_11_215455_ChangeServiceVariablesValidationRules.php b/database/migrations/2017_03_11_215455_ChangeServiceVariablesValidationRules.php new file mode 100644 index 0000000..21fa514 --- /dev/null +++ b/database/migrations/2017_03_11_215455_ChangeServiceVariablesValidationRules.php @@ -0,0 +1,47 @@ +renameColumn('regex', 'rules'); + }); + + DB::transaction(function () { + foreach (DB::table('service_variables')->get() as $variable) { + $variable->rules = ($variable->required) ? 'required|regex:' . $variable->rules : 'regex:' . $variable->rules; + $variable->save(); + } + }); + + Schema::table('service_variables', function (Blueprint $table) { + $table->dropColumn('required'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('service_variables', function (Blueprint $table) { + $table->renameColumn('rules', 'regex'); + $table->boolean('required')->default(true)->before('regex'); + }); + + DB::transaction(function () { + foreach (DB::table('service_variables')->get() as $variable) { + $variable->regex = str_replace(['required|regex:', 'regex:'], '', $variable->regex); + $variable->save(); + } + }); + } +} diff --git a/database/migrations/2017_03_12_150648_MoveFunctionsFromFileToDatabase.php b/database/migrations/2017_03_12_150648_MoveFunctionsFromFileToDatabase.php new file mode 100644 index 0000000..3628ba7 --- /dev/null +++ b/database/migrations/2017_03_12_150648_MoveFunctionsFromFileToDatabase.php @@ -0,0 +1,114 @@ + + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +const rfr = require('rfr'); +const _ = require('lodash'); + +const Core = rfr('src/services/index.js'); + +class Service extends Core {} + +module.exports = Service; +EOF; + + private $default_mc = <<<'EOF' +'use strict'; + +/** + * Pterodactyl - Daemon + * Copyright (c) 2015 - 2017 Dane Everitt + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +const rfr = require('rfr'); +const _ = require('lodash'); + +const Core = rfr('src/services/index.js'); + +class Service extends Core { + onConsole(data) { + // Hide the output spam from Bungeecord getting pinged. + if (_.endsWith(data, '<-> InitialHandler has connected')) return; + return super.onConsole(data); + } +} + +module.exports = Service; +EOF; + + /** + * Run the migrations. + */ + public function up() + { + Schema::table('services', function (Blueprint $table) { + $table->text('index_file')->after('startup'); + }); + + DB::transaction(function () { + DB::table('services')->where('author', 'ptrdctyl-v040-11e6-8b77-86f30ca893d3')->where('folder', '!=', 'minecraft')->update([ + 'index_file' => $this->default, + ]); + + DB::table('services')->where('author', 'ptrdctyl-v040-11e6-8b77-86f30ca893d3')->where('folder', 'minecraft')->update([ + 'index_file' => $this->default_mc, + ]); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('services', function (Blueprint $table) { + $table->dropColumn('index_file'); + }); + } +} diff --git a/database/migrations/2017_03_14_175631_RenameServicePacksToSingluarPacks.php b/database/migrations/2017_03_14_175631_RenameServicePacksToSingluarPacks.php new file mode 100644 index 0000000..d01012e --- /dev/null +++ b/database/migrations/2017_03_14_175631_RenameServicePacksToSingluarPacks.php @@ -0,0 +1,40 @@ +dropForeign(['option_id']); + }); + + Schema::rename('service_packs', 'packs'); + + Schema::table('packs', function (Blueprint $table) { + $table->foreign('option_id')->references('id')->on('service_options'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('packs', function (Blueprint $table) { + $table->dropForeign(['option_id']); + }); + + Schema::rename('packs', 'service_packs'); + + Schema::table('service_packs', function (Blueprint $table) { + $table->foreign('option_id')->references('id')->on('service_options'); + }); + } +} diff --git a/database/migrations/2017_03_14_200326_AddLockedStatusToTable.php b/database/migrations/2017_03_14_200326_AddLockedStatusToTable.php new file mode 100644 index 0000000..b1a8ee3 --- /dev/null +++ b/database/migrations/2017_03_14_200326_AddLockedStatusToTable.php @@ -0,0 +1,28 @@ +boolean('locked')->default(false)->after('visible'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('packs', function (Blueprint $table) { + $table->dropColumn('locked'); + }); + } +} diff --git a/database/migrations/2017_03_16_181109_ReOrganizeDatabaseServersToDatabaseHost.php b/database/migrations/2017_03_16_181109_ReOrganizeDatabaseServersToDatabaseHost.php new file mode 100644 index 0000000..a7166df --- /dev/null +++ b/database/migrations/2017_03_16_181109_ReOrganizeDatabaseServersToDatabaseHost.php @@ -0,0 +1,44 @@ +dropForeign(['linked_node']); + }); + + Schema::rename('database_servers', 'database_hosts'); + + Schema::table('database_hosts', function (Blueprint $table) { + $table->renameColumn('linked_node', 'node_id'); + + $table->foreign('node_id')->references('id')->on('nodes'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('database_hosts', function (Blueprint $table) { + $table->dropForeign(['node_id']); + }); + + Schema::rename('database_hosts', 'database_servers'); + + Schema::table('database_servers', function (Blueprint $table) { + $table->renameColumn('node_id', 'linked_node'); + + $table->foreign('linked_node')->references('id')->on('nodes'); + }); + } +} diff --git a/database/migrations/2017_03_16_181515_CleanupDatabasesDatabase.php b/database/migrations/2017_03_16_181515_CleanupDatabasesDatabase.php new file mode 100644 index 0000000..bc6fb45 --- /dev/null +++ b/database/migrations/2017_03_16_181515_CleanupDatabasesDatabase.php @@ -0,0 +1,36 @@ +dropForeign(['db_server']); + + $table->renameColumn('db_server', 'database_host_id'); + + $table->foreign('database_host_id')->references('id')->on('database_hosts'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('databases', function (Blueprint $table) { + $table->dropForeign(['database_host_id']); + + $table->renameColumn('database_host_id', 'db_server'); + + $table->foreign('db_server')->references('id')->on('database_hosts'); + }); + } +} diff --git a/database/migrations/2017_03_18_204953_AddForeignKeyToPacks.php b/database/migrations/2017_03_18_204953_AddForeignKeyToPacks.php new file mode 100644 index 0000000..3f26a1e --- /dev/null +++ b/database/migrations/2017_03_18_204953_AddForeignKeyToPacks.php @@ -0,0 +1,28 @@ +foreign('pack_id')->references('id')->on('packs'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('servers', function (Blueprint $table) { + $table->dropForeign(['pack_id']); + }); + } +} diff --git a/database/migrations/2017_03_31_221948_AddServerDescriptionColumn.php b/database/migrations/2017_03_31_221948_AddServerDescriptionColumn.php new file mode 100644 index 0000000..e8ebcb2 --- /dev/null +++ b/database/migrations/2017_03_31_221948_AddServerDescriptionColumn.php @@ -0,0 +1,28 @@ +text('description')->after('name'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('servers', function (Blueprint $table) { + $table->dropColumn('description'); + }); + } +} diff --git a/database/migrations/2017_04_02_163232_DropDeletedAtColumnFromServers.php b/database/migrations/2017_04_02_163232_DropDeletedAtColumnFromServers.php new file mode 100644 index 0000000..3cd08f1 --- /dev/null +++ b/database/migrations/2017_04_02_163232_DropDeletedAtColumnFromServers.php @@ -0,0 +1,28 @@ +dropColumn('deleted_at'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('servers', function (Blueprint $table) { + $table->timestamp('deleted_at')->nullable(); + }); + } +} diff --git a/database/migrations/2017_04_15_125021_UpgradeTaskSystem.php b/database/migrations/2017_04_15_125021_UpgradeTaskSystem.php new file mode 100644 index 0000000..d069e1b --- /dev/null +++ b/database/migrations/2017_04_15_125021_UpgradeTaskSystem.php @@ -0,0 +1,48 @@ +dropForeign(['server']); + + $table->renameColumn('server', 'server_id'); + $table->unsignedInteger('user_id')->nullable()->after('id'); + + $table->foreign('server_id')->references('id')->on('servers'); + $table->foreign('user_id')->references('id')->on('users'); + }); + + DB::transaction(function () { + foreach (Task::all() as $task) { + $task->user_id = $task->server->owner_id; + $task->save(); + } + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('tasks', function (Blueprint $table) { +// $table->dropForeign(['server_id']); +// $table->dropForeign(['user_id']); + + $table->renameColumn('server_id', 'server'); + $table->dropColumn('user_id'); + + $table->foreign('server')->references('id')->on('servers'); + }); + } +} diff --git a/database/migrations/2017_04_20_171943_AddScriptsToServiceOptions.php b/database/migrations/2017_04_20_171943_AddScriptsToServiceOptions.php new file mode 100644 index 0000000..ba2f57c --- /dev/null +++ b/database/migrations/2017_04_20_171943_AddScriptsToServiceOptions.php @@ -0,0 +1,34 @@ +text('script_install')->after('startup')->nullable(); + $table->boolean('script_is_privileged')->default(true)->after('startup'); + $table->string('script_entry')->default('ash')->after('startup'); + $table->string('script_container')->default('alpine:3.4')->after('startup'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('service_options', function (Blueprint $table) { + $table->dropColumn('script_install'); + $table->dropColumn('script_is_privileged'); + $table->dropColumn('script_entry'); + $table->dropColumn('script_container'); + }); + } +} diff --git a/database/migrations/2017_04_21_151432_AddServiceScriptTrackingToServers.php b/database/migrations/2017_04_21_151432_AddServiceScriptTrackingToServers.php new file mode 100644 index 0000000..2bc8f27 --- /dev/null +++ b/database/migrations/2017_04_21_151432_AddServiceScriptTrackingToServers.php @@ -0,0 +1,28 @@ +boolean('skip_scripts')->default(false)->after('description'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('servers', function (Blueprint $table) { + $table->dropColumn('skip_scripts'); + }); + } +} diff --git a/database/migrations/2017_04_27_145300_AddCopyScriptFromColumn.php b/database/migrations/2017_04_27_145300_AddCopyScriptFromColumn.php new file mode 100644 index 0000000..514d17e --- /dev/null +++ b/database/migrations/2017_04_27_145300_AddCopyScriptFromColumn.php @@ -0,0 +1,31 @@ +unsignedInteger('copy_script_from')->nullable()->after('script_container'); + + $table->foreign('copy_script_from')->references('id')->on('service_options'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('service_options', function (Blueprint $table) { + $table->dropForeign(['copy_script_from']); + $table->dropColumn('copy_script_from'); + }); + } +} diff --git a/database/migrations/2017_04_27_223629_AddAbilityToDefineConnectionOverSSLWithDaemonBehindProxy.php b/database/migrations/2017_04_27_223629_AddAbilityToDefineConnectionOverSSLWithDaemonBehindProxy.php new file mode 100644 index 0000000..aa5e044 --- /dev/null +++ b/database/migrations/2017_04_27_223629_AddAbilityToDefineConnectionOverSSLWithDaemonBehindProxy.php @@ -0,0 +1,28 @@ +boolean('behind_proxy')->after('scheme')->default(false); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('nodes', function (Blueprint $table) { + $table->dropColumn('behind_proxy'); + }); + } +} diff --git a/database/migrations/2017_05_01_141528_DeleteDownloadTable.php b/database/migrations/2017_05_01_141528_DeleteDownloadTable.php new file mode 100644 index 0000000..7dcae3c --- /dev/null +++ b/database/migrations/2017_05_01_141528_DeleteDownloadTable.php @@ -0,0 +1,30 @@ +increments('id'); + $table->char('token', 36)->unique(); + $table->char('server', 36); + $table->text('path'); + $table->timestamps(); + }); + } +} diff --git a/database/migrations/2017_05_01_141559_DeleteNodeConfigurationTable.php b/database/migrations/2017_05_01_141559_DeleteNodeConfigurationTable.php new file mode 100644 index 0000000..90c8c4b --- /dev/null +++ b/database/migrations/2017_05_01_141559_DeleteNodeConfigurationTable.php @@ -0,0 +1,33 @@ +increments('id'); + $table->char('token', 32); + $table->unsignedInteger('node_id'); + $table->timestamps(); + }); + + Schema::table('node_configuration_tokens', function (Blueprint $table) { + $table->foreign('node_id')->references('id')->on('nodes'); + }); + } +} diff --git a/database/migrations/2017_06_10_152951_add_external_id_to_users.php b/database/migrations/2017_06_10_152951_add_external_id_to_users.php new file mode 100644 index 0000000..9ce5057 --- /dev/null +++ b/database/migrations/2017_06_10_152951_add_external_id_to_users.php @@ -0,0 +1,28 @@ +unsignedInteger('external_id')->after('id')->nullable()->unique(); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('external_id'); + }); + } +} diff --git a/database/migrations/2017_06_25_133923_ChangeForeignKeyToBeOnCascadeDelete.php b/database/migrations/2017_06_25_133923_ChangeForeignKeyToBeOnCascadeDelete.php new file mode 100644 index 0000000..a089ab4 --- /dev/null +++ b/database/migrations/2017_06_25_133923_ChangeForeignKeyToBeOnCascadeDelete.php @@ -0,0 +1,32 @@ +dropForeign(['key_id']); + + $table->foreign('key_id')->references('id')->on('api_keys')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('api_permissions', function (Blueprint $table) { + $table->dropForeign(['key_id']); + + $table->foreign('key_id')->references('id')->on('api_keys'); + }); + } +} diff --git a/database/migrations/2017_07_08_152806_ChangeUserPermissionsToDeleteOnUserDeletion.php b/database/migrations/2017_07_08_152806_ChangeUserPermissionsToDeleteOnUserDeletion.php new file mode 100644 index 0000000..0bfc7d5 --- /dev/null +++ b/database/migrations/2017_07_08_152806_ChangeUserPermissionsToDeleteOnUserDeletion.php @@ -0,0 +1,48 @@ +dropForeign(['subuser_id']); + + $table->foreign('subuser_id')->references('id')->on('subusers')->onDelete('cascade'); + }); + + Schema::table('subusers', function (Blueprint $table) { + $table->dropForeign(['user_id']); + $table->dropForeign(['server_id']); + + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + $table->foreign('server_id')->references('id')->on('servers')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('subusers', function (Blueprint $table) { + $table->dropForeign(['user_id']); + $table->dropForeign(['server_id']); + + $table->foreign('user_id')->references('id')->on('users'); + $table->foreign('server_id')->references('id')->on('servers'); + }); + + Schema::table('permissions', function (Blueprint $table) { + $table->dropForeign(['subuser_id']); + + $table->foreign('subuser_id')->references('id')->on('subusers'); + }); + } +} diff --git a/database/migrations/2017_07_08_154416_SetAllocationToReferenceNullOnServerDelete.php b/database/migrations/2017_07_08_154416_SetAllocationToReferenceNullOnServerDelete.php new file mode 100644 index 0000000..fb156ba --- /dev/null +++ b/database/migrations/2017_07_08_154416_SetAllocationToReferenceNullOnServerDelete.php @@ -0,0 +1,32 @@ +dropForeign(['server_id']); + + $table->foreign('server_id')->references('id')->on('servers')->onDelete('set null'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('allocations', function (Blueprint $table) { + $table->dropForeign(['server_id']); + + $table->foreign('server_id')->references('id')->on('servers'); + }); + } +} diff --git a/database/migrations/2017_07_08_154650_CascadeDeletionWhenAServerOrVariableIsDeleted.php b/database/migrations/2017_07_08_154650_CascadeDeletionWhenAServerOrVariableIsDeleted.php new file mode 100644 index 0000000..5ae9a29 --- /dev/null +++ b/database/migrations/2017_07_08_154650_CascadeDeletionWhenAServerOrVariableIsDeleted.php @@ -0,0 +1,36 @@ +dropForeign(['server_id']); + $table->dropForeign(['variable_id']); + + $table->foreign('server_id')->references('id')->on('servers')->onDelete('cascade'); + $table->foreign('variable_id')->references('id')->on('service_variables')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('server_variables', function (Blueprint $table) { + $table->dropForeign(['server_id']); + $table->dropForeign(['variable_id']); + + $table->foreign('server_id')->references('id')->on('servers'); + $table->foreign('variable_id')->references('id')->on('service_variables'); + }); + } +} diff --git a/database/migrations/2017_07_24_194433_DeleteTaskWhenParentServerIsDeleted.php b/database/migrations/2017_07_24_194433_DeleteTaskWhenParentServerIsDeleted.php new file mode 100644 index 0000000..89e1102 --- /dev/null +++ b/database/migrations/2017_07_24_194433_DeleteTaskWhenParentServerIsDeleted.php @@ -0,0 +1,27 @@ +dropForeign(['server_id']); + + $table->foreign('server_id')->references('id')->on('servers')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + } +} diff --git a/database/migrations/2017_08_05_115800_CascadeNullValuesForDatabaseHostWhenNodeIsDeleted.php b/database/migrations/2017_08_05_115800_CascadeNullValuesForDatabaseHostWhenNodeIsDeleted.php new file mode 100644 index 0000000..a33b78a --- /dev/null +++ b/database/migrations/2017_08_05_115800_CascadeNullValuesForDatabaseHostWhenNodeIsDeleted.php @@ -0,0 +1,30 @@ +dropForeign(['node_id']); + $table->foreign('node_id')->references('id')->on('nodes')->onDelete('set null'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('database_hosts', function (Blueprint $table) { + $table->dropForeign(['node_id']); + $table->foreign('node_id')->references('id')->on('nodes'); + }); + } +} diff --git a/database/migrations/2017_08_05_144104_AllowNegativeValuesForOverallocation.php b/database/migrations/2017_08_05_144104_AllowNegativeValuesForOverallocation.php new file mode 100644 index 0000000..77b7f98 --- /dev/null +++ b/database/migrations/2017_08_05_144104_AllowNegativeValuesForOverallocation.php @@ -0,0 +1,30 @@ +integer('disk_overallocate')->default(0)->nullable(false)->change(); + $table->integer('memory_overallocate')->default(0)->nullable(false)->change(); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('nodes', function (Blueprint $table) { + DB::statement('ALTER TABLE nodes MODIFY disk_overallocate MEDIUMINT UNSIGNED NULL, + MODIFY memory_overallocate MEDIUMINT UNSIGNED NULL'); + }); + } +} diff --git a/database/migrations/2017_08_05_174811_SetAllocationUnqiueUsingMultipleFields.php b/database/migrations/2017_08_05_174811_SetAllocationUnqiueUsingMultipleFields.php new file mode 100644 index 0000000..f7aab7c --- /dev/null +++ b/database/migrations/2017_08_05_174811_SetAllocationUnqiueUsingMultipleFields.php @@ -0,0 +1,30 @@ +unique(['node_id', 'ip', 'port']); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('allocations', function (Blueprint $table) { + $table->dropForeign(['node_id']); + $table->dropUnique(['node_id', 'ip', 'port']); + $table->foreign('node_id')->references('id')->on('nodes'); + }); + } +} diff --git a/database/migrations/2017_08_15_214555_CascadeDeletionWhenAParentServiceIsDeleted.php b/database/migrations/2017_08_15_214555_CascadeDeletionWhenAParentServiceIsDeleted.php new file mode 100644 index 0000000..074f872 --- /dev/null +++ b/database/migrations/2017_08_15_214555_CascadeDeletionWhenAParentServiceIsDeleted.php @@ -0,0 +1,32 @@ +dropForeign(['service_id']); + + $table->foreign('service_id')->references('id')->on('services')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('service_options', function (Blueprint $table) { + $table->dropForeign(['service_id']); + + $table->foreign('service_id')->references('id')->on('services'); + }); + } +} diff --git a/database/migrations/2017_08_18_215428_RemovePackWhenParentServiceOptionIsDeleted.php b/database/migrations/2017_08_18_215428_RemovePackWhenParentServiceOptionIsDeleted.php new file mode 100644 index 0000000..1b8f1a5 --- /dev/null +++ b/database/migrations/2017_08_18_215428_RemovePackWhenParentServiceOptionIsDeleted.php @@ -0,0 +1,32 @@ +dropForeign(['option_id']); + + $table->foreign('option_id')->references('id')->on('service_options')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('packs', function (Blueprint $table) { + $table->dropForeign(['option_id']); + + $table->foreign('option_id')->references('id')->on('service_options'); + }); + } +} diff --git a/database/migrations/2017_09_10_225749_RenameTasksTableForStructureRefactor.php b/database/migrations/2017_09_10_225749_RenameTasksTableForStructureRefactor.php new file mode 100644 index 0000000..12eada7 --- /dev/null +++ b/database/migrations/2017_09_10_225749_RenameTasksTableForStructureRefactor.php @@ -0,0 +1,23 @@ +increments('id'); + $table->unsignedInteger('server_id'); + $table->string('name')->nullable(); + $table->string('cron_day_of_week'); + $table->string('cron_day_of_month'); + $table->string('cron_hour'); + $table->string('cron_minute'); + $table->boolean('is_active'); + $table->boolean('is_processing'); + $table->timestamp('last_run_at')->nullable(); + $table->timestamp('next_run_at')->nullable(); + $table->timestamps(); + + $table->foreign('server_id')->references('id')->on('servers')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::dropIfExists('schedules'); + } +} diff --git a/database/migrations/2017_09_10_230309_CreateNewTasksTableForSchedules.php b/database/migrations/2017_09_10_230309_CreateNewTasksTableForSchedules.php new file mode 100644 index 0000000..9c225a8 --- /dev/null +++ b/database/migrations/2017_09_10_230309_CreateNewTasksTableForSchedules.php @@ -0,0 +1,36 @@ +increments('id'); + $table->unsignedInteger('schedule_id'); + $table->unsignedInteger('sequence_id'); + $table->string('action'); + $table->text('payload'); + $table->unsignedInteger('time_offset'); + $table->boolean('is_queued'); + $table->timestamps(); + + $table->index(['schedule_id', 'sequence_id']); + $table->foreign('schedule_id')->references('id')->on('schedules')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::dropIfExists('tasks'); + } +} diff --git a/database/migrations/2017_09_11_002938_TransferOldTasksToNewScheduler.php b/database/migrations/2017_09_11_002938_TransferOldTasksToNewScheduler.php new file mode 100644 index 0000000..2a20ef1 --- /dev/null +++ b/database/migrations/2017_09_11_002938_TransferOldTasksToNewScheduler.php @@ -0,0 +1,75 @@ +get(); + + DB::beginTransaction(); + $tasks->each(function ($task) { + $schedule = DB::table('schedules')->insertGetId([ + 'server_id' => $task->server_id, + 'name' => null, + 'cron_day_of_week' => $task->day_of_week, + 'cron_day_of_month' => $task->day_of_month, + 'cron_hour' => $task->hour, + 'cron_minute' => $task->minute, + 'is_active' => (bool) $task->active, + 'is_processing' => false, + 'last_run_at' => $task->last_run, + 'next_run_at' => $task->next_run, + 'created_at' => $task->created_at, + 'updated_at' => Carbon::now()->toDateTimeString(), + ]); + + DB::table('tasks')->insert([ + 'schedule_id' => $schedule, + 'sequence_id' => 1, + 'action' => $task->action, + 'payload' => $task->data, + 'time_offset' => 0, + 'is_queued' => false, + 'updated_at' => Carbon::now()->toDateTimeString(), + 'created_at' => Carbon::now()->toDateTimeString(), + ]); + + DB::table('tasks_old')->delete($task->id); + DB::commit(); + }); + + Schema::dropIfExists('tasks_old'); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::create('tasks_old', function (Blueprint $table) { + $table->increments('id'); + $table->unsignedInteger('user_id')->nullable(); + $table->unsignedInteger('server_id'); + $table->tinyInteger('active')->default(1); + $table->string('action'); + $table->text('data'); + $table->unsignedTinyInteger('queued')->default(0); + $table->string('year')->default('*'); + $table->string('month')->default('*'); + $table->string('day_of_week')->default('*'); + $table->string('day_of_month')->default('*'); + $table->string('minute')->default('*'); + $table->timestamp('last_run')->nullable(); + $table->timestamp('next_run'); + $table->timestamps(); + }); + } +} diff --git a/database/migrations/2017_09_13_211810_UpdateOldPermissionsToPointToNewScheduleSystem.php b/database/migrations/2017_09_13_211810_UpdateOldPermissionsToPointToNewScheduleSystem.php new file mode 100644 index 0000000..ba3a8ba --- /dev/null +++ b/database/migrations/2017_09_13_211810_UpdateOldPermissionsToPointToNewScheduleSystem.php @@ -0,0 +1,43 @@ +where('permission', 'like', '%-task%')->get(); + foreach ($permissions as $record) { + $parts = explode('-', $record->permission); + if (!in_array(array_get($parts, 1), ['tasks', 'task']) || count($parts) !== 2) { + continue; + } + + $newPermission = $parts[0] . '-' . str_replace('task', 'schedule', $parts[1]); + + DB::table('permissions')->where('id', '=', $record->id)->update(['permission' => $newPermission]); + } + } + + /** + * Reverse the migrations. + */ + public function down() + { + $permissions = DB::table('permissions')->where('permission', 'like', '%-schedule%')->get(); + foreach ($permissions as $record) { + $parts = explode('-', $record->permission); + if (!in_array(array_get($parts, 1), ['schedules', 'schedule']) || count($parts) !== 2) { + continue; + } + + $newPermission = $parts[0] . '-' . str_replace('schedule', 'task', $parts[1]); + + DB::table('permissions')->where('id', '=', $record->id)->update(['permission' => $newPermission]); + } + } +} diff --git a/database/migrations/2017_09_23_170933_CreateDaemonKeysTable.php b/database/migrations/2017_09_23_170933_CreateDaemonKeysTable.php new file mode 100644 index 0000000..cfbfc88 --- /dev/null +++ b/database/migrations/2017_09_23_170933_CreateDaemonKeysTable.php @@ -0,0 +1,35 @@ +increments('id'); + $table->unsignedInteger('server_id'); + $table->unsignedInteger('user_id'); + $table->string('secret')->unique(); + $table->timestamp('expires_at'); + $table->timestamps(); + + $table->index(['server_id', 'user_id']); + $table->foreign('server_id')->references('id')->on('servers')->onDelete('cascade'); + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::dropIfExists('daemon_keys'); + } +} diff --git a/database/migrations/2017_09_23_173628_RemoveDaemonSecretFromServersTable.php b/database/migrations/2017_09_23_173628_RemoveDaemonSecretFromServersTable.php new file mode 100644 index 0000000..84cb2d9 --- /dev/null +++ b/database/migrations/2017_09_23_173628_RemoveDaemonSecretFromServersTable.php @@ -0,0 +1,52 @@ +select('id', 'owner_id')->get(); + $servers->each(function ($server) use (&$inserts) { + $inserts[] = [ + 'user_id' => $server->owner_id, + 'server_id' => $server->id, + 'secret' => DaemonKeyRepositoryInterface::INTERNAL_KEY_IDENTIFIER . str_random(40), + 'expires_at' => Carbon::now()->addMinutes(config('pterodactyl.api.key_expire_time', 720))->toDateTimeString(), + 'created_at' => Carbon::now()->toDateTimeString(), + 'updated_at' => Carbon::now()->toDateTimeString(), + ]; + }); + + DB::transaction(function () use ($inserts) { + DB::table('daemon_keys')->insert($inserts); + }); + + Schema::table('servers', function (Blueprint $table) { + $table->dropUnique(['daemonSecret']); + $table->dropColumn('daemonSecret'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('servers', function (Blueprint $table) { + $table->char('daemonSecret', 36)->after('startup')->unique(); + }); + + DB::table('daemon_keys')->truncate(); + } +} diff --git a/database/migrations/2017_09_23_185022_RemoveDaemonSecretFromSubusersTable.php b/database/migrations/2017_09_23_185022_RemoveDaemonSecretFromSubusersTable.php new file mode 100644 index 0000000..d4d2dd6 --- /dev/null +++ b/database/migrations/2017_09_23_185022_RemoveDaemonSecretFromSubusersTable.php @@ -0,0 +1,57 @@ +get(); + $subusers->each(function ($subuser) use (&$inserts) { + $inserts[] = [ + 'user_id' => $subuser->user_id, + 'server_id' => $subuser->server_id, + 'secret' => DaemonKeyRepositoryInterface::INTERNAL_KEY_IDENTIFIER . str_random(40), + 'expires_at' => Carbon::now()->addMinutes(config('pterodactyl.api.key_expire_time', 720))->toDateTimeString(), + 'created_at' => Carbon::now()->toDateTimeString(), + 'updated_at' => Carbon::now()->toDateTimeString(), + ]; + }); + + DB::transaction(function () use ($inserts) { + DB::table('daemon_keys')->insert($inserts); + }); + + Schema::table('subusers', function (Blueprint $table) { + $table->dropUnique(['daemonSecret']); + $table->dropColumn('daemonSecret'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('subusers', function (Blueprint $table) { + $table->char('daemonSecret', 36)->after('server_id'); + }); + + $subusers = DB::table('subusers')->get(); + $subusers->each(function ($subuser) { + DB::table('daemon_keys')->where('user_id', $subuser->user_id)->where('server_id', $subuser->server_id)->delete(); + }); + + Schema::table('subusers', function (Blueprint $table) { + $table->unique('daemonSecret'); + }); + } +} diff --git a/database/migrations/2017_10_02_202000_ChangeServicesToUseAMoreUniqueIdentifier.php b/database/migrations/2017_10_02_202000_ChangeServicesToUseAMoreUniqueIdentifier.php new file mode 100644 index 0000000..dffa768 --- /dev/null +++ b/database/migrations/2017_10_02_202000_ChangeServicesToUseAMoreUniqueIdentifier.php @@ -0,0 +1,55 @@ +dropUnique(['name']); + $table->dropUnique(['file']); + + $table->string('author')->change(); + $table->char('uuid', 36)->after('id'); + $table->dropColumn('folder'); + $table->dropColumn('startup'); + $table->dropColumn('index_file'); + }); + + DB::table('services')->get(['id', 'author', 'uuid'])->each(function ($service) { + DB::table('services')->where('id', $service->id)->update([ + 'author' => ($service->author === 'ptrdctyl-v040-11e6-8b77-86f30ca893d3') ? 'support@pterodactyl.io' : 'unknown@unknown-author.com', + 'uuid' => Uuid::uuid4()->toString(), + ]); + }); + + Schema::table('services', function (Blueprint $table) { + $table->unique('uuid'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('services', function (Blueprint $table) { + $table->dropColumn('uuid'); + $table->string('folder')->nullable(); + $table->text('startup')->nullable(); + $table->text('index_file'); + $table->string('author', 36)->change(); + + $table->unique('name'); + $table->unique('folder', 'services_file_unique'); + }); + } +} diff --git a/database/migrations/2017_10_02_202007_ChangeToABetterUniqueServiceConfiguration.php b/database/migrations/2017_10_02_202007_ChangeToABetterUniqueServiceConfiguration.php new file mode 100644 index 0000000..5c9df79 --- /dev/null +++ b/database/migrations/2017_10_02_202007_ChangeToABetterUniqueServiceConfiguration.php @@ -0,0 +1,59 @@ +char('uuid', 36)->after('id'); + $table->string('author')->after('service_id'); + $table->dropColumn('tag'); + }); + + DB::transaction(function () { + DB::table('service_options')->select([ + 'service_options.id', + 'service_options.uuid', + 'services.author AS service_author', + ])->join('services', 'services.id', '=', 'service_options.service_id')->get()->each(function ($option) { + DB::table('service_options')->where('id', $option->id)->update([ + 'author' => $option->service_author, + 'uuid' => Uuid::uuid4()->toString(), + ]); + }); + }); + + Schema::table('service_options', function (Blueprint $table) { + $table->unique('uuid'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('service_options', function (Blueprint $table) { + $table->dropColumn('uuid'); + $table->dropColumn('author'); + $table->string('tag'); + }); + + DB::transaction(function () { + DB::table('service_options')->select(['id', 'tag'])->get()->each(function ($option) { + DB::table('service_options')->where('id', $option->id)->update([ + 'tag' => str_random(10), + ]); + }); + }); + } +} diff --git a/database/migrations/2017_10_03_233202_CascadeDeletionWhenServiceOptionIsDeleted.php b/database/migrations/2017_10_03_233202_CascadeDeletionWhenServiceOptionIsDeleted.php new file mode 100644 index 0000000..3b19e3d --- /dev/null +++ b/database/migrations/2017_10_03_233202_CascadeDeletionWhenServiceOptionIsDeleted.php @@ -0,0 +1,32 @@ +dropForeign(['option_id']); + + $table->foreign('option_id')->references('id')->on('service_options')->onDelete('CASCADE'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('service_variables', function (Blueprint $table) { + $table->dropForeign(['option_id']); + + $table->foreign('option_id')->references('id')->on('service_options'); + }); + } +} diff --git a/database/migrations/2017_10_06_214026_ServicesToNestsConversion.php b/database/migrations/2017_10_06_214026_ServicesToNestsConversion.php new file mode 100644 index 0000000..e7b7013 --- /dev/null +++ b/database/migrations/2017_10_06_214026_ServicesToNestsConversion.php @@ -0,0 +1,59 @@ +dropForeign(['service_id']); + $table->renameColumn('service_id', 'nest_id'); + + $table->foreign('nest_id')->references('id')->on('nests'); + }); + + Schema::table('service_options', function (Blueprint $table) { + $table->dropForeign(['service_id']); + $table->renameColumn('service_id', 'nest_id'); + + $table->foreign('nest_id')->references('id')->on('nests')->onDelete('CASCADE'); + }); + + Schema::enableForeignKeyConstraints(); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::disableForeignKeyConstraints(); + + Schema::rename('nests', 'services'); + + Schema::table('servers', function (Blueprint $table) { + $table->dropForeign(['nest_id']); + $table->renameColumn('nest_id', 'service_id'); + + $table->foreign('service_id')->references('id')->on('services'); + }); + + Schema::table('service_options', function (Blueprint $table) { + $table->dropForeign(['nest_id']); + $table->renameColumn('nest_id', 'service_id'); + + $table->foreign('service_id')->references('id')->on('services')->onDelete('CASCADE'); + }); + + Schema::enableForeignKeyConstraints(); + } +} diff --git a/database/migrations/2017_10_06_214053_ServiceOptionsToEggsConversion.php b/database/migrations/2017_10_06_214053_ServiceOptionsToEggsConversion.php new file mode 100644 index 0000000..d2c55c0 --- /dev/null +++ b/database/migrations/2017_10_06_214053_ServiceOptionsToEggsConversion.php @@ -0,0 +1,93 @@ +dropForeign(['config_from']); + $table->dropForeign(['copy_script_from']); + }); + + Schema::rename('service_options', 'eggs'); + + Schema::table('packs', function (Blueprint $table) { + $table->dropForeign(['option_id']); + $table->renameColumn('option_id', 'egg_id'); + + $table->foreign('egg_id')->references('id')->on('eggs')->onDelete('CASCADE'); + }); + + Schema::table('servers', function (Blueprint $table) { + $table->dropForeign(['option_id']); + $table->renameColumn('option_id', 'egg_id'); + + $table->foreign('egg_id')->references('id')->on('eggs'); + }); + + Schema::table('eggs', function (Blueprint $table) { + $table->foreign('config_from')->references('id')->on('eggs')->onDelete('SET NULL'); + $table->foreign('copy_script_from')->references('id')->on('eggs')->onDelete('SET NULL'); + }); + + Schema::table('service_variables', function (Blueprint $table) { + $table->dropForeign(['option_id']); + $table->renameColumn('option_id', 'egg_id'); + + $table->foreign('egg_id')->references('id')->on('eggs')->onDelete('CASCADE'); + }); + + Schema::enableForeignKeyConstraints(); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::disableForeignKeyConstraints(); + + Schema::table('eggs', function (Blueprint $table) { + $table->dropForeign(['config_from']); + $table->dropForeign(['copy_script_from']); + }); + + Schema::rename('eggs', 'service_options'); + + Schema::table('packs', function (Blueprint $table) { + $table->dropForeign(['egg_id']); + $table->renameColumn('egg_id', 'option_id'); + + $table->foreign('option_id')->references('id')->on('service_options')->onDelete('CASCADE'); + }); + + Schema::table('servers', function (Blueprint $table) { + $table->dropForeign(['egg_id']); + $table->renameColumn('egg_id', 'option_id'); + + $table->foreign('option_id')->references('id')->on('service_options'); + }); + + Schema::table('service_options', function (Blueprint $table) { + $table->foreign('config_from')->references('id')->on('service_options')->onDelete('SET NULL'); + $table->foreign('copy_script_from')->references('id')->on('service_options')->onDelete('SET NULL'); + }); + + Schema::table('service_variables', function (Blueprint $table) { + $table->dropForeign(['egg_id']); + $table->renameColumn('egg_id', 'option_id'); + + $table->foreign('option_id')->references('id')->on('options')->onDelete('CASCADE'); + }); + + Schema::enableForeignKeyConstraints(); + } +} diff --git a/database/migrations/2017_10_06_215741_ServiceVariablesToEggVariablesConversion.php b/database/migrations/2017_10_06_215741_ServiceVariablesToEggVariablesConversion.php new file mode 100644 index 0000000..ef7d381 --- /dev/null +++ b/database/migrations/2017_10_06_215741_ServiceVariablesToEggVariablesConversion.php @@ -0,0 +1,43 @@ +dropForeign(['variable_id']); + + $table->foreign('variable_id')->references('id')->on('egg_variables')->onDelete('CASCADE'); + }); + + Schema::enableForeignKeyConstraints(); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::disableForeignKeyConstraints(); + + Schema::rename('egg_variables', 'service_variables'); + + Schema::table('server_variables', function (Blueprint $table) { + $table->dropForeign(['variable_id']); + + $table->foreign('variable_id')->references('id')->on('service_variables')->onDelete('CASCADE'); + }); + + Schema::enableForeignKeyConstraints(); + } +} diff --git a/database/migrations/2017_10_24_222238_RemoveLegacySFTPInformation.php b/database/migrations/2017_10_24_222238_RemoveLegacySFTPInformation.php new file mode 100644 index 0000000..e41acd2 --- /dev/null +++ b/database/migrations/2017_10_24_222238_RemoveLegacySFTPInformation.php @@ -0,0 +1,32 @@ +dropUnique(['username']); + + $table->dropColumn('username'); + $table->dropColumn('sftp_password'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('servers', function (Blueprint $table) { + $table->string('username')->nullable()->after('image')->unique(); + $table->text('sftp_password')->after('image'); + }); + } +} diff --git a/database/migrations/2017_11_11_161922_Add2FaLastAuthorizationTimeColumn.php b/database/migrations/2017_11_11_161922_Add2FaLastAuthorizationTimeColumn.php new file mode 100644 index 0000000..b90b150 --- /dev/null +++ b/database/migrations/2017_11_11_161922_Add2FaLastAuthorizationTimeColumn.php @@ -0,0 +1,60 @@ +text('totp_secret')->nullable()->change(); + $table->timestampTz('totp_authenticated_at')->after('totp_secret')->nullable(); + }); + + DB::transaction(function () { + DB::table('users')->get()->each(function ($user) { + if (is_null($user->totp_secret)) { + return; + } + + DB::table('users')->where('id', $user->id)->update([ + 'totp_secret' => Crypt::encrypt($user->totp_secret), + 'updated_at' => Carbon::now()->toAtomString(), + ]); + }); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + DB::transaction(function () { + DB::table('users')->get()->each(function ($user) { + if (is_null($user->totp_secret)) { + return; + } + + DB::table('users')->where('id', $user->id)->update([ + 'totp_secret' => Crypt::decrypt($user->totp_secret), + 'updated_at' => Carbon::now()->toAtomString(), + ]); + }); + }); + + DB::statement('ALTER TABLE users MODIFY totp_secret CHAR(16) DEFAULT NULL'); + + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('totp_authenticated_at'); + }); + } +} diff --git a/database/migrations/2017_11_19_122708_MigratePubPrivFormatToSingleKey.php b/database/migrations/2017_11_19_122708_MigratePubPrivFormatToSingleKey.php new file mode 100644 index 0000000..c2947ee --- /dev/null +++ b/database/migrations/2017_11_19_122708_MigratePubPrivFormatToSingleKey.php @@ -0,0 +1,59 @@ +get()->each(function ($item) { + try { + $decrypted = Crypt::decrypt($item->secret); + } catch (DecryptException $exception) { + $decrypted = str_random(32); + } finally { + DB::table('api_keys')->where('id', $item->id)->update([ + 'secret' => $decrypted, + ]); + } + }); + }); + + Schema::table('api_keys', function (Blueprint $table) { + $table->dropColumn('public'); + $table->string('secret', 32)->change(); + }); + + DB::statement('ALTER TABLE `api_keys` CHANGE `secret` `token` CHAR(32) NOT NULL, ADD UNIQUE INDEX `api_keys_token_unique` (`token`(32))'); + } + + /** + * Reverse the migrations. + */ + public function down() + { + DB::statement('ALTER TABLE `api_keys` CHANGE `token` `secret` TEXT, DROP INDEX `api_keys_token_unique`'); + + Schema::table('api_keys', function (Blueprint $table) { + $table->char('public', 16)->after('user_id'); + }); + + DB::transaction(function () { + DB::table('api_keys')->get()->each(function ($item) { + DB::table('api_keys')->where('id', $item->id)->update([ + 'public' => str_random(16), + 'secret' => Crypt::encrypt($item->secret), + ]); + }); + }); + } +} diff --git a/database/migrations/2017_12_04_184012_DropAllocationsWhenNodeIsDeleted.php b/database/migrations/2017_12_04_184012_DropAllocationsWhenNodeIsDeleted.php new file mode 100644 index 0000000..d281095 --- /dev/null +++ b/database/migrations/2017_12_04_184012_DropAllocationsWhenNodeIsDeleted.php @@ -0,0 +1,32 @@ +dropForeign(['node_id']); + + $table->foreign('node_id')->references('id')->on('nodes')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('allocations', function (Blueprint $table) { + $table->dropForeign(['node_id']); + + $table->foreign('node_id')->references('id')->on('nodes'); + }); + } +} diff --git a/database/migrations/2017_12_12_220426_MigrateSettingsTableToNewFormat.php b/database/migrations/2017_12_12_220426_MigrateSettingsTableToNewFormat.php new file mode 100644 index 0000000..1bdaf64 --- /dev/null +++ b/database/migrations/2017_12_12_220426_MigrateSettingsTableToNewFormat.php @@ -0,0 +1,30 @@ +truncate(); + Schema::table('settings', function (Blueprint $table) { + $table->increments('id')->first(); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::table('settings', function (Blueprint $table) { + $table->dropColumn('id'); + }); + } +} diff --git a/database/migrations/2018_01_01_122821_AllowNegativeValuesForServerSwap.php b/database/migrations/2018_01_01_122821_AllowNegativeValuesForServerSwap.php new file mode 100644 index 0000000..8f9938d --- /dev/null +++ b/database/migrations/2018_01_01_122821_AllowNegativeValuesForServerSwap.php @@ -0,0 +1,32 @@ +integer('swap')->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('servers', function (Blueprint $table) { + $table->unsignedInteger('swap')->change(); + }); + } +} diff --git a/database/migrations/2018_01_11_213943_AddApiKeyPermissionColumns.php b/database/migrations/2018_01_11_213943_AddApiKeyPermissionColumns.php new file mode 100644 index 0000000..adc6d26 --- /dev/null +++ b/database/migrations/2018_01_11_213943_AddApiKeyPermissionColumns.php @@ -0,0 +1,62 @@ +unsignedTinyInteger('r_servers')->default(0); + $table->unsignedTinyInteger('r_nodes')->default(0); + $table->unsignedTinyInteger('r_allocations')->default(0); + $table->unsignedTinyInteger('r_users')->default(0); + $table->unsignedTinyInteger('r_locations')->default(0); + $table->unsignedTinyInteger('r_nests')->default(0); + $table->unsignedTinyInteger('r_eggs')->default(0); + $table->unsignedTinyInteger('r_database_hosts')->default(0); + $table->unsignedTinyInteger('r_server_databases')->default(0); + $table->unsignedTinyInteger('r_packs')->default(0); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::create('api_permissions', function (Blueprint $table) { + $table->increments('id'); + $table->unsignedInteger('key_id'); + $table->string('permission'); + + $table->foreign('key_id')->references('id')->on('api_keys')->onDelete('cascade'); + }); + + Schema::table('api_keys', function (Blueprint $table) { + $table->dropColumn([ + 'r_servers', + 'r_nodes', + 'r_allocations', + 'r_users', + 'r_locations', + 'r_nests', + 'r_eggs', + 'r_database_hosts', + 'r_server_databases', + 'r_packs', + ]); + }); + } +} diff --git a/database/migrations/2018_01_13_142012_SetupTableForKeyEncryption.php b/database/migrations/2018_01_13_142012_SetupTableForKeyEncryption.php new file mode 100644 index 0000000..1d36b36 --- /dev/null +++ b/database/migrations/2018_01_13_142012_SetupTableForKeyEncryption.php @@ -0,0 +1,44 @@ +char('identifier', 16)->nullable()->unique()->after('user_id'); + $table->dropUnique(['token']); + }); + + Schema::table('api_keys', function (Blueprint $table) { + $table->text('token')->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + * + * @throws \Exception + * @throws \Throwable + */ + public function down() + { + Schema::table('api_keys', function (Blueprint $table) { + $table->dropColumn('identifier'); + $table->string('token', 32)->unique()->change(); + }); + } +} diff --git a/database/migrations/2018_01_13_145209_AddLastUsedAtColumn.php b/database/migrations/2018_01_13_145209_AddLastUsedAtColumn.php new file mode 100644 index 0000000..e0f86b9 --- /dev/null +++ b/database/migrations/2018_01_13_145209_AddLastUsedAtColumn.php @@ -0,0 +1,46 @@ +unsignedTinyInteger('key_type')->after('user_id')->default(0); + $table->timestamp('last_used_at')->after('memo')->nullable(); + $table->dropColumn('expires_at'); + + $table->dropForeign(['user_id']); + }); + + Schema::table('api_keys', function (Blueprint $table) { + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('api_keys', function (Blueprint $table) { + $table->timestamp('expires_at')->after('memo')->nullable(); + $table->dropColumn('last_used_at', 'key_type'); + $table->dropForeign(['user_id']); + }); + + Schema::table('api_keys', function (Blueprint $table) { + $table->foreign('user_id')->references('id')->on('users'); + }); + } +} diff --git a/database/migrations/2018_02_04_145617_AllowTextInUserExternalId.php b/database/migrations/2018_02_04_145617_AllowTextInUserExternalId.php new file mode 100644 index 0000000..6a4a04e --- /dev/null +++ b/database/migrations/2018_02_04_145617_AllowTextInUserExternalId.php @@ -0,0 +1,32 @@ +string('external_id')->nullable()->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('users', function (Blueprint $table) { + $table->unsignedInteger('external_id')->change(); + }); + } +} diff --git a/database/migrations/2018_02_10_151150_remove_unique_index_on_external_id_column.php b/database/migrations/2018_02_10_151150_remove_unique_index_on_external_id_column.php new file mode 100644 index 0000000..b587cdc --- /dev/null +++ b/database/migrations/2018_02_10_151150_remove_unique_index_on_external_id_column.php @@ -0,0 +1,32 @@ +dropUnique(['external_id']); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('users', function (Blueprint $table) { + $table->unique(['external_id']); + }); + } +} diff --git a/database/migrations/2018_02_17_134254_ensure_unique_allocation_id_on_servers_table.php b/database/migrations/2018_02_17_134254_ensure_unique_allocation_id_on_servers_table.php new file mode 100644 index 0000000..bff7bbf --- /dev/null +++ b/database/migrations/2018_02_17_134254_ensure_unique_allocation_id_on_servers_table.php @@ -0,0 +1,35 @@ +unique(['allocation_id']); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('servers', function (Blueprint $table) { + $table->dropForeign(['allocation_id']); + $table->dropUnique(['allocation_id']); + + $table->foreign('allocation_id')->references('id')->on('allocations'); + }); + } +} diff --git a/database/migrations/2018_02_24_112356_add_external_id_column_to_servers_table.php b/database/migrations/2018_02_24_112356_add_external_id_column_to_servers_table.php new file mode 100644 index 0000000..2c8af99 --- /dev/null +++ b/database/migrations/2018_02_24_112356_add_external_id_column_to_servers_table.php @@ -0,0 +1,32 @@ +string('external_id')->after('id')->nullable()->unique(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('servers', function (Blueprint $table) { + $table->dropColumn('external_id'); + }); + } +} diff --git a/database/migrations/2018_02_25_160152_remove_default_null_value_on_table.php b/database/migrations/2018_02_25_160152_remove_default_null_value_on_table.php new file mode 100644 index 0000000..6469867 --- /dev/null +++ b/database/migrations/2018_02_25_160152_remove_default_null_value_on_table.php @@ -0,0 +1,38 @@ +string('external_id')->default(null)->change(); + }); + + DB::transaction(function () { + DB::table('users')->where('external_id', '=', 'NULL')->update([ + 'external_id' => null, + ]); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // This should not be rolled back. + } +} diff --git a/database/migrations/2018_02_25_160604_define_unique_index_on_users_external_id.php b/database/migrations/2018_02_25_160604_define_unique_index_on_users_external_id.php new file mode 100644 index 0000000..0a9b8af --- /dev/null +++ b/database/migrations/2018_02_25_160604_define_unique_index_on_users_external_id.php @@ -0,0 +1,32 @@ +index(['external_id']); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('users', function (Blueprint $table) { + $table->dropIndex(['external_id']); + }); + } +} diff --git a/database/migrations/2018_03_01_192831_add_database_and_port_limit_columns_to_servers_table.php b/database/migrations/2018_03_01_192831_add_database_and_port_limit_columns_to_servers_table.php new file mode 100644 index 0000000..4e85e8a --- /dev/null +++ b/database/migrations/2018_03_01_192831_add_database_and_port_limit_columns_to_servers_table.php @@ -0,0 +1,33 @@ +unsignedInteger('database_limit')->after('installed')->nullable()->default(0); + $table->unsignedInteger('allocation_limit')->after('installed')->nullable()->default(0); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('servers', function (Blueprint $table) { + $table->dropColumn(['database_limit', 'allocation_limit']); + }); + } +} diff --git a/database/migrations/2018_03_15_124536_add_description_to_nodes.php b/database/migrations/2018_03_15_124536_add_description_to_nodes.php new file mode 100644 index 0000000..7208a42 --- /dev/null +++ b/database/migrations/2018_03_15_124536_add_description_to_nodes.php @@ -0,0 +1,32 @@ +text('description')->after('name'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('nodes', function (Blueprint $table) { + $table->dropColumn('description'); + }); + } +} diff --git a/database/migrations/2018_05_04_123826_add_maintenance_to_nodes.php b/database/migrations/2018_05_04_123826_add_maintenance_to_nodes.php new file mode 100644 index 0000000..04fdf00 --- /dev/null +++ b/database/migrations/2018_05_04_123826_add_maintenance_to_nodes.php @@ -0,0 +1,32 @@ +boolean('maintenance_mode')->after('behind_proxy')->default(false); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('nodes', function (Blueprint $table) { + $table->dropColumn('maintenance_mode'); + }); + } +} diff --git a/database/migrations/2018_09_03_143756_allow_egg_variables_to_have_longer_values.php b/database/migrations/2018_09_03_143756_allow_egg_variables_to_have_longer_values.php new file mode 100644 index 0000000..1996509 --- /dev/null +++ b/database/migrations/2018_09_03_143756_allow_egg_variables_to_have_longer_values.php @@ -0,0 +1,32 @@ +text('default_value')->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('egg_variables', function (Blueprint $table) { + $table->string('default_value')->change(); + }); + } +} diff --git a/database/migrations/2018_09_03_144005_allow_server_variables_to_have_longer_values.php b/database/migrations/2018_09_03_144005_allow_server_variables_to_have_longer_values.php new file mode 100644 index 0000000..cc90e0e --- /dev/null +++ b/database/migrations/2018_09_03_144005_allow_server_variables_to_have_longer_values.php @@ -0,0 +1,32 @@ +text('variable_value')->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('server_variables', function (Blueprint $table) { + $table->string('variable_value')->change(); + }); + } +} diff --git a/database/migrations/2019_03_02_142328_set_allocation_limit_default_null.php b/database/migrations/2019_03_02_142328_set_allocation_limit_default_null.php new file mode 100644 index 0000000..d91ce63 --- /dev/null +++ b/database/migrations/2019_03_02_142328_set_allocation_limit_default_null.php @@ -0,0 +1,32 @@ +unsignedInteger('allocation_limit')->nullable()->default(null)->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('servers', function (Blueprint $table) { + $table->unsignedInteger('allocation_limit')->nullable()->default(0)->change(); + }); + } +} diff --git a/database/migrations/2019_03_02_151321_fix_unique_index_to_account_for_host.php b/database/migrations/2019_03_02_151321_fix_unique_index_to_account_for_host.php new file mode 100644 index 0000000..59425ae --- /dev/null +++ b/database/migrations/2019_03_02_151321_fix_unique_index_to_account_for_host.php @@ -0,0 +1,42 @@ +dropUnique(['database']); + $table->dropUnique(['username']); + + $table->unique(['database_host_id', 'database']); + $table->unique(['database_host_id', 'username']); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('databases', function (Blueprint $table) { + $table->dropForeign(['database_host_id']); + + $table->dropUnique(['database_host_id', 'database']); + $table->dropUnique(['database_host_id', 'username']); + + $table->unique(['database']); + $table->unique(['username']); + }); + } +} diff --git a/database/migrations/2020_03_22_163911_merge_permissions_table_into_subusers.php b/database/migrations/2020_03_22_163911_merge_permissions_table_into_subusers.php new file mode 100644 index 0000000..27d2667 --- /dev/null +++ b/database/migrations/2020_03_22_163911_merge_permissions_table_into_subusers.php @@ -0,0 +1,129 @@ + P::ACTION_CONTROL_START, + 'power-stop' => P::ACTION_CONTROL_STOP, + 'power-restart' => P::ACTION_CONTROL_RESTART, + 'power-kill' => P::ACTION_CONTROL_STOP, + 'send-command' => P::ACTION_CONTROL_CONSOLE, + 'list-subusers' => P::ACTION_USER_READ, + 'view-subuser' => P::ACTION_USER_READ, + 'edit-subuser' => P::ACTION_USER_UPDATE, + 'create-subuser' => P::ACTION_USER_CREATE, + 'delete-subuser' => P::ACTION_USER_DELETE, + 'view-allocations' => P::ACTION_ALLOCATION_READ, + 'edit-allocation' => P::ACTION_ALLOCATION_UPDATE, + 'view-startup' => P::ACTION_STARTUP_READ, + 'edit-startup' => P::ACTION_STARTUP_UPDATE, + 'view-databases' => P::ACTION_DATABASE_READ, + // Better to just break this flow a bit than accidentally grant a dangerous permission. + 'reset-db-password' => P::ACTION_DATABASE_UPDATE, + 'delete-database' => P::ACTION_DATABASE_DELETE, + 'create-database' => P::ACTION_DATABASE_CREATE, + 'access-sftp' => P::ACTION_FILE_SFTP, + 'list-files' => P::ACTION_FILE_READ, + 'edit-files' => P::ACTION_FILE_READ_CONTENT, + 'save-files' => P::ACTION_FILE_UPDATE, + 'create-files' => P::ACTION_FILE_CREATE, + 'delete-files' => P::ACTION_FILE_DELETE, + 'compress-files' => P::ACTION_FILE_ARCHIVE, + 'list-schedules' => P::ACTION_SCHEDULE_READ, + 'view-schedule' => P::ACTION_SCHEDULE_READ, + 'edit-schedule' => P::ACTION_SCHEDULE_UPDATE, + 'create-schedule' => P::ACTION_SCHEDULE_CREATE, + 'delete-schedule' => P::ACTION_SCHEDULE_DELETE, + // Skipping these permissions as they are granted if you have more specific read/write permissions. + 'move-files' => null, + 'copy-files' => null, + 'decompress-files' => null, + 'upload-files' => null, + 'download-files' => null, + // These permissions do not exist in 1.0 + 'toggle-schedule' => null, + 'queue-schedule' => null, + ]; + + /** + * Run the migrations. + * + * @return void + */ + public function up() + { + Schema::table('subusers', function (Blueprint $table) { + $table->json('permissions')->nullable()->after('server_id'); + }); + + $cursor = DB::table('permissions') + ->select(['subuser_id']) + ->selectRaw('GROUP_CONCAT(permission) as permissions') + ->from('permissions') + ->groupBy(['subuser_id']) + ->cursor(); + + DB::transaction(function () use (&$cursor) { + $cursor->each(function ($datum) { + $updated = Collection::make(explode(',', $datum->permissions)) + ->map(function ($value) { + return self::$permissionsMap[$value] ?? null; + })->filter(function ($value) { + return !is_null($value) && $value !== Permission::ACTION_WEBSOCKET_CONNECT; + }) + // All subusers get this permission, so make sure it gets pushed into the array. + ->merge([Permission::ACTION_WEBSOCKET_CONNECT]) + ->unique() + ->values() + ->toJson(); + + DB::update('UPDATE subusers SET permissions = ? WHERE id = ?', [$updated, $datum->subuser_id]); + }); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + $flipped = array_flip(array_filter(self::$permissionsMap)); + + foreach (DB::select('SELECT id, permissions FROM subusers') as $datum) { + $values = []; + foreach (json_decode($datum->permissions, true) as $permission) { + $v = $flipped[$permission] ?? null; + if (!empty($v)) { + $values[] = $datum->id; + $values[] = $v; + } + } + + if (!empty($values)) { + $string = 'VALUES ' . implode(', ', array_fill(0, count($values) / 2, '(?, ?)')); + + DB::insert('INSERT INTO permissions(`subuser_id`, `permission`) ' . $string, $values); + } + } + + Schema::table('subusers', function (Blueprint $table) { + $table->dropColumn('permissions'); + }); + } +} diff --git a/database/migrations/2020_03_22_164814_drop_permissions_table.php b/database/migrations/2020_03_22_164814_drop_permissions_table.php new file mode 100644 index 0000000..da9d677 --- /dev/null +++ b/database/migrations/2020_03_22_164814_drop_permissions_table.php @@ -0,0 +1,34 @@ +increments('id'); + $table->unsignedInteger('subuser_id'); + $table->string('permission'); + + $table->foreign('subuser_id')->references('id')->on('subusers')->onDelete('cascade'); + }); + } +} diff --git a/database/migrations/2020_04_03_203624_add_threads_column_to_servers_table.php b/database/migrations/2020_04_03_203624_add_threads_column_to_servers_table.php new file mode 100644 index 0000000..9b0202c --- /dev/null +++ b/database/migrations/2020_04_03_203624_add_threads_column_to_servers_table.php @@ -0,0 +1,32 @@ +string('threads')->nullable()->after('cpu'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('servers', function (Blueprint $table) { + $table->dropColumn('threads'); + }); + } +} diff --git a/database/migrations/2020_04_03_230614_create_backups_table.php b/database/migrations/2020_04_03_230614_create_backups_table.php new file mode 100644 index 0000000..daa35dd --- /dev/null +++ b/database/migrations/2020_04_03_230614_create_backups_table.php @@ -0,0 +1,59 @@ +TABLE_NAME, $result->TABLE_NAME . '_plugin_bak'); + } + + Schema::create('backups', function (Blueprint $table) { + $table->bigIncrements('id'); + $table->unsignedInteger('server_id'); + $table->char('uuid', 36); + $table->string('name'); + $table->text('ignored_files'); + $table->string('disk'); + $table->string('sha256_hash')->nullable(); + $table->integer('bytes')->default(0); + $table->timestamp('completed_at')->nullable(); + $table->timestamps(); + $table->softDeletes(); + + $table->unique('uuid'); + $table->foreign('server_id')->references('id')->on('servers')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('backups'); + } +} diff --git a/database/migrations/2020_04_04_131016_add_table_server_transfers.php b/database/migrations/2020_04_04_131016_add_table_server_transfers.php new file mode 100644 index 0000000..096b538 --- /dev/null +++ b/database/migrations/2020_04_04_131016_add_table_server_transfers.php @@ -0,0 +1,45 @@ +increments('id'); + $table->integer('server_id')->unsigned(); + $table->tinyInteger('successful')->unsigned()->default(0); + $table->integer('old_node')->unsigned(); + $table->integer('new_node')->unsigned(); + $table->integer('old_allocation')->unsigned(); + $table->integer('new_allocation')->unsigned(); + $table->string('old_additional_allocations')->nullable(); + $table->string('new_additional_allocations')->nullable(); + $table->timestamps(); + + $table->foreign('server_id')->references('id')->on('servers')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('server_transfers'); + } +} diff --git a/database/migrations/2020_04_10_141024_store_node_tokens_as_encrypted_value.php b/database/migrations/2020_04_10_141024_store_node_tokens_as_encrypted_value.php new file mode 100644 index 0000000..6544679 --- /dev/null +++ b/database/migrations/2020_04_10_141024_store_node_tokens_as_encrypted_value.php @@ -0,0 +1,88 @@ +dropUnique(['daemonSecret']); + }); + + Schema::table('nodes', function (Blueprint $table) { + $table->char('uuid', 36)->after('id'); + $table->char('daemon_token_id', 16)->after('upload_size'); + $table->renameColumn('daemonSecret', 'daemon_token'); + }); + + Schema::table('nodes', function (Blueprint $table) { + $table->text('daemon_token')->change(); + }); + + /** @var \Illuminate\Contracts\Encryption\Encrypter $encrypter */ + $encrypter = Container::getInstance()->make(Encrypter::class); + + foreach (DB::select('SELECT id, daemon_token FROM nodes') as $datum) { + DB::update('UPDATE nodes SET uuid = ?, daemon_token_id = ?, daemon_token = ? WHERE id = ?', [ + Uuid::uuid4()->toString(), + substr($datum->daemon_token, 0, 16), + $encrypter->encrypt(substr($datum->daemon_token, 16)), + $datum->id, + ]); + } + + Schema::table('nodes', function (Blueprint $table) { + $table->unique(['uuid']); + $table->unique(['daemon_token_id']); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + DB::transaction(function () { + /** @var \Illuminate\Contracts\Encryption\Encrypter $encrypter */ + $encrypter = Container::getInstance()->make(Encrypter::class); + + foreach (DB::select('SELECT id, daemon_token_id, daemon_token FROM nodes') as $datum) { + DB::update('UPDATE nodes SET daemon_token = ? WHERE id = ?', [ + $datum->daemon_token_id . $encrypter->decrypt($datum->daemon_token), + $datum->id, + ]); + } + }); + + Schema::table('nodes', function (Blueprint $table) { + $table->dropUnique(['uuid']); + $table->dropUnique(['daemon_token_id']); + }); + + Schema::table('nodes', function (Blueprint $table) { + $table->dropColumn(['uuid', 'daemon_token_id']); + $table->renameColumn('daemon_token', 'daemonSecret'); + }); + + Schema::table('nodes', function (Blueprint $table) { + $table->string('daemonSecret', 36)->change(); + $table->unique(['daemonSecret']); + }); + } +} diff --git a/database/migrations/2020_04_17_203438_allow_nullable_descriptions.php b/database/migrations/2020_04_17_203438_allow_nullable_descriptions.php new file mode 100644 index 0000000..dfd55fb --- /dev/null +++ b/database/migrations/2020_04_17_203438_allow_nullable_descriptions.php @@ -0,0 +1,56 @@ +text('description')->nullable()->change(); + }); + + Schema::table('nests', function (Blueprint $table) { + $table->text('description')->nullable()->change(); + }); + + Schema::table('nodes', function (Blueprint $table) { + $table->text('description')->nullable()->change(); + }); + + Schema::table('locations', function (Blueprint $table) { + $table->text('long')->nullable()->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('eggs', function (Blueprint $table) { + $table->text('description')->nullable(false)->change(); + }); + + Schema::table('nests', function (Blueprint $table) { + $table->text('description')->nullable(false)->change(); + }); + + Schema::table('nodes', function (Blueprint $table) { + $table->text('description')->nullable(false)->change(); + }); + + Schema::table('locations', function (Blueprint $table) { + $table->text('long')->nullable(false)->change(); + }); + } +} diff --git a/database/migrations/2020_04_22_055500_add_max_connections_column.php b/database/migrations/2020_04_22_055500_add_max_connections_column.php new file mode 100644 index 0000000..02253df --- /dev/null +++ b/database/migrations/2020_04_22_055500_add_max_connections_column.php @@ -0,0 +1,32 @@ +integer('max_connections')->nullable()->default(0)->after('password'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('databases', function (Blueprint $table) { + $table->dropColumn('max_connections'); + }); + } +} diff --git a/database/migrations/2020_04_26_111208_add_backup_limit_to_servers.php b/database/migrations/2020_04_26_111208_add_backup_limit_to_servers.php new file mode 100644 index 0000000..b0f859c --- /dev/null +++ b/database/migrations/2020_04_26_111208_add_backup_limit_to_servers.php @@ -0,0 +1,47 @@ +unsignedInteger('backup_limit')->default(0)->change(); + }); + } else { + Schema::table('servers', function (Blueprint $table) { + $table->unsignedInteger('backup_limit')->default(0)->after('database_limit'); + }); + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('servers', function (Blueprint $table) { + $table->dropColumn('backup_limit'); + }); + } +} diff --git a/database/migrations/2020_05_20_234655_add_mounts_table.php b/database/migrations/2020_05_20_234655_add_mounts_table.php new file mode 100644 index 0000000..09846a0 --- /dev/null +++ b/database/migrations/2020_05_20_234655_add_mounts_table.php @@ -0,0 +1,53 @@ +increments('id')->unique(); + $table->char('uuid', 36)->unique(); + $table->string('name')->unique(); + $table->text('description')->nullable(); + $table->string('source'); + $table->string('target'); + $table->tinyInteger('read_only')->unsigned(); + $table->tinyInteger('user_mountable')->unsigned(); + }); + + Schema::create('egg_mount', function (Blueprint $table) { + $table->integer('egg_id'); + $table->integer('mount_id'); + + $table->unique(['egg_id', 'mount_id']); + }); + + Schema::create('mount_node', function (Blueprint $table) { + $table->integer('node_id'); + $table->integer('mount_id'); + + $table->unique(['node_id', 'mount_id']); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('mount_node'); + Schema::dropIfExists('egg_mount'); + Schema::dropIfExists('mounts'); + } +} diff --git a/database/migrations/2020_05_21_192756_add_mount_server_table.php b/database/migrations/2020_05_21_192756_add_mount_server_table.php new file mode 100644 index 0000000..682bd57 --- /dev/null +++ b/database/migrations/2020_05_21_192756_add_mount_server_table.php @@ -0,0 +1,33 @@ +integer('server_id'); + $table->integer('mount_id'); + + $table->unique(['server_id', 'mount_id']); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('mount_server'); + } +} diff --git a/database/migrations/2020_07_02_213612_create_user_recovery_tokens_table.php b/database/migrations/2020_07_02_213612_create_user_recovery_tokens_table.php new file mode 100644 index 0000000..9b0743a --- /dev/null +++ b/database/migrations/2020_07_02_213612_create_user_recovery_tokens_table.php @@ -0,0 +1,35 @@ +id(); + $table->unsignedInteger('user_id'); + $table->string('token'); + $table->timestamp('created_at')->nullable(); + + $table->foreign('user_id')->references('id')->on('users')->cascadeOnDelete(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('recovery_tokens'); + } +} diff --git a/database/migrations/2020_07_09_201845_add_notes_column_for_allocations.php b/database/migrations/2020_07_09_201845_add_notes_column_for_allocations.php new file mode 100644 index 0000000..711495e --- /dev/null +++ b/database/migrations/2020_07_09_201845_add_notes_column_for_allocations.php @@ -0,0 +1,32 @@ +string('notes')->nullable()->after('server_id'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('allocations', function (Blueprint $table) { + $table->dropColumn('notes'); + }); + } +} diff --git a/database/migrations/2020_08_20_205533_add_backup_state_column_to_backups.php b/database/migrations/2020_08_20_205533_add_backup_state_column_to_backups.php new file mode 100644 index 0000000..9e6faa4 --- /dev/null +++ b/database/migrations/2020_08_20_205533_add_backup_state_column_to_backups.php @@ -0,0 +1,32 @@ +boolean('is_successful')->after('uuid')->default(true); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('backups', function (Blueprint $table) { + $table->dropColumn('is_successful'); + }); + } +} diff --git a/database/migrations/2020_08_22_132500_update_bytes_to_unsigned_bigint.php b/database/migrations/2020_08_22_132500_update_bytes_to_unsigned_bigint.php new file mode 100644 index 0000000..e8e9c38 --- /dev/null +++ b/database/migrations/2020_08_22_132500_update_bytes_to_unsigned_bigint.php @@ -0,0 +1,32 @@ +unsignedBigInteger('bytes')->default(0)->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('backups', function (Blueprint $table) { + $table->integer('bytes')->default(0)->change(); + }); + } +} diff --git a/database/migrations/2020_08_23_175331_modify_checksums_column_for_backups.php b/database/migrations/2020_08_23_175331_modify_checksums_column_for_backups.php new file mode 100644 index 0000000..0de248b --- /dev/null +++ b/database/migrations/2020_08_23_175331_modify_checksums_column_for_backups.php @@ -0,0 +1,41 @@ +renameColumn('sha256_hash', 'checksum'); + }); + + Schema::table('backups', function (Blueprint $table) { + DB::update('UPDATE backups SET checksum = CONCAT(\'sha256:\', checksum)'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('backups', function (Blueprint $table) { + $table->renameColumn('checksum', 'sha256_hash'); + }); + + Schema::table('backups', function (Blueprint $table) { + DB::update('UPDATE backups SET sha256_hash = SUBSTRING(sha256_hash, 8)'); + }); + } +} diff --git a/database/migrations/2020_09_13_110007_drop_packs_from_servers.php b/database/migrations/2020_09_13_110007_drop_packs_from_servers.php new file mode 100644 index 0000000..638435a --- /dev/null +++ b/database/migrations/2020_09_13_110007_drop_packs_from_servers.php @@ -0,0 +1,34 @@ +dropForeign(['pack_id']); + $table->dropColumn('pack_id'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('servers', function (Blueprint $table) { + $table->unsignedInteger('pack_id')->after('egg_id')->nullable(); + $table->foreign('pack_id')->references('id')->on('packs'); + }); + } +} diff --git a/database/migrations/2020_09_13_110021_drop_packs_from_api_key_permissions.php b/database/migrations/2020_09_13_110021_drop_packs_from_api_key_permissions.php new file mode 100644 index 0000000..9bcce8d --- /dev/null +++ b/database/migrations/2020_09_13_110021_drop_packs_from_api_key_permissions.php @@ -0,0 +1,32 @@ +dropColumn('r_packs'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('api_keys', function (Blueprint $table) { + $table->unsignedTinyInteger('r_packs')->default(0); + }); + } +} diff --git a/database/migrations/2020_09_13_110047_drop_packs_table.php b/database/migrations/2020_09_13_110047_drop_packs_table.php new file mode 100644 index 0000000..4f83c0f --- /dev/null +++ b/database/migrations/2020_09_13_110047_drop_packs_table.php @@ -0,0 +1,43 @@ +increments('id'); + $table->unsignedInteger('egg_id'); + $table->char('uuid', 36)->unique(); + $table->string('name'); + $table->string('version'); + $table->text('description')->nullable(); + $table->tinyInteger('selectable')->default(1); + $table->tinyInteger('visible')->default(1); + $table->tinyInteger('locked')->default(0); + $table->timestamps(); + }); + + Schema::table('packs', function (Blueprint $table) { + $table->foreign('egg_id')->references('id')->on('eggs')->cascadeOnDelete(); + }); + } +} diff --git a/database/migrations/2020_09_13_113503_drop_daemon_key_table.php b/database/migrations/2020_09_13_113503_drop_daemon_key_table.php new file mode 100644 index 0000000..7b90d41 --- /dev/null +++ b/database/migrations/2020_09_13_113503_drop_daemon_key_table.php @@ -0,0 +1,40 @@ +increments('id'); + $table->unsignedInteger('server_id'); + $table->unsignedInteger('user_id'); + $table->string('secret')->unique(); + $table->timestamp('expires_at'); + $table->timestamps(); + }); + + Schema::table('daemon_keys', function (Blueprint $table) { + $table->foreign('server_id')->references('id')->on('servers')->cascadeOnDelete(); + $table->foreign('user_id')->references('id')->on('users')->cascadeOnDelete(); + }); + } +} diff --git a/database/migrations/2020_10_10_165437_change_unique_database_name_to_account_for_server.php b/database/migrations/2020_10_10_165437_change_unique_database_name_to_account_for_server.php new file mode 100644 index 0000000..7420989 --- /dev/null +++ b/database/migrations/2020_10_10_165437_change_unique_database_name_to_account_for_server.php @@ -0,0 +1,40 @@ +dropUnique(['database_host_id', 'database']); + }); + + Schema::table('databases', function (Blueprint $table) { + $table->unique(['database_host_id', 'server_id', 'database']); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('databases', function (Blueprint $table) { + $table->dropUnique(['database_host_id', 'server_id', 'database']); + }); + + Schema::table('databases', function (Blueprint $table) { + $table->unique(['database_host_id', 'database']); + }); + } +} diff --git a/database/migrations/2020_10_26_194904_remove_nullable_from_schedule_name_field.php b/database/migrations/2020_10_26_194904_remove_nullable_from_schedule_name_field.php new file mode 100644 index 0000000..69593e6 --- /dev/null +++ b/database/migrations/2020_10_26_194904_remove_nullable_from_schedule_name_field.php @@ -0,0 +1,35 @@ +string('name')->nullable(false)->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('schedules', function (Blueprint $table) { + $table->string('name')->nullable()->change(); + }); + } +} diff --git a/database/migrations/2020_11_02_201014_add_features_column_to_eggs.php b/database/migrations/2020_11_02_201014_add_features_column_to_eggs.php new file mode 100644 index 0000000..1a001ae --- /dev/null +++ b/database/migrations/2020_11_02_201014_add_features_column_to_eggs.php @@ -0,0 +1,32 @@ +json('features')->after('description')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('eggs', function (Blueprint $table) { + $table->dropColumn('features'); + }); + } +} diff --git a/database/migrations/2020_12_12_102435_support_multiple_docker_images_and_updates.php b/database/migrations/2020_12_12_102435_support_multiple_docker_images_and_updates.php new file mode 100644 index 0000000..776d3c6 --- /dev/null +++ b/database/migrations/2020_12_12_102435_support_multiple_docker_images_and_updates.php @@ -0,0 +1,51 @@ +json('docker_images')->after('docker_image')->nullable(); + $table->text('update_url')->after('docker_images')->nullable(); + }); + + Schema::table('eggs', function (Blueprint $table) { + DB::statement('UPDATE `eggs` SET `docker_images` = JSON_ARRAY(docker_image)'); + }); + + Schema::table('eggs', function (Blueprint $table) { + $table->dropColumn('docker_image'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('eggs', function (Blueprint $table) { + $table->text('docker_image')->after('docker_images'); + }); + + Schema::table('eggs', function (Blueprint $table) { + DB::statement('UPDATE `eggs` SET `docker_image` = JSON_UNQUOTE(JSON_EXTRACT(docker_images, "$[0]"))'); + }); + + Schema::table('eggs', function (Blueprint $table) { + $table->dropColumn('docker_images'); + $table->dropColumn('update_url'); + }); + } +} diff --git a/database/migrations/2020_12_14_013707_make_successful_nullable_in_server_transfers.php b/database/migrations/2020_12_14_013707_make_successful_nullable_in_server_transfers.php new file mode 100644 index 0000000..0a28852 --- /dev/null +++ b/database/migrations/2020_12_14_013707_make_successful_nullable_in_server_transfers.php @@ -0,0 +1,32 @@ +boolean('successful')->nullable()->default(null)->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('server_transfers', function (Blueprint $table) { + $table->boolean('successful')->default(0)->change(); + }); + } +} diff --git a/database/migrations/2020_12_17_014330_add_archived_field_to_server_transfers_table.php b/database/migrations/2020_12_17_014330_add_archived_field_to_server_transfers_table.php new file mode 100644 index 0000000..1162d8a --- /dev/null +++ b/database/migrations/2020_12_17_014330_add_archived_field_to_server_transfers_table.php @@ -0,0 +1,38 @@ +boolean('archived')->default(0)->after('new_additional_allocations'); + }); + + // Update archived to all be true on existing transfers. + Schema::table('server_transfers', function (Blueprint $table) { + DB::statement('UPDATE `server_transfers` SET `archived` = 1 WHERE `successful` = 1'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('server_transfers', function (Blueprint $table) { + $table->dropColumn('archived'); + }); + } +} diff --git a/database/migrations/2020_12_24_092449_make_allocation_fields_json.php b/database/migrations/2020_12_24_092449_make_allocation_fields_json.php new file mode 100644 index 0000000..bceec9d --- /dev/null +++ b/database/migrations/2020_12_24_092449_make_allocation_fields_json.php @@ -0,0 +1,34 @@ +json('old_additional_allocations')->nullable()->change(); + $table->json('new_additional_allocations')->nullable()->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('server_transfers', function (Blueprint $table) { + $table->string('old_additional_allocations')->nullable()->change(); + $table->string('new_additional_allocations')->nullable()->change(); + }); + } +} diff --git a/database/migrations/2020_12_26_184914_add_upload_id_column_to_backups_table.php b/database/migrations/2020_12_26_184914_add_upload_id_column_to_backups_table.php new file mode 100644 index 0000000..2e1c505 --- /dev/null +++ b/database/migrations/2020_12_26_184914_add_upload_id_column_to_backups_table.php @@ -0,0 +1,32 @@ +text('upload_id')->nullable()->after('uuid'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('backups', function (Blueprint $table) { + $table->dropColumn('upload_id'); + }); + } +} diff --git a/database/migrations/2021_01_10_153937_add_file_denylist_to_egg_configs.php b/database/migrations/2021_01_10_153937_add_file_denylist_to_egg_configs.php new file mode 100644 index 0000000..8d617fc --- /dev/null +++ b/database/migrations/2021_01_10_153937_add_file_denylist_to_egg_configs.php @@ -0,0 +1,32 @@ +text('file_denylist')->after('docker_images'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('eggs', function (Blueprint $table) { + $table->dropColumn('file_denylist'); + }); + } +} diff --git a/database/migrations/2021_01_13_013420_add_cron_month.php b/database/migrations/2021_01_13_013420_add_cron_month.php new file mode 100644 index 0000000..85e5342 --- /dev/null +++ b/database/migrations/2021_01_13_013420_add_cron_month.php @@ -0,0 +1,32 @@ +string('cron_month')->after('cron_day_of_week'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('schedules', function (Blueprint $table) { + $table->dropColumn('cron_month'); + }); + } +} diff --git a/database/migrations/2021_01_17_102401_create_audit_logs_table.php b/database/migrations/2021_01_17_102401_create_audit_logs_table.php new file mode 100644 index 0000000..f67e7d6 --- /dev/null +++ b/database/migrations/2021_01_17_102401_create_audit_logs_table.php @@ -0,0 +1,42 @@ +id(); + $table->char('uuid', 36); + $table->boolean('is_system')->default(false); + $table->unsignedInteger('user_id')->nullable(); + $table->unsignedInteger('server_id')->nullable(); + $table->string('action'); + $table->string('subaction')->nullable(); + $table->json('device'); + $table->json('metadata'); + $table->timestamp('created_at', 0); + + $table->foreign('user_id')->references('id')->on('users')->onDelete('set null'); + $table->foreign('server_id')->references('id')->on('servers')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('audit_logs'); + } +} diff --git a/database/migrations/2021_01_17_152623_add_generic_server_status_column.php b/database/migrations/2021_01_17_152623_add_generic_server_status_column.php new file mode 100644 index 0000000..12e6abb --- /dev/null +++ b/database/migrations/2021_01_17_152623_add_generic_server_status_column.php @@ -0,0 +1,55 @@ +string('status')->nullable()->after('description'); + }); + + DB::transaction(function () { + DB::update('UPDATE servers SET `status` = \'suspended\' WHERE `suspended` = 1'); + DB::update('UPDATE servers SET `status` = \'installing\' WHERE `installed` = 0'); + DB::update('UPDATE servers SET `status` = \'install_failed\' WHERE `installed` = 2'); + }); + + Schema::table('servers', function (Blueprint $table) { + $table->dropColumn('suspended'); + $table->dropColumn('installed'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('servers', function (Blueprint $table) { + $table->unsignedTinyInteger('suspended')->default(0); + $table->unsignedTinyInteger('installed')->default(0); + }); + + DB::transaction(function () { + DB::update('UPDATE servers SET `suspended` = 1 WHERE `status` = \'suspended\''); + DB::update('UPDATE servers SET `installed` = 1 WHERE `status` IS NULL'); + DB::update('UPDATE servers SET `installed` = 2 WHERE `status` = \'install_failed\''); + }); + + Schema::table('servers', function (Blueprint $table) { + $table->dropColumn('status'); + }); + } +} diff --git a/database/migrations/2021_01_26_210502_update_file_denylist_to_json.php b/database/migrations/2021_01_26_210502_update_file_denylist_to_json.php new file mode 100644 index 0000000..af49611 --- /dev/null +++ b/database/migrations/2021_01_26_210502_update_file_denylist_to_json.php @@ -0,0 +1,40 @@ +dropColumn('file_denylist'); + }); + + Schema::table('eggs', function (Blueprint $table) { + $table->json('file_denylist')->nullable()->after('docker_images'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('eggs', function (Blueprint $table) { + $table->dropColumn('file_denylist'); + }); + + Schema::table('eggs', function (Blueprint $table) { + $table->text('file_denylist')->after('docker_images'); + }); + } +} diff --git a/database/migrations/2021_02_23_205021_add_index_for_server_and_action.php b/database/migrations/2021_02_23_205021_add_index_for_server_and_action.php new file mode 100644 index 0000000..8881254 --- /dev/null +++ b/database/migrations/2021_02_23_205021_add_index_for_server_and_action.php @@ -0,0 +1,39 @@ +index(['action', 'server_id']); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('audit_logs', function (Blueprint $table) { + $table->dropIndex(['action', 'server_id']); + }); + } +} diff --git a/database/migrations/2021_02_23_212657_make_sftp_port_unsigned_int.php b/database/migrations/2021_02_23_212657_make_sftp_port_unsigned_int.php new file mode 100644 index 0000000..8eea848 --- /dev/null +++ b/database/migrations/2021_02_23_212657_make_sftp_port_unsigned_int.php @@ -0,0 +1,32 @@ +unsignedSmallInteger('daemonSFTP')->default(2022)->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('nodes', function (Blueprint $table) { + $table->smallInteger('daemonSFTP')->default(2022)->change(); + }); + } +} diff --git a/database/migrations/2021_03_21_104718_force_cron_month_field_to_have_value_if_missing.php b/database/migrations/2021_03_21_104718_force_cron_month_field_to_have_value_if_missing.php new file mode 100644 index 0000000..57e1299 --- /dev/null +++ b/database/migrations/2021_03_21_104718_force_cron_month_field_to_have_value_if_missing.php @@ -0,0 +1,31 @@ +unsignedTinyInteger('continue_on_failure')->after('is_queued')->default(0); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('tasks', function (Blueprint $table) { + $table->dropColumn('continue_on_failure'); + }); + } +} diff --git a/database/migrations/2021_05_01_092523_add_only_run_when_server_online_option_to_schedules.php b/database/migrations/2021_05_01_092523_add_only_run_when_server_online_option_to_schedules.php new file mode 100644 index 0000000..91bb43b --- /dev/null +++ b/database/migrations/2021_05_01_092523_add_only_run_when_server_online_option_to_schedules.php @@ -0,0 +1,32 @@ +unsignedTinyInteger('only_when_online')->after('is_processing')->default(0); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('schedules', function (Blueprint $table) { + $table->dropColumn('only_when_online'); + }); + } +} diff --git a/database/migrations/2021_05_03_201016_add_support_for_locking_a_backup.php b/database/migrations/2021_05_03_201016_add_support_for_locking_a_backup.php new file mode 100644 index 0000000..bafa4dd --- /dev/null +++ b/database/migrations/2021_05_03_201016_add_support_for_locking_a_backup.php @@ -0,0 +1,32 @@ +unsignedTinyInteger('is_locked')->after('is_successful')->default(0); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('backups', function (Blueprint $table) { + $table->dropColumn('is_locked'); + }); + } +} diff --git a/database/migrations/2021_07_12_013420_remove_userinteraction.php b/database/migrations/2021_07_12_013420_remove_userinteraction.php new file mode 100644 index 0000000..05321d4 --- /dev/null +++ b/database/migrations/2021_07_12_013420_remove_userinteraction.php @@ -0,0 +1,28 @@ +update([ + 'config_startup' => DB::raw('JSON_REMOVE(config_startup, \'$.userInteraction\')'), + ]); + } + + public function down() + { + // Add blank User Interaction array back to startup config + DB::table('eggs')->update([ + 'config_startup' => DB::raw('JSON_SET(config_startup, \'$.userInteraction\', JSON_ARRAY())'), + ]); + } +} diff --git a/database/migrations/2021_07_17_211512_create_user_ssh_keys_table.php b/database/migrations/2021_07_17_211512_create_user_ssh_keys_table.php new file mode 100644 index 0000000..d5b8a13 --- /dev/null +++ b/database/migrations/2021_07_17_211512_create_user_ssh_keys_table.php @@ -0,0 +1,34 @@ +increments('id'); + $table->unsignedInteger('user_id'); + $table->string('name'); + $table->string('fingerprint'); + $table->text('public_key'); + $table->timestamps(); + $table->softDeletes(); + + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::dropIfExists('user_ssh_keys'); + } +} diff --git a/database/migrations/2021_08_03_210600_change_successful_field_to_default_to_false_on_backups_table.php b/database/migrations/2021_08_03_210600_change_successful_field_to_default_to_false_on_backups_table.php new file mode 100644 index 0000000..d47b0e5 --- /dev/null +++ b/database/migrations/2021_08_03_210600_change_successful_field_to_default_to_false_on_backups_table.php @@ -0,0 +1,38 @@ +boolean('is_successful')->after('uuid')->default(false)->change(); + }); + + // Convert currently processing backups to the new format so things don't break. + DB::table('backups')->select('id')->where('is_successful', 1)->whereNull('completed_at')->update([ + 'is_successful' => 0, + ]); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('backups', function (Blueprint $table) { + $table->boolean('is_successful')->after('uuid')->default(true)->change(); + }); + } +} diff --git a/database/migrations/2021_08_21_175111_add_foreign_keys_to_mount_node_table.php b/database/migrations/2021_08_21_175111_add_foreign_keys_to_mount_node_table.php new file mode 100644 index 0000000..fad8dc1 --- /dev/null +++ b/database/migrations/2021_08_21_175111_add_foreign_keys_to_mount_node_table.php @@ -0,0 +1,59 @@ +unsignedInteger('node_id')->change(); + $table->unsignedInteger('mount_id')->change(); + }); + + // Fetch an array of node and mount ids to check relations against. + $nodes = DB::table('nodes')->select('id')->pluck('id')->toArray(); + $mounts = DB::table('mounts')->select('id')->pluck('id')->toArray(); + + // Drop any relations that are missing a node or mount. + DB::table('mount_node') + ->select('node_id', 'mount_id') + ->whereNotIn('node_id', $nodes) + ->orWhereNotIn('mount_id', $mounts) + ->delete(); + + Schema::table('mount_node', function (Blueprint $table) { + $table->foreign('node_id') + ->references('id') + ->on('nodes') + ->cascadeOnDelete() + ->cascadeOnUpdate(); + $table->foreign('mount_id')->references('id') + ->on('mounts') + ->cascadeOnDelete() + ->cascadeOnUpdate(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('mount_node', function (Blueprint $table) { + $table->dropForeign(['node_id']); + $table->dropForeign(['mount_id']); + }); + } +} diff --git a/database/migrations/2021_08_21_175118_add_foreign_keys_to_mount_server_table.php b/database/migrations/2021_08_21_175118_add_foreign_keys_to_mount_server_table.php new file mode 100644 index 0000000..9c5a403 --- /dev/null +++ b/database/migrations/2021_08_21_175118_add_foreign_keys_to_mount_server_table.php @@ -0,0 +1,58 @@ +unsignedInteger('server_id')->change(); + $table->unsignedInteger('mount_id')->change(); + }); + + // Fetch an array of node and mount ids to check relations against. + $servers = DB::table('servers')->select('id')->pluck('id')->toArray(); + $mounts = DB::table('mounts')->select('id')->pluck('id')->toArray(); + + // Drop any relations that are missing a server or mount. + DB::table('mount_server') + ->select('server_id', 'mount_id') + ->whereNotIn('server_id', $servers) + ->orWhereNotIn('mount_id', $mounts) + ->delete(); + + Schema::table('mount_server', function (Blueprint $table) { + $table->foreign('server_id') + ->references('id') + ->on('servers') + ->cascadeOnDelete() + ->cascadeOnUpdate(); + $table->foreign('mount_id')->references('id') + ->on('mounts') + ->cascadeOnDelete() + ->cascadeOnUpdate(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('mount_server', function (Blueprint $table) { + $table->dropForeign(['server_id']); + $table->dropForeign(['mount_id']); + }); + } +} diff --git a/database/migrations/2021_08_21_180921_add_foreign_keys_to_egg_mount_table.php b/database/migrations/2021_08_21_180921_add_foreign_keys_to_egg_mount_table.php new file mode 100644 index 0000000..7bf9950 --- /dev/null +++ b/database/migrations/2021_08_21_180921_add_foreign_keys_to_egg_mount_table.php @@ -0,0 +1,58 @@ +unsignedInteger('egg_id')->change(); + $table->unsignedInteger('mount_id')->change(); + }); + + // Fetch an array of node and mount ids to check relations against. + $eggs = DB::table('eggs')->select('id')->pluck('id')->toArray(); + $mounts = DB::table('mounts')->select('id')->pluck('id')->toArray(); + + // Drop any relations that are missing an egg or mount. + DB::table('egg_mount') + ->select('egg_id', 'mount_id') + ->whereNotIn('egg_id', $eggs) + ->orWhereNotIn('mount_id', $mounts) + ->delete(); + + Schema::table('egg_mount', function (Blueprint $table) { + $table->foreign('egg_id') + ->references('id') + ->on('eggs') + ->cascadeOnDelete() + ->cascadeOnUpdate(); + $table->foreign('mount_id')->references('id') + ->on('mounts') + ->cascadeOnDelete() + ->cascadeOnUpdate(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('egg_mount', function (Blueprint $table) { + $table->dropForeign(['egg_id']); + $table->dropForeign(['mount_id']); + }); + } +} diff --git a/database/migrations/2022_01_25_030847_drop_google_analytics.php b/database/migrations/2022_01_25_030847_drop_google_analytics.php new file mode 100644 index 0000000..5daf0bc --- /dev/null +++ b/database/migrations/2022_01_25_030847_drop_google_analytics.php @@ -0,0 +1,31 @@ +where('key', 'settings::app:analytics')->delete(); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + DB::table('settings')->insert( + [ + 'key' => 'settings::app:analytics', + ] + ); + } +} diff --git a/database/migrations/2022_05_07_165334_migrate_egg_images_array_to_new_format.php b/database/migrations/2022_05_07_165334_migrate_egg_images_array_to_new_format.php new file mode 100644 index 0000000..78dfe6e --- /dev/null +++ b/database/migrations/2022_05_07_165334_migrate_egg_images_array_to_new_format.php @@ -0,0 +1,40 @@ + value pairings to support naming the + * images provided. + */ + public function up() + { + DB::table('eggs')->select(['id', 'docker_images'])->cursor()->each(function ($egg) { + $images = is_null($egg->docker_images) ? [] : json_decode($egg->docker_images, true, 512, JSON_THROW_ON_ERROR); + + $results = []; + foreach ($images as $key => $value) { + $results[is_int($key) ? $value : $key] = $value; + } + + DB::table('eggs')->where('id', $egg->id)->update(['docker_images' => $results]); + }); + } + + /** + * Reverse the migrations. This just keeps the values from the docker images array. + * + * @return void + */ + public function down() + { + DB::table('eggs')->select(['id', 'docker_images'])->cursor()->each(function ($egg) { + DB::table('eggs')->where('id', $egg->id)->update([ + 'docker_images' => array_values(json_decode($egg->docker_images, true, 512, JSON_THROW_ON_ERROR)), + ]); + }); + } +} diff --git a/database/migrations/2022_05_28_135717_create_activity_logs_table.php b/database/migrations/2022_05_28_135717_create_activity_logs_table.php new file mode 100644 index 0000000..448439d --- /dev/null +++ b/database/migrations/2022_05_28_135717_create_activity_logs_table.php @@ -0,0 +1,37 @@ +id(); + $table->uuid('batch')->nullable(); + $table->string('event')->index(); + $table->string('ip'); + $table->text('description')->nullable(); + $table->nullableNumericMorphs('actor'); + $table->json('properties'); + $table->timestamp('timestamp')->useCurrent()->onUpdate(null); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('activity_logs'); + } +} diff --git a/database/migrations/2022_05_29_140349_create_activity_log_actors_table.php b/database/migrations/2022_05_29_140349_create_activity_log_actors_table.php new file mode 100644 index 0000000..6dc45d7 --- /dev/null +++ b/database/migrations/2022_05_29_140349_create_activity_log_actors_table.php @@ -0,0 +1,32 @@ +id(); + $table->foreignId('activity_log_id')->references('id')->on('activity_logs')->cascadeOnDelete(); + $table->numericMorphs('subject'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('activity_log_subjects'); + } +} diff --git a/database/migrations/2022_06_18_112822_track_api_key_usage_for_activity_events.php b/database/migrations/2022_06_18_112822_track_api_key_usage_for_activity_events.php new file mode 100644 index 0000000..6e35df9 --- /dev/null +++ b/database/migrations/2022_06_18_112822_track_api_key_usage_for_activity_events.php @@ -0,0 +1,31 @@ +unsignedInteger('api_key_id')->nullable()->after('actor_id'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('activity_logs', function (Blueprint $table) { + $table->dropColumn('api_key_id'); + }); + } +}; diff --git a/database/migrations/2022_08_16_214400_add_force_outgoing_ip_column_to_eggs_table.php b/database/migrations/2022_08_16_214400_add_force_outgoing_ip_column_to_eggs_table.php new file mode 100644 index 0000000..eb3a56b --- /dev/null +++ b/database/migrations/2022_08_16_214400_add_force_outgoing_ip_column_to_eggs_table.php @@ -0,0 +1,32 @@ +boolean('force_outgoing_ip')->default(false); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('eggs', function (Blueprint $table) { + $table->dropColumn('force_outgoing_ip'); + }); + } +} diff --git a/database/migrations/2022_08_16_230204_add_installed_at_column_to_servers_table.php b/database/migrations/2022_08_16_230204_add_installed_at_column_to_servers_table.php new file mode 100644 index 0000000..50a5e23 --- /dev/null +++ b/database/migrations/2022_08_16_230204_add_installed_at_column_to_servers_table.php @@ -0,0 +1,32 @@ +timestamp('installed_at')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('servers', function (Blueprint $table) { + $table->dropColumn('installed_at'); + }); + } +} diff --git a/database/migrations/2022_12_12_213937_update_mail_settings_to_new_format.php b/database/migrations/2022_12_12_213937_update_mail_settings_to_new_format.php new file mode 100644 index 0000000..97b6510 --- /dev/null +++ b/database/migrations/2022_12_12_213937_update_mail_settings_to_new_format.php @@ -0,0 +1,77 @@ +keys, 0); + $oldSettings = $settings->filter(fn (Setting $setting) => in_array($setting->key, $oldKeys)); + + // Gets the second column in our key table and gets all matching settings. + $newKeys = array_column($this->keys, 1); + $newSettings = $settings->filter(fn (Setting $setting) => in_array($setting->key, $newKeys)); + + // Map all the old settings to their new key. + $oldSettings->map(function (Setting $setting) use ($oldKeys) { + $row = array_search($setting->key, $oldKeys, true); + $setting->key = $this->keys[$row][1]; + + return $setting; + // Check if any settings with the new key already exist. + })->filter(function (Setting $setting) use ($newSettings) { + if ($newSettings->contains('key', $setting->key)) { + return false; + } + + return true; + // Update the settings to use their new keys if they don't already exist. + })->each(fn (Setting $setting) => $setting->save()); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + DB::transaction(function () { + $settings = Setting::all(); + + // Gets the second column in our key table and gets all matching settings. + $newKeys = array_column($this->keys, 0); + $newSettings = $settings->filter(fn (Setting $setting) => in_array($setting->key, $newKeys)); + + // Delete all settings that already have the new key. + $newSettings->each(fn (Setting $setting) => $setting->delete()); + + // Gets the first column in our key table and gets all matching settings. + $oldKeys = array_column($this->keys, 1); + $oldSettings = $settings->filter(fn (Setting $setting) => in_array($setting->key, $oldKeys)); + + // Map all the old settings to their new key. + $oldSettings->map(function (Setting $setting) use ($oldKeys) { + $row = array_search($setting->key, $oldKeys, true); + $setting->key = $this->keys[$row][0]; + + return $setting; + // Update the settings to use their new keys if they don't already exist. + })->each(fn (Setting $setting) => $setting->save()); + }); + } +}; diff --git a/database/migrations/2023_01_24_210051_add_uuid_column_to_failed_jobs_table.php b/database/migrations/2023_01_24_210051_add_uuid_column_to_failed_jobs_table.php new file mode 100644 index 0000000..e3482e2 --- /dev/null +++ b/database/migrations/2023_01_24_210051_add_uuid_column_to_failed_jobs_table.php @@ -0,0 +1,34 @@ +string('uuid')->after('id')->nullable()->unique(); + }); + + DB::table('failed_jobs')->whereNull('uuid')->cursor()->each(function ($job) { + DB::table('failed_jobs') + ->where('id', $job->id) + ->update(['uuid' => (string) Illuminate\Support\Str::uuid()]); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('failed_jobs', function (Blueprint $table) { + $table->dropColumn('uuid'); + }); + } +}; diff --git a/database/migrations/2023_02_23_191004_add_expires_at_column_to_api_keys_table.php b/database/migrations/2023_02_23_191004_add_expires_at_column_to_api_keys_table.php new file mode 100644 index 0000000..49e19bf --- /dev/null +++ b/database/migrations/2023_02_23_191004_add_expires_at_column_to_api_keys_table.php @@ -0,0 +1,27 @@ +timestamp('expires_at')->nullable()->after('last_used_at'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('api_keys', function (Blueprint $table) { + $table->dropColumn('expires_at'); + }); + } +}; diff --git a/docker-compose.example.yml b/docker-compose.example.yml new file mode 100644 index 0000000..a0fbaba --- /dev/null +++ b/docker-compose.example.yml @@ -0,0 +1,78 @@ +version: '3.8' +x-common: + database: + &db-environment + # Do not remove the "&db-password" from the end of the line below, it is important + # for Panel functionality. + MYSQL_PASSWORD: &db-password "CHANGE_ME" + MYSQL_ROOT_PASSWORD: "CHANGE_ME_TOO" + panel: + &panel-environment + APP_URL: "http://example.com" + # A list of valid timezones can be found here: http://php.net/manual/en/timezones.php + APP_TIMEZONE: "UTC" + APP_SERVICE_AUTHOR: "noreply@example.com" + # Uncomment the line below and set to a non-empty value if you want to use Let's Encrypt + # to generate an SSL certificate for the Panel. + # LE_EMAIL: "" + mail: + &mail-environment + MAIL_FROM: "noreply@example.com" + MAIL_DRIVER: "smtp" + MAIL_HOST: "mail" + MAIL_PORT: "1025" + MAIL_USERNAME: "" + MAIL_PASSWORD: "" + MAIL_ENCRYPTION: "true" + +# +# ------------------------------------------------------------------------------------------ +# DANGER ZONE BELOW +# +# The remainder of this file likely does not need to be changed. Please only make modifications +# below if you understand what you are doing. +# +services: + database: + image: mariadb:10.5 + restart: always + command: --default-authentication-plugin=mysql_native_password + volumes: + - "/srv/pterodactyl/database:/var/lib/mysql" + environment: + <<: *db-environment + MYSQL_DATABASE: "panel" + MYSQL_USER: "pterodactyl" + cache: + image: redis:alpine + restart: always + panel: + image: ghcr.io/pterodactyl/panel:latest + restart: always + ports: + - "80:80" + - "443:443" + links: + - database + - cache + volumes: + - "/srv/pterodactyl/var/:/app/var/" + - "/srv/pterodactyl/nginx/:/etc/nginx/http.d/" + - "/srv/pterodactyl/certs/:/etc/letsencrypt/" + - "/srv/pterodactyl/logs/:/app/storage/logs" + environment: + <<: [*panel-environment, *mail-environment] + DB_PASSWORD: *db-password + APP_ENV: "production" + APP_ENVIRONMENT_ONLY: "false" + CACHE_DRIVER: "redis" + SESSION_DRIVER: "redis" + QUEUE_DRIVER: "redis" + REDIS_HOST: "cache" + DB_HOST: "database" + DB_PORT: "3306" +networks: + default: + ipam: + config: + - subnet: 172.20.0.0/16 diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..afb0fb1 --- /dev/null +++ b/flake.lock @@ -0,0 +1,205 @@ +{ + "nodes": { + "dream2nix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "purescript-overlay": "purescript-overlay", + "pyproject-nix": "pyproject-nix" + }, + "locked": { + "lastModified": 1710268378, + "narHash": "sha256-O61PtxUHbmwI6Ltjn9jY3QY2hRPPz7pe3BHhIYK4QkU=", + "owner": "nix-community", + "repo": "dream2nix", + "rev": "cd782df677aad08f7193c97376d615943c3cd4c9", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "dream2nix", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "mk-node-package": { + "inputs": { + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs" + ], + "npmlock2nix": "npmlock2nix", + "pnpm2nix": "pnpm2nix" + }, + "locked": { + "lastModified": 1633790997, + "narHash": "sha256-1mk4EwNkWtTNpeRivZmJTzB+92g07maeFRVUMnnRh1U=", + "owner": "winston0410", + "repo": "mkNodePackage", + "rev": "a7eca5e027c8b260dca4ece7d8dd187f92420611", + "type": "github" + }, + "original": { + "owner": "winston0410", + "repo": "mkNodePackage", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1709961763, + "narHash": "sha256-6H95HGJHhEZtyYA3rIQpvamMKAGoa8Yh2rFV29QnuGw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "3030f185ba6a4bf4f18b87f345f104e6a6961f34", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "npmlock2nix": { + "flake": false, + "locked": { + "lastModified": 1633729941, + "narHash": "sha256-v2YPcEWI1Wz8ErivorubgLcDT06H6YzFT7uhp1ymqnE=", + "owner": "winston0410", + "repo": "npmlock2nix", + "rev": "6ade47a330b6919defb45c0eb984a64234aa8468", + "type": "github" + }, + "original": { + "owner": "winston0410", + "ref": "issue113", + "repo": "npmlock2nix", + "type": "github" + } + }, + "pnpm2nix": { + "flake": false, + "locked": { + "lastModified": 1594396611, + "narHash": "sha256-UXOUQ+2A89/zaxYhTHiRrRBU5exbUWrg+FoJYMcNwuI=", + "owner": "nix-community", + "repo": "pnpm2nix", + "rev": "f67be0925a91b92f54d99dbdead7a06920b979ac", + "type": "github" + }, + "original": { + "owner": "nix-community", + "ref": "master", + "repo": "pnpm2nix", + "type": "github" + } + }, + "purescript-overlay": { + "inputs": { + "nixpkgs": [ + "dream2nix", + "nixpkgs" + ], + "slimlock": "slimlock" + }, + "locked": { + "lastModified": 1696022621, + "narHash": "sha256-eMjFmsj2G1E0Q5XiibUNgFjTiSz0GxIeSSzzVdoN730=", + "owner": "thomashoneyman", + "repo": "purescript-overlay", + "rev": "047c7933abd6da8aa239904422e22d190ce55ead", + "type": "github" + }, + "original": { + "owner": "thomashoneyman", + "repo": "purescript-overlay", + "type": "github" + } + }, + "pyproject-nix": { + "flake": false, + "locked": { + "lastModified": 1702448246, + "narHash": "sha256-hFg5s/hoJFv7tDpiGvEvXP0UfFvFEDgTdyHIjDVHu1I=", + "owner": "davhau", + "repo": "pyproject.nix", + "rev": "5a06a2697b228c04dd2f35659b4b659ca74f7aeb", + "type": "github" + }, + "original": { + "owner": "davhau", + "ref": "dream2nix", + "repo": "pyproject.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "dream2nix": "dream2nix", + "flake-utils": "flake-utils", + "mk-node-package": "mk-node-package", + "nixpkgs": "nixpkgs" + } + }, + "slimlock": { + "inputs": { + "nixpkgs": [ + "dream2nix", + "purescript-overlay", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1688610262, + "narHash": "sha256-Wg0ViDotFWGWqKIQzyYCgayeH8s4U1OZcTiWTQYdAp4=", + "owner": "thomashoneyman", + "repo": "slimlock", + "rev": "b5c6cdcaf636ebbebd0a1f32520929394493f1a6", + "type": "github" + }, + "original": { + "owner": "thomashoneyman", + "repo": "slimlock", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..43d760e --- /dev/null +++ b/flake.nix @@ -0,0 +1,262 @@ +{ + description = "Pterodactyl Panel"; + + inputs = { + dream2nix = { + url = "github:nix-community/dream2nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + flake-utils = { + url = "github:numtide/flake-utils"; + }; + + mk-node-package = { + url = "github:winston0410/mkNodePackage"; + inputs = { + flake-utils.follows = "flake-utils"; + nixpkgs.follows = "nixpkgs"; + }; + }; + + nixpkgs = { + url = "github:NixOS/nixpkgs/nixos-unstable"; + }; + }; + + outputs = { + self, + dream2nix, + flake-utils, + mk-node-package, + nixpkgs, + ... + }: + flake-utils.lib.eachDefaultSystem ( + system: let + version = "latest"; + + pkgs = import nixpkgs {inherit system;}; + mkNodePackage = mk-node-package.lib."${system}".mkNodePackage; + + php = pkgs.php; # PHP 8.2 + phpPackages = pkgs.phpPackages; # PHP 8.2 + + phpWithExtensions = php.buildEnv { + extensions = { + enabled, + all, + }: + enabled + ++ (with all; [ + redis + xdebug + ]); + extraConfig = '' + xdebug.mode=debug + ''; + }; + composer = phpPackages.composer.override {php = phpWithExtensions;}; + + caCertificates = pkgs.runCommand "ca-certificates" {} '' + mkdir -p $out/etc/ssl/certs $out/etc/pki/tls/certs + ln -s ${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt $out/etc/ssl/certs/ca-bundle.crt + ln -s ${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt $out/etc/ssl/certs/ca-certificates.crt + ln -s ${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt $out/etc/pki/tls/certs/ca-bundle.crt + ''; + + caddyfile = pkgs.writeText "Caddyfile" '' + :80 { + root * /var/www/html/public/ + file_server + + header { + -Server + -X-Powered-By + Referrer-Policy "same-origin" + X-Frame-Options "deny" + X-XSS-Protection "1; mode=block" + X-Content-Type-Options "nosniff" + } + + encode gzip zstd + + php_fastcgi localhost:9000 + + try_files {path} {path}/ /index.php?{query} + } + ''; + + phpfpmConf = pkgs.writeText "php-fpm.conf" '' + [global] + error_log = /dev/stderr + daemonize = no + + [www] + user = nobody + group = nobody + + listen = 0.0.0.0:9000 + + pm = dynamic + pm.start_servers = 4 + pm.min_spare_servers = 4 + pm.max_spare_servers = 16 + pm.max_children = 64 + pm.max_requests = 256 + + clear_env = no + catch_workers_output = yes + + decorate_workers_output = no + ''; + + configs = pkgs.runCommand "configs" {} '' + mkdir -p $out/etc/caddy + ln -s ${caddyfile} $out/etc/caddy/Caddyfile + ln -s ${phpfpmConf} $out/etc/php-fpm.conf + ''; + + src = with pkgs.lib; + cleanSource (cleanSourceWith { + filter = name: type: let + baseName = baseNameOf (toString name); + in + !(builtins.elem baseName [ + ".direnv" + ".github" + "bootstrap/cache" + "node_modules" + "public/build" + "public/hot" + "storage" + "vendor" + ".editorconfig" + ".env" + ".env.testing" + ".envrc" + ".gitignore" + ".php-cs-fixer.cache" + ".phpunit.result.cache" + "BUILDING.md" + "CODE_OF_CONDUCT.md" + "CONTRIBUTING.md" + "docker-compose.development.yaml" + "docker-compose.example.yaml" + "docker-compose.yaml" + "flake.lock" + "flake.nix" + "shell.nix" + ]); + src = ./.; + }); + + app = + (dream2nix.lib.makeFlakeOutputs { + config.projectRoot = src; + source = src; + settings = [ + { + translator = "composer-lock"; + subsystemInfo.noDev = true; + } + ]; + systems = [system]; + autoProjects = true; + }) + .packages + ."${system}" + ."pterodactyl/panel"; + + ui = mkNodePackage { + inherit src version; + + pname = "pterodactyl"; + buildInputs = []; + + buildPhase = '' + yarn run build:production + ''; + + installPhase = '' + mkdir -p $out + cp -r public/build $out + ''; + }; + + panel = pkgs.stdenv.mkDerivation { + inherit src version; + + pname = "pterodactyl"; + buildInputs = [app ui]; + + installPhase = '' + cp -r ${app}/lib/vendor/pterodactyl/panel $out + + chmod 755 $out + chmod 755 $out/public + + mkdir -p $out/public/build + cp -r ${ui}/build/* $out/public/build + + rm $out/composer.json.orig + ''; + }; + in { + defaultPackage = panel; + devShell = import ./shell.nix {inherit composer phpWithExtensions pkgs;}; + + packages = { + inherit panel; + + development = with pkgs; + dockerTools.buildImage { + name = "pterodactyl/development"; + tag = "panel"; + + copyToRoot = pkgs.buildEnv { + name = "image-root"; + paths = [ + bash + dockerTools.fakeNss + caCertificates + caddy + composer + configs + coreutils + mysql80 + nodejs_18 + nodePackages.yarn + phpWithExtensions + ]; + pathsToLink = ["/bin" "/etc"]; + }; + }; + + oci = with pkgs; + dockerTools.buildImage { + name = "pterodactyl/panel"; + tag = version; + + copyToRoot = buildEnv { + name = "image-root"; + paths = [ + dockerTools.fakeNss + caCertificates + caddy + configs + phpWithExtensions + + panel + ]; + pathsToLink = ["/bin" "/etc"]; + }; + + config = { + Cmd = []; + }; + }; + }; + } + ); +} diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..abb02f3 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,28 @@ +const { pathsToModuleNameMapper } = require('ts-jest'); +const { compilerOptions } = require('./tsconfig'); + +/** @type {import('ts-jest').InitialOptionsTsJest} */ +module.exports = { + preset: 'ts-jest', + globals: { + 'ts-jest': { + isolatedModules: true, + }, + }, + moduleFileExtensions: ['js', 'ts', 'tsx', 'd.ts', 'json', 'node'], + moduleNameMapper: { + '\\.(jpe?g|png|gif|svg)$': '/resources/scripts/__mocks__/file.ts', + '\\.(s?css|less)$': 'identity-obj-proxy', + ...pathsToModuleNameMapper(compilerOptions.paths, { + prefix: '/', + }), + }, + setupFilesAfterEnv: [ + '/resources/scripts/setup-tests.ts', + ], + transform: { + '.*\\.[t|j]sx$': 'babel-jest', + '.*\\.ts$': 'ts-jest', + }, + testPathIgnorePatterns: ['/node_modules/'], +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..4b44308 --- /dev/null +++ b/package.json @@ -0,0 +1,157 @@ +{ + "name": "pterodactyl-panel", + "engines": { + "node": ">=14" + }, + "dependencies": { + "@floating-ui/react-dom-interactions": "^0.6.6", + "@fortawesome/fontawesome-svg-core": "^1.2.32", + "@fortawesome/free-solid-svg-icons": "^5.15.1", + "@fortawesome/react-fontawesome": "^0.1.11", + "@headlessui/react": "^1.6.4", + "@heroicons/react": "^1.0.6", + "@hot-loader/react-dom": "^16.14.0", + "@preact/signals-react": "^1.2.1", + "@tailwindcss/forms": "^0.5.2", + "@tailwindcss/line-clamp": "^0.4.0", + "axios": "^0.27.2", + "boring-avatars": "^1.7.0", + "chart.js": "^3.8.0", + "classnames": "^2.3.1", + "codemirror": "^5.57.0", + "copy-to-clipboard": "^3.3.1", + "date-fns": "^2.28.0", + "debounce": "^1.2.0", + "deepmerge-ts": "^4.2.1", + "easy-peasy": "^4.0.1", + "events": "^3.0.0", + "formik": "^2.2.6", + "framer-motion": "^6.3.10", + "i18next": "^21.8.9", + "i18next-http-backend": "^1.4.1", + "i18next-multiload-backend-adapter": "^1.0.0", + "qrcode.react": "^1.0.1", + "react": "^16.14.0", + "react-chartjs-2": "^4.2.0", + "react-dom": "npm:@hot-loader/react-dom", + "react-fast-compare": "^3.2.0", + "react-hot-loader": "^4.12.21", + "react-i18next": "^11.2.1", + "react-router-dom": "^5.1.2", + "react-transition-group": "^4.4.1", + "reaptcha": "^1.7.2", + "sockette": "^2.0.6", + "styled-components": "^5.2.1", + "styled-components-breakpoint": "^3.0.0-preview.20", + "swr": "^0.2.3", + "tailwindcss": "^3.0.24", + "use-fit-text": "^2.4.0", + "uuid": "^8.3.2", + "xterm": "^4.19.0", + "xterm-addon-fit": "^0.5.0", + "xterm-addon-search": "^0.9.0", + "xterm-addon-search-bar": "^0.2.0", + "xterm-addon-web-links": "^0.6.0", + "yup": "^0.29.1" + }, + "devDependencies": { + "@babel/core": "^7.12.1", + "@babel/plugin-proposal-class-properties": "^7.12.1", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.1", + "@babel/plugin-proposal-object-rest-spread": "^7.12.1", + "@babel/plugin-proposal-optional-chaining": "^7.12.1", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-transform-modules-commonjs": "^7.18.2", + "@babel/plugin-transform-react-jsx": "^7.12.1", + "@babel/plugin-transform-runtime": "^7.12.1", + "@babel/preset-env": "^7.12.1", + "@babel/preset-react": "^7.12.1", + "@babel/preset-typescript": "^7.12.1", + "@babel/runtime": "^7.12.1", + "@testing-library/dom": "^8.14.0", + "@testing-library/jest-dom": "^5.16.4", + "@testing-library/react": "12.1.5", + "@testing-library/user-event": "^14.2.1", + "@types/codemirror": "^0.0.98", + "@types/debounce": "^1.2.0", + "@types/events": "^3.0.0", + "@types/jest": "^28.1.3", + "@types/node": "^14.11.10", + "@types/qrcode.react": "^1.0.1", + "@types/react": "^16.14.0", + "@types/react-copy-to-clipboard": "^4.3.0", + "@types/react-dom": "^16.9.16", + "@types/react-redux": "^7.1.1", + "@types/react-router": "^5.1.3", + "@types/react-router-dom": "^5.1.3", + "@types/react-transition-group": "^4.4.0", + "@types/styled-components": "^5.1.7", + "@types/uuid": "^3.4.5", + "@types/webpack-env": "^1.15.2", + "@types/yup": "^0.29.3", + "@typescript-eslint/eslint-plugin": "^5.29.0", + "@typescript-eslint/parser": "^5.29.0", + "autoprefixer": "^10.4.7", + "babel-jest": "^28.1.1", + "babel-loader": "^8.2.5", + "babel-plugin-styled-components": "^2.0.7", + "cross-env": "^7.0.2", + "css-loader": "^5.2.7", + "eslint": "^8.18.0", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-jest-dom": "^4.0.2", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-prettier": "^4.0.0", + "eslint-plugin-react": "^7.30.1", + "eslint-plugin-react-hooks": "^4.6.0", + "fork-ts-checker-webpack-plugin": "^6.2.10", + "identity-obj-proxy": "^3.0.0", + "jest": "^28.1.1", + "postcss": "^8.4.14", + "postcss-import": "^14.1.0", + "postcss-loader": "^4.0.0", + "postcss-nesting": "^10.1.8", + "postcss-preset-env": "^7.7.1", + "prettier": "^2.7.1", + "redux-devtools-extension": "^2.13.8", + "source-map-loader": "^1.1.3", + "style-loader": "^2.0.0", + "svg-url-loader": "^7.1.1", + "terser-webpack-plugin": "^4.2.3", + "ts-essentials": "^9.1.2", + "ts-jest": "^28.0.5", + "twin.macro": "^2.8.2", + "typescript": "^4.7.3", + "webpack": "^4.43.0", + "webpack-assets-manifest": "^3.1.1", + "webpack-bundle-analyzer": "^3.8.0", + "webpack-cli": "^3.3.12", + "webpack-dev-server": "^3.11.0", + "yarn-deduplicate": "^1.1.1" + }, + "scripts": { + "clean": "cd public/assets && find . \\( -name \"*.js\" -o -name \"*.map\" -o -name \"*.json\" \\) -type f -delete", + "test": "jest", + "lint": "eslint ./resources/scripts/**/*.{ts,tsx} --ext .ts,.tsx", + "watch": "crossx-env NODE_ENV=development ./node_modules/.bin/webpack --watch --progress", + "build": "yarn run clean && cross-env NODE_ENV=development ./node_modules/.bin/webpack --progress", + "build:production": "yarn run clean && cross-env NODE_ENV=production ./node_modules/.bin/webpack --mode production --progress", + "serve": "yarn run clean && cross-env WEBPACK_PUBLIC_PATH=/webpack@hmr/ NODE_ENV=development webpack-dev-server --host 0.0.0.0 --port 8081 --hot" + }, + "browserslist": [ + "> 0.5%", + "last 2 versions", + "firefox esr", + "not dead" + ], + "babelMacros": { + "twin": { + "preset": "styled-components" + }, + "styledComponents": { + "pure": true, + "displayName": true, + "fileName": true + } + } +} diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..8d47f56 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,30 @@ + + + + + ./app + + + + + ./tests/Integration + + + ./tests/Unit + + + + + + + + + + + + diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..0cc56ab --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,17 @@ +module.exports = { + plugins: [ + require('postcss-import'), + // We want to make use of nesting following the CSS Nesting spec, and not the + // SASS style nesting. + // + // @see https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-nesting + require('tailwindcss/nesting')(require('postcss-nesting')), + require('tailwindcss'), + require('autoprefixer'), + require('postcss-preset-env')({ + features: { + 'nesting-rules': false, + }, + }), + ], +}; diff --git a/public/.gitignore b/public/.gitignore new file mode 100644 index 0000000..477f0a1 --- /dev/null +++ b/public/.gitignore @@ -0,0 +1,4 @@ +assets +assets/* +!assets/svgs +!assets/svgs/*.svg diff --git a/public/.htaccess b/public/.htaccess new file mode 100644 index 0000000..b75525b --- /dev/null +++ b/public/.htaccess @@ -0,0 +1,21 @@ + + + Options -MultiViews -Indexes + + + RewriteEngine On + + # Handle Authorization Header + RewriteCond %{HTTP:Authorization} . + RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + + # Redirect Trailing Slashes If Not A Folder... + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_URI} (.+)/$ + RewriteRule ^ %1 [L,R=301] + + # Handle Front Controller... + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^ index.php [L] + diff --git a/public/favicons/android-chrome-192x192.png b/public/favicons/android-chrome-192x192.png new file mode 100644 index 0000000..aed30b2 Binary files /dev/null and b/public/favicons/android-chrome-192x192.png differ diff --git a/public/favicons/android-chrome-512x512.png b/public/favicons/android-chrome-512x512.png new file mode 100644 index 0000000..5ca796c Binary files /dev/null and b/public/favicons/android-chrome-512x512.png differ diff --git a/public/favicons/android-icon-144x144.png b/public/favicons/android-icon-144x144.png new file mode 100644 index 0000000..7e98c1c Binary files /dev/null and b/public/favicons/android-icon-144x144.png differ diff --git a/public/favicons/android-icon-192x192.png b/public/favicons/android-icon-192x192.png new file mode 100644 index 0000000..80ce4d3 Binary files /dev/null and b/public/favicons/android-icon-192x192.png differ diff --git a/public/favicons/android-icon-36x36.png b/public/favicons/android-icon-36x36.png new file mode 100644 index 0000000..729e212 Binary files /dev/null and b/public/favicons/android-icon-36x36.png differ diff --git a/public/favicons/android-icon-48x48.png b/public/favicons/android-icon-48x48.png new file mode 100644 index 0000000..9e5fe26 Binary files /dev/null and b/public/favicons/android-icon-48x48.png differ diff --git a/public/favicons/android-icon-72x72.png b/public/favicons/android-icon-72x72.png new file mode 100644 index 0000000..e6bb769 Binary files /dev/null and b/public/favicons/android-icon-72x72.png differ diff --git a/public/favicons/android-icon-96x96.png b/public/favicons/android-icon-96x96.png new file mode 100644 index 0000000..f9b8fbe Binary files /dev/null and b/public/favicons/android-icon-96x96.png differ diff --git a/public/favicons/apple-icon-114x114.png b/public/favicons/apple-icon-114x114.png new file mode 100644 index 0000000..3920011 Binary files /dev/null and b/public/favicons/apple-icon-114x114.png differ diff --git a/public/favicons/apple-icon-120x120.png b/public/favicons/apple-icon-120x120.png new file mode 100644 index 0000000..81b72ba Binary files /dev/null and b/public/favicons/apple-icon-120x120.png differ diff --git a/public/favicons/apple-icon-144x144.png b/public/favicons/apple-icon-144x144.png new file mode 100644 index 0000000..7e98c1c Binary files /dev/null and b/public/favicons/apple-icon-144x144.png differ diff --git a/public/favicons/apple-icon-152x152.png b/public/favicons/apple-icon-152x152.png new file mode 100644 index 0000000..6623bd5 Binary files /dev/null and b/public/favicons/apple-icon-152x152.png differ diff --git a/public/favicons/apple-icon-180x180.png b/public/favicons/apple-icon-180x180.png new file mode 100644 index 0000000..0503d52 Binary files /dev/null and b/public/favicons/apple-icon-180x180.png differ diff --git a/public/favicons/apple-icon-57x57.png b/public/favicons/apple-icon-57x57.png new file mode 100644 index 0000000..466878e Binary files /dev/null and b/public/favicons/apple-icon-57x57.png differ diff --git a/public/favicons/apple-icon-60x60.png b/public/favicons/apple-icon-60x60.png new file mode 100644 index 0000000..09a79d9 Binary files /dev/null and b/public/favicons/apple-icon-60x60.png differ diff --git a/public/favicons/apple-icon-72x72.png b/public/favicons/apple-icon-72x72.png new file mode 100644 index 0000000..e6bb769 Binary files /dev/null and b/public/favicons/apple-icon-72x72.png differ diff --git a/public/favicons/apple-icon-76x76.png b/public/favicons/apple-icon-76x76.png new file mode 100644 index 0000000..f656479 Binary files /dev/null and b/public/favicons/apple-icon-76x76.png differ diff --git a/public/favicons/apple-icon-precomposed.png b/public/favicons/apple-icon-precomposed.png new file mode 100644 index 0000000..277e51b Binary files /dev/null and b/public/favicons/apple-icon-precomposed.png differ diff --git a/public/favicons/apple-icon.png b/public/favicons/apple-icon.png new file mode 100644 index 0000000..277e51b Binary files /dev/null and b/public/favicons/apple-icon.png differ diff --git a/public/favicons/apple-touch-icon.png b/public/favicons/apple-touch-icon.png new file mode 100644 index 0000000..5ec6cfc Binary files /dev/null and b/public/favicons/apple-touch-icon.png differ diff --git a/public/favicons/browserconfig.xml b/public/favicons/browserconfig.xml new file mode 100644 index 0000000..c554148 --- /dev/null +++ b/public/favicons/browserconfig.xml @@ -0,0 +1,2 @@ + +#ffffff \ No newline at end of file diff --git a/public/favicons/df4b367461890fa5fd0d9339d3c3f9c6.ico.zip b/public/favicons/df4b367461890fa5fd0d9339d3c3f9c6.ico.zip new file mode 100644 index 0000000..8d2efae Binary files /dev/null and b/public/favicons/df4b367461890fa5fd0d9339d3c3f9c6.ico.zip differ diff --git a/public/favicons/favicon-16x16.png b/public/favicons/favicon-16x16.png new file mode 100644 index 0000000..9597c6f Binary files /dev/null and b/public/favicons/favicon-16x16.png differ diff --git a/public/favicons/favicon-32x32.png b/public/favicons/favicon-32x32.png new file mode 100644 index 0000000..737917e Binary files /dev/null and b/public/favicons/favicon-32x32.png differ diff --git a/public/favicons/favicon-96x96.png b/public/favicons/favicon-96x96.png new file mode 100644 index 0000000..f9b8fbe Binary files /dev/null and b/public/favicons/favicon-96x96.png differ diff --git a/public/favicons/favicon.ico b/public/favicons/favicon.ico new file mode 100644 index 0000000..bc22288 Binary files /dev/null and b/public/favicons/favicon.ico differ diff --git a/public/favicons/manifest.json b/public/favicons/manifest.json new file mode 100644 index 0000000..013d4a6 --- /dev/null +++ b/public/favicons/manifest.json @@ -0,0 +1,41 @@ +{ + "name": "App", + "icons": [ + { + "src": "\/android-icon-36x36.png", + "sizes": "36x36", + "type": "image\/png", + "density": "0.75" + }, + { + "src": "\/android-icon-48x48.png", + "sizes": "48x48", + "type": "image\/png", + "density": "1.0" + }, + { + "src": "\/android-icon-72x72.png", + "sizes": "72x72", + "type": "image\/png", + "density": "1.5" + }, + { + "src": "\/android-icon-96x96.png", + "sizes": "96x96", + "type": "image\/png", + "density": "2.0" + }, + { + "src": "\/android-icon-144x144.png", + "sizes": "144x144", + "type": "image\/png", + "density": "3.0" + }, + { + "src": "\/android-icon-192x192.png", + "sizes": "192x192", + "type": "image\/png", + "density": "4.0" + } + ] +} \ No newline at end of file diff --git a/public/favicons/ms-icon-144x144.png b/public/favicons/ms-icon-144x144.png new file mode 100644 index 0000000..7e98c1c Binary files /dev/null and b/public/favicons/ms-icon-144x144.png differ diff --git a/public/favicons/ms-icon-150x150.png b/public/favicons/ms-icon-150x150.png new file mode 100644 index 0000000..cdd392c Binary files /dev/null and b/public/favicons/ms-icon-150x150.png differ diff --git a/public/favicons/ms-icon-310x310.png b/public/favicons/ms-icon-310x310.png new file mode 100644 index 0000000..7b84fc1 Binary files /dev/null and b/public/favicons/ms-icon-310x310.png differ diff --git a/public/favicons/ms-icon-70x70.png b/public/favicons/ms-icon-70x70.png new file mode 100644 index 0000000..383bb76 Binary files /dev/null and b/public/favicons/ms-icon-70x70.png differ diff --git a/public/favicons/mstile-150x150.png b/public/favicons/mstile-150x150.png new file mode 100644 index 0000000..9a8fa88 Binary files /dev/null and b/public/favicons/mstile-150x150.png differ diff --git a/public/favicons/safari-pinned-tab.svg b/public/favicons/safari-pinned-tab.svg new file mode 100644 index 0000000..fd23e1d --- /dev/null +++ b/public/favicons/safari-pinned-tab.svg @@ -0,0 +1,475 @@ + + + + +Created by potrace 1.11, written by Peter Selinger 2001-2013 + + + + + diff --git a/public/index.php b/public/index.php new file mode 100644 index 0000000..796d290 --- /dev/null +++ b/public/index.php @@ -0,0 +1,73 @@ + + */ +define('LARAVEL_START', microtime(true)); + +/* +|-------------------------------------------------------------------------- +| Check If Application Is Under Maintenance +|-------------------------------------------------------------------------- +| +| If the application is maintenance / demo mode via the "down" command we +| will require this file so that any pre-rendered template can be shown +| instead of starting the framework, which could cause an exception. +| +*/ + +if (file_exists(__DIR__ . '/../storage/framework/maintenance.php')) { + require __DIR__ . '/../storage/framework/maintenance.php'; +} + +/* +|-------------------------------------------------------------------------- +| Register The Auto Loader +|-------------------------------------------------------------------------- +| +| Composer provides a convenient, automatically generated class loader for +| our application. We just need to utilize it! We'll simply require it +| into the script here so that we don't have to worry about manual +| loading any of our classes later on. It feels great to relax. +| +*/ + +require __DIR__ . '/../vendor/autoload.php'; + +/* +|-------------------------------------------------------------------------- +| Turn On The Lights +|-------------------------------------------------------------------------- +| +| We need to illuminate PHP development, so let us turn on the lights. +| This bootstraps the framework and gets it ready for use, then it +| will load up this application so that we can run it and send +| the responses back to the browser and delight our users. +| +*/ + +$app = require_once __DIR__ . '/../bootstrap/app.php'; + +/* +|-------------------------------------------------------------------------- +| Run The Application +|-------------------------------------------------------------------------- +| +| Once we have the application, we can handle the incoming request +| through the kernel, and send the associated response back to +| the client's browser allowing them to enjoy the creative +| and wonderful application we have prepared for them. +| +*/ + +$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class); + +$response = $kernel->handle( + $request = Illuminate\Http\Request::capture() +); + +$response->send(); + +$kernel->terminate($request, $response); diff --git a/public/js/autocomplete.js b/public/js/autocomplete.js new file mode 100644 index 0000000..15c4379 --- /dev/null +++ b/public/js/autocomplete.js @@ -0,0 +1,6 @@ +// Hacky fix for browsers ignoring autocomplete="off" +$(document).ready(function() { + $('.form-autocomplete-stop').on('click', function () { + $(this).removeAttr('readonly').blur().focus(); + }); +}); diff --git a/public/js/keyboard.polyfill.js b/public/js/keyboard.polyfill.js new file mode 100644 index 0000000..cc78b36 --- /dev/null +++ b/public/js/keyboard.polyfill.js @@ -0,0 +1,121 @@ +/* global define, KeyboardEvent, module */ + +(function () { + + var keyboardeventKeyPolyfill = { + polyfill: polyfill, + keys: { + 3: 'Cancel', + 6: 'Help', + 8: 'Backspace', + 9: 'Tab', + 12: 'Clear', + 13: 'Enter', + 16: 'Shift', + 17: 'Control', + 18: 'Alt', + 19: 'Pause', + 20: 'CapsLock', + 27: 'Escape', + 28: 'Convert', + 29: 'NonConvert', + 30: 'Accept', + 31: 'ModeChange', + 32: ' ', + 33: 'PageUp', + 34: 'PageDown', + 35: 'End', + 36: 'Home', + 37: 'ArrowLeft', + 38: 'ArrowUp', + 39: 'ArrowRight', + 40: 'ArrowDown', + 41: 'Select', + 42: 'Print', + 43: 'Execute', + 44: 'PrintScreen', + 45: 'Insert', + 46: 'Delete', + 48: ['0', ')'], + 49: ['1', '!'], + 50: ['2', '@'], + 51: ['3', '#'], + 52: ['4', '$'], + 53: ['5', '%'], + 54: ['6', '^'], + 55: ['7', '&'], + 56: ['8', '*'], + 57: ['9', '('], + 91: 'OS', + 93: 'ContextMenu', + 144: 'NumLock', + 145: 'ScrollLock', + 181: 'VolumeMute', + 182: 'VolumeDown', + 183: 'VolumeUp', + 186: [';', ':'], + 187: ['=', '+'], + 188: [',', '<'], + 189: ['-', '_'], + 190: ['.', '>'], + 191: ['/', '?'], + 192: ['`', '~'], + 219: ['[', '{'], + 220: ['\\', '|'], + 221: [']', '}'], + 222: ["'", '"'], + 224: 'Meta', + 225: 'AltGraph', + 246: 'Attn', + 247: 'CrSel', + 248: 'ExSel', + 249: 'EraseEof', + 250: 'Play', + 251: 'ZoomOut' + } + }; + + // Function keys (F1-24). + var i; + for (i = 1; i < 25; i++) { + keyboardeventKeyPolyfill.keys[111 + i] = 'F' + i; + } + + // Printable ASCII characters. + var letter = ''; + for (i = 65; i < 91; i++) { + letter = String.fromCharCode(i); + keyboardeventKeyPolyfill.keys[i] = [letter.toLowerCase(), letter.toUpperCase()]; + } + + function polyfill () { + if (!('KeyboardEvent' in window) || + 'key' in KeyboardEvent.prototype) { + return false; + } + + // Polyfill `key` on `KeyboardEvent`. + var proto = { + get: function (x) { + var key = keyboardeventKeyPolyfill.keys[this.which || this.keyCode]; + + if (Array.isArray(key)) { + key = key[+this.shiftKey]; + } + + return key; + } + }; + Object.defineProperty(KeyboardEvent.prototype, 'key', proto); + return proto; + } + + if (typeof define === 'function' && define.amd) { + define('keyboardevent-key-polyfill', keyboardeventKeyPolyfill); + } else if (typeof exports !== 'undefined' && typeof module !== 'undefined') { + module.exports = keyboardeventKeyPolyfill; + } else if (window) { + window.keyboardeventKeyPolyfill = keyboardeventKeyPolyfill; + } + +})(); diff --git a/public/js/laroute.js b/public/js/laroute.js new file mode 100644 index 0000000..ca120c0 --- /dev/null +++ b/public/js/laroute.js @@ -0,0 +1,195 @@ +(function () { + + var laroute = (function () { + + var routes = { + + absolute: false, + rootUrl: 'http://pterodactyl.local', + routes : [{"host":null,"methods":["GET","HEAD"],"uri":"_debugbar\/open","name":"debugbar.openhandler","action":"Barryvdh\Debugbar\Controllers\OpenHandlerController@handle"},{"host":null,"methods":["GET","HEAD"],"uri":"_debugbar\/clockwork\/{id}","name":"debugbar.clockwork","action":"Barryvdh\Debugbar\Controllers\OpenHandlerController@clockwork"},{"host":null,"methods":["GET","HEAD"],"uri":"_debugbar\/assets\/stylesheets","name":"debugbar.assets.css","action":"Barryvdh\Debugbar\Controllers\AssetController@css"},{"host":null,"methods":["GET","HEAD"],"uri":"_debugbar\/assets\/javascript","name":"debugbar.assets.js","action":"Barryvdh\Debugbar\Controllers\AssetController@js"},{"host":null,"methods":["DELETE"],"uri":"_debugbar\/cache\/{key}\/{tags?}","name":"debugbar.cache.delete","action":"Barryvdh\Debugbar\Controllers\CacheController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"\/","name":"index","action":"Pterodactyl\Http\Controllers\Base\IndexController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"status\/{server}","name":"index.status","action":"Pterodactyl\Http\Controllers\Base\IndexController@status"},{"host":null,"methods":["GET","HEAD"],"uri":"account","name":"account","action":"Pterodactyl\Http\Controllers\Base\AccountController@index"},{"host":null,"methods":["POST"],"uri":"account","name":null,"action":"Pterodactyl\Http\Controllers\Base\AccountController@update"},{"host":null,"methods":["GET","HEAD"],"uri":"account\/api","name":"account.api","action":"Pterodactyl\Http\Controllers\Base\ClientApiController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"account\/api\/new","name":"account.api.new","action":"Pterodactyl\Http\Controllers\Base\ClientApiController@create"},{"host":null,"methods":["POST"],"uri":"account\/api\/new","name":null,"action":"Pterodactyl\Http\Controllers\Base\ClientApiController@store"},{"host":null,"methods":["DELETE"],"uri":"account\/api\/revoke\/{identifier}","name":"account.api.revoke","action":"Pterodactyl\Http\Controllers\Base\ClientApiController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"account\/security","name":"account.security","action":"Pterodactyl\Http\Controllers\Base\SecurityController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"account\/security\/revoke\/{id}","name":"account.security.revoke","action":"Pterodactyl\Http\Controllers\Base\SecurityController@revoke"},{"host":null,"methods":["PUT"],"uri":"account\/security\/totp","name":"account.security.totp","action":"Pterodactyl\Http\Controllers\Base\SecurityController@generateTotp"},{"host":null,"methods":["POST"],"uri":"account\/security\/totp","name":"account.security.totp.set","action":"Pterodactyl\Http\Controllers\Base\SecurityController@setTotp"},{"host":null,"methods":["DELETE"],"uri":"account\/security\/totp","name":"account.security.totp.disable","action":"Pterodactyl\Http\Controllers\Base\SecurityController@disableTotp"},{"host":null,"methods":["GET","HEAD"],"uri":"admin","name":"admin.index","action":"Pterodactyl\Http\Controllers\Admin\BaseController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/statistics","name":"admin.statistics","action":"Pterodactyl\Http\Controllers\Admin\StatisticsController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/api","name":"admin.api.index","action":"Pterodactyl\Http\Controllers\Admin\ApiController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/api\/new","name":"admin.api.new","action":"Pterodactyl\Http\Controllers\Admin\ApiController@create"},{"host":null,"methods":["POST"],"uri":"admin\/api\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ApiController@store"},{"host":null,"methods":["DELETE"],"uri":"admin\/api\/revoke\/{identifier}","name":"admin.api.delete","action":"Pterodactyl\Http\Controllers\Admin\ApiController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/locations","name":"admin.locations","action":"Pterodactyl\Http\Controllers\Admin\LocationController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/locations\/view\/{location}","name":"admin.locations.view","action":"Pterodactyl\Http\Controllers\Admin\LocationController@view"},{"host":null,"methods":["POST"],"uri":"admin\/locations","name":null,"action":"Pterodactyl\Http\Controllers\Admin\LocationController@create"},{"host":null,"methods":["PATCH"],"uri":"admin\/locations\/view\/{location}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\LocationController@update"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/databases","name":"admin.databases","action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/databases\/view\/{host}","name":"admin.databases.view","action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@view"},{"host":null,"methods":["POST"],"uri":"admin\/databases","name":null,"action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@create"},{"host":null,"methods":["PATCH"],"uri":"admin\/databases\/view\/{host}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@update"},{"host":null,"methods":["DELETE"],"uri":"admin\/databases\/view\/{host}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/settings","name":"admin.settings","action":"Pterodactyl\Http\Controllers\Admin\Settings\IndexController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/settings\/mail","name":"admin.settings.mail","action":"Pterodactyl\Http\Controllers\Admin\Settings\MailController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/settings\/mail\/test","name":"admin.settings.mail.test","action":"Pterodactyl\Http\Controllers\Admin\Settings\MailController@test"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/settings\/advanced","name":"admin.settings.advanced","action":"Pterodactyl\Http\Controllers\Admin\Settings\AdvancedController@index"},{"host":null,"methods":["PATCH"],"uri":"admin\/settings","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Settings\IndexController@update"},{"host":null,"methods":["PATCH"],"uri":"admin\/settings\/mail","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Settings\MailController@update"},{"host":null,"methods":["PATCH"],"uri":"admin\/settings\/advanced","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Settings\AdvancedController@update"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users","name":"admin.users","action":"Pterodactyl\Http\Controllers\Admin\UserController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users\/accounts.json","name":"admin.users.json","action":"Pterodactyl\Http\Controllers\Admin\UserController@json"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users\/new","name":"admin.users.new","action":"Pterodactyl\Http\Controllers\Admin\UserController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users\/view\/{user}","name":"admin.users.view","action":"Pterodactyl\Http\Controllers\Admin\UserController@view"},{"host":null,"methods":["POST"],"uri":"admin\/users\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\UserController@store"},{"host":null,"methods":["PATCH"],"uri":"admin\/users\/view\/{user}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\UserController@update"},{"host":null,"methods":["DELETE"],"uri":"admin\/users\/view\/{user}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\UserController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers","name":"admin.servers","action":"Pterodactyl\Http\Controllers\Admin\ServersController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/new","name":"admin.servers.new","action":"Pterodactyl\Http\Controllers\Admin\ServersController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}","name":"admin.servers.view","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}\/details","name":"admin.servers.view.details","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewDetails"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}\/build","name":"admin.servers.view.build","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewBuild"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}\/startup","name":"admin.servers.view.startup","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewStartup"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}\/database","name":"admin.servers.view.database","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewDatabase"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}\/manage","name":"admin.servers.view.manage","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewManage"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}\/delete","name":"admin.servers.view.delete","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewDelete"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@store"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/build","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@updateBuild"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/startup","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@saveStartup"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/database","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@newDatabase"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/manage\/toggle","name":"admin.servers.view.manage.toggle","action":"Pterodactyl\Http\Controllers\Admin\ServersController@toggleInstall"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/manage\/rebuild","name":"admin.servers.view.manage.rebuild","action":"Pterodactyl\Http\Controllers\Admin\ServersController@rebuildContainer"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/manage\/suspension","name":"admin.servers.view.manage.suspension","action":"Pterodactyl\Http\Controllers\Admin\ServersController@manageSuspension"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/manage\/reinstall","name":"admin.servers.view.manage.reinstall","action":"Pterodactyl\Http\Controllers\Admin\ServersController@reinstallServer"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/delete","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@delete"},{"host":null,"methods":["PATCH"],"uri":"admin\/servers\/view\/{server}\/details","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@setDetails"},{"host":null,"methods":["PATCH"],"uri":"admin\/servers\/view\/{server}\/database","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@resetDatabasePassword"},{"host":null,"methods":["DELETE"],"uri":"admin\/servers\/view\/{server}\/database\/{database}\/delete","name":"admin.servers.view.database.delete","action":"Pterodactyl\Http\Controllers\Admin\ServersController@deleteDatabase"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes","name":"admin.nodes","action":"Pterodactyl\Http\Controllers\Admin\NodesController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/new","name":"admin.nodes.new","action":"Pterodactyl\Http\Controllers\Admin\NodesController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{node}","name":"admin.nodes.view","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{node}\/settings","name":"admin.nodes.view.settings","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewSettings"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{node}\/configuration","name":"admin.nodes.view.configuration","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewConfiguration"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{node}\/allocation","name":"admin.nodes.view.allocation","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewAllocation"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{node}\/servers","name":"admin.nodes.view.servers","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewServers"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{node}\/settings\/token","name":"admin.nodes.view.configuration.token","action":"Pterodactyl\Http\Controllers\Admin\NodesController@setToken"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\NodesController@store"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/view\/{node}\/allocation","name":null,"action":"Pterodactyl\Http\Controllers\Admin\NodesController@createAllocation"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/view\/{node}\/allocation\/remove","name":"admin.nodes.view.allocation.removeBlock","action":"Pterodactyl\Http\Controllers\Admin\NodesController@allocationRemoveBlock"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/view\/{node}\/allocation\/alias","name":"admin.nodes.view.allocation.setAlias","action":"Pterodactyl\Http\Controllers\Admin\NodesController@allocationSetAlias"},{"host":null,"methods":["PATCH"],"uri":"admin\/nodes\/view\/{node}\/settings","name":null,"action":"Pterodactyl\Http\Controllers\Admin\NodesController@updateSettings"},{"host":null,"methods":["DELETE"],"uri":"admin\/nodes\/view\/{node}\/delete","name":"admin.nodes.view.delete","action":"Pterodactyl\Http\Controllers\Admin\NodesController@delete"},{"host":null,"methods":["DELETE"],"uri":"admin\/nodes\/view\/{node}\/allocation\/remove\/{allocation}","name":"admin.nodes.view.allocation.removeSingle","action":"Pterodactyl\Http\Controllers\Admin\NodesController@allocationRemoveSingle"},{"host":null,"methods":["DELETE"],"uri":"admin\/nodes\/view\/{node}\/allocations","name":"admin.nodes.view.allocation.removeMultiple","action":"Pterodactyl\Http\Controllers\Admin\NodesController@allocationRemoveMultiple"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nests","name":"admin.nests","action":"Pterodactyl\Http\Controllers\Admin\Nests\NestController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nests\/new","name":"admin.nests.new","action":"Pterodactyl\Http\Controllers\Admin\Nests\NestController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nests\/view\/{nest}","name":"admin.nests.view","action":"Pterodactyl\Http\Controllers\Admin\Nests\NestController@view"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nests\/egg\/new","name":"admin.nests.egg.new","action":"Pterodactyl\Http\Controllers\Admin\Nests\EggController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nests\/egg\/{egg}","name":"admin.nests.egg.view","action":"Pterodactyl\Http\Controllers\Admin\Nests\EggController@view"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nests\/egg\/{egg}\/export","name":"admin.nests.egg.export","action":"Pterodactyl\Http\Controllers\Admin\Nests\EggShareController@export"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nests\/egg\/{egg}\/variables","name":"admin.nests.egg.variables","action":"Pterodactyl\Http\Controllers\Admin\Nests\EggVariableController@view"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nests\/egg\/{egg}\/scripts","name":"admin.nests.egg.scripts","action":"Pterodactyl\Http\Controllers\Admin\Nests\EggScriptController@index"},{"host":null,"methods":["POST"],"uri":"admin\/nests\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\NestController@store"},{"host":null,"methods":["POST"],"uri":"admin\/nests\/import","name":"admin.nests.egg.import","action":"Pterodactyl\Http\Controllers\Admin\Nests\EggShareController@import"},{"host":null,"methods":["POST"],"uri":"admin\/nests\/egg\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\EggController@store"},{"host":null,"methods":["POST"],"uri":"admin\/nests\/egg\/{egg}\/variables","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\EggVariableController@store"},{"host":null,"methods":["PUT"],"uri":"admin\/nests\/egg\/{egg}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\EggShareController@update"},{"host":null,"methods":["PATCH"],"uri":"admin\/nests\/view\/{nest}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\NestController@update"},{"host":null,"methods":["PATCH"],"uri":"admin\/nests\/egg\/{egg}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\EggController@update"},{"host":null,"methods":["PATCH"],"uri":"admin\/nests\/egg\/{egg}\/scripts","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\EggScriptController@update"},{"host":null,"methods":["PATCH"],"uri":"admin\/nests\/egg\/{egg}\/variables\/{variable}","name":"admin.nests.egg.variables.edit","action":"Pterodactyl\Http\Controllers\Admin\Nests\EggVariableController@update"},{"host":null,"methods":["DELETE"],"uri":"admin\/nests\/view\/{nest}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\NestController@destroy"},{"host":null,"methods":["DELETE"],"uri":"admin\/nests\/egg\/{egg}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\EggController@destroy"},{"host":null,"methods":["DELETE"],"uri":"admin\/nests\/egg\/{egg}\/variables\/{variable}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\EggVariableController@destroy"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/packs","name":"admin.packs","action":"Pterodactyl\Http\Controllers\Admin\PackController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/packs\/new","name":"admin.packs.new","action":"Pterodactyl\Http\Controllers\Admin\PackController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/packs\/new\/template","name":"admin.packs.new.template","action":"Pterodactyl\Http\Controllers\Admin\PackController@newTemplate"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/packs\/view\/{pack}","name":"admin.packs.view","action":"Pterodactyl\Http\Controllers\Admin\PackController@view"},{"host":null,"methods":["POST"],"uri":"admin\/packs\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\PackController@store"},{"host":null,"methods":["POST"],"uri":"admin\/packs\/view\/{pack}\/export\/{files?}","name":"admin.packs.view.export","action":"Pterodactyl\Http\Controllers\Admin\PackController@export"},{"host":null,"methods":["PATCH"],"uri":"admin\/packs\/view\/{pack}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\PackController@update"},{"host":null,"methods":["DELETE"],"uri":"admin\/packs\/view\/{pack}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\PackController@destroy"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/login","name":"auth.login","action":"Pterodactyl\Http\Controllers\Auth\LoginController@showLoginForm"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/login\/totp","name":"auth.totp","action":"Pterodactyl\Http\Controllers\Auth\LoginController@totp"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/password","name":"auth.password","action":"Pterodactyl\Http\Controllers\Auth\ForgotPasswordController@showLinkRequestForm"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/password\/reset\/{token}","name":"auth.reset","action":"Pterodactyl\Http\Controllers\Auth\ResetPasswordController@showResetForm"},{"host":null,"methods":["POST"],"uri":"auth\/login","name":null,"action":"Pterodactyl\Http\Controllers\Auth\LoginController@login"},{"host":null,"methods":["POST"],"uri":"auth\/login\/totp","name":null,"action":"Pterodactyl\Http\Controllers\Auth\LoginController@loginUsingTotp"},{"host":null,"methods":["POST"],"uri":"auth\/password","name":null,"action":"Pterodactyl\Http\Controllers\Auth\ForgotPasswordController@sendResetLinkEmail"},{"host":null,"methods":["POST"],"uri":"auth\/password\/reset","name":"auth.reset.post","action":"Pterodactyl\Http\Controllers\Auth\ResetPasswordController@reset"},{"host":null,"methods":["POST"],"uri":"auth\/password\/reset\/{token}","name":null,"action":"Pterodactyl\Http\Controllers\Auth\ForgotPasswordController@sendResetLinkEmail"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/logout","name":"auth.logout","action":"Pterodactyl\Http\Controllers\Auth\LoginController@logout"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}","name":"server.index","action":"Pterodactyl\Http\Controllers\Server\ConsoleController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/console","name":"server.console","action":"Pterodactyl\Http\Controllers\Server\ConsoleController@console"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/allocation","name":"server.settings.allocation","action":"Pterodactyl\Http\Controllers\Server\Settings\AllocationController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/name","name":"server.settings.name","action":"Pterodactyl\Http\Controllers\Server\Settings\NameController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/sftp","name":"server.settings.sftp","action":"Pterodactyl\Http\Controllers\Server\Settings\SftpController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/startup","name":"server.settings.startup","action":"Pterodactyl\Http\Controllers\Server\Settings\StartupController@index"},{"host":null,"methods":["PATCH"],"uri":"server\/{server}\/settings\/allocation","name":null,"action":"Pterodactyl\Http\Controllers\Server\Settings\AllocationController@update"},{"host":null,"methods":["PATCH"],"uri":"server\/{server}\/settings\/name","name":null,"action":"Pterodactyl\Http\Controllers\Server\Settings\NameController@update"},{"host":null,"methods":["PATCH"],"uri":"server\/{server}\/settings\/startup","name":null,"action":"Pterodactyl\Http\Controllers\Server\Settings\StartupController@update"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/databases","name":"server.databases.index","action":"Pterodactyl\Http\Controllers\Server\DatabaseController@index"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/databases\/new","name":"server.databases.new","action":"Pterodactyl\Http\Controllers\Server\DatabaseController@store"},{"host":null,"methods":["PATCH"],"uri":"server\/{server}\/databases\/password","name":"server.databases.password","action":"Pterodactyl\Http\Controllers\Server\DatabaseController@update"},{"host":null,"methods":["DELETE"],"uri":"server\/{server}\/databases\/delete\/{database}","name":"server.databases.delete","action":"Pterodactyl\Http\Controllers\Server\DatabaseController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files","name":"server.files.index","action":"Pterodactyl\Http\Controllers\Server\Files\FileActionsController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files\/add","name":"server.files.add","action":"Pterodactyl\Http\Controllers\Server\Files\FileActionsController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files\/edit\/{file}","name":"server.files.edit","action":"Pterodactyl\Http\Controllers\Server\Files\FileActionsController@view"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files\/download\/{file}","name":"server.files.edit","action":"Pterodactyl\Http\Controllers\Server\Files\DownloadController@index"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/files\/directory-list","name":"server.files.directory-list","action":"Pterodactyl\Http\Controllers\Server\Files\RemoteRequestController@directory"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/files\/save","name":"server.files.save","action":"Pterodactyl\Http\Controllers\Server\Files\RemoteRequestController@store"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/users","name":"server.subusers","action":"Pterodactyl\Http\Controllers\Server\SubuserController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/users\/new","name":"server.subusers.new","action":"Pterodactyl\Http\Controllers\Server\SubuserController@create"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/users\/new","name":null,"action":"Pterodactyl\Http\Controllers\Server\SubuserController@store"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/users\/view\/{subuser}","name":"server.subusers.view","action":"Pterodactyl\Http\Controllers\Server\SubuserController@view"},{"host":null,"methods":["PATCH"],"uri":"server\/{server}\/users\/view\/{subuser}","name":null,"action":"Pterodactyl\Http\Controllers\Server\SubuserController@update"},{"host":null,"methods":["DELETE"],"uri":"server\/{server}\/users\/view\/{subuser}","name":null,"action":"Pterodactyl\Http\Controllers\Server\SubuserController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/schedules","name":"server.schedules","action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/schedules\/new","name":"server.schedules.new","action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@create"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/schedules\/new","name":null,"action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@store"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/schedules\/view\/{schedule}","name":"server.schedules.view","action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@view"},{"host":null,"methods":["PATCH"],"uri":"server\/{server}\/schedules\/view\/{schedule}","name":null,"action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@update"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/schedules\/view\/{schedule}\/toggle","name":"server.schedules.toggle","action":"Pterodactyl\Http\Controllers\Server\Tasks\ActionController@toggle"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/schedules\/view\/{schedule}\/trigger","name":"server.schedules.trigger","action":"Pterodactyl\Http\Controllers\Server\Tasks\ActionController@trigger"},{"host":null,"methods":["DELETE"],"uri":"server\/{server}\/schedules\/view\/{schedule}","name":null,"action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/users","name":"api.application.users","action":"Pterodactyl\Http\Controllers\Api\Application\Users\UserController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/users\/{user}","name":"api.application.users.view","action":"Pterodactyl\Http\Controllers\Api\Application\Users\UserController@view"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/users\/external\/{external_id}","name":"api.application.users.external","action":"Pterodactyl\Http\Controllers\Api\Application\Users\ExternalUserController@index"},{"host":null,"methods":["POST"],"uri":"api\/application\/users","name":null,"action":"Pterodactyl\Http\Controllers\Api\Application\Users\UserController@store"},{"host":null,"methods":["PATCH"],"uri":"api\/application\/users\/{user}","name":null,"action":"Pterodactyl\Http\Controllers\Api\Application\Users\UserController@update"},{"host":null,"methods":["DELETE"],"uri":"api\/application\/users\/{user}","name":null,"action":"Pterodactyl\Http\Controllers\Api\Application\Users\UserController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/nodes","name":"api.application.nodes","action":"Pterodactyl\Http\Controllers\Api\Application\Nodes\NodeController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/nodes\/{node}","name":"api.application.nodes.view","action":"Pterodactyl\Http\Controllers\Api\Application\Nodes\NodeController@view"},{"host":null,"methods":["POST"],"uri":"api\/application\/nodes","name":null,"action":"Pterodactyl\Http\Controllers\Api\Application\Nodes\NodeController@store"},{"host":null,"methods":["PATCH"],"uri":"api\/application\/nodes\/{node}","name":null,"action":"Pterodactyl\Http\Controllers\Api\Application\Nodes\NodeController@update"},{"host":null,"methods":["DELETE"],"uri":"api\/application\/nodes\/{node}","name":null,"action":"Pterodactyl\Http\Controllers\Api\Application\Nodes\NodeController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/nodes\/{node}\/allocations","name":"api.application.allocations","action":"Pterodactyl\Http\Controllers\Api\Application\Nodes\AllocationController@index"},{"host":null,"methods":["POST"],"uri":"api\/application\/nodes\/{node}\/allocations","name":null,"action":"Pterodactyl\Http\Controllers\Api\Application\Nodes\AllocationController@store"},{"host":null,"methods":["DELETE"],"uri":"api\/application\/nodes\/{node}\/allocations\/{allocation}","name":"api.application.allocations.view","action":"Pterodactyl\Http\Controllers\Api\Application\Nodes\AllocationController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/locations","name":"api.applications.locations","action":"Pterodactyl\Http\Controllers\Api\Application\Locations\LocationController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/locations\/{location}","name":"api.application.locations.view","action":"Pterodactyl\Http\Controllers\Api\Application\Locations\LocationController@view"},{"host":null,"methods":["POST"],"uri":"api\/application\/locations","name":null,"action":"Pterodactyl\Http\Controllers\Api\Application\Locations\LocationController@store"},{"host":null,"methods":["PATCH"],"uri":"api\/application\/locations\/{location}","name":null,"action":"Pterodactyl\Http\Controllers\Api\Application\Locations\LocationController@update"},{"host":null,"methods":["DELETE"],"uri":"api\/application\/locations\/{location}","name":null,"action":"Pterodactyl\Http\Controllers\Api\Application\Locations\LocationController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/servers","name":"api.application.servers","action":"Pterodactyl\Http\Controllers\Api\Application\Servers\ServerController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/servers\/{server}","name":"api.application.servers.view","action":"Pterodactyl\Http\Controllers\Api\Application\Servers\ServerController@view"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/servers\/external\/{external_id}","name":"api.application.servers.external","action":"Pterodactyl\Http\Controllers\Api\Application\Servers\ExternalServerController@index"},{"host":null,"methods":["PATCH"],"uri":"api\/application\/servers\/{server}\/details","name":"api.application.servers.details","action":"Pterodactyl\Http\Controllers\Api\Application\Servers\ServerDetailsController@details"},{"host":null,"methods":["PATCH"],"uri":"api\/application\/servers\/{server}\/build","name":"api.application.servers.build","action":"Pterodactyl\Http\Controllers\Api\Application\Servers\ServerDetailsController@build"},{"host":null,"methods":["PATCH"],"uri":"api\/application\/servers\/{server}\/startup","name":"api.application.servers.startup","action":"Pterodactyl\Http\Controllers\Api\Application\Servers\StartupController@index"},{"host":null,"methods":["POST"],"uri":"api\/application\/servers","name":null,"action":"Pterodactyl\Http\Controllers\Api\Application\Servers\ServerController@store"},{"host":null,"methods":["POST"],"uri":"api\/application\/servers\/{server}\/suspend","name":"api.application.servers.suspend","action":"Pterodactyl\Http\Controllers\Api\Application\Servers\ServerManagementController@suspend"},{"host":null,"methods":["POST"],"uri":"api\/application\/servers\/{server}\/unsuspend","name":"api.application.servers.unsuspend","action":"Pterodactyl\Http\Controllers\Api\Application\Servers\ServerManagementController@unsuspend"},{"host":null,"methods":["POST"],"uri":"api\/application\/servers\/{server}\/reinstall","name":"api.application.servers.reinstall","action":"Pterodactyl\Http\Controllers\Api\Application\Servers\ServerManagementController@reinstall"},{"host":null,"methods":["POST"],"uri":"api\/application\/servers\/{server}\/rebuild","name":"api.application.servers.rebuild","action":"Pterodactyl\Http\Controllers\Api\Application\Servers\ServerManagementController@rebuild"},{"host":null,"methods":["DELETE"],"uri":"api\/application\/servers\/{server}","name":null,"action":"Pterodactyl\Http\Controllers\Api\Application\Servers\ServerController@delete"},{"host":null,"methods":["DELETE"],"uri":"api\/application\/servers\/{server}\/{force?}","name":null,"action":"Pterodactyl\Http\Controllers\Api\Application\Servers\ServerController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/servers\/{server}\/databases","name":"api.application.servers.databases","action":"Pterodactyl\Http\Controllers\Api\Application\Servers\DatabaseController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/servers\/{server}\/databases\/{database}","name":"api.application.servers.databases.view","action":"Pterodactyl\Http\Controllers\Api\Application\Servers\DatabaseController@view"},{"host":null,"methods":["POST"],"uri":"api\/application\/servers\/{server}\/databases","name":null,"action":"Pterodactyl\Http\Controllers\Api\Application\Servers\DatabaseController@store"},{"host":null,"methods":["POST"],"uri":"api\/application\/servers\/{server}\/databases\/{database}\/reset-password","name":null,"action":"Pterodactyl\Http\Controllers\Api\Application\Servers\DatabaseController@resetPassword"},{"host":null,"methods":["DELETE"],"uri":"api\/application\/servers\/{server}\/databases\/{database}","name":null,"action":"Pterodactyl\Http\Controllers\Api\Application\Servers\DatabaseController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/nests","name":"api.application.nests","action":"Pterodactyl\Http\Controllers\Api\Application\Nests\NestController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/nests\/{nest}","name":"api.application.nests.view","action":"Pterodactyl\Http\Controllers\Api\Application\Nests\NestController@view"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/nests\/{nest}\/eggs","name":"api.application.nests.eggs","action":"Pterodactyl\Http\Controllers\Api\Application\Nests\EggController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/nests\/{nest}\/eggs\/{egg}","name":"api.application.nests.eggs.view","action":"Pterodactyl\Http\Controllers\Api\Application\Nests\EggController@view"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/client","name":"api.client.index","action":"Pterodactyl\Http\Controllers\Api\Client\ClientController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/client\/servers\/{server}","name":"api.client.servers.view","action":"Pterodactyl\Http\Controllers\Api\Client\Servers\ServerController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/client\/servers\/{server}\/utilization","name":"api.client.servers.resources","action":"Pterodactyl\Http\Controllers\Api\Client\Servers\ResourceUtilizationController@index"},{"host":null,"methods":["POST"],"uri":"api\/client\/servers\/{server}\/command","name":"api.client.servers.command","action":"Pterodactyl\Http\Controllers\Api\Client\Servers\CommandController@index"},{"host":null,"methods":["POST"],"uri":"api\/client\/servers\/{server}\/power","name":"api.client.servers.power","action":"Pterodactyl\Http\Controllers\Api\Client\Servers\PowerController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/remote\/authenticate\/{token}","name":"api.remote.authenticate","action":"Pterodactyl\Http\Controllers\Api\Remote\ValidateKeyController@index"},{"host":null,"methods":["POST"],"uri":"api\/remote\/download-file","name":"api.remote.download_file","action":"Pterodactyl\Http\Controllers\Api\Remote\FileDownloadController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/remote\/eggs","name":"api.remote.eggs","action":"Pterodactyl\Http\Controllers\Api\Remote\EggRetrievalController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/remote\/eggs\/{uuid}","name":"api.remote.eggs.download","action":"Pterodactyl\Http\Controllers\Api\Remote\EggRetrievalController@download"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/remote\/scripts\/{uuid}","name":"api.remote.scripts","action":"Pterodactyl\Http\Controllers\Api\Remote\EggInstallController@index"},{"host":null,"methods":["POST"],"uri":"api\/remote\/sftp","name":"api.remote.sftp","action":"Pterodactyl\Http\Controllers\Api\Remote\SftpController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/packs\/pull\/{uuid}","name":"daemon.pack.pull","action":"Pterodactyl\Http\Controllers\Daemon\PackController@pull"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/packs\/pull\/{uuid}\/hash","name":"daemon.pack.hash","action":"Pterodactyl\Http\Controllers\Daemon\PackController@hash"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/configure\/{token}","name":"daemon.configuration","action":"Pterodactyl\Http\Controllers\Daemon\ActionController@configuration"},{"host":null,"methods":["POST"],"uri":"daemon\/install","name":"daemon.install","action":"Pterodactyl\Http\Controllers\Daemon\ActionController@markInstall"}], + prefix: '', + + route : function (name, parameters, route) { + route = route || this.getByName(name); + + if ( ! route ) { + return undefined; + } + + return this.toRoute(route, parameters); + }, + + url: function (url, parameters) { + parameters = parameters || []; + + var uri = url + '/' + parameters.join('/'); + + return this.getCorrectUrl(uri); + }, + + toRoute : function (route, parameters) { + var uri = this.replaceNamedParameters(route.uri, parameters); + var qs = this.getRouteQueryString(parameters); + + if (this.absolute && this.isOtherHost(route)){ + return "//" + route.host + "/" + uri + qs; + } + + return this.getCorrectUrl(uri + qs); + }, + + isOtherHost: function (route){ + return route.host && route.host != window.location.hostname; + }, + + replaceNamedParameters : function (uri, parameters) { + uri = uri.replace(/\{(.*?)\??\}/g, function(match, key) { + if (parameters.hasOwnProperty(key)) { + var value = parameters[key]; + delete parameters[key]; + return value; + } else { + return match; + } + }); + + // Strip out any optional parameters that were not given + uri = uri.replace(/\/\{.*?\?\}/g, ''); + + return uri; + }, + + getRouteQueryString : function (parameters) { + var qs = []; + for (var key in parameters) { + if (parameters.hasOwnProperty(key)) { + qs.push(key + '=' + parameters[key]); + } + } + + if (qs.length < 1) { + return ''; + } + + return '?' + qs.join('&'); + }, + + getByName : function (name) { + for (var key in this.routes) { + if (this.routes.hasOwnProperty(key) && this.routes[key].name === name) { + return this.routes[key]; + } + } + }, + + getByAction : function(action) { + for (var key in this.routes) { + if (this.routes.hasOwnProperty(key) && this.routes[key].action === action) { + return this.routes[key]; + } + } + }, + + getCorrectUrl: function (uri) { + var url = this.prefix + '/' + uri.replace(/^\/?/, ''); + + if ( ! this.absolute) { + return url; + } + + return this.rootUrl.replace('/\/?$/', '') + url; + } + }; + + var getLinkAttributes = function(attributes) { + if ( ! attributes) { + return ''; + } + + var attrs = []; + for (var key in attributes) { + if (attributes.hasOwnProperty(key)) { + attrs.push(key + '="' + attributes[key] + '"'); + } + } + + return attrs.join(' '); + }; + + var getHtmlLink = function (url, title, attributes) { + title = title || url; + attributes = getLinkAttributes(attributes); + + return '' + title + ''; + }; + + return { + // Generate a url for a given controller action. + // Router.action('HomeController@getIndex', [params = {}]) + action : function (name, parameters) { + parameters = parameters || {}; + + return routes.route(name, parameters, routes.getByAction(name)); + }, + + // Generate a url for a given named route. + // Router.route('routeName', [params = {}]) + route : function (route, parameters) { + parameters = parameters || {}; + + return routes.route(route, parameters); + }, + + // Generate a fully qualified URL to the given path. + // Router.route('url', [params = {}]) + url : function (route, parameters) { + parameters = parameters || {}; + + return routes.url(route, parameters); + }, + + // Generate a html link to the given url. + // Router.link_to('foo/bar', [title = url], [attributes = {}]) + link_to : function (url, title, attributes) { + url = this.url(url); + + return getHtmlLink(url, title, attributes); + }, + + // Generate a html link to the given route. + // Router.link_to_route('route.name', [title=url], [parameters = {}], [attributes = {}]) + link_to_route : function (route, title, parameters, attributes) { + var url = this.route(route, parameters); + + return getHtmlLink(url, title, attributes); + }, + + // Generate a html link to the given controller action. + // Router.link_to_action('HomeController@getIndex', [title=url], [parameters = {}], [attributes = {}]) + link_to_action : function(action, title, parameters, attributes) { + var url = this.action(action, parameters); + + return getHtmlLink(url, title, attributes); + } + + }; + + }).call(this); + + /** + * Expose the class either via AMD, CommonJS or the global object + */ + if (typeof define === 'function' && define.amd) { + define(function () { + return laroute; + }); + } + else if (typeof module === 'object' && module.exports){ + module.exports = laroute; + } + else { + window.Router = laroute; + } + +}).call(this); + diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..1f53798 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / diff --git a/public/themes/pterodactyl/css/checkbox.css b/public/themes/pterodactyl/css/checkbox.css new file mode 100644 index 0000000..a75e63a --- /dev/null +++ b/public/themes/pterodactyl/css/checkbox.css @@ -0,0 +1,232 @@ +/** + * Bootsnip - "Bootstrap Checkboxes/Radios" + * Bootstrap 3.2.0 Snippet by i-heart-php + * + * Copyright (c) 2013 Bootsnipp.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + .checkbox { + padding-left: 20px; +} +.checkbox label { + display: inline-block; + position: relative; + padding-left: 5px; +} +.checkbox label::before { + content: ""; + display: inline-block; + position: absolute; + width: 17px; + height: 17px; + left: 0; + top: 2.5px; + margin-left: -20px; + border: 1px solid #cccccc; + border-radius: 3px; + background-color: #fff; + -webkit-transition: border 0.15s ease-in-out, color 0.15s ease-in-out; + -o-transition: border 0.15s ease-in-out, color 0.15s ease-in-out; + transition: border 0.15s ease-in-out, color 0.15s ease-in-out; +} +.checkbox label::after { + display: inline-block; + position: absolute; + width: 16px; + height: 16px; + left: 0; + top: 2.5px; + margin-left: -20px; + padding-left: 3px; + padding-top: 1px; + font-size: 11px; + color: #555555; +} +.checkbox input[type="checkbox"] { + opacity: 0; +} +.checkbox input[type="checkbox"]:focus + label::before { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.checkbox input[type="checkbox"]:checked + label::after { + font-family: 'FontAwesome'; + content: "\f00c"; +} +.checkbox input[type="checkbox"]:disabled + label { + opacity: 0.65; +} +.checkbox input[type="checkbox"]:disabled + label::before { + background-color: #eeeeee; + cursor: not-allowed; +} +.checkbox.checkbox-circle label::before { + border-radius: 50%; +} +.checkbox.checkbox-inline { + margin-top: 0; +} +.checkbox-primary input[type="checkbox"]:checked + label::before { + background-color: #428bca; + border-color: #428bca; +} +.checkbox-primary input[type="checkbox"]:checked + label::after { + color: #fff; +} +.checkbox-danger input[type="checkbox"]:checked + label::before { + background-color: #d9534f; + border-color: #d9534f; +} +.checkbox-danger input[type="checkbox"]:checked + label::after { + color: #fff; +} +.checkbox-info input[type="checkbox"]:checked + label::before { + background-color: #5bc0de; + border-color: #5bc0de; +} +.checkbox-info input[type="checkbox"]:checked + label::after { + color: #fff; +} +.checkbox-warning input[type="checkbox"]:checked + label::before { + background-color: #f0ad4e; + border-color: #f0ad4e; +} +.checkbox-warning input[type="checkbox"]:checked + label::after { + color: #fff; +} +.checkbox-success input[type="checkbox"]:checked + label::before { + background-color: #5cb85c; + border-color: #5cb85c; +} +.checkbox-success input[type="checkbox"]:checked + label::after { + color: #fff; +} +.radio { + padding-left: 20px; +} +.radio label { + display: inline-block; + position: relative; + padding-left: 5px; +} +.radio label::before { + content: ""; + display: inline-block; + position: absolute; + width: 17px; + height: 17px; + left: 0; + margin-left: -20px; + border: 1px solid #cccccc; + border-radius: 50%; + background-color: #fff; + -webkit-transition: border 0.15s ease-in-out; + -o-transition: border 0.15s ease-in-out; + transition: border 0.15s ease-in-out; +} +.radio label::after { + display: inline-block; + position: absolute; + content: " "; + width: 11px; + height: 11px; + left: 3px; + top: 3px; + margin-left: -20px; + border-radius: 50%; + background-color: #555555; + -webkit-transform: scale(0, 0); + -ms-transform: scale(0, 0); + -o-transform: scale(0, 0); + transform: scale(0, 0); + -webkit-transition: -webkit-transform 0.1s cubic-bezier(0.8, -0.33, 0.2, 1.33); + -moz-transition: -moz-transform 0.1s cubic-bezier(0.8, -0.33, 0.2, 1.33); + -o-transition: -o-transform 0.1s cubic-bezier(0.8, -0.33, 0.2, 1.33); + transition: transform 0.1s cubic-bezier(0.8, -0.33, 0.2, 1.33); +} +.radio input[type="radio"] { + opacity: 0; +} +.radio input[type="radio"]:focus + label::before { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.radio input[type="radio"]:checked + label::after { + -webkit-transform: scale(1, 1); + -ms-transform: scale(1, 1); + -o-transform: scale(1, 1); + transform: scale(1, 1); +} +.radio input[type="radio"]:disabled + label { + opacity: 0.65; +} +.radio input[type="radio"]:disabled + label::before { + cursor: not-allowed; +} +.radio.radio-inline { + margin-top: 0; +} +.radio-primary input[type="radio"] + label::after { + background-color: #428bca; +} +.radio-primary input[type="radio"]:checked + label::before { + border-color: #428bca; +} +.radio-primary input[type="radio"]:checked + label::after { + background-color: #428bca; +} +.radio-danger input[type="radio"] + label::after { + background-color: #d9534f; +} +.radio-danger input[type="radio"]:checked + label::before { + border-color: #d9534f; +} +.radio-danger input[type="radio"]:checked + label::after { + background-color: #d9534f; +} +.radio-info input[type="radio"] + label::after { + background-color: #5bc0de; +} +.radio-info input[type="radio"]:checked + label::before { + border-color: #5bc0de; +} +.radio-info input[type="radio"]:checked + label::after { + background-color: #5bc0de; +} +.radio-warning input[type="radio"] + label::after { + background-color: #f0ad4e; +} +.radio-warning input[type="radio"]:checked + label::before { + border-color: #f0ad4e; +} +.radio-warning input[type="radio"]:checked + label::after { + background-color: #f0ad4e; +} +.radio-success input[type="radio"] + label::after { + background-color: #5cb85c; +} +.radio-success input[type="radio"]:checked + label::before { + border-color: #5cb85c; +} +.radio-success input[type="radio"]:checked + label::after { + background-color: #5cb85c; +} diff --git a/public/themes/pterodactyl/css/pterodactyl.css b/public/themes/pterodactyl/css/pterodactyl.css new file mode 100644 index 0000000..8e3580f --- /dev/null +++ b/public/themes/pterodactyl/css/pterodactyl.css @@ -0,0 +1,835 @@ +/** + * Pterodactyl - Panel + * Copyright (c) 2015 - 2017 Dane Everitt + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + @import 'checkbox.css'; + +.login-page { + background: #10529f; +} + +#login-position-elements { + margin: 25% auto; +} + +.login-logo { + color: #fff; + font-weight: 400; +} + +.login-copyright { + color: rgba(255, 255, 255, 0.3); +} + +.login-copyright > a { + color: rgba(255, 255, 255, 0.6); +} + +.particles-js-canvas-el { + position: absolute; + width: 100%; + height: 100%; + top: 0; + z-index: -1; +} + +.pterodactyl-login-box { + background: rgba(0, 0, 0, 0.25); + border-radius: 3px; + padding: 20px; +} + +.pterodactyl-login-input > input { + background: rgba(0, 0, 0, 0.4); + border: 1px solid #000; + border-radius: 2px; + color: #fff; +} + +.pterodactyl-login-input > .form-control-feedback { + color: #fff; +} + +.pterodactyl-login-button--main { + background: rgba(0, 0, 0, 0.4); + border: 1px solid #000; + border-radius: 2px; + color: #fff; +} + +.pterodactyl-login-button--main:hover { + background: rgba(0, 0, 0, 0.7); + border: 1px solid #000; + border-radius: 2px; + color: #fff; +} + +.pterodactyl-login-button--left { + background: rgba(255, 255, 255, 0.4); + border: 1px solid rgba(255, 255, 255, 0.6); + border-radius: 2px; + color: #fff; +} + +.pterodactyl-login-button--left:hover { + background: rgba(255, 255, 255, 0.6); + border: 1px solid rgba(255, 255, 255, 0.8); + border-radius: 2px; + color: #fff; +} + +.weight-100 { + font-weight: 100; +} + +.weight-300 { + font-weight: 300; +} + +.btn-clear { + background: transparent; +} + +.user-panel > .info { + position: relative; + left: 0; +} + +code { + background-color: #eef1f6; + color: #596981; + border-radius: 2px; + padding-left: 4px; + padding-right: 4px; + line-height: 1.4; + font-size: 85%; + border: 1px solid rgba(0, 0, 0, .1); + display: inline-block; +} + +p { + line-height: 1.6 !important; +} + +p.small { + margin-top: 3px !important; +} + +.control-sidebar-dark .control-sidebar-menu > li > a.active { + background: #1e282c; +} + +.callout-nomargin { + margin: 0; +} + +.table { + font-size: 14px !important; +} + +.table .min-size { + width:1px; + white-space: nowrap; +} + +@media (max-width:767px) { + .box-header > .box-tools { + position: relative !important; + padding: 0px 10px 10px; + } +} + +.middle, .align-middle { + vertical-align: middle !important; +} + +#fileOptionMenu.dropdown-menu > li > a { + padding:3px 6px; +} + +.hasFileHover { + border: 2px dashed #0087F7; + border-top: 0 !important; + border-radius: 5px; + margin: 0; + opacity: 0.5; +} + +.hasFileHover * { + pointer-events: none !important; +} + +td.has-progress { + padding: 0px !important; + border-top: 0px !important; +} + +.progress.progress-table-bottom { + margin: 0 !important; + height:5px !important; + padding:0; + border:0; +} + +.muted { + filter: alpha(opacity=20); + opacity: 0.2; +} + +.muted-hover:hover { + filter: alpha(opacity=100); + opacity: 1; +} + +.use-pointer { + cursor: pointer !important; +} + +.input-loader { + display: none; + position:relative; + top: -25px; + float: right; + right: 5px; + color: #cccccc; + height: 0; +} + +.box-header > .form-group { + margin-bottom: 0; +} + +.box-header > .form-group > div > p.small { + margin: 0; +} + +.no-margin { + margin: 0 !important; +} + +li.select2-results__option--highlighted[aria-selected="false"] > .user-block > .username > a { + color: #fff; +} + +li.select2-results__option--highlighted[aria-selected="false"] > .user-block > .description { + color: #eee; +} + +.select2-selection.select2-selection--multiple { + min-height: 36px !important; +} + +.select2-search--inline .select2-search__field:focus { + outline: none; + border: 0 !important; +} + +.img-bordered-xs { + border: 1px solid #d2d6de; + padding: 1px; +} + +span[aria-labelledby="select2-pUserId-container"] { + padding-left: 2px !important; +} + +.box { + box-shadow: 0 0 0 1px rgba(89, 105, 128, .1), 0 1px 3px 0 rgba(89, 105, 128, .1), 0 1px 2px 0 rgba(0, 0, 0, .05) !important; +} + +.alert-danger { + color: #ffffff !important; + background: #d64242 !important; + border: 1px solid #841d1d; +} + +.alert-info { + color: #ffffff !important; + background: #408fec !important; + border: 1px solid #1055a5; +} + +.alert-success { + color: #ffffff !important; + background: #51b060 !important; + border: 1px solid #2b5f33; +} + +.alert-warning { + color: #ffffff !important; + background: #fa9636 !important; + border: 1px solid #b45b05; +} + +.callout-slim a { + color: #555 !important; +} + +.bg-purple { + background-color: #79589f !important; +} + +.label-default { + background-color: #eef1f6 !important; +} + +.callout.callout-info.callout-slim { + border: 1px solid #1055a5 !important; + border-left: 5px solid #1055a5 !important; + border-right: 5px solid #1055a5 !important; + color: #777 !important; + background: transparent !important; +} + +.callout.callout-danger.callout-slim { + border: 1px solid #c23321 !important; + border-left: 5px solid #c23321 !important; + border-right: 5px solid #c23321 !important; + color: #777 !important; + background: transparent !important; +} + +.callout.callout-warning.callout-slim { + border: 1px solid #c87f0a !important; + border-left: 5px solid #c87f0a !important; + border-right: 5px solid #c87f0a !important; + color: #777 !important; + background: transparent !important; +} + +.callout.callout-success.callout-slim { + border: 1px solid #00733e !important; + border-left: 5px solid #00733e !important; + border-right: 5px solid #00733e !important; + color: #777 !important; + background: transparent !important; +} + +.callout.callout-default.callout-slim { + border: 1px solid #eee !important; + border-left: 5px solid #eee !important; + border-right: 5px solid #eee !important; + color: #777 !important; + background: transparent !important; +} + +.callout code { + background-color: #515f6cbb; + color: #c3c3c3; + border: 1px solid rgba(0, 0, 0, .25); +} + +.tab-pane .box-footer { + margin: 0 -10px -10px; +} + +.select2-container{ width: 100% !important; } + +.nav-tabs-custom > .nav-tabs > li:hover { + border-top-color:#3c8dbc; +} + +.nav-tabs-custom > .nav-tabs > li.active.tab-danger, .nav-tabs-custom > .nav-tabs > li.tab-danger:hover { + border-top-color: #c23321; +} + +.nav-tabs-custom > .nav-tabs > li.active.tab-success, .nav-tabs-custom > .nav-tabs > li.tab-success:hover { + border-top-color: #00733e; +} + +.nav-tabs-custom > .nav-tabs > li.active.tab-info, .nav-tabs-custom > .nav-tabs > li.tab-info:hover { + border-top-color: #0097bc; +} + +.nav-tabs-custom > .nav-tabs > li.active.tab-warning, .nav-tabs-custom > .nav-tabs > li.tab-warning:hover { + border-top-color: #c87f0a; +} + +.nav-tabs-custom.nav-tabs-floating > .nav-tabs { + border-bottom: 0px !important; +} + +.nav-tabs-custom.nav-tabs-floating > .nav-tabs > li { + margin-bottom: 0px !important; +} + +.nav-tabs-custom.nav-tabs-floating > .nav-tabs > li:first-child.active, +.nav-tabs-custom.nav-tabs-floating > .nav-tabs > li:first-child:hover { + border-radius: 3px 0 0 0; +} + +.nav-tabs-custom.nav-tabs-floating > .nav-tabs > li:first-child.active > a { + border-radius: 0 0 0 3px; +} + +.position-relative { + position: relative; +} + +.no-pad-bottom { + padding-bottom: 0 !important; +} + +.no-margin-bottom { + margin-bottom: 0 !important; +} + +.btn-icon > i.fa { + line-height: 1.5; +} + +.btn.active, .btn.active.focus { + background-color: #408fec; +} + +.strong { + font-weight: bold !important; +} + +.server-description > td { + padding-top: 0 !important; + border-top: 0 !important; +} + +tr:hover + tr.server-description { + background-color: #f5f5f5 !important; +} + +.login-corner-info { + position: absolute; + bottom: 5px; + right: 10px; + color: white; +} + +input.form-autocomplete-stop[readonly] { + background: inherit; + cursor: text; +} + +/* fix Google Recaptcha badge */ +.grecaptcha-badge { + bottom: 54px !important; + background: white; + box-shadow: none !important; +} + +.dropdown-massactions { + min-width: 80px; +} + +.select-all-files { + position: relative; + bottom: 1px; + margin-right: 7px !important; +} + +.select-file { + position: relative; + bottom: 1px; + margin-right: 2px !important; +} + +.select-folder { + position: relative; + bottom: 1px; + margin-right: 5px !important; +} + +label.control-label > span { + font-size: 80%; + font-weight: 400; + font-style: italic; + color: #dd4b39; +} + +label.control-label > span.field-required:before { + content: "required"; + color: #dd4b39; +} + +label.control-label > span.field-optional:before { + content: "optional"; + color: #bbbbbb; +} + +.pagination > li > a, .pagination > li > span { + padding: 3px 10px !important; +} + +.logo-mini > img { + height: 42px; + width: auto; +} + +.search01 { + width: 30%; +} + +.number-info-box-content { + padding: 15px 10px 0; +} + + +/* ******* + + > Version v1.0 + +******* */ + +body { + color: #cad1d8; +} + +.fixed .main-header { + box-shadow: 0 4px 8px 0 rgba(0,0,0,.12), 0 2px 4px 0 rgba(0,0,0,.08); +} + +.skin-blue .wrapper, .skin-blue .main-sidebar, .skin-blue .left-side { + background-color: #181f27; + box-shadow: 0 4px 8px 0 rgba(0,0,0,.12), 0 2px 4px 0 rgba(0,0,0,.08); +} + +.skin-blue .main-header .logo { + background-color: #1f2933; + color: #9aa5b1; +} + +.skin-blue .main-header .navbar .sidebar-toggle { + color: #9aa5b1; +} + +.skin-blue .main-header .navbar .nav>li>a { + color: #9aa5b1; +} + +.skin-blue .sidebar-menu>li.header { + color: #797979; + background: #0e111582; +} + +.skin-blue .main-header .navbar { + background-color: #1f2933; +} + +.skin-blue .main-header .navbar .sidebar-toggle:hover { + background-color: #1c252e; +} + +.skin-blue .main-header .logo:hover { + background-color: #1c252e; +} + +.main-footer { + background: #1f2933; + color: #9aa5b1; + border-top: 1px solid #1f2933; +} + +.skin-blue .sidebar-menu>li.active>a { + border-left-color: #099aa5; +} + +.text-gray { + color: #9aa5b1 !important; +} + +.text-green { + color: #00a65a !important; +} + +.text-muted { + color: #9aa5b1 !important; +} + +.text-danger { + color: #ff1c00; +} + +.content-wrapper { + background-color: #33404d; +} + +.btn-success { + background-color: #189a1c; + border-color: #0f8513; +} + +.btn.btn-green:hover { + background-color: #0f8513; + border-color: #0e7717; +} + +.btn-primary { + background-color: #0967d3; + border-color: #0550b3; +} + +.btn.btn-primary:hover { + background-color: #0550b3; + border-color: #0345a0; +} + +.box { + box-shadow: 0 4px 8px 0 rgba(0,0,0,.12), 0 2px 4px 0 rgba(0,0,0,.08) !important; + background: #3f4d5a; + border-top: 3px solid #1f2933; +} + +.box-header { + color: #cad1d8; + background: #1f2933; +} + +.box-header.with-border { + border-bottom: 1px solid #1f2933; +} + +.box.box-default { + border-top-color: #1f2933; +} + +.box-footer { + border-top: 1px solid #4d5b69; + background-color: #3f4d5a; +} +.content-header>.breadcrumb>li>a { + color: #cad1d8; +} + +.breadcrumb>.active { + color: #cad1d8; +} + +.h1 .small, .h1 small, .h2 .small, .h2 small, .h3 .small, .h3 small, .h4 .small, .h4 small, .h5 .small, .h5 small, .h6 .small, .h6 small, h1 .small, h1 small, h2 .small, h2 small, h3 .small, h3 small, h4 .small, h4 small, h5 .small, h5 small, h6 .small, h6 small { + color: #cad1d8; +} + +.table>thead>tr>th, .table>tbody>tr>th, .table>tfoot>tr>th, .table>thead>tr>td, .table>tbody>tr>td, .table>tfoot>tr>td { + border-top: 1px solid #4d5b69; +} + +.table>thead>tr>th { + border-bottom: 2px solid #4d5b69; +} + +.table-hover>tbody>tr:hover { + background-color: #33404d; +} + +a { + color: #007eff; +} + +.nav-tabs-custom { + background: #1f2933; +} + +.nav-tabs-custom>.nav-tabs>li.active { + border-top-color: #099aa5; +} + +.nav-tabs-custom>.nav-tabs>li.active>a { + border-top-color: transparent; + border-left-color: #15161c; + border-right-color: #15161c; + background: #13181e; +} + +.nav-tabs-custom>.nav-tabs>li>a { + color: #9aa5b1; +} + +.nav-tabs-custom>.nav-tabs>li.active>a, .nav-tabs-custom>.nav-tabs>li.active:hover>a { + color: #9aa5b1; +} + +input.form-control { + padding: .75rem; + background-color: #515f6cbb; + border-width: 1px; + border-color: #606d7b; + border-radius: .25rem; + color: #cad1d8; + box-shadow: none; + -webkit-transition: border .15s linear,box-shaodw .15s ease-in; + transition: border .15s linear,box-shaodw .15s ease-in; +} + +textarea.form-control { + padding: .75rem; + background-color: #515f6cbb; + border-width: 1px; + border-color: #606d7b; + border-radius: .25rem; + color: #cad1d8; + box-shadow: none; + -webkit-transition: border .15s linear,box-shaodw .15s ease-in; + transition: border .15s linear,box-shaodw .15s ease-in; +} + +.input-group .input-group-addon { + border-color: #606d7b; + background-color: #515f6cbb; + color: #cad1d8; +} + +.select2-container--default .select2-selection--single, .select2-selection .select2-selection--single { + border: 1px solid #606d7b; +} + +.select2-container--default .select2-selection--single { + background-color: #515f6cbb; +} + +.select2-container--default .select2-selection--single .select2-selection__rendered { + color: #cad1d8; +} + +.select2-container--default .select2-selection--multiple { + background-color: #515f6cbb; +} + +.select2-container--default .select2-selection--multiple { + border: 1px solid #606d7b; + border-radius: 0; +} + +code { + background-color: #515f6cbb; + color: #c3c3c3; + border: 1px solid rgba(0, 0, 0, .25); +} + +.btn-default { + background-color: #33404d; + color: #cad1d8; + border-color: #606d7b; +} + +.select2-results__option { + background-color: #b5bcc1; + color: #444; +} + +.select2-container--default .select2-results__option--highlighted[aria-selected] { + background-color: #3c8dbc; +} + +.modal-body { + background: #3f4d5a; +} + +.modal-header { + background: #3f4d5a; + border-bottom-color: #4d5b69; +} + +.modal-footer { + background: #3f4d5a; + border-top-color: #4d5b69; +} + +@media (max-width: 991px) { + .content-header>.breadcrumb { + background: #1f2933 !important; + } +} + +.nav-tabs-custom>.nav-tabs>li.active>a, .nav-tabs-custom>.nav-tabs>li.active:hover>a { + background-color: #101216; +} + +.select2-container--default .select2-results__option[aria-selected=true], .select2-container--default .select2-results__option[aria-selected=true]:hover { + color: #fff; +} + +.select2-dropdown { + background-color: #515f6cbb; + border: 1px solid #606d7b; +} +.select2-container--default.select2-container--focus .select2-selection--multiple, .select2-container--default .select2-search--dropdown .select2-search__field { + border-color: #d2d6de !important; + background-color: #515f6cbb; +} + +.select2-container--default .select2-results__option--highlighted[aria-selected] { + background-color: #099aa5; +} + +a { + color: #288afb; +} + +a:hover { + color: #206ec7; +} + +.form-control { + border-color: #606d7b; + background-color: #515f6cbb; + color: #cad1d8; +} + +.form-control[disabled], .form-control[readonly], +fieldset[disabled] .form-control { + background-color: #1f2933; + color: #cad1d8; + cursor: not-allowed; +} + +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #515f6cbb; + border: 1px solid #606d7b; +} + +.well-lg { + padding: 24px; +} + +.well-sm { + padding: 9px; +} + +.small-box h3, .small-box p { + color: #c3c3c3; +} + +.small-box-footer { + color: #288afb; +} + +.small-box .icon { + color: #cad1d8; +} + +.bg-gray { + background-color: #33404d !important; +} + +pre { + color: #cad1d8; + background-color: #515f6cbb; + border-color: #1f2933; +} diff --git a/public/themes/pterodactyl/css/terminal.css b/public/themes/pterodactyl/css/terminal.css new file mode 100644 index 0000000..d2dc4d0 --- /dev/null +++ b/public/themes/pterodactyl/css/terminal.css @@ -0,0 +1,105 @@ +/*Design for Terminal*/ +@import url('https://fonts.googleapis.com/css?family=Source+Code+Pro'); + +#terminal-body { + background: rgb(26, 26, 26); + margin: 0; + width: 100%; + height: 100%; + overflow: hidden; +} + +#terminal { + font-family: 'Source Code Pro', monospace; + color: rgb(223, 223, 223); + background: rgb(26, 26, 26); + font-size: 12px; + line-height: 14px; + padding: 10px 10px 0; + box-sizing: border-box; + height: 500px; + max-height: 500px; + overflow-y: auto; + overflow-x: hidden; + border-radius: 5px 5px 0 0; +} + +#terminal > .cmd { + padding: 1px 0; + word-wrap: break-word; + white-space: pre-wrap; +} + +#terminal_input { + width: 100%; + background: rgb(26, 26, 26); + border-radius: 0 0 5px 5px; + padding: 0 0 0 10px !important; +} + +.terminal_input--input, .terminal_input--prompt { + font-family: 'Source Code Pro', monospace; + margin-bottom: 0; + border: 0 !important; + background: transparent !important; + color: rgb(223, 223, 223); + font-size: 12px; + padding: 1px 0 4px !important; +} +.terminal_input--input { + margin-left: 6px; + line-height: 1; + outline: none !important; +} + +.terminal-notify { + position: absolute; + right: 30px; + bottom: 30px; + padding: 3.5px 7px; + border-radius: 3px; + background: #fff; + color: #000; + opacity: .5; + font-size: 16px; + cursor: pointer; + z-index: 10; +} + +.terminal-notify:hover { + opacity: .9; +} + +.ansi-black-fg { color: rgb(0, 0, 0); } +.ansi-red-fg { color: rgb(166, 0, 44); } +.ansi-green-fg { color: rgb(55, 106, 27); } +.ansi-yellow-fg { color: rgb(241, 133, 24); } +.ansi-blue-fg { color: rgb(17, 56, 163); } +.ansi-magenta-fg { color: rgb(67, 0, 117); } +.ansi-cyan-fg { color: rgb(18, 95, 105); } +.ansi-white-fg { color: rgb(255, 255, 255); } +.ansi-bright-black-fg { color: rgb(51, 51, 51); } +.ansi-bright-red-fg { color: rgb(223, 45, 39); } +.ansi-bright-green-fg { color: rgb(105, 175, 45); } +.ansi-bright-yellow-fg { color: rgb(254, 232, 57); } +.ansi-bright-blue-fg { color: rgb(68, 145, 240); } +.ansi-bright-magenta-fg { color: rgb(151, 50, 174); } +.ansi-bright-cyan-fg{ color: rgb(37, 173, 98); } +.ansi-bright-white-fg { color: rgb(208, 208, 208); } + +.ansi-black-bg { background: rgb(0, 0, 0); } +.ansi-red-bg { background: rgb(166, 0, 44); } +.ansi-green-bg { background: rgb(55, 106, 27); } +.ansi-yellow-bg { background: rgb(241, 133, 24); } +.ansi-blue-bg { background: rgb(17, 56, 163); } +.ansi-magenta-bg { background: rgb(67, 0, 117); } +.ansi-cyan-bg { background: rgb(18, 95, 105); } +.ansi-white-bg { background: rgb(255, 255, 255); } +.ansi-bright-black-bg { background: rgb(51, 51, 51); } +.ansi-bright-red-bg { background: rgb(223, 45, 39); } +.ansi-bright-green-bg { background: rgb(105, 175, 45); } +.ansi-bright-yellow-bg { background: rgb(254, 232, 57); } +.ansi-bright-blue-bg { background: rgb(68, 145, 240); } +.ansi-bright-magenta-bg { background: rgb(151, 50, 174); } +.ansi-bright-cyan-bg { background: rgb(37, 173, 98); } +.ansi-bright-white-bg { background: rgb(208, 208, 208); } diff --git a/public/themes/pterodactyl/js/admin/functions.js b/public/themes/pterodactyl/js/admin/functions.js new file mode 100644 index 0000000..26115cd --- /dev/null +++ b/public/themes/pterodactyl/js/admin/functions.js @@ -0,0 +1,23 @@ +// Copyright (c) 2015 - 2017 Dane Everitt +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +$.urlParam=function(name){var results=new RegExp("[\\?&]"+name+"=([^&#]*)").exec(decodeURIComponent(window.location.href));if(results==null){return null}else{return results[1]||0}};function getPageName(url){var index=url.lastIndexOf("/")+1;var filenameWithExtension=url.substr(index);var filename=filenameWithExtension.split(".")[0];return filename} +// Remeber Active Tab and Navigate to it on Reload +for(var queryParameters={},queryString=location.search.substring(1),re=/([^&=]+)=([^&]*)/g,m;m=re.exec(queryString);)queryParameters[decodeURIComponent(m[1])]=decodeURIComponent(m[2]);$("a[data-toggle='tab']").click(function(){queryParameters.tab=$(this).attr("href").substring(1),window.history.pushState(null,null,location.pathname+"?"+$.param(queryParameters))}); +if($.urlParam('tab') != null){$('.nav.nav-tabs a[href="#' + $.urlParam('tab') + '"]').tab('show');} diff --git a/public/themes/pterodactyl/js/admin/new-server.js b/public/themes/pterodactyl/js/admin/new-server.js new file mode 100644 index 0000000..1437c04 --- /dev/null +++ b/public/themes/pterodactyl/js/admin/new-server.js @@ -0,0 +1,222 @@ +// Copyright (c) 2015 - 2017 Dane Everitt +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +$(document).ready(function() { + $('#pNestId').select2({ + placeholder: 'Select a Nest', + }).change(); + + $('#pEggId').select2({ + placeholder: 'Select a Nest Egg', + }); + + $('#pPackId').select2({ + placeholder: 'Select a Service Pack', + }); + + $('#pNodeId').select2({ + placeholder: 'Select a Node', + }).change(); + + $('#pAllocation').select2({ + placeholder: 'Select a Default Allocation', + }); + + $('#pAllocationAdditional').select2({ + placeholder: 'Select Additional Allocations', + }); +}); + +let lastActiveBox = null; +$(document).on('click', function (event) { + if (lastActiveBox !== null) { + lastActiveBox.removeClass('box-primary'); + } + + lastActiveBox = $(event.target).closest('.box'); + lastActiveBox.addClass('box-primary'); +}); + +$('#pNodeId').on('change', function () { + currentNode = $(this).val(); + $.each(Pterodactyl.nodeData, function (i, v) { + if (v.id == currentNode) { + $('#pAllocation').html('').select2({ + data: v.allocations, + placeholder: 'Select a Default Allocation', + }); + + updateAdditionalAllocations(); + } + }); +}); + +$('#pNestId').on('change', function (event) { + $('#pEggId').html('').select2({ + data: $.map(_.get(Pterodactyl.nests, $(this).val() + '.eggs', []), function (item) { + return { + id: item.id, + text: item.name, + }; + }), + }).change(); +}); + +$('#pEggId').on('change', function (event) { + let parentChain = _.get(Pterodactyl.nests, $('#pNestId').val(), null); + let objectChain = _.get(parentChain, 'eggs.' + $(this).val(), null); + + const images = _.get(objectChain, 'docker_images', {}) + $('#pDefaultContainer').html(''); + const keys = Object.keys(images); + for (let i = 0; i < keys.length; i++) { + let opt = document.createElement('option'); + opt.value = images[keys[i]]; + opt.innerText = keys[i] + " (" + images[keys[i]] + ")"; + $('#pDefaultContainer').append(opt); + } + + if (!_.get(objectChain, 'startup', false)) { + $('#pStartup').val(_.get(parentChain, 'startup', 'ERROR: Startup Not Defined!')); + } else { + $('#pStartup').val(_.get(objectChain, 'startup')); + } + + $('#pPackId').html('').select2({ + data: [{ id: 0, text: 'No Service Pack' }].concat( + $.map(_.get(objectChain, 'packs', []), function (item, i) { + return { + id: item.id, + text: item.name + ' (' + item.version + ')', + }; + }) + ), + }); + + function escapeHtml(str) { + var div = document.createElement('div'); + div.appendChild(document.createTextNode(str)); + return div.innerHTML; + } + + const variableIds = {}; + $('#appendVariablesTo').html(''); + $.each(_.get(objectChain, 'variables', []), function (i, item) { + variableIds[item.env_variable] = 'var_ref_' + item.id; + + let isRequired = (item.required === 1) ? 'Required ' : ''; + let dataAppend = ' \ +
\ + \ + \ +

' + escapeHtml(item.description) + '
\ + Access in Startup: {{' + escapeHtml(item.env_variable) + '}}
\ + Validation Rules: ' + escapeHtml(item.rules) + '

\ +
\ + '; + $('#appendVariablesTo').append(dataAppend); + }); + + // If you receive a warning on this line, it should be fine to ignore. this function is + // defined in "resources/views/admin/servers/new.blade.php" near the bottom of the file. + serviceVariablesUpdated($('#pEggId').val(), variableIds); +}); + +$('#pAllocation').on('change', function () { + updateAdditionalAllocations(); +}); + +function updateAdditionalAllocations() { + let currentAllocation = $('#pAllocation').val(); + let currentNode = $('#pNodeId').val(); + + $.each(Pterodactyl.nodeData, function (i, v) { + if (v.id == currentNode) { + let allocations = []; + + for (let i = 0; i < v.allocations.length; i++) { + const allocation = v.allocations[i]; + + if (allocation.id != currentAllocation) { + allocations.push(allocation); + } + } + + $('#pAllocationAdditional').html('').select2({ + data: allocations, + placeholder: 'Select Additional Allocations', + }); + } + }); +} + +function initUserIdSelect(data) { + function escapeHtml(str) { + var div = document.createElement('div'); + div.appendChild(document.createTextNode(str)); + return div.innerHTML; + } + + $('#pUserId').select2({ + ajax: { + url: '/admin/users/accounts.json', + dataType: 'json', + delay: 250, + + data: function (params) { + return { + filter: { email: params.term }, + page: params.page, + }; + }, + + processResults: function (data, params) { + return { results: data }; + }, + + cache: true, + }, + + data: data, + escapeMarkup: function (markup) { return markup; }, + minimumInputLength: 2, + templateResult: function (data) { + if (data.loading) return escapeHtml(data.text); + + return '
\ + User Image \ + \ + ' + escapeHtml(data.name_first) + ' ' + escapeHtml(data.name_last) +' \ + \ + ' + escapeHtml(data.email) + ' - ' + escapeHtml(data.username) + ' \ +
'; + }, + templateSelection: function (data) { + return '
\ + \ + User Image \ + \ + \ + ' + escapeHtml(data.name_first) + ' ' + escapeHtml(data.name_last) + ' (' + escapeHtml(data.email) + ') \ + \ +
'; + } + + }); +} diff --git a/public/themes/pterodactyl/js/admin/server/transfer.js b/public/themes/pterodactyl/js/admin/server/transfer.js new file mode 100644 index 0000000..5c2664a --- /dev/null +++ b/public/themes/pterodactyl/js/admin/server/transfer.js @@ -0,0 +1,56 @@ +$(document).ready(function () { + $('#pNodeId').select2({ + placeholder: 'Select a Node', + }).change(); + + $('#pAllocation').select2({ + placeholder: 'Select a Default Allocation', + }); + + $('#pAllocationAdditional').select2({ + placeholder: 'Select Additional Allocations', + }); +}); + +$('#pNodeId').on('change', function () { + let currentNode = $(this).val(); + + $.each(Pterodactyl.nodeData, function (i, v) { + if (v.id == currentNode) { + $('#pAllocation').html('').select2({ + data: v.allocations, + placeholder: 'Select a Default Allocation', + }); + + updateAdditionalAllocations(); + } + }); +}); + +$('#pAllocation').on('change', function () { + updateAdditionalAllocations(); +}); + +function updateAdditionalAllocations() { + let currentAllocation = $('#pAllocation').val(); + let currentNode = $('#pNodeId').val(); + + $.each(Pterodactyl.nodeData, function (i, v) { + if (v.id == currentNode) { + let allocations = []; + + for (let i = 0; i < v.allocations.length; i++) { + const allocation = v.allocations[i]; + + if (allocation.id != currentAllocation) { + allocations.push(allocation); + } + } + + $('#pAllocationAdditional').html('').select2({ + data: allocations, + placeholder: 'Select Additional Allocations', + }); + } + }); +} diff --git a/public/themes/pterodactyl/js/plugins/minecraft/eula.js b/public/themes/pterodactyl/js/plugins/minecraft/eula.js new file mode 100644 index 0000000..27e78c9 --- /dev/null +++ b/public/themes/pterodactyl/js/plugins/minecraft/eula.js @@ -0,0 +1,65 @@ +// Copyright (c) 2015 - 2017 Dane Everitt +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +$(document).ready(function () { + Socket.on('console', function (data) { + if (typeof data === 'undefined' || typeof data.line === 'undefined') { + return; + } + + if (~data.line.indexOf('You need to agree to the EULA in order to run the server')) { + swal({ + title: 'EULA Acceptance', + text: 'By pressing \'I Accept\' below you are indicating your agreement to the Mojang EULA.', + type: 'info', + html: true, + showCancelButton: true, + showConfirmButton: true, + cancelButtonText: 'I do not Accept', + confirmButtonText: 'I Accept', + closeOnConfirm: false, + showLoaderOnConfirm: true + }, function () { + $.ajax({ + type: 'POST', + url: Pterodactyl.meta.saveFile, + headers: { 'X-CSRF-Token': Pterodactyl.meta.csrfToken, }, + data: { + file: 'eula.txt', + contents: 'eula=true' + } + }).done(function (data) { + $('[data-attr="power"][data-action="start"]').trigger('click'); + swal({ + type: 'success', + title: '', + text: 'The EULA for this server has been accepted, restarting server now.', + }); + }).fail(function (jqXHR) { + console.error(jqXHR); + swal({ + title: 'Whoops!', + text: 'An error occurred while attempting to set the EULA as accepted: ' + jqXHR.responseJSON.error, + type: 'error' + }) + }); + }); + } + }); +}); diff --git a/public/themes/pterodactyl/vendor/ace/ace.js b/public/themes/pterodactyl/vendor/ace/ace.js new file mode 100644 index 0000000..b4b7a55 --- /dev/null +++ b/public/themes/pterodactyl/vendor/ace/ace.js @@ -0,0 +1,14 @@ +(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="",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})}),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((i.match(/(?:MSIE |Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]):parseFloat((i.match(/(?:Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]),t.isOldIE=t.isIE&&t.isIE<9,t.isGecko=t.isMozilla=(window.Controllers||window.controllers)&&window.navigator.product==="Gecko",t.isOldGecko=t.isGecko&&parseInt((i.match(/rv:(\d+)/)||[])[1],10)<4,t.isOpera=window.opera&&Object.prototype.toString.call(window.opera)=="[object Opera]",t.isWebKit=parseFloat(i.split("WebKit/")[1])||undefined,t.isChrome=parseFloat(i.split(" Chrome/")[1])||undefined,t.isAIR=i.indexOf("AdobeAIR")>=0,t.isIPad=i.indexOf("iPad")>=0,t.isTouchPad=i.indexOf("TouchPad")>=0,t.isChromeOS=i.indexOf(" CrOS ")>=0}),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.addTouchMoveListener=function(e,n){if("ontouchmove"in e){var r,i;t.addListener(e,"touchstart",function(e){var t=e.changedTouches[0];r=t.clientX,i=t.clientY}),t.addListener(e,"touchmove",function(e){var t=1,s=e.changedTouches[0];e.wheelX=-(s.clientX-r)/t,e.wheelY=-(s.clientY-i)/t,r=s.clientX,i=s.clientY,n(e)})}},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;t.addListener(n,"message",function i(s){s.data==r&&(t.stopPropagation(s),t.removeListener(n,"message",i),e())}),n.postMessage(r,"*")}}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)}}),define("ace/lib/lang",["require","exports","module"],function(e,t,n){"use strict";t.last=function(e){return e[e.length-1]},t.stringReverse=function(e){return e.split("").reverse().join("")},t.stringRepeat=function(e,t){var n="";while(t>0){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;n=53&&O()},I=o.delayedCall(j,50);r.addListener(n,"compositionstart",B),i.isGecko?r.addListener(n,"text",function(){I.schedule()}):(r.addListener(n,"keyup",function(){I.schedule()}),r.addListener(n,"keydown",function(){I.schedule()})),r.addListener(n,"compositionend",F),this.getElement=function(){return n},this.setReadOnly=function(e){n.readOnly=e},this.onContextMenu=function(e){L=!0,b(t.selection.isEmpty()),t._emit("nativecontextmenu",{target:t,domEvent:e}),this.moveToMouse(e,!0)},this.moveToMouse=function(e,o){if(!o&&i.isOldIE)return;p||(p=n.style.cssText),n.style.cssText=(o?"z-index:100000;":"")+"height:"+n.style.height+";"+(i.isIE?"opacity:0.1;":"");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){n.style.left=e.clientX-l-2+"px",n.style.top=Math.min(e.clientY-f-2,c)+"px"};h(e);if(e.type!="mousedown")return;t.renderer.$keepTextAreaAtCursor&&(t.renderer.$keepTextAreaAtCursor=null),clearTimeout(q),i.isWin&&!i.isOldIE&&r.capture(t.container,h,R)},this.onContextMenuClose=R;var q,U=function(e){t.textInput.onContextMenu(e),R()};r.addListener(n,"mouseup",U),r.addListener(n,"mousedown",function(e){e.preventDefault(),R()}),r.addListener(t.renderer.scroller,"contextmenu",U),r.addListener(n,"contextmenu",U)};t.TextInput=f}),define("ace/mouse/default_handlers",["require","exports","module","ace/lib/dom","ace/lib/event","ace/lib/useragent"],function(e,t,n){"use strict";function u(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)),t.setDefaultHandler("touchmove",this.onTouchMove.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 a(e,t,n,r){return Math.sqrt(Math.pow(n-e,2)+Math.pow(r-t,2))}function f(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/dom"),i=e("../lib/event"),s=e("../lib/useragent"),o=0;(function(){this.onMouseDown=function(e){var t=e.inSelection(),n=e.getDocumentPosition();this.mousedownEvent=e;var r=this.editor,i=e.getButton();if(i!==0){var s=r.getSelectionRange(),o=s.isEmpty();r.$blockScrolling++,(o||i==1)&&r.selection.moveToPosition(n),r.$blockScrolling--,i==2&&r.textInput.onContextMenu(e.domEvent);return}this.mousedownEvent.time=Date.now();if(t&&!r.isFocused()){r.focus();if(this.$focusTimout&&!this.$clickSelection&&!r.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;n.$blockScrolling++,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"),n.$blockScrolling--},this.select=function(){var e,t=this.editor,n=t.renderer.screenToTextCoordinates(this.x,this.y);t.$blockScrolling++;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=f(this.$clickSelection,n);n=i.cursor,e=i.anchor}t.selection.setSelectionAnchor(e.row,e.column)}t.selection.selectToPosition(n),t.$blockScrolling--,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);n.$blockScrolling++;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=f(this.$clickSelection,r);r=u.cursor,t=u.anchor}n.selection.setSelectionAnchor(t.row,t.column)}n.selection.selectToPosition(r),n.$blockScrolling--,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=a(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y),t=Date.now();(e>o||t-this.mousedownEvent.time>this.$focusTimout)&&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=e.domEvent.timeStamp,n=t-(this.$lastScrollTime||0),r=this.editor,i=r.renderer.isScrollableBy(e.wheelX*e.speed,e.wheelY*e.speed);if(i||n<200)return this.$lastScrollTime=t,r.renderer.scrollBy(e.wheelX*e.speed,e.wheelY*e.speed),e.stop()},this.onTouchMove=function(e){var t=e.domEvent.timeStamp,n=t-(this.$lastScrollTime||0),r=this.editor,i=r.renderer.isScrollableBy(e.wheelX*e.speed,e.wheelY*e.speed);if(i||n<200)return this.$lastScrollTime=t,r.renderer.scrollBy(e.wheelX*e.speed,e.wheelY*e.speed),e.stop()}}).call(u.prototype),t.DefaultHandlers=u}),define("ace/tooltip",["require","exports","module","ace/lib/oop","ace/lib/dom"],function(e,t,n){"use strict";function s(e){this.isOpen=!1,this.$element=null,this.$parentNode=e}var r=e("./lib/oop"),i=e("./lib/dom");(function(){this.$init=function(){return this.$element=i.createElement("div"),this.$element.className="ace_tooltip",this.$element.style.display="none",this.$parentNode.appendChild(this.$element),this.$element},this.getElement=function(){return this.$element||this.$init()},this.setText=function(e){i.setInnerText(this.getElement(),e)},this.setHtml=function(e){this.getElement().innerHTML=e},this.setPosition=function(e,t){this.getElement().style.left=e+"px",this.getElement().style.top=t+"px"},this.setClassName=function(e){i.addCssClass(this.getElement(),e)},this.show=function(e,t,n){e!=null&&this.setText(e),t!=null&&n!=null&&this.setPosition(t,n),this.isOpen||(this.getElement().style.display="block",this.isOpen=!0)},this.hide=function(){this.isOpen&&(this.getElement().style.display="none",this.isOpen=!1)},this.getHeight=function(){return this.getElement().offsetHeight},this.getWidth=function(){return this.getElement().offsetWidth}}).call(s.prototype),t.Tooltip=s}),define("ace/mouse/default_gutter_handler",["require","exports","module","ace/lib/dom","ace/lib/oop","ace/lib/event","ace/tooltip"],function(e,t,n){"use strict";function u(e){function l(){var r=u.getDocumentPosition().row,s=n.$annotations[r];if(!s)return c();var o=t.session.getLength();if(r==o){var a=t.renderer.pixelToScreenCoordinates(0,u.y).row,l=u.$pos;if(a>t.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}),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)}),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.$blockScrolling+=1,t.moveCursorToPosition(e),t.$blockScrolling-=1,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.$blockScrolling+=1,t.selection.fromOrientedRange(m),t.$blockScrolling-=1,t.isFocused()&&!w&&t.renderer.$cursorLayer.setBlinking(!t.getReadOnly()),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.renderer.$cursorLayer.setBlinking(!0)}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.renderer.$cursorLayer.setBlinking(!this.editor.getReadOnly()),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}),define("ace/lib/net",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";var r=e("./dom");t.get=function(e,t){var n=new XMLHttpRequest;n.open("GET",e,!0),n.onreadystatechange=function(){n.readyState===4&&t(n.responseText)},n.send(null)},t.loadScript=function(e,t){var n=r.getDocumentHead(),i=document.createElement("script");i.src=e,n.appendChild(i),i.onload=i.onreadystatechange=function(e,n){if(n||!i.readyState||i.readyState=="loaded"||i.readyState=="complete")i=i.onload=i.onreadystatechange=null,n||t()}},t.qualifyURL=function(e){var t=document.createElement("a");return t.href=e,t.href}}),define("ace/lib/event_emitter",["require","exports","module"],function(e,t,n){"use strict";var r={},i=function(){this.propagationStopped=!0},s=function(){this.defaultPrevented=!0};r._emit=r._dispatchEvent=function(e,t){this._eventRegistry||(this._eventRegistry={}),this._defaultHandlers||(this._defaultHandlers={});var n=this._eventRegistry[e]||[],r=this._defaultHandlers[e];if(!n.length&&!r)return;if(typeof t!="object"||!t)t={};t.type||(t.type=e),t.stopPropagation||(t.stopPropagation=i),t.preventDefault||(t.preventDefault=s),n=n.slice();for(var o=0;o1&&(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)},t.init=f}),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/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("../config"),l=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")),r.addTouchMoveListener(e.container,this.onTouchMove.bind(this,"touchmove"));var f=e.renderer.$gutter;r.addListener(f,"mousedown",this.onMouseEvent.bind(this,"guttermousedown")),r.addListener(f,"click",this.onMouseEvent.bind(this,"gutterclick")),r.addListener(f,"dblclick",this.onMouseEvent.bind(this,"gutterdblclick")),r.addListener(f,"mousemove",this.onMouseEvent.bind(this,"guttermousemove")),r.addListener(u,"mousedown",n),r.addListener(f,"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.onTouchMove=function(e,t){var n=new u(t,this.editor);n.speed=1,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.renderer;n.$keepTextAreaAtCursor&&(n.$keepTextAreaAtCursor=null);var s=this,o=function(e){if(!e)return;if(i.isWebKit&&!e.which&&s.releaseMouse)return s.releaseMouse();s.x=e.clientX,s.y=e.clientY,t&&t(e),s.mouseEvent=new u(e,s.editor),s.$mouseMoved=!0},a=function(e){clearInterval(l),f(),s[s.state+"End"]&&s[s.state+"End"](e),s.state="",n.$keepTextAreaAtCursor==null&&(n.$keepTextAreaAtCursor=!0,n.$moveTextAreaToCursor()),s.isMousePressed=!1,s.$onCaptureMouseMove=s.releaseMouse=null,e&&s.onMouseEvent("mouseup",e)},f=function(){s[s.state]&&s[s.state](),s.$mouseMoved=!1};if(i.isOldIE&&e.domEvent.type=="dblclick")return setTimeout(function(){a(e)});s.$onCaptureMouseMove=o,s.releaseMouse=r.capture(this.editor.container,o,a);var l=setInterval(f,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(l.prototype),f.defineOptions(l.prototype,"mouseHandler",{scrollSpeed:{initialValue:2},dragDelay:{initialValue:i.isMac?150:0},dragEnabled:{initialValue:!0},focusTimout:{initialValue:0},tooltipFollowsMouse:{initialValue:!0}}),t.MouseHandler=l}),define("ace/mouse/fold_handler",["require","exports","module"],function(e,t,n){"use strict";function r(e){e.on("click",function(t){var n=t.getDocumentPosition(),r=e.session,i=r.getFoldAt(n.row,n.column,1);i&&(t.getAccelKey()?r.removeFold(i):r.expandFold(i),t.stop())}),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()}})}t.FoldHandler=r}),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}),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.rowt.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(){var e=this.doc.getLength()-1;this.setSelectionAnchor(0,0),this.moveCursorTo(e,this.doc.getLine(e).length)},this.setRange=this.setSelectionRange=function(e,t){t?(this.setSelectionAnchor(e.end.row,e.end.column),this.selectTo(e.start.row,e.start.column)):(this.setSelectionAnchor(e.start.row,e.start.column),this.selectTo(e.end.row,e.end.column)),this.getRange().isEmpty()&&(this.$isEmpty=!0),this.$desiredColumn=null},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.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.session.isTabStop(e)&&this.doc.getLine(e.row).slice(e.column-n,e.column).split(" ").length-1==n?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),i;this.session.nonTokenRe.lastIndex=0,this.session.tokenRe.lastIndex=0;var s=this.session.getFoldAt(e,t,1);if(s){this.moveCursorTo(s.end.row,s.end.column);return}if(i=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}if(o=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,n=0,r,i=/\s/,s=this.session.tokenRe;s.lastIndex=0;if(t=this.session.tokenRe.exec(e))n=this.session.tokenRe.lastIndex;else{while((r=e[n])&&i.test(r))n++;if(n<1){s.lastIndex=0;while((r=e[n])&&!s.test(r)){s.lastIndex=0,n++;if(i.test(r)){if(n>2){n--;break}while((r=e[n])&&i.test(r))n++;if(n>2)break}}}}return s.lastIndex=0,n},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);t===0&&(this.$desiredColumn?n.column=this.$desiredColumn:this.$desiredColumn=n.column);var r=this.session.screenToDocumentPosition(n.row+e,n.column);e!==0&&t===0&&r.row===this.lead.row&&r.column===this.lead.column&&this.session.lineWidgets&&this.session.lineWidgets[r.row]&&(r.row>0||e>0)&&r.row++,this.moveCursorTo(r.row,r.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,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){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}),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}),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()}}}).call(r.prototype),t.TokenIterator=r}),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=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:""}},h=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)]}},p=function(){this.add("braces","insertion",function(e,t,n,r,i){var s=n.getCursorPosition(),u=r.doc.getLine(s.row);if(i=="{"){c(n);var a=n.getSelectionRange(),l=r.doc.getTextRange(a);if(l!==""&&l!=="{"&&n.getWrapBehavioursEnabled())return h(a,l,"{","}");if(p.isSaneInsertion(n,r))return/[\]\}\)]/.test(u[s.column])||n.inMultiSelectMode?(p.recordAutoInsert(n,r,"}"),{text:"{}",selection:[1,1]}):(p.recordMaybeInsert(n,r,"{"),{text:"{",selection:[1,1]})}else if(i=="}"){c(n);var d=u.substring(s.column,s.column+1);if(d=="}"){var v=r.$findOpeningBracket("}",{column:s.column+1,row:s.row});if(v!==null&&p.isAutoInsertedClosing(s,u,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}else{if(i=="\n"||i=="\r\n"){c(n);var m="";p.isMaybeInsertedClosing(s,u)&&(m=o.stringRepeat("}",f.maybeInsertedBrackets),p.clearMaybeInsertedClosing());var d=u.substring(s.column,s.column+1);if(d==="}"){var g=r.findMatchingBracket({row:s.row,column:s.column+1},"}");if(!g)return null;var y=this.$getIndent(r.getLine(g.row))}else{if(!m){p.clearMaybeInsertedClosing();return}var y=this.$getIndent(u)}var b=y+r.getTabString();return{text:"\n"+b+"\n"+y+m,selection:[1,b.length,1,b.length]}}p.clearMaybeInsertedClosing()}}),this.add("braces","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="{"){c(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=="("){c(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return h(s,o,"(",")");if(p.isSaneInsertion(n,r))return p.recordAutoInsert(n,r,")"),{text:"()",selection:[1,1]}}else if(i==")"){c(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&&p.isAutoInsertedClosing(u,a,i))return p.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=="("){c(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=="["){c(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return h(s,o,"[","]");if(p.isSaneInsertion(n,r))return p.recordAutoInsert(n,r,"]"),{text:"[]",selection:[1,1]}}else if(i=="]"){c(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&&p.isAutoInsertedClosing(u,a,i))return p.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=="["){c(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){if(i=='"'||i=="'"){if(this.lineCommentStart&&this.lineCommentStart.indexOf(i)!=-1)return;c(n);var s=i,o=n.getSelectionRange(),u=r.doc.getTextRange(o);if(u!==""&&u!=="'"&&u!='"'&&n.getWrapBehavioursEnabled())return h(o,u,s,s);if(!u){var a=n.getCursorPosition(),f=r.doc.getLine(a.row),l=f.substring(a.column-1,a.column),p=f.substring(a.column,a.column+1),d=r.getTokenAt(a.row,a.column),v=r.getTokenAt(a.row,a.column+1);if(l=="\\"&&d&&/escape/.test(d.type))return null;var m=d&&/string|escape/.test(d.type),g=!v||/string|escape/.test(v.type),y;if(p==s)y=m!==g,y&&/string\.end/.test(v.type)&&(y=!1);else{if(m&&!g)return null;if(m&&g)return null;var b=r.$mode.tokenRe;b.lastIndex=0;var w=b.test(l);b.lastIndex=0;var E=b.test(l);if(w||E)return null;if(p&&!/[\s;,.})\]\\]/.test(p))return null;y=!0}return{text:y?s+s:"",selection:[1,1]}}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&(s=='"'||s=="'")){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==s)return i.end.column++,i}})};p.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)},p.$matchTokenType=function(e,t){return t.indexOf(e.type||e)>-1},p.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++},p.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++},p.isAutoInsertedClosing=function(e,t,n){return f.autoInsertedBrackets>0&&e.row===f.autoInsertedRow&&n===f.autoInsertedLineEnd[0]&&t.substr(e.column)===f.autoInsertedLineEnd},p.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},p.popAutoInsertedClosing=function(){f.autoInsertedLineEnd=f.autoInsertedLineEnd.substr(1),f.autoInsertedBrackets--},p.clearMaybeInsertedClosing=function(){f&&(f.maybeInsertedBrackets=0,f.maybeInsertedRow=-1)},r.inherits(p,i),t.CstyleBehaviour=p}),define("ace/unicode",["require","exports","module"],function(e,t,n){"use strict";function r(e){var n=/\w{4}/g;for(var r in e)t.packages[r]=e[r].replace(n,"\\u$&")}t.packages={},r({L:"0041-005A0061-007A00AA00B500BA00C0-00D600D8-00F600F8-02C102C6-02D102E0-02E402EC02EE0370-037403760377037A-037D03860388-038A038C038E-03A103A3-03F503F7-0481048A-05250531-055605590561-058705D0-05EA05F0-05F20621-064A066E066F0671-06D306D506E506E606EE06EF06FA-06FC06FF07100712-072F074D-07A507B107CA-07EA07F407F507FA0800-0815081A082408280904-0939093D09500958-0961097109720979-097F0985-098C098F09900993-09A809AA-09B009B209B6-09B909BD09CE09DC09DD09DF-09E109F009F10A05-0A0A0A0F0A100A13-0A280A2A-0A300A320A330A350A360A380A390A59-0A5C0A5E0A72-0A740A85-0A8D0A8F-0A910A93-0AA80AAA-0AB00AB20AB30AB5-0AB90ABD0AD00AE00AE10B05-0B0C0B0F0B100B13-0B280B2A-0B300B320B330B35-0B390B3D0B5C0B5D0B5F-0B610B710B830B85-0B8A0B8E-0B900B92-0B950B990B9A0B9C0B9E0B9F0BA30BA40BA8-0BAA0BAE-0BB90BD00C05-0C0C0C0E-0C100C12-0C280C2A-0C330C35-0C390C3D0C580C590C600C610C85-0C8C0C8E-0C900C92-0CA80CAA-0CB30CB5-0CB90CBD0CDE0CE00CE10D05-0D0C0D0E-0D100D12-0D280D2A-0D390D3D0D600D610D7A-0D7F0D85-0D960D9A-0DB10DB3-0DBB0DBD0DC0-0DC60E01-0E300E320E330E40-0E460E810E820E840E870E880E8A0E8D0E94-0E970E99-0E9F0EA1-0EA30EA50EA70EAA0EAB0EAD-0EB00EB20EB30EBD0EC0-0EC40EC60EDC0EDD0F000F40-0F470F49-0F6C0F88-0F8B1000-102A103F1050-1055105A-105D106110651066106E-10701075-1081108E10A0-10C510D0-10FA10FC1100-1248124A-124D1250-12561258125A-125D1260-1288128A-128D1290-12B012B2-12B512B8-12BE12C012C2-12C512C8-12D612D8-13101312-13151318-135A1380-138F13A0-13F41401-166C166F-167F1681-169A16A0-16EA1700-170C170E-17111720-17311740-17511760-176C176E-17701780-17B317D717DC1820-18771880-18A818AA18B0-18F51900-191C1950-196D1970-19741980-19AB19C1-19C71A00-1A161A20-1A541AA71B05-1B331B45-1B4B1B83-1BA01BAE1BAF1C00-1C231C4D-1C4F1C5A-1C7D1CE9-1CEC1CEE-1CF11D00-1DBF1E00-1F151F18-1F1D1F20-1F451F48-1F4D1F50-1F571F591F5B1F5D1F5F-1F7D1F80-1FB41FB6-1FBC1FBE1FC2-1FC41FC6-1FCC1FD0-1FD31FD6-1FDB1FE0-1FEC1FF2-1FF41FF6-1FFC2071207F2090-209421022107210A-211321152119-211D212421262128212A-212D212F-2139213C-213F2145-2149214E218321842C00-2C2E2C30-2C5E2C60-2CE42CEB-2CEE2D00-2D252D30-2D652D6F2D80-2D962DA0-2DA62DA8-2DAE2DB0-2DB62DB8-2DBE2DC0-2DC62DC8-2DCE2DD0-2DD62DD8-2DDE2E2F300530063031-3035303B303C3041-3096309D-309F30A1-30FA30FC-30FF3105-312D3131-318E31A0-31B731F0-31FF3400-4DB54E00-9FCBA000-A48CA4D0-A4FDA500-A60CA610-A61FA62AA62BA640-A65FA662-A66EA67F-A697A6A0-A6E5A717-A71FA722-A788A78BA78CA7FB-A801A803-A805A807-A80AA80C-A822A840-A873A882-A8B3A8F2-A8F7A8FBA90A-A925A930-A946A960-A97CA984-A9B2A9CFAA00-AA28AA40-AA42AA44-AA4BAA60-AA76AA7AAA80-AAAFAAB1AAB5AAB6AAB9-AABDAAC0AAC2AADB-AADDABC0-ABE2AC00-D7A3D7B0-D7C6D7CB-D7FBF900-FA2DFA30-FA6DFA70-FAD9FB00-FB06FB13-FB17FB1DFB1F-FB28FB2A-FB36FB38-FB3CFB3EFB40FB41FB43FB44FB46-FBB1FBD3-FD3DFD50-FD8FFD92-FDC7FDF0-FDFBFE70-FE74FE76-FEFCFF21-FF3AFF41-FF5AFF66-FFBEFFC2-FFC7FFCA-FFCFFFD2-FFD7FFDA-FFDC",Ll:"0061-007A00AA00B500BA00DF-00F600F8-00FF01010103010501070109010B010D010F01110113011501170119011B011D011F01210123012501270129012B012D012F01310133013501370138013A013C013E014001420144014601480149014B014D014F01510153015501570159015B015D015F01610163016501670169016B016D016F0171017301750177017A017C017E-0180018301850188018C018D019201950199-019B019E01A101A301A501A801AA01AB01AD01B001B401B601B901BA01BD-01BF01C601C901CC01CE01D001D201D401D601D801DA01DC01DD01DF01E101E301E501E701E901EB01ED01EF01F001F301F501F901FB01FD01FF02010203020502070209020B020D020F02110213021502170219021B021D021F02210223022502270229022B022D022F02310233-0239023C023F0240024202470249024B024D024F-02930295-02AF037103730377037B-037D039003AC-03CE03D003D103D5-03D703D903DB03DD03DF03E103E303E503E703E903EB03ED03EF-03F303F503F803FB03FC0430-045F04610463046504670469046B046D046F04710473047504770479047B047D047F0481048B048D048F04910493049504970499049B049D049F04A104A304A504A704A904AB04AD04AF04B104B304B504B704B904BB04BD04BF04C204C404C604C804CA04CC04CE04CF04D104D304D504D704D904DB04DD04DF04E104E304E504E704E904EB04ED04EF04F104F304F504F704F904FB04FD04FF05010503050505070509050B050D050F05110513051505170519051B051D051F0521052305250561-05871D00-1D2B1D62-1D771D79-1D9A1E011E031E051E071E091E0B1E0D1E0F1E111E131E151E171E191E1B1E1D1E1F1E211E231E251E271E291E2B1E2D1E2F1E311E331E351E371E391E3B1E3D1E3F1E411E431E451E471E491E4B1E4D1E4F1E511E531E551E571E591E5B1E5D1E5F1E611E631E651E671E691E6B1E6D1E6F1E711E731E751E771E791E7B1E7D1E7F1E811E831E851E871E891E8B1E8D1E8F1E911E931E95-1E9D1E9F1EA11EA31EA51EA71EA91EAB1EAD1EAF1EB11EB31EB51EB71EB91EBB1EBD1EBF1EC11EC31EC51EC71EC91ECB1ECD1ECF1ED11ED31ED51ED71ED91EDB1EDD1EDF1EE11EE31EE51EE71EE91EEB1EED1EEF1EF11EF31EF51EF71EF91EFB1EFD1EFF-1F071F10-1F151F20-1F271F30-1F371F40-1F451F50-1F571F60-1F671F70-1F7D1F80-1F871F90-1F971FA0-1FA71FB0-1FB41FB61FB71FBE1FC2-1FC41FC61FC71FD0-1FD31FD61FD71FE0-1FE71FF2-1FF41FF61FF7210A210E210F2113212F21342139213C213D2146-2149214E21842C30-2C5E2C612C652C662C682C6A2C6C2C712C732C742C76-2C7C2C812C832C852C872C892C8B2C8D2C8F2C912C932C952C972C992C9B2C9D2C9F2CA12CA32CA52CA72CA92CAB2CAD2CAF2CB12CB32CB52CB72CB92CBB2CBD2CBF2CC12CC32CC52CC72CC92CCB2CCD2CCF2CD12CD32CD52CD72CD92CDB2CDD2CDF2CE12CE32CE42CEC2CEE2D00-2D25A641A643A645A647A649A64BA64DA64FA651A653A655A657A659A65BA65DA65FA663A665A667A669A66BA66DA681A683A685A687A689A68BA68DA68FA691A693A695A697A723A725A727A729A72BA72DA72F-A731A733A735A737A739A73BA73DA73FA741A743A745A747A749A74BA74DA74FA751A753A755A757A759A75BA75DA75FA761A763A765A767A769A76BA76DA76FA771-A778A77AA77CA77FA781A783A785A787A78CFB00-FB06FB13-FB17FF41-FF5A",Lu:"0041-005A00C0-00D600D8-00DE01000102010401060108010A010C010E01100112011401160118011A011C011E01200122012401260128012A012C012E01300132013401360139013B013D013F0141014301450147014A014C014E01500152015401560158015A015C015E01600162016401660168016A016C016E017001720174017601780179017B017D018101820184018601870189-018B018E-0191019301940196-0198019C019D019F01A001A201A401A601A701A901AC01AE01AF01B1-01B301B501B701B801BC01C401C701CA01CD01CF01D101D301D501D701D901DB01DE01E001E201E401E601E801EA01EC01EE01F101F401F6-01F801FA01FC01FE02000202020402060208020A020C020E02100212021402160218021A021C021E02200222022402260228022A022C022E02300232023A023B023D023E02410243-02460248024A024C024E03700372037603860388-038A038C038E038F0391-03A103A3-03AB03CF03D2-03D403D803DA03DC03DE03E003E203E403E603E803EA03EC03EE03F403F703F903FA03FD-042F04600462046404660468046A046C046E04700472047404760478047A047C047E0480048A048C048E04900492049404960498049A049C049E04A004A204A404A604A804AA04AC04AE04B004B204B404B604B804BA04BC04BE04C004C104C304C504C704C904CB04CD04D004D204D404D604D804DA04DC04DE04E004E204E404E604E804EA04EC04EE04F004F204F404F604F804FA04FC04FE05000502050405060508050A050C050E05100512051405160518051A051C051E0520052205240531-055610A0-10C51E001E021E041E061E081E0A1E0C1E0E1E101E121E141E161E181E1A1E1C1E1E1E201E221E241E261E281E2A1E2C1E2E1E301E321E341E361E381E3A1E3C1E3E1E401E421E441E461E481E4A1E4C1E4E1E501E521E541E561E581E5A1E5C1E5E1E601E621E641E661E681E6A1E6C1E6E1E701E721E741E761E781E7A1E7C1E7E1E801E821E841E861E881E8A1E8C1E8E1E901E921E941E9E1EA01EA21EA41EA61EA81EAA1EAC1EAE1EB01EB21EB41EB61EB81EBA1EBC1EBE1EC01EC21EC41EC61EC81ECA1ECC1ECE1ED01ED21ED41ED61ED81EDA1EDC1EDE1EE01EE21EE41EE61EE81EEA1EEC1EEE1EF01EF21EF41EF61EF81EFA1EFC1EFE1F08-1F0F1F18-1F1D1F28-1F2F1F38-1F3F1F48-1F4D1F591F5B1F5D1F5F1F68-1F6F1FB8-1FBB1FC8-1FCB1FD8-1FDB1FE8-1FEC1FF8-1FFB21022107210B-210D2110-211221152119-211D212421262128212A-212D2130-2133213E213F214521832C00-2C2E2C602C62-2C642C672C692C6B2C6D-2C702C722C752C7E-2C802C822C842C862C882C8A2C8C2C8E2C902C922C942C962C982C9A2C9C2C9E2CA02CA22CA42CA62CA82CAA2CAC2CAE2CB02CB22CB42CB62CB82CBA2CBC2CBE2CC02CC22CC42CC62CC82CCA2CCC2CCE2CD02CD22CD42CD62CD82CDA2CDC2CDE2CE02CE22CEB2CEDA640A642A644A646A648A64AA64CA64EA650A652A654A656A658A65AA65CA65EA662A664A666A668A66AA66CA680A682A684A686A688A68AA68CA68EA690A692A694A696A722A724A726A728A72AA72CA72EA732A734A736A738A73AA73CA73EA740A742A744A746A748A74AA74CA74EA750A752A754A756A758A75AA75CA75EA760A762A764A766A768A76AA76CA76EA779A77BA77DA77EA780A782A784A786A78BFF21-FF3A",Lt:"01C501C801CB01F21F88-1F8F1F98-1F9F1FA8-1FAF1FBC1FCC1FFC",Lm:"02B0-02C102C6-02D102E0-02E402EC02EE0374037A0559064006E506E607F407F507FA081A0824082809710E460EC610FC17D718431AA71C78-1C7D1D2C-1D611D781D9B-1DBF2071207F2090-20942C7D2D6F2E2F30053031-3035303B309D309E30FC-30FEA015A4F8-A4FDA60CA67FA717-A71FA770A788A9CFAA70AADDFF70FF9EFF9F",Lo:"01BB01C0-01C3029405D0-05EA05F0-05F20621-063F0641-064A066E066F0671-06D306D506EE06EF06FA-06FC06FF07100712-072F074D-07A507B107CA-07EA0800-08150904-0939093D09500958-096109720979-097F0985-098C098F09900993-09A809AA-09B009B209B6-09B909BD09CE09DC09DD09DF-09E109F009F10A05-0A0A0A0F0A100A13-0A280A2A-0A300A320A330A350A360A380A390A59-0A5C0A5E0A72-0A740A85-0A8D0A8F-0A910A93-0AA80AAA-0AB00AB20AB30AB5-0AB90ABD0AD00AE00AE10B05-0B0C0B0F0B100B13-0B280B2A-0B300B320B330B35-0B390B3D0B5C0B5D0B5F-0B610B710B830B85-0B8A0B8E-0B900B92-0B950B990B9A0B9C0B9E0B9F0BA30BA40BA8-0BAA0BAE-0BB90BD00C05-0C0C0C0E-0C100C12-0C280C2A-0C330C35-0C390C3D0C580C590C600C610C85-0C8C0C8E-0C900C92-0CA80CAA-0CB30CB5-0CB90CBD0CDE0CE00CE10D05-0D0C0D0E-0D100D12-0D280D2A-0D390D3D0D600D610D7A-0D7F0D85-0D960D9A-0DB10DB3-0DBB0DBD0DC0-0DC60E01-0E300E320E330E40-0E450E810E820E840E870E880E8A0E8D0E94-0E970E99-0E9F0EA1-0EA30EA50EA70EAA0EAB0EAD-0EB00EB20EB30EBD0EC0-0EC40EDC0EDD0F000F40-0F470F49-0F6C0F88-0F8B1000-102A103F1050-1055105A-105D106110651066106E-10701075-1081108E10D0-10FA1100-1248124A-124D1250-12561258125A-125D1260-1288128A-128D1290-12B012B2-12B512B8-12BE12C012C2-12C512C8-12D612D8-13101312-13151318-135A1380-138F13A0-13F41401-166C166F-167F1681-169A16A0-16EA1700-170C170E-17111720-17311740-17511760-176C176E-17701780-17B317DC1820-18421844-18771880-18A818AA18B0-18F51900-191C1950-196D1970-19741980-19AB19C1-19C71A00-1A161A20-1A541B05-1B331B45-1B4B1B83-1BA01BAE1BAF1C00-1C231C4D-1C4F1C5A-1C771CE9-1CEC1CEE-1CF12135-21382D30-2D652D80-2D962DA0-2DA62DA8-2DAE2DB0-2DB62DB8-2DBE2DC0-2DC62DC8-2DCE2DD0-2DD62DD8-2DDE3006303C3041-3096309F30A1-30FA30FF3105-312D3131-318E31A0-31B731F0-31FF3400-4DB54E00-9FCBA000-A014A016-A48CA4D0-A4F7A500-A60BA610-A61FA62AA62BA66EA6A0-A6E5A7FB-A801A803-A805A807-A80AA80C-A822A840-A873A882-A8B3A8F2-A8F7A8FBA90A-A925A930-A946A960-A97CA984-A9B2AA00-AA28AA40-AA42AA44-AA4BAA60-AA6FAA71-AA76AA7AAA80-AAAFAAB1AAB5AAB6AAB9-AABDAAC0AAC2AADBAADCABC0-ABE2AC00-D7A3D7B0-D7C6D7CB-D7FBF900-FA2DFA30-FA6DFA70-FAD9FB1DFB1F-FB28FB2A-FB36FB38-FB3CFB3EFB40FB41FB43FB44FB46-FBB1FBD3-FD3DFD50-FD8FFD92-FDC7FDF0-FDFBFE70-FE74FE76-FEFCFF66-FF6FFF71-FF9DFFA0-FFBEFFC2-FFC7FFCA-FFCFFFD2-FFD7FFDA-FFDC",M:"0300-036F0483-04890591-05BD05BF05C105C205C405C505C70610-061A064B-065E067006D6-06DC06DE-06E406E706E806EA-06ED07110730-074A07A6-07B007EB-07F30816-0819081B-08230825-08270829-082D0900-0903093C093E-094E0951-0955096209630981-098309BC09BE-09C409C709C809CB-09CD09D709E209E30A01-0A030A3C0A3E-0A420A470A480A4B-0A4D0A510A700A710A750A81-0A830ABC0ABE-0AC50AC7-0AC90ACB-0ACD0AE20AE30B01-0B030B3C0B3E-0B440B470B480B4B-0B4D0B560B570B620B630B820BBE-0BC20BC6-0BC80BCA-0BCD0BD70C01-0C030C3E-0C440C46-0C480C4A-0C4D0C550C560C620C630C820C830CBC0CBE-0CC40CC6-0CC80CCA-0CCD0CD50CD60CE20CE30D020D030D3E-0D440D46-0D480D4A-0D4D0D570D620D630D820D830DCA0DCF-0DD40DD60DD8-0DDF0DF20DF30E310E34-0E3A0E47-0E4E0EB10EB4-0EB90EBB0EBC0EC8-0ECD0F180F190F350F370F390F3E0F3F0F71-0F840F860F870F90-0F970F99-0FBC0FC6102B-103E1056-1059105E-10601062-10641067-106D1071-10741082-108D108F109A-109D135F1712-17141732-1734175217531772177317B6-17D317DD180B-180D18A91920-192B1930-193B19B0-19C019C819C91A17-1A1B1A55-1A5E1A60-1A7C1A7F1B00-1B041B34-1B441B6B-1B731B80-1B821BA1-1BAA1C24-1C371CD0-1CD21CD4-1CE81CED1CF21DC0-1DE61DFD-1DFF20D0-20F02CEF-2CF12DE0-2DFF302A-302F3099309AA66F-A672A67CA67DA6F0A6F1A802A806A80BA823-A827A880A881A8B4-A8C4A8E0-A8F1A926-A92DA947-A953A980-A983A9B3-A9C0AA29-AA36AA43AA4CAA4DAA7BAAB0AAB2-AAB4AAB7AAB8AABEAABFAAC1ABE3-ABEAABECABEDFB1EFE00-FE0FFE20-FE26",Mn:"0300-036F0483-04870591-05BD05BF05C105C205C405C505C70610-061A064B-065E067006D6-06DC06DF-06E406E706E806EA-06ED07110730-074A07A6-07B007EB-07F30816-0819081B-08230825-08270829-082D0900-0902093C0941-0948094D0951-095509620963098109BC09C1-09C409CD09E209E30A010A020A3C0A410A420A470A480A4B-0A4D0A510A700A710A750A810A820ABC0AC1-0AC50AC70AC80ACD0AE20AE30B010B3C0B3F0B41-0B440B4D0B560B620B630B820BC00BCD0C3E-0C400C46-0C480C4A-0C4D0C550C560C620C630CBC0CBF0CC60CCC0CCD0CE20CE30D41-0D440D4D0D620D630DCA0DD2-0DD40DD60E310E34-0E3A0E47-0E4E0EB10EB4-0EB90EBB0EBC0EC8-0ECD0F180F190F350F370F390F71-0F7E0F80-0F840F860F870F90-0F970F99-0FBC0FC6102D-10301032-10371039103A103D103E10581059105E-10601071-1074108210851086108D109D135F1712-17141732-1734175217531772177317B7-17BD17C617C9-17D317DD180B-180D18A91920-19221927192819321939-193B1A171A181A561A58-1A5E1A601A621A65-1A6C1A73-1A7C1A7F1B00-1B031B341B36-1B3A1B3C1B421B6B-1B731B801B811BA2-1BA51BA81BA91C2C-1C331C361C371CD0-1CD21CD4-1CE01CE2-1CE81CED1DC0-1DE61DFD-1DFF20D0-20DC20E120E5-20F02CEF-2CF12DE0-2DFF302A-302F3099309AA66FA67CA67DA6F0A6F1A802A806A80BA825A826A8C4A8E0-A8F1A926-A92DA947-A951A980-A982A9B3A9B6-A9B9A9BCAA29-AA2EAA31AA32AA35AA36AA43AA4CAAB0AAB2-AAB4AAB7AAB8AABEAABFAAC1ABE5ABE8ABEDFB1EFE00-FE0FFE20-FE26",Mc:"0903093E-09400949-094C094E0982098309BE-09C009C709C809CB09CC09D70A030A3E-0A400A830ABE-0AC00AC90ACB0ACC0B020B030B3E0B400B470B480B4B0B4C0B570BBE0BBF0BC10BC20BC6-0BC80BCA-0BCC0BD70C01-0C030C41-0C440C820C830CBE0CC0-0CC40CC70CC80CCA0CCB0CD50CD60D020D030D3E-0D400D46-0D480D4A-0D4C0D570D820D830DCF-0DD10DD8-0DDF0DF20DF30F3E0F3F0F7F102B102C10311038103B103C105610571062-10641067-106D108310841087-108C108F109A-109C17B617BE-17C517C717C81923-19261929-192B193019311933-193819B0-19C019C819C91A19-1A1B1A551A571A611A631A641A6D-1A721B041B351B3B1B3D-1B411B431B441B821BA11BA61BA71BAA1C24-1C2B1C341C351CE11CF2A823A824A827A880A881A8B4-A8C3A952A953A983A9B4A9B5A9BAA9BBA9BD-A9C0AA2FAA30AA33AA34AA4DAA7BABE3ABE4ABE6ABE7ABE9ABEAABEC",Me:"0488048906DE20DD-20E020E2-20E4A670-A672",N:"0030-003900B200B300B900BC-00BE0660-066906F0-06F907C0-07C90966-096F09E6-09EF09F4-09F90A66-0A6F0AE6-0AEF0B66-0B6F0BE6-0BF20C66-0C6F0C78-0C7E0CE6-0CEF0D66-0D750E50-0E590ED0-0ED90F20-0F331040-10491090-10991369-137C16EE-16F017E0-17E917F0-17F91810-18191946-194F19D0-19DA1A80-1A891A90-1A991B50-1B591BB0-1BB91C40-1C491C50-1C5920702074-20792080-20892150-21822185-21892460-249B24EA-24FF2776-27932CFD30073021-30293038-303A3192-31953220-32293251-325F3280-328932B1-32BFA620-A629A6E6-A6EFA830-A835A8D0-A8D9A900-A909A9D0-A9D9AA50-AA59ABF0-ABF9FF10-FF19",Nd:"0030-00390660-066906F0-06F907C0-07C90966-096F09E6-09EF0A66-0A6F0AE6-0AEF0B66-0B6F0BE6-0BEF0C66-0C6F0CE6-0CEF0D66-0D6F0E50-0E590ED0-0ED90F20-0F291040-10491090-109917E0-17E91810-18191946-194F19D0-19DA1A80-1A891A90-1A991B50-1B591BB0-1BB91C40-1C491C50-1C59A620-A629A8D0-A8D9A900-A909A9D0-A9D9AA50-AA59ABF0-ABF9FF10-FF19",Nl:"16EE-16F02160-21822185-218830073021-30293038-303AA6E6-A6EF",No:"00B200B300B900BC-00BE09F4-09F90BF0-0BF20C78-0C7E0D70-0D750F2A-0F331369-137C17F0-17F920702074-20792080-20892150-215F21892460-249B24EA-24FF2776-27932CFD3192-31953220-32293251-325F3280-328932B1-32BFA830-A835",P:"0021-00230025-002A002C-002F003A003B003F0040005B-005D005F007B007D00A100AB00B700BB00BF037E0387055A-055F0589058A05BE05C005C305C605F305F40609060A060C060D061B061E061F066A-066D06D40700-070D07F7-07F90830-083E0964096509700DF40E4F0E5A0E5B0F04-0F120F3A-0F3D0F850FD0-0FD4104A-104F10FB1361-13681400166D166E169B169C16EB-16ED1735173617D4-17D617D8-17DA1800-180A1944194519DE19DF1A1E1A1F1AA0-1AA61AA8-1AAD1B5A-1B601C3B-1C3F1C7E1C7F1CD32010-20272030-20432045-20512053-205E207D207E208D208E2329232A2768-277527C527C627E6-27EF2983-299829D8-29DB29FC29FD2CF9-2CFC2CFE2CFF2E00-2E2E2E302E313001-30033008-30113014-301F3030303D30A030FBA4FEA4FFA60D-A60FA673A67EA6F2-A6F7A874-A877A8CEA8CFA8F8-A8FAA92EA92FA95FA9C1-A9CDA9DEA9DFAA5C-AA5FAADEAADFABEBFD3EFD3FFE10-FE19FE30-FE52FE54-FE61FE63FE68FE6AFE6BFF01-FF03FF05-FF0AFF0C-FF0FFF1AFF1BFF1FFF20FF3B-FF3DFF3FFF5BFF5DFF5F-FF65",Pd:"002D058A05BE140018062010-20152E172E1A301C303030A0FE31FE32FE58FE63FF0D",Ps:"0028005B007B0F3A0F3C169B201A201E2045207D208D23292768276A276C276E27702772277427C527E627E827EA27EC27EE2983298529872989298B298D298F299129932995299729D829DA29FC2E222E242E262E283008300A300C300E3010301430163018301A301DFD3EFE17FE35FE37FE39FE3BFE3DFE3FFE41FE43FE47FE59FE5BFE5DFF08FF3BFF5BFF5FFF62",Pe:"0029005D007D0F3B0F3D169C2046207E208E232A2769276B276D276F27712773277527C627E727E927EB27ED27EF298429862988298A298C298E2990299229942996299829D929DB29FD2E232E252E272E293009300B300D300F3011301530173019301B301E301FFD3FFE18FE36FE38FE3AFE3CFE3EFE40FE42FE44FE48FE5AFE5CFE5EFF09FF3DFF5DFF60FF63",Pi:"00AB2018201B201C201F20392E022E042E092E0C2E1C2E20",Pf:"00BB2019201D203A2E032E052E0A2E0D2E1D2E21",Pc:"005F203F20402054FE33FE34FE4D-FE4FFF3F",Po:"0021-00230025-0027002A002C002E002F003A003B003F0040005C00A100B700BF037E0387055A-055F058905C005C305C605F305F40609060A060C060D061B061E061F066A-066D06D40700-070D07F7-07F90830-083E0964096509700DF40E4F0E5A0E5B0F04-0F120F850FD0-0FD4104A-104F10FB1361-1368166D166E16EB-16ED1735173617D4-17D617D8-17DA1800-18051807-180A1944194519DE19DF1A1E1A1F1AA0-1AA61AA8-1AAD1B5A-1B601C3B-1C3F1C7E1C7F1CD3201620172020-20272030-2038203B-203E2041-20432047-205120532055-205E2CF9-2CFC2CFE2CFF2E002E012E06-2E082E0B2E0E-2E162E182E192E1B2E1E2E1F2E2A-2E2E2E302E313001-3003303D30FBA4FEA4FFA60D-A60FA673A67EA6F2-A6F7A874-A877A8CEA8CFA8F8-A8FAA92EA92FA95FA9C1-A9CDA9DEA9DFAA5C-AA5FAADEAADFABEBFE10-FE16FE19FE30FE45FE46FE49-FE4CFE50-FE52FE54-FE57FE5F-FE61FE68FE6AFE6BFF01-FF03FF05-FF07FF0AFF0CFF0EFF0FFF1AFF1BFF1FFF20FF3CFF61FF64FF65",S:"0024002B003C-003E005E0060007C007E00A2-00A900AC00AE-00B100B400B600B800D700F702C2-02C502D2-02DF02E5-02EB02ED02EF-02FF03750384038503F604820606-0608060B060E060F06E906FD06FE07F609F209F309FA09FB0AF10B700BF3-0BFA0C7F0CF10CF20D790E3F0F01-0F030F13-0F170F1A-0F1F0F340F360F380FBE-0FC50FC7-0FCC0FCE0FCF0FD5-0FD8109E109F13601390-139917DB194019E0-19FF1B61-1B6A1B74-1B7C1FBD1FBF-1FC11FCD-1FCF1FDD-1FDF1FED-1FEF1FFD1FFE20442052207A-207C208A-208C20A0-20B8210021012103-21062108210921142116-2118211E-2123212521272129212E213A213B2140-2144214A-214D214F2190-2328232B-23E82400-24262440-244A249C-24E92500-26CD26CF-26E126E326E8-26FF2701-27042706-2709270C-27272729-274B274D274F-27522756-275E2761-276727942798-27AF27B1-27BE27C0-27C427C7-27CA27CC27D0-27E527F0-29822999-29D729DC-29FB29FE-2B4C2B50-2B592CE5-2CEA2E80-2E992E9B-2EF32F00-2FD52FF0-2FFB300430123013302030363037303E303F309B309C319031913196-319F31C0-31E33200-321E322A-32503260-327F328A-32B032C0-32FE3300-33FF4DC0-4DFFA490-A4C6A700-A716A720A721A789A78AA828-A82BA836-A839AA77-AA79FB29FDFCFDFDFE62FE64-FE66FE69FF04FF0BFF1C-FF1EFF3EFF40FF5CFF5EFFE0-FFE6FFE8-FFEEFFFCFFFD",Sm:"002B003C-003E007C007E00AC00B100D700F703F60606-060820442052207A-207C208A-208C2140-2144214B2190-2194219A219B21A021A321A621AE21CE21CF21D221D421F4-22FF2308-230B23202321237C239B-23B323DC-23E125B725C125F8-25FF266F27C0-27C427C7-27CA27CC27D0-27E527F0-27FF2900-29822999-29D729DC-29FB29FE-2AFF2B30-2B442B47-2B4CFB29FE62FE64-FE66FF0BFF1C-FF1EFF5CFF5EFFE2FFE9-FFEC",Sc:"002400A2-00A5060B09F209F309FB0AF10BF90E3F17DB20A0-20B8A838FDFCFE69FF04FFE0FFE1FFE5FFE6",Sk:"005E006000A800AF00B400B802C2-02C502D2-02DF02E5-02EB02ED02EF-02FF0375038403851FBD1FBF-1FC11FCD-1FCF1FDD-1FDF1FED-1FEF1FFD1FFE309B309CA700-A716A720A721A789A78AFF3EFF40FFE3",So:"00A600A700A900AE00B000B60482060E060F06E906FD06FE07F609FA0B700BF3-0BF80BFA0C7F0CF10CF20D790F01-0F030F13-0F170F1A-0F1F0F340F360F380FBE-0FC50FC7-0FCC0FCE0FCF0FD5-0FD8109E109F13601390-1399194019E0-19FF1B61-1B6A1B74-1B7C210021012103-21062108210921142116-2118211E-2123212521272129212E213A213B214A214C214D214F2195-2199219C-219F21A121A221A421A521A7-21AD21AF-21CD21D021D121D321D5-21F32300-2307230C-231F2322-2328232B-237B237D-239A23B4-23DB23E2-23E82400-24262440-244A249C-24E92500-25B625B8-25C025C2-25F72600-266E2670-26CD26CF-26E126E326E8-26FF2701-27042706-2709270C-27272729-274B274D274F-27522756-275E2761-276727942798-27AF27B1-27BE2800-28FF2B00-2B2F2B452B462B50-2B592CE5-2CEA2E80-2E992E9B-2EF32F00-2FD52FF0-2FFB300430123013302030363037303E303F319031913196-319F31C0-31E33200-321E322A-32503260-327F328A-32B032C0-32FE3300-33FF4DC0-4DFFA490-A4C6A828-A82BA836A837A839AA77-AA79FDFDFFE4FFE8FFEDFFEEFFFCFFFD",Z:"002000A01680180E2000-200A20282029202F205F3000",Zs:"002000A01680180E2000-200A202F205F3000",Zl:"2028",Zp:"2029",C:"0000-001F007F-009F00AD03780379037F-0383038B038D03A20526-05300557055805600588058B-059005C8-05CF05EB-05EF05F5-0605061C061D0620065F06DD070E070F074B074C07B2-07BF07FB-07FF082E082F083F-08FF093A093B094F095609570973-097809800984098D098E0991099209A909B109B3-09B509BA09BB09C509C609C909CA09CF-09D609D8-09DB09DE09E409E509FC-0A000A040A0B-0A0E0A110A120A290A310A340A370A3A0A3B0A3D0A43-0A460A490A4A0A4E-0A500A52-0A580A5D0A5F-0A650A76-0A800A840A8E0A920AA90AB10AB40ABA0ABB0AC60ACA0ACE0ACF0AD1-0ADF0AE40AE50AF00AF2-0B000B040B0D0B0E0B110B120B290B310B340B3A0B3B0B450B460B490B4A0B4E-0B550B58-0B5B0B5E0B640B650B72-0B810B840B8B-0B8D0B910B96-0B980B9B0B9D0BA0-0BA20BA5-0BA70BAB-0BAD0BBA-0BBD0BC3-0BC50BC90BCE0BCF0BD1-0BD60BD8-0BE50BFB-0C000C040C0D0C110C290C340C3A-0C3C0C450C490C4E-0C540C570C5A-0C5F0C640C650C70-0C770C800C810C840C8D0C910CA90CB40CBA0CBB0CC50CC90CCE-0CD40CD7-0CDD0CDF0CE40CE50CF00CF3-0D010D040D0D0D110D290D3A-0D3C0D450D490D4E-0D560D58-0D5F0D640D650D76-0D780D800D810D840D97-0D990DB20DBC0DBE0DBF0DC7-0DC90DCB-0DCE0DD50DD70DE0-0DF10DF5-0E000E3B-0E3E0E5C-0E800E830E850E860E890E8B0E8C0E8E-0E930E980EA00EA40EA60EA80EA90EAC0EBA0EBE0EBF0EC50EC70ECE0ECF0EDA0EDB0EDE-0EFF0F480F6D-0F700F8C-0F8F0F980FBD0FCD0FD9-0FFF10C6-10CF10FD-10FF1249124E124F12571259125E125F1289128E128F12B112B612B712BF12C112C612C712D7131113161317135B-135E137D-137F139A-139F13F5-13FF169D-169F16F1-16FF170D1715-171F1737-173F1754-175F176D17711774-177F17B417B517DE17DF17EA-17EF17FA-17FF180F181A-181F1878-187F18AB-18AF18F6-18FF191D-191F192C-192F193C-193F1941-1943196E196F1975-197F19AC-19AF19CA-19CF19DB-19DD1A1C1A1D1A5F1A7D1A7E1A8A-1A8F1A9A-1A9F1AAE-1AFF1B4C-1B4F1B7D-1B7F1BAB-1BAD1BBA-1BFF1C38-1C3A1C4A-1C4C1C80-1CCF1CF3-1CFF1DE7-1DFC1F161F171F1E1F1F1F461F471F4E1F4F1F581F5A1F5C1F5E1F7E1F7F1FB51FC51FD41FD51FDC1FF01FF11FF51FFF200B-200F202A-202E2060-206F20722073208F2095-209F20B9-20CF20F1-20FF218A-218F23E9-23FF2427-243F244B-245F26CE26E226E4-26E727002705270A270B2728274C274E2753-2755275F27602795-279727B027BF27CB27CD-27CF2B4D-2B4F2B5A-2BFF2C2F2C5F2CF2-2CF82D26-2D2F2D66-2D6E2D70-2D7F2D97-2D9F2DA72DAF2DB72DBF2DC72DCF2DD72DDF2E32-2E7F2E9A2EF4-2EFF2FD6-2FEF2FFC-2FFF3040309730983100-3104312E-3130318F31B8-31BF31E4-31EF321F32FF4DB6-4DBF9FCC-9FFFA48D-A48FA4C7-A4CFA62C-A63FA660A661A674-A67BA698-A69FA6F8-A6FFA78D-A7FAA82C-A82FA83A-A83FA878-A87FA8C5-A8CDA8DA-A8DFA8FC-A8FFA954-A95EA97D-A97FA9CEA9DA-A9DDA9E0-A9FFAA37-AA3FAA4EAA4FAA5AAA5BAA7C-AA7FAAC3-AADAAAE0-ABBFABEEABEFABFA-ABFFD7A4-D7AFD7C7-D7CAD7FC-F8FFFA2EFA2FFA6EFA6FFADA-FAFFFB07-FB12FB18-FB1CFB37FB3DFB3FFB42FB45FBB2-FBD2FD40-FD4FFD90FD91FDC8-FDEFFDFEFDFFFE1A-FE1FFE27-FE2FFE53FE67FE6C-FE6FFE75FEFD-FF00FFBF-FFC1FFC8FFC9FFD0FFD1FFD8FFD9FFDD-FFDFFFE7FFEF-FFFBFFFEFFFF",Cc:"0000-001F007F-009F",Cf:"00AD0600-060306DD070F17B417B5200B-200F202A-202E2060-2064206A-206FFEFFFFF9-FFFB",Co:"E000-F8FF",Cs:"D800-DFFF",Cn:"03780379037F-0383038B038D03A20526-05300557055805600588058B-059005C8-05CF05EB-05EF05F5-05FF06040605061C061D0620065F070E074B074C07B2-07BF07FB-07FF082E082F083F-08FF093A093B094F095609570973-097809800984098D098E0991099209A909B109B3-09B509BA09BB09C509C609C909CA09CF-09D609D8-09DB09DE09E409E509FC-0A000A040A0B-0A0E0A110A120A290A310A340A370A3A0A3B0A3D0A43-0A460A490A4A0A4E-0A500A52-0A580A5D0A5F-0A650A76-0A800A840A8E0A920AA90AB10AB40ABA0ABB0AC60ACA0ACE0ACF0AD1-0ADF0AE40AE50AF00AF2-0B000B040B0D0B0E0B110B120B290B310B340B3A0B3B0B450B460B490B4A0B4E-0B550B58-0B5B0B5E0B640B650B72-0B810B840B8B-0B8D0B910B96-0B980B9B0B9D0BA0-0BA20BA5-0BA70BAB-0BAD0BBA-0BBD0BC3-0BC50BC90BCE0BCF0BD1-0BD60BD8-0BE50BFB-0C000C040C0D0C110C290C340C3A-0C3C0C450C490C4E-0C540C570C5A-0C5F0C640C650C70-0C770C800C810C840C8D0C910CA90CB40CBA0CBB0CC50CC90CCE-0CD40CD7-0CDD0CDF0CE40CE50CF00CF3-0D010D040D0D0D110D290D3A-0D3C0D450D490D4E-0D560D58-0D5F0D640D650D76-0D780D800D810D840D97-0D990DB20DBC0DBE0DBF0DC7-0DC90DCB-0DCE0DD50DD70DE0-0DF10DF5-0E000E3B-0E3E0E5C-0E800E830E850E860E890E8B0E8C0E8E-0E930E980EA00EA40EA60EA80EA90EAC0EBA0EBE0EBF0EC50EC70ECE0ECF0EDA0EDB0EDE-0EFF0F480F6D-0F700F8C-0F8F0F980FBD0FCD0FD9-0FFF10C6-10CF10FD-10FF1249124E124F12571259125E125F1289128E128F12B112B612B712BF12C112C612C712D7131113161317135B-135E137D-137F139A-139F13F5-13FF169D-169F16F1-16FF170D1715-171F1737-173F1754-175F176D17711774-177F17DE17DF17EA-17EF17FA-17FF180F181A-181F1878-187F18AB-18AF18F6-18FF191D-191F192C-192F193C-193F1941-1943196E196F1975-197F19AC-19AF19CA-19CF19DB-19DD1A1C1A1D1A5F1A7D1A7E1A8A-1A8F1A9A-1A9F1AAE-1AFF1B4C-1B4F1B7D-1B7F1BAB-1BAD1BBA-1BFF1C38-1C3A1C4A-1C4C1C80-1CCF1CF3-1CFF1DE7-1DFC1F161F171F1E1F1F1F461F471F4E1F4F1F581F5A1F5C1F5E1F7E1F7F1FB51FC51FD41FD51FDC1FF01FF11FF51FFF2065-206920722073208F2095-209F20B9-20CF20F1-20FF218A-218F23E9-23FF2427-243F244B-245F26CE26E226E4-26E727002705270A270B2728274C274E2753-2755275F27602795-279727B027BF27CB27CD-27CF2B4D-2B4F2B5A-2BFF2C2F2C5F2CF2-2CF82D26-2D2F2D66-2D6E2D70-2D7F2D97-2D9F2DA72DAF2DB72DBF2DC72DCF2DD72DDF2E32-2E7F2E9A2EF4-2EFF2FD6-2FEF2FFC-2FFF3040309730983100-3104312E-3130318F31B8-31BF31E4-31EF321F32FF4DB6-4DBF9FCC-9FFFA48D-A48FA4C7-A4CFA62C-A63FA660A661A674-A67BA698-A69FA6F8-A6FFA78D-A7FAA82C-A82FA83A-A83FA878-A87FA8C5-A8CDA8DA-A8DFA8FC-A8FFA954-A95EA97D-A97FA9CEA9DA-A9DDA9E0-A9FFAA37-AA3FAA4EAA4FAA5AAA5BAA7C-AA7FAAC3-AADAAAE0-ABBFABEEABEFABFA-ABFFD7A4-D7AFD7C7-D7CAD7FC-D7FFFA2EFA2FFA6EFA6FFADA-FAFFFB07-FB12FB18-FB1CFB37FB3DFB3FFB42FB45FBB2-FBD2FD40-FD4FFD90FD91FDC8-FDEFFDFEFDFFFE1A-FE1FFE27-FE2FFE53FE67FE6C-FE6FFE75FEFDFEFEFF00FFBF-FFC1FFC8FFC9FFD0FFD1FFD8FFD9FFDD-FFDFFFE7FFEF-FFF8FFFEFFFF"})}),define("ace/mode/text",["require","exports","module","ace/tokenizer","ace/mode/text_highlight_rules","ace/mode/behaviour/cstyle","ace/unicode","ace/lib/lang","ace/token_iterator","ace/range"],function(e,t,n){"use strict";var r=e("../tokenizer").Tokenizer,i=e("./text_highlight_rules").TextHighlightRules,s=e("./behaviour/cstyle").CstyleBehaviour,o=e("../unicode"),u=e("../lib/lang"),a=e("../token_iterator").TokenIterator,f=e("../range").Range,l=function(){this.HighlightRules=i};(function(){this.$defaultBehaviour=new s,this.tokenRe=new RegExp("^["+o.packages.L+o.packages.Mn+o.packages.Mc+o.packages.Nd+o.packages.Pc+"\\$_]+","g"),this.nonTokenRe=new RegExp("^(?:[^"+o.packages.L+o.packages.Mn+o.packages.Mc+o.packages.Nd+o.packages.Pc+"\\$_]|\\s])+","g"),this.getTokenizer=function(){return this.$tokenizer||(this.$highlightRules=this.$highlightRules||new this.HighlightRules(this.$highlightRuleConfig),this.$tokenizer=new r(this.$highlightRules.getRules())),this.$tokenizer},this.lineCommentStart="",this.blockComment="",this.toggleCommentLines=function(e,t,n,r){function w(e){for(var t=n;t<=r;t++)e(i.getLine(t),t)}var i=t.doc,s=!0,o=!0,a=Infinity,f=t.getTabSize(),l=!1;if(!this.lineCommentStart){if(!this.blockComment)return!1;var c=this.blockComment.start,h=this.blockComment.end,p=new RegExp("^(\\s*)(?:"+u.escapeRegExp(c)+")"),d=new RegExp("(?:"+u.escapeRegExp(h)+")\\s*$"),v=function(e,t){if(g(e,t))return;if(!s||/\S/.test(e))i.insertInLine({row:t,column:e.length},h),i.insertInLine({row:t,column:a},c)},m=function(e,t){var n;(n=e.match(d))&&i.removeInLine(t,e.length-n[0].length,e.length),(n=e.match(p))&&i.removeInLine(t,n[1].length,n[0].length)},g=function(e,n){if(p.test(e))return!0;var r=t.getTokens(n);for(var i=0;i2?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)}),a==Infinity&&(a=E,s=!1,o=!1),l&&a%f!=0&&(a=Math.floor(a/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 a(t,r.row,r.column),o=s.getCurrentToken(),u=t.selection,l=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 f(m,g,m,g+i.start.length);break}o=s.stepBackward()}var s=new a(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 f(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);l.start.row==c&&(l.start.column+=h),l.end.row==c&&(l.end.column+=h),t.selection.fromOrientedRange(l)},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)e[t]&&(this.$embeds.push(t),this.$modes[t]=new e[t]);var n=["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))}}}),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)}),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,i=e.start.row,s=e.start.column,o=0,u=0;do{o=u,u+=t-1;var a=n.slice(o,u);if(u>r){e.lines=a,e.start.row=i+o,e.start.column=s;break}a.push(""),this.applyDelta({start:this.pos(i+o,s),end:this.pos(i+u,s=0),action:e.action,lines:a},!0)}while(!0)},this.revertDelta=function(e){this.applyDelta({start:this.clonePos(e.start),end:this.clonePos(e.end),action:e.action=="insert"?"remove":"insert",lines:e.lines.slice()})},this.indexToPosition=function(e,t){var n=this.$lines||this.getAllLines(),r=this.getNewLineCharacter().length;for(var i=t||0,s=n.length;i20){n.running=setTimeout(n.$worker,20);break}}n.currentLine=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}),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}),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.rowr)break;l.start.row==r&&l.start.column>=t.column&&(l.start.column!=t.column||!this.$insertRight)&&(l.start.column+=o,l.start.row+=s);if(l.end.row==r&&l.end.column>=t.column){if(l.end.column==t.column&&this.$insertRight)continue;l.end.column==t.column&&o>0&&al.start.column&&l.end.column==u[a+1].start.column&&(l.end.column-=o),l.end.column+=o,l.end.row+=s}}if(s!=0&&a=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;if(u0&&(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(t=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}),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,o),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.$deltas=[],this.$deltasDoc=[],this.$deltasFold=[],this.$informUndoManager&&this.$informUndoManager.cancel();if(e){var t=this;this.$syncInformUndoManager=function(){t.$informUndoManager.cancel(),t.$deltasFold.length&&(t.$deltas.push({group:"fold",deltas:t.$deltasFold}),t.$deltasFold=[]),t.$deltasDoc.length&&(t.$deltas.push({group:"doc",deltas:t.$deltasDoc}),t.$deltasDoc=[]),t.$deltas.length>0&&e.execute({action:"aceupdate",args:[t.$deltas,t],merge:t.mergeUndoDeltas}),t.mergeUndoDeltas=!1,t.$deltas=[]},this.$informUndoManager=i.delayedCall(this.$syncInformUndoManager)}},this.markUndoGroup=function(){this.$syncInformUndoManager&&this.$syncInformUndoManager()},this.$defaultUndoManager={undo:function(){},redo:function(){},reset: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.$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;var n=null;for(var r=e.length-1;r!=-1;r--){var i=e[r];i.group=="doc"?(this.doc.revertDeltas(i.deltas),n=this.$getUndoSelection(i.deltas,!0,n)):i.deltas.forEach(function(e){this.addFolds(e.folds)},this)}return this.$fromUndo=!1,n&&this.$undoSelect&&!t&&this.selection.setSelectionRange(n),n},this.redoChanges=function(e,t){if(!e.length)return;this.$fromUndo=!0;var n=null;for(var r=0;re.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,l=s.start,o=l.row-a.row,u=l.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 f(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 f(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.$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(),s=this.$wrapData,o=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,s){var o;if(e!=null){o=this.$getDisplayTokens(e,a.length),o[0]=n;for(var f=1;fr-b){var w=a+r-b;if(e[w-1]>=p&&e[w]>=p){y(w);continue}if(e[w]==n||e[w]==u){for(w;w!=a-1;w--)if(e[w]==n)break;if(w>a){y(w);continue}w=a+r;for(w;w>2)),a-1);while(w>E&&e[w]E&&e[w]E&&e[w]==l)w--}else while(w>E&&e[w]E){y(++w);continue}w=a+r,e[w]==t&&w--,y(w-b)}return s},this.$getDisplayTokens=function(n,r){var i=[],s;r=r||0;for(var o=0;o39&&u<48||u>57&&u<64?i.push(l):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 o=a[f],r=this.$docRowCache[f],c=e>a[l-1];else var c=!l;var h=this.getLength()-1,p=this.getNextFoldLine(r),d=p?p.start.row:Infinity;while(o<=e){u=this.getRowLength(r);if(o+u>e||r>=h)break;o+=u,r++,r>d&&(r=p.end.row+1,p=this.getNextFoldLine(r,p),d=p?p.start.row:Infinity),c&&(this.$docRowCache.push(r),this.$screenRowCache.push(o))}if(p&&p.start.row<=r)n=this.getFoldDisplayLine(p),r=p.start.row;else{if(o+u<=e||r>h)return{row:h,column:this.getLine(h).length};n=this.getLine(r),p=null}var v=0;if(this.$useWrapMode){var m=this.$wrapData[r];if(m){var g=Math.floor(e-o);s=m[g],g>0&&m.length&&(v=m.indent,i=m[g-1]||m[m.length-1],n=n.substring(i))}}return i+=this.$getStringScreenWidth(n,t-v)[1],this.$useWrapMode&&i>=s&&(i=s-1),p?p.idxToPosition(i):{row:r,column:i}},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()}}).call(p.prototype),e("./edit_session/folding").Folding.call(p.prototype),e("./edit_session/bracket_match").BracketMatch.call(p.prototype),s.defineOptions(p.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.$modified=!0,this.$resetRowCache(0),this.$updateWrapData(0,this.getLength()-1)))},initialValue:"auto"},indentedSoftWrap:{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){if(isNaN(e)||this.$tabSize===e)return;this.$modified=!0,this.$rowLengthCache=[],this.$tabSize=e,this._signal("changeTabSize")},initialValue:4,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}}}),t.EditSession=p}),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){if(!e.start){var o=e.offset+(i||0);r=new s(n,o,n,o+e.length);if(!e.length&&t.start&&t.start.start&&t.skipCurrent!=0&&r.isEqual(t.start))return r=null,!1}else r=e;return!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=0;u--)if(i(o[u],t,s))return!0};else var u=function(e,t,s){var o=r.getMatchOffsets(e,n);for(var u=0;u=o;r--)if(n(e.getLine(r),r))return;if(t.wrap==0)return;for(r=u,o=s.row;r>=o;r--)if(n(e.getLine(r),r))return}:function(n){var r=s.row,i=e.getLine(r).substr(s.column);if(n(i,r,s.column))return;for(r+=1;r<=u;r++)if(n(e.getLine(r),r))return;if(t.wrap==0)return;for(r=o,u=s.row;r<=u;r++)if(n(e.getLine(r),r))return};return{forEach:a}}}).call(o.prototype),t.Search=o}),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||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||n.isDefault?r=-100: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}),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;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}),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("ace/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("ace/ext/error_marker",function(t){t.showErrorMarker(e,-1)})},scrollIntoView:"animate",readOnly:!0},{name:"selectall",bindKey:o("Ctrl-A","Command-A"),exec:function(e){e.selectAll()},readOnly:!0},{name:"centerselection",bindKey:o(null,"Ctrl-L"),exec:function(e){e.centerSelection()},readOnly:!0},{name:"gotoline",bindKey:o("Ctrl-L","Command-L"),exec:function(e){var t=parseInt(prompt("Enter line number:"),10);isNaN(t)||e.gotoLine(t)},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",bindKey:o(null,"Ctrl-Command-Option-0"),exec:function(e){e.session.foldAll()},scrollIntoView:"center",readOnly:!0},{name:"foldOther",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",bindKey:o("Alt-Shift-0","Command-Option-Shift-0"),exec:function(e){e.session.unfold()},scrollIntoView:"center",readOnly:!0},{name:"findnext",bindKey:o("Ctrl-K","Command-G"),exec:function(e){e.findNext()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"findprevious",bindKey:o("Ctrl-Shift-K","Command-Shift-G"),exec:function(e){e.findPrevious()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"selectOrFindNext",bindKey:o("Alt-K","Ctrl-G"),exec:function(e){e.selection.isEmpty()?e.selection.selectWord():e.findNext()},readOnly:!0},{name:"selectOrFindPrevious",bindKey:o("Alt-Shift-K","Ctrl-Shift-G"),exec:function(e){e.selection.isEmpty()?e.selection.selectWord():e.findPrevious()},readOnly:!0},{name:"find",bindKey:o("Ctrl-F","Command-F"),exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e)})},readOnly:!0},{name:"overwrite",bindKey:"Insert",exec:function(e){e.toggleOverwrite()},readOnly:!0},{name:"selecttostart",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",bindKey:o("Ctrl-Home","Command-Home|Command-Up"),exec:function(e){e.navigateFileStart()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"selectup",bindKey:o("Shift-Up","Shift-Up|Ctrl-Shift-P"),exec:function(e){e.getSelection().selectUp()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"golineup",bindKey:o("Up","Up|Ctrl-P"),exec:function(e,t){e.navigateUp(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttoend",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",bindKey:o("Ctrl-End","Command-End|Command-Down"),exec:function(e){e.navigateFileEnd()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"selectdown",bindKey:o("Shift-Down","Shift-Down|Ctrl-Shift-N"),exec:function(e){e.getSelection().selectDown()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"golinedown",bindKey:o("Down","Down|Ctrl-N"),exec:function(e,t){e.navigateDown(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectwordleft",bindKey:o("Ctrl-Shift-Left","Option-Shift-Left"),exec:function(e){e.getSelection().selectWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotowordleft",bindKey:o("Ctrl-Left","Option-Left"),exec:function(e){e.navigateWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttolinestart",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",bindKey:o("Alt-Left|Home","Command-Left|Home|Ctrl-A"),exec:function(e){e.navigateLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectleft",bindKey:o("Shift-Left","Shift-Left|Ctrl-Shift-B"),exec:function(e){e.getSelection().selectLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotoleft",bindKey:o("Left","Left|Ctrl-B"),exec:function(e,t){e.navigateLeft(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectwordright",bindKey:o("Ctrl-Shift-Right","Option-Shift-Right"),exec:function(e){e.getSelection().selectWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotowordright",bindKey:o("Ctrl-Right","Option-Right"),exec:function(e){e.navigateWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttolineend",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",bindKey:o("Alt-Right|End","Command-Right|End|Ctrl-E"),exec:function(e){e.navigateLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectright",bindKey:o("Shift-Right","Shift-Right"),exec:function(e){e.getSelection().selectRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotoright",bindKey:o("Right","Right|Ctrl-F"),exec:function(e,t){e.navigateRight(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectpagedown",bindKey:"Shift-PageDown",exec:function(e){e.selectPageDown()},readOnly:!0},{name:"pagedown",bindKey:o(null,"Option-PageDown"),exec:function(e){e.scrollPageDown()},readOnly:!0},{name:"gotopagedown",bindKey:o("PageDown","PageDown|Ctrl-V"),exec:function(e){e.gotoPageDown()},readOnly:!0},{name:"selectpageup",bindKey:"Shift-PageUp",exec:function(e){e.selectPageUp()},readOnly:!0},{name:"pageup",bindKey:o(null,"Option-PageUp"),exec:function(e){e.scrollPageUp()},readOnly:!0},{name:"gotopageup",bindKey:"PageUp",exec:function(e){e.gotoPageUp()},readOnly:!0},{name:"scrollup",bindKey:o("Ctrl-Up",null),exec:function(e){e.renderer.scrollBy(0,-2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"scrolldown",bindKey:o("Ctrl-Down",null),exec:function(e){e.renderer.scrollBy(0,2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"selectlinestart",bindKey:"Shift-Home",exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectlineend",bindKey:"Shift-End",exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"togglerecording",bindKey:o("Ctrl-Alt-E","Command-Option-E"),exec:function(e){e.commands.toggleRecording(e)},readOnly:!0},{name:"replaymacro",bindKey:o("Ctrl-Shift-E","Command-Shift-E"),exec:function(e){e.commands.replay(e)},readOnly:!0},{name:"jumptomatching",bindKey:o("Ctrl-P","Ctrl-P"),exec:function(e){e.jumpToMatching()},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"selecttomatching",bindKey:o("Ctrl-Shift-P","Ctrl-Shift-P"),exec:function(e){e.jumpToMatching(!0)},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"expandToMatching",bindKey:o("Ctrl-Shift-M","Ctrl-Shift-M"),exec:function(e){e.jumpToMatching(!0,!0)},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"passKeysToBrowser",bindKey:o(null,null),exec:function(){},passEvent:!0,readOnly:!0},{name:"copy",exec:function(e){},readOnly:!0},{name:"cut",exec:function(e){var t=e.getSelectionRange();e._emit("cut",t),e.selection.isEmpty()||(e.session.remove(t),e.clearSelection())},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"paste",exec:function(e,t){e.$handlePaste(t)},scrollIntoView:"cursor"},{name:"removeline",bindKey:o("Ctrl-D","Command-D"),exec:function(e){e.removeLines()},scrollIntoView:"cursor",multiSelectAction:"forEachLine"},{name:"duplicateSelection",bindKey:o("Ctrl-Shift-D","Command-Shift-D"),exec:function(e){e.duplicateSelection()},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"sortlines",bindKey:o("Ctrl-Alt-S","Command-Alt-S"),exec:function(e){e.sortLines()},scrollIntoView:"selection",multiSelectAction:"forEachLine"},{name:"togglecomment",bindKey:o("Ctrl-/","Command-/"),exec:function(e){e.toggleCommentLines()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"toggleBlockComment",bindKey:o("Ctrl-Shift-/","Command-Shift-/"),exec:function(e){e.toggleBlockComment()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"modifyNumberUp",bindKey:o("Ctrl-Shift-Up","Alt-Shift-Up"),exec:function(e){e.modifyNumber(1)},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"modifyNumberDown",bindKey:o("Ctrl-Shift-Down","Alt-Shift-Down"),exec:function(e){e.modifyNumber(-1)},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"replace",bindKey:o("Ctrl-H","Command-Option-F"),exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e,!0)})}},{name:"undo",bindKey:o("Ctrl-Z","Command-Z"),exec:function(e){e.undo()}},{name:"redo",bindKey:o("Ctrl-Shift-Z|Ctrl-Y","Command-Shift-Z|Command-Y"),exec:function(e){e.redo()}},{name:"copylinesup",bindKey:o("Alt-Shift-Up","Command-Option-Up"),exec:function(e){e.copyLinesUp()},scrollIntoView:"cursor"},{name:"movelinesup",bindKey:o("Alt-Up","Option-Up"),exec:function(e){e.moveLinesUp()},scrollIntoView:"cursor"},{name:"copylinesdown",bindKey:o("Alt-Shift-Down","Command-Option-Down"),exec:function(e){e.copyLinesDown()},scrollIntoView:"cursor"},{name:"movelinesdown",bindKey:o("Alt-Down","Option-Down"),exec:function(e){e.moveLinesDown()},scrollIntoView:"cursor"},{name:"del",bindKey:o("Delete","Delete|Ctrl-D|Shift-Delete"),exec:function(e){e.remove("right")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"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",bindKey:o("Shift-Delete",null),exec:function(e){if(!e.selection.isEmpty())return!1;e.remove("left")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolinestart",bindKey:o("Alt-Backspace","Command-Backspace"),exec:function(e){e.removeToLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolineend",bindKey:o("Alt-Delete","Ctrl-K"),exec:function(e){e.removeToLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removewordleft",bindKey:o("Ctrl-Backspace","Alt-Backspace|Ctrl-Alt-Backspace"),exec:function(e){e.removeWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removewordright",bindKey:o("Ctrl-Delete","Alt-Delete"),exec:function(e){e.removeWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"outdent",bindKey:o("Shift-Tab","Shift-Tab"),exec:function(e){e.blockOutdent()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"indent",bindKey:o("Tab","Tab"),exec:function(e){e.indent()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"blockoutdent",bindKey:o("Ctrl-[","Ctrl-["),exec:function(e){e.blockOutdent()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"blockindent",bindKey:o("Ctrl-]","Ctrl-]"),exec:function(e){e.blockIndent()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"insertstring",exec:function(e,t){e.insert(t)},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"inserttext",exec:function(e,t){e.insert(r.stringRepeat(t.text||"",t.times||1))},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"splitline",bindKey:o(null,"Ctrl-O"),exec:function(e){e.splitLine()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"transposeletters",bindKey:o("Ctrl-T","Ctrl-T"),exec:function(e){e.transposeLetters()},multiSelectAction:function(e){e.transposeSelections(1)},scrollIntoView:"cursor"},{name:"touppercase",bindKey:o("Ctrl-U","Ctrl-U"),exec:function(e){e.toUpperCase()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"tolowercase",bindKey:o("Ctrl-Shift-U","Ctrl-Shift-U"),exec:function(e){e.toLowerCase()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"expandtoline",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",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",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;o0&&this.$blockScrolling--;var n=t&&t.scrollIntoView;if(n){switch(n){case"center-animate":n="animate";case"center":this.renderer.scrollCursorIntoView(null,.5);break;case"animate":case"cursor":this.renderer.scrollCursorIntoView();break;case"selectionPart":var r=this.selection.getRange(),i=this.renderer.layerConfig;(r.start.row>=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)}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"){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.$blockScrolling+=1,this.onCursorChange(),this.$blockScrolling-=1,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})},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-1,r=t.end.column+1,i=e.getLine(t.start.row),s=i.length,o=i.substring(Math.max(n,0),Math.min(r,s));if(n>=0&&/^[\w\d]/.test(o)||r<=s&&/[\w\d]$/.test(o))return;o=i.substring(t.start.column,t.end.column);if(!/^[\w\d]+$/.test(o))return;var u=this.$search.$assembleRegExp({wholeWord:!0,caseSensitive:!0,needle:o});return u},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();return this._signal("copy",e),e},this.onCopy=function(){this.commands.exec("copy",this)},this.onCut=function(){this.commands.exec("cut",this)},this.onPaste=function(e,t){var n={text:e,event:t};this.commands.exec("paste",this,n)},this.$handlePaste=function(e){typeof e=="string"&&(e={text:e}),this._signal("paste",e);var t=e.text;if(!this.inMultiSelectMode||this.inVirtualSelectionMode)this.insert(t);else{var n=t.split(/\r\n|\r|\n/),r=this.selection.rangeList.ranges;if(n.length>r.length||n.length<2||!n[1])return this.commands.exec("insertstring",this,t);for(var i=r.length;i--;){var s=r[i];s.isEmpty()||this.session.remove(s),this.session.insert(s.start,n[i])}}},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.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()){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){this.keyBinding.onTextInput(e)},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.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 r=new p(0,0,0,0);for(var i=e.first;i<=e.last;i++){var s=t.getLine(i);r.start.row=i,r.end.row=i,r.end.column=s.length,t.replace(r,n[i-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&&np+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(this.getCursorPosition())},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);this.$blockScrolling++,t===!0?this.selection.$moveSelection(function(){this.moveCursorBy(i,0)}):t===!1&&(this.selection.moveCursorBy(i,0),this.selection.clearSelection()),this.$blockScrolling--;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.$blockScrolling+=1,this.selection.selectAll(),this.$blockScrolling-=1},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),this.$blockScrolling-=1,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.$blockScrolling+=1,this.session.unfold(e),this.selection.setSelectionRange(e),this.$blockScrolling-=1;var n=this.renderer.scrollTop;this.renderer.scrollSelectionIntoView(e.start,e.end,.5),t!==!1&&this.renderer.animateScrolling(n)},this.undo=function(){this.$blockScrolling++,this.session.getUndoManager().undo(),this.$blockScrolling--,this.renderer.scrollCursorIntoView(null,.5)},this.redo=function(){this.$blockScrolling++,this.session.getUndoManager().redo(),this.$blockScrolling--,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))}}).call(b.prototype),g.defineOptions(b.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.$resetCursorStyle()},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},hScrollBarAlwaysVisible:"renderer",vScrollBarAlwaysVisible:"renderer",highlightGutterLine:"renderer",animatedScroll:"renderer",showInvisibles:"renderer",showPrintMargin:"renderer",printMarginColumn:"renderer",printMargin:"renderer",fadeFoldWidgets:"renderer",showFoldWidgets:"renderer",showLineNumbers:"renderer",showGutter:"renderer",displayIndentGuides:"renderer",fontSize:"renderer",fontFamily:"renderer",maxLines:"renderer",minLines:"renderer",scrollPastEnd:"renderer",fixedWidthGutter:"renderer",theme:"renderer",scrollSpeed:"$mouseHandler",dragDelay:"$mouseHandler",dragEnabled:"$mouseHandler",focusTimout:"$mouseHandler",tooltipFollowsMouse:"$mouseHandler",firstLineNumber:"session",overwrite:"session",newLineMode:"session",useWorker:"session",useSoftTabs:"session",tabSize:"session",wrap:"session",indentedSoftWrap:"session",foldStyle:"session",mode:"session"}),t.Editor=b}),define("ace/undomanager",["require","exports","module"],function(e,t,n){"use strict";var r=function(){this.reset()};(function(){function e(e){return{action:e.action,start:e.start,end:e.end,lines:e.lines.length==1?null:e.lines,text:e.lines.length==1?e.lines[0]:null}}function t(e){return{action:e.action,start:e.start,end:e.end,lines:e.lines||[e.text]}}function n(e,t){var n=new Array(e.length);for(var r=0;r0},this.hasRedo=function(){return this.$redoStack.length>0},this.markClean=function(){this.dirtyCounter=0},this.isClean=function(){return this.dirtyCounter===0},this.$serializeDeltas=function(t){return n(t,e)},this.$deserializeDeltas=function(e){return n(e,t)}}).call(r.prototype),t.UndoManager=r}),define("ace/layer/gutter",["require","exports","module","ace/lib/dom","ace/lib/oop","ace/lib/lang","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("../lib/dom"),i=e("../lib/oop"),s=e("../lib/lang"),o=e("../lib/event_emitter").EventEmitter,u=function(e){this.element=r.createElement("div"),this.element.className="ace_layer ace_gutter-layer",e.appendChild(this.element),this.setShowFoldWidgets(this.$showFoldWidgets),this.gutterWidth=0,this.$annotations=[],this.$updateAnnotations=this.$updateAnnotations.bind(this),this.$cells=[]};(function(){i.implement(this,o),this.setSession=function(e){this.session&&this.session.removeEventListener("change",this.$updateAnnotations),this.session=e,e&&e.on("change",this.$updateAnnotations)},this.addGutterDecoration=function(e,t){window.console&&console.warn&&console.warn("deprecated use session.addGutterDecoration"),this.session.addGutterDecoration(e,t)},this.removeGutterDecoration=function(e,t){window.console&&console.warn&&console.warn("deprecated use session.removeGutterDecoration"),this.session.removeGutterDecoration(e,t)},this.setAnnotations=function(e){this.$annotations=[];for(var t=0;to&&(v=s.end.row+1,s=t.getNextFoldLine(v,s),o=s?s.start.row:Infinity);if(v>i){while(this.$cells.length>d+1)p=this.$cells.pop(),this.element.removeChild(p.element);break}p=this.$cells[++d],p||(p={element:null,textNode:null,foldWidget:null},p.element=r.createElement("div"),p.textNode=document.createTextNode(""),p.element.appendChild(p.textNode),this.element.appendChild(p.element),this.$cells[d]=p);var m="ace_gutter-cell ";a[v]&&(m+=a[v]),f[v]&&(m+=f[v]),this.$annotations[v]&&(m+=this.$annotations[v].className),p.element.className!=m&&(p.element.className=m);var g=t.getRowLength(v)*e.lineHeight+"px";g!=p.element.style.height&&(p.element.style.height=g);if(u){var y=u[v];y==null&&(y=u[v]=t.getFoldWidget(v))}if(y){p.foldWidget||(p.foldWidget=r.createElement("span"),p.element.appendChild(p.foldWidget));var m="ace_fold-widget ace_"+y;y=="start"&&v==o&&vn.right-t.right)return"foldWidgets"}}).call(u.prototype),t.Gutter=u}),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.update=function(e){var e=e||this.config;if(!e)return;this.config=e;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)}this.element.innerHTML=t.join("")},this.$getTop=function(e,t){return(e-t.firstRowScreen)*t.lineHeight},this.drawTextMarker=function(t,n,i,s,o){var u=this.session,a=n.start.row,f=n.end.row,l=a,c=0,h=0,p=u.getScreenLastRowColumn(l),d=new r(l,n.start.column,l,h);for(;l<=f;l++)d.start.row=d.end.row=l,d.start.column=l==a?n.start.column:u.getRowWrapIndent(l),d.end.column=p,c=h,h=p,p=l+1p,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||"",e.push("
"),u=this.$getTop(t.end.row,r);var f=t.end.column*r.characterWidth;e.push("
"),o=(t.end.row-t.start.row-1)*r.lineHeight;if(o<=0)return;u=this.$getTop(t.start.row+1,r);var l=(t.start.column?1:0)|(t.end.column?0:8);e.push("
")},this.drawSingleLineMarker=function(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;e.push("
")},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),e.push("
")},this.drawScreenLineMarker=function(e,t,n,r,i){var s=this.$getTop(t.start.row,r),o=r.lineHeight;e.push("
")}}).call(s.prototype),t.Marker=s}),define("ace/layer/text",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/useragent","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("../lib/useragent"),u=e("../lib/event_emitter").EventEmitter,a=function(e){this.element=i.createElement("div"),this.element.className="ace_layer ace_text-layer",e.appendChild(this.element),this.$updateEolChar=this.$updateEolChar.bind(this)};(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.$updateEolChar=function(){var e=this.session.doc.getNewLineCharacter()=="\n"?this.EOL_CHAR_LF:this.EOL_CHAR_CRLF;if(this.EOL_CHAR!=e)return this.EOL_CHAR=e,!0},this.setPadding=function(e){this.$padding=e,this.element.style.padding="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;n"+s.stringRepeat(this.TAB_CHAR,n)+""):t.push(s.stringRepeat(" ",n));if(this.displayIndentGuides){this.$indentGuideRe=/\s\S| \t|\t |\s$/;var r="ace_indent-guide",i="",o="";if(this.showInvisibles){r+=" ace_invisible",i=" ace_invisible_space",o=" ace_invisible_tab";var u=s.stringRepeat(this.SPACE_CHAR,this.tabSize),a=s.stringRepeat(this.TAB_CHAR,this.tabSize)}else var u=s.stringRepeat(" ",this.tabSize),a=u;this.$tabStrings[" "]=""+u+"",this.$tabStrings[" "]=""+a+""}},this.updateLines=function(e,t,n){(this.config.lastRow!=e.lastRow||this.config.firstRow!=e.firstRow)&&this.scrollLines(e),this.config=e;var r=Math.max(t,e.firstRow),i=Math.min(n,e.lastRow),s=this.element.childNodes,o=0;for(var u=e.firstRow;uf&&(u=a.end.row+1,a=this.session.getNextFoldLine(u,a),f=a?a.start.row:Infinity);if(u>i)break;var l=s[o++];if(l){var c=[];this.$renderLine(c,u,!this.$useLineGroups(),u==f?a:!1),l.style.height=e.lineHeight*this.session.getRowLength(u)+"px",l.innerHTML=c.join("")}u++}},this.scrollLines=function(e){var t=this.config;this.config=e;if(!t||t.lastRow0;r--)n.removeChild(n.firstChild);if(t.lastRow>e.lastRow)for(var r=this.session.getFoldedRowCount(e.lastRow+1,t.lastRow);r>0;r--)n.removeChild(n.lastChild);if(e.firstRowt.lastRow){var i=this.$renderLinesFragment(e,t.lastRow+1,e.lastRow);n.appendChild(i)}},this.$renderLinesFragment=function(e,t,n){var r=this.element.ownerDocument.createDocumentFragment(),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=i.createElement("div"),f=[];this.$renderLine(f,s,!1,s==u?o:!1),a.innerHTML=f.join("");if(this.$useLineGroups())a.className="ace_line_group",r.appendChild(a),a.style.height=e.lineHeight*this.session.getRowLength(s)+"px";else while(a.firstChild)r.appendChild(a.firstChild);s++}return r},this.update=function(e){this.config=e;var t=[],n=e.firstRow,r=e.lastRow,i=n,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>r)break;this.$useLineGroups()&&t.push("
"),this.$renderLine(t,i,!1,i==o?s:!1),this.$useLineGroups()&&t.push("
"),i++}this.element.innerHTML=t.join("")},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\u3000\uFEFF\uFFF9-\uFFFC])|[\u1100-\u115F\u11A3-\u11A7\u11FA-\u11FF\u2329-\u232A\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3000-\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]/g,u=function(e,n,r,o,u){if(n)return i.showInvisibles?""+s.stringRepeat(i.SPACE_CHAR,e.length)+"":e;if(e=="&")return"&";if(e=="<")return"<";if(e==">")return">";if(e==" "){var a=i.session.getScreenTabSize(t+o);return t+=a-1,i.$tabStrings[a]}if(e=="\u3000"){var f=i.showInvisibles?"ace_cjk ace_invisible ace_invisible_space":"ace_cjk",l=i.showInvisibles?i.SPACE_CHAR:"";return t+=1,""+l+""}return r?""+i.SPACE_CHAR+"":(t+=1,""+e+"")},a=r.replace(o,u);if(!this.$textToken[n.type]){var f="ace_"+n.type.replace(/\./g," ace_"),l="";n.type=="fold"&&(l=" style='width:"+n.value.length*this.config.characterWidth+"px;' "),e.push("",a,"")}else e.push(a);return t+r.length},this.renderIndentGuide=function(e,t,n){var r=t.search(this.$indentGuideRe);return r<=0||r>=n?t:t[0]==" "?(r-=r%this.tabSize,e.push(s.stringRepeat(this.$tabStrings[" "],r/this.tabSize)),t.substr(r)):t[0]==" "?(e.push(s.stringRepeat(this.$tabStrings[" "],r)),t.substr(r)):t},this.$renderWrappedLine=function(e,t,n,r){var i=0,o=0,u=n[0],a=0;for(var f=0;f=u)a=this.$renderToken(e,a,l,c.substring(0,u-i)),c=c.substring(u-i),i=u,r||e.push("","
"),e.push(s.stringRepeat("\u00a0",n.indent)),o++,a=0,u=n[o]||Number.MAX_VALUE;c.length!=0&&(i+=c.length,a=this.$renderToken(e,a,l,c))}}},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;s");if(i.length){var s=this.session.getRowSplitData(t);s&&s.length?this.$renderWrappedLine(e,i,s,n):this.$renderSimpleLine(e,i)}this.showInvisibles&&(r&&(t=r.end.row),e.push("",t==this.session.getLength()-1?this.EOF_CHAR:this.EOL_CHAR,"")),n||e.push("
")},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(){clearInterval(this.$pollSizeChangesTimer),this.$measureNode&&this.$measureNode.parentNode.removeChild(this.$measureNode),delete this.$measureNode}}).call(a.prototype),t.Text=a}),define("ace/layer/cursor",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";var r=e("../lib/dom"),i,s=function(e){this.element=r.createElement("div"),this.element.className="ace_layer ace_cursor-layer",e.appendChild(this.element),i===undefined&&(i=!("opacity"in this.element.style)),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=(i?this.$updateVisibility:this.$updateOpacity).bind(this)};(function(){this.$updateVisibility=function(e){var t=this.cursors;for(var n=t.length;n--;)t[n].style.visibility=e?"":"hidden"},this.$updateOpacity=function(e){var t=this.cursors;for(var n=t.length;n--;)t[n].style.opacity=e?"":"0"},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&&!i&&(this.smoothBlinking=e,r.setCssClass(this.element,"ace_smooth-blinking",e),this.$updateCursors(!0),this.$updateCursors=this.$updateOpacity.bind(this),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.smoothBlinking&&r.removeCssClass(this.element,"ace_smooth-blinking"),e(!0);if(!this.isBlinking||!this.blinkInterval||!this.isVisible)return;this.smoothBlinking&&setTimeout(function(){r.addCssClass(this.element,"ace_smooth-blinking")}.bind(this));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+n.column*this.config.characterWidth,i=(n.row-(t?this.config.firstRowScreen:0))*this.config.lineHeight;return{left:r,top:i}},this.update=function(e){this.config=e;var t=this.session.$selectionMarkers,n=0,r=0;if(t===undefined||t.length===0)t=[{cursor:null}];for(var n=0,i=t.length;ne.height+e.offset||s.top<0)&&n>1)continue;var o=(this.cursors[r++]||this.addCursor()).style;this.drawCursor?this.drawCursor(o,s,e,t[n],this.session):(o.left=s.left+"px",o.top=s.top+"px",o.width=e.characterWidth+"px",o.height=e.lineHeight+"px")}while(this.cursors.length>r)this.removeCursor();var u=this.session.getOverwrite();this.$setOverwrite(u),this.$pixelPos=s,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(s.prototype),t.Cursor=s}),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.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"};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 this.isVisible?this.width: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}),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.window=t||window};(function(){this.schedule=function(e){this.changes=this.changes|e;if(!this.pending&&this.changes){this.pending=!0;var t=this;r.nextFrame(function(){t.pending=!1;var e;while(e=t.changes)t.changes=0,t.onRender(e)},this.window)}}}).call(i.prototype),t.RenderLoop=i}),define("ace/layer/font_metrics",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","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/useragent"),u=e("../lib/event_emitter").EventEmitter,a=0,f=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),a||this.$testFractionalRect(),this.$measureNode.innerHTML=s.stringRepeat("X",a),this.$characterSize={width:0,height:0},this.checkForSizeChanges()};(function(){r.implement(this,u),this.$characterSize={width:0,height:0},this.$testFractionalRect=function(){var e=i.createElement("div");this.$setMeasureNodeStyles(e.style),e.style.width="0.2px",document.documentElement.appendChild(e);var t=e.getBoundingClientRect().width;t>0&&t<1?a=50:a=100,e.parentNode.removeChild(e)},this.$setMeasureNodeStyles=function(e,t){e.width=e.height="auto",e.left=e.top="0px",e.visibility="hidden",e.position="absolute",e.whiteSpace="pre",o.isIE<8?e["font-family"]="inherit":e.font="inherit",e.overflow=t?"hidden":"visible"},this.checkForSizeChanges=function(){var 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.$pollSizeChanges=function(){if(this.$pollSizeChangesTimer)return this.$pollSizeChangesTimer;var e=this;return this.$pollSizeChangesTimer=setInterval(function(){e.checkForSizeChanges()},500)},this.setPolling=function(e){e?this.$pollSizeChanges():this.$pollSizeChangesTimer&&(clearInterval(this.$pollSizeChangesTimer),this.$pollSizeChangesTimer=0)},this.$measureSizes=function(){if(a===50){var e=null;try{e=this.$measureNode.getBoundingClientRect()}catch(t){e={width:0,height:0}}var n={height:e.height,width:e.width/a}}else var n={height:this.$measureNode.clientHeight,width:this.$measureNode.clientWidth/a};return n.width===0||n.height===0?null:n},this.$measureCharWidth=function(e){this.$main.innerHTML=s.stringRepeat(e,a);var t=this.$main.getBoundingClientRect();return t.width/a},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.el&&this.el.parentNode&&this.el.parentNode.removeChild(this.el)}}).call(f.prototype)}),define("ace/virtual_renderer",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/config","ace/lib/useragent","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"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./config"),o=e("./lib/useragent"),u=e("./layer/gutter").Gutter,a=e("./layer/marker").Marker,f=e("./layer/text").Text,l=e("./layer/cursor").Cursor,c=e("./scrollbar").HScrollBar,h=e("./scrollbar").VScrollBar,p=e("./renderloop").RenderLoop,d=e("./layer/font_metrics").FontMetrics,v=e("./lib/event_emitter").EventEmitter,m='.ace_editor {position: relative;overflow: hidden;font: 12px/normal \'Monaco\', \'Menlo\', \'Ubuntu Mono\', \'Consolas\', \'source-code-pro\', monospace;direction: ltr;text-align: left;}.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;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;min-width: 100%;}.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;}.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 {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 {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;text-indent: -1em;-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: inherit;color: inherit;z-index: 1000;opacity: 1;text-indent: 0;}.ace_layer {z-index: 1;position: absolute;overflow: hidden;word-wrap: normal;white-space: pre;height: 100%;width: 100%;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;pointer-events: none;}.ace_gutter-layer {position: relative;width: auto;text-align: right;pointer-events: auto;}.ace_text-layer {font: inherit !important;}.ace_cjk {display: inline-block;text-align: center;}.ace_cursor-layer {z-index: 4;}.ace_cursor {z-index: 4;position: absolute;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;border-left: 2px solid;transform: translatez(0);}.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 {-webkit-transition: opacity 0.18s;transition: opacity 0.18s;}.ace_editor.ace_multiselect .ace_cursor {border-left-width: 1px;}.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;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;}.ace_line .ace_fold {-moz-box-sizing: border-box;-webkit-box-sizing: border-box;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: -webkit-linear-gradient(top, transparent, rgba(0, 0, 0, 0.1));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;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;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 {-moz-box-sizing: border-box;-webkit-box-sizing: border-box;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_fold-widget.ace_invalid {background-color: #FFB4B4;border-color: #DE5555;}.ace_fade-fold-widgets .ace_fold-widget {-webkit-transition: opacity 0.4s ease 0.05s;transition: opacity 0.4s ease 0.05s;opacity: 0;}.ace_fade-fold-widgets:hover .ace_fold-widget {-webkit-transition: opacity 0.05s ease 0.05s;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_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;}';i.importCssString(m,"ace_editor.css");var g=function(e,t){var n=this;this.container=e||i.createElement("div"),this.$keepTextAreaAtCursor=!o.isOldIE,i.addCssClass(this.container,"ace_editor"),this.setTheme(t),this.$gutter=i.createElement("div"),this.$gutter.className="ace_gutter",this.container.appendChild(this.$gutter),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 u(this.$gutter),this.$gutterLayer.on("changeGutterWidth",this.onGutterResize.bind(this)),this.$markerBack=new a(this.content);var r=this.$textLayer=new f(this.content);this.canvas=r.element,this.$markerFront=new a(this.content),this.$cursorLayer=new l(this.content),this.$horizScroll=!1,this.$vScroll=!1,this.scrollBar=this.scrollBarV=new h(this.container,this),this.scrollBarH=new c(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 d(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.$loop=new p(this.$renderChanges.bind(this),this.container.ownerDocument.defaultView),this.$loop.schedule(this.CHANGE_FULL),this.updateCharacterSize(),this.setPadding(4),s.resetOptions(this),s._emit("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,v),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.scrollBarV.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.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 i=0,s=this.$size,o={width:s.width,height:s.height,scrollerHeight:s.scrollerHeight,scrollerWidth:s.scrollerWidth};r&&(e||s.height!=r)&&(s.height=r,i|=this.CHANGE_SIZE,s.scrollerHeight=s.height,this.$horizScroll&&(s.scrollerHeight-=this.scrollBarH.getHeight()),this.scrollBarV.element.style.bottom=this.scrollBarH.getHeight()+"px",i|=this.CHANGE_SCROLL);if(n&&(e||s.width!=n)){i|=this.CHANGE_SIZE,s.width=n,t==null&&(t=this.$showGutter?this.$gutter.offsetWidth:0),this.gutterWidth=t,this.scrollBarH.element.style.left=this.scroller.style.left=t+"px",s.scrollerWidth=Math.max(0,n-t-this.scrollBarV.getWidth()),this.scrollBarH.element.style.right=this.scroller.style.right=this.scrollBarV.getWidth()+"px",this.scroller.style.bottom=this.scrollBarH.getHeight()+"px";if(this.session&&this.session.getUseWrapMode()&&this.adjustWrapLimit()||e)i|=this.CHANGE_FULL}return s.$dirty=!n||!r,i&&this._signal("resize",o),i},this.onGutterResize=function(){var e=this.$showGutter?this.$gutter.offsetWidth:0;e!=this.gutterWidth&&(this.$changes|=this.$updateCachedSize(!0,e,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.$loop.schedule(this.CHANGE_MARKER))},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.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.$updateGutterLineHighlight=function(){var e=this.$cursorLayer.$pixelPos,t=this.layerConfig.lineHeight;if(this.session.getUseWrapMode()){var n=this.session.selection.getCursor();n.column=0,e=this.$cursorLayer.getPixelPosition(n,!0),t*=this.session.getRowLength(n.row)}this.$gutterLineHighlight.style.top=e.top-this.layerConfig.offset+"px",this.$gutterLineHighlight.style.height=t+"px"},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=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.$keepTextAreaAtCursor)return;var e=this.layerConfig,t=this.$cursorLayer.$pixelPos.top,n=this.$cursorLayer.$pixelPos.left;t-=e.offset;var r=this.textarea.style,i=this.lineHeight;if(t<0||t>e.height-i){r.top=r.left="0";return}var s=this.characterWidth;if(this.$composition){var o=this.textarea.value.replace(/^\x01+/,"");s*=this.session.$getStringScreenWidth(o)[0]+2,i+=2}n-=this.scrollLeft,n>this.$size.scrollerWidth-s&&(n=this.$size.scrollerWidth-s),n+=this.gutterWidth,r.height=i+"px",r.width=s+"px",r.left=Math.min(n,this.$size.scrollerWidth-s)+"px",r.top=Math.min(t,this.$size.height-i)+"px"},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.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");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();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())}n=this.layerConfig,this.$updateScrollBarV(),e&this.CHANGE_H_SCROLL&&this.$updateScrollBarH(),this.$gutterLayer.element.style.marginTop=-n.offset+"px",this.content.style.marginTop=-n.offset+"px",this.content.style.width=n.width+2*this.$padding+"px",this.content.style.height=n.minHeight+"px"}e&this.CHANGE_H_SCROLL&&(this.content.style.marginLeft=-this.scrollLeft+"px",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.$highlightGutterLine&&this.$updateGutterLineHighlight(),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&&this.$gutterLayer.update(n),this.$markerBack.update(n),this.$markerFront.update(n),this.$cursorLayer.update(n),this.$highlightGutterLine&&this.$updateGutterLineHighlight(),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.$cursorLayer.update(n),this.$moveTextAreaToCursor(),this.$highlightGutterLine&&this.$updateGutterLineHighlight()),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=e>t;if(n!=this.desiredHeight||this.$size.height!=this.desiredHeight||r!=this.$vScroll){r!=this.$vScroll&&(this.$vScroll=r,this.scrollBarV.setVisible(r));var i=this.container.clientWidth;this.container.style.height=n+"px",this.$updateCachedSize(!0,this.$gutterWidth,i,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=this.scrollTop%this.lineHeight,l=t.scrollerHeight+this.lineHeight,c=!this.$maxLines&&this.$scrollPastEnd?(t.scrollerHeight-this.lineHeight)*this.$scrollPastEnd:0;i+=c;var h=this.scrollMargin;this.session.setScrollTop(Math.max(-h.top,Math.min(this.scrollTop,i-t.scrollerHeight+h.bottom))),this.session.setScrollLeft(Math.max(-h.left,Math.min(this.scrollLeft,s+2*this.$padding-t.scrollerWidth+h.right)));var p=!n&&(this.$vScrollBarAlwaysVisible||t.scrollerHeight-i+c<0||this.scrollTop>h.top),d=a!==p;d&&(this.$vScroll=p,this.scrollBarV.setVisible(p));var v=Math.ceil(l/this.lineHeight)-1,m=Math.max(0,Math.round((this.scrollTop-f)/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),l=t.scrollerHeight+e.getRowLength(g)*w+b,f=this.scrollTop-y*w;var S=0;this.layerConfig.width!=s&&(S=this.CHANGE_H_SCROLL);if(u||d)S=this.$updateCachedSize(!0,this.gutterWidth,t.width,t.height),this._signal("scrollbarVisibilityChanged"),d&&(s=this.$getLongestLine());return this.layerConfig={width:s,padding:this.$padding,firstRow:m,firstRowScreen:y,lastRow:g,lineHeight:w,characterWidth:this.characterWidth,minHeight:l,maxHeight:i,offset:f,gutterOffset:w?Math.max(0,Math.ceil((f+t.height-t.scrollerHeight)/w)):0,height:this.$size.scrollerHeight},S},this.$updateLines=function(){var e=this.$changedLines.firstRow,t=this.$changedLines.lastRow;this.$changedLines=null;var n=this.layerConfig;if(e>n.lastRow+1)return;if(ts?(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=this.scroller.getBoundingClientRect(),r=(e+this.scrollLeft-n.left-this.$padding)/this.characterWidth,i=Math.floor((t+this.scrollTop-n.top)/this.lineHeight),s=Math.round(r);return{row:i,column:s,side:r-s>0?1:-1}},this.screenToTextCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=Math.round((e+this.scrollLeft-n.left-this.$padding)/this.characterWidth),i=(t+this.scrollTop-n.top)/this.lineHeight;return this.session.screenToDocumentPosition(i,Math.max(r,0))},this.textToScreenCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=this.session.documentToScreenPosition(e,t),i=this.$padding+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||(this.$composition={keepTextAreaAtCursor:this.$keepTextAreaAtCursor,cssText:this.textarea.style.cssText}),this.$keepTextAreaAtCursor=!0,i.addCssClass(this.textarea,"ace_composition"),this.textarea.style.cssText="",this.$moveTextAreaToCursor()},this.setCompositionText=function(e){this.$moveTextAreaToCursor()},this.hideComposition=function(){if(!this.$composition)return;i.removeCssClass(this.textarea,"ace_composition"),this.$keepTextAreaAtCursor=this.$composition.keepTextAreaAtCursor,this.textarea.style.cssText=this.$composition.cssText,this.$composition=null},this.setTheme=function(e,t){function o(r){if(n.$themeId!=e)return t&&t();if(!r||!r.cssClass)throw new Error("couldn't load module "+e+" or it didn't call define");i.importCssString(r.cssText,r.cssClass,n.container.ownerDocument),n.theme&&i.removeCssClass(n.container,n.theme.cssClass);var s="padding"in r?r.padding:"padding"in(n.theme||{})?4:n.$padding;n.$padding&&s!=n.$padding&&n.setPadding(s),n.$theme=r.cssClass,n.theme=r,i.addCssClass(n.container,r.cssClass),i.setCssClass(n.container,"ace_dark",r.isDark),n.$size&&(n.$size.width=0,n.$updateSizeAsync()),n._dispatchEvent("themeLoaded",{theme:r}),t&&t()}var n=this;this.$themeId=e,n._dispatchEvent("themeChange",{theme:e});if(!e||typeof e=="string"){var r=e||this.$options.theme.initialValue;s.loadModule(["theme",r],o)}else o(e)},this.getTheme=function(){return this.$themeId},this.setStyle=function(e,t){i.setCssClass(this.container,e,t!==!1)},this.unsetStyle=function(e){i.removeCssClass(this.container,e)},this.setCursorStyle=function(e){this.scroller.style.cursor!=e&&(this.scroller.style.cursor=e)},this.setMouseCursor=function(e){this.scroller.style.cursor=e},this.destroy=function(){this.$textLayer.destroy(),this.$cursorLayer.destroy()}}).call(g.prototype),s.defineOptions(g.prototype,"renderer",{animatedScroll:{initialValue:!1},showInvisibles:{set:function(e){this.$textLayer.setShowInvisibles(e)&&this.$loop.schedule(this.CHANGE_TEXT)},initialValue:!1},showPrintMargin:{set:function(){this.$updatePrintMargin()},initialValue:!0},printMarginColumn:{set:function(){this.$updatePrintMargin()},initialValue:80},printMargin:{set:function(e){typeof e=="number"&&(this.$printMarginColumn=e),this.$showPrintMargin=!!e,this.$updatePrintMargin()},get:function(){return this.$showPrintMargin&&this.$printMarginColumn}},showGutter:{set:function(e){this.$gutter.style.display=e?"block":"none",this.$loop.schedule(this.CHANGE_FULL),this.onGutterResize()},initialValue:!0},fadeFoldWidgets:{set:function(e){i.setCssClass(this.$gutter,"ace_fade-fold-widgets",e)},initialValue:!1},showFoldWidgets:{set:function(e){this.$gutterLayer.setShowFoldWidgets(e)},initialValue:!0},showLineNumbers:{set:function(e){this.$gutterLayer.setShowLineNumbers(e),this.$loop.schedule(this.CHANGE_GUTTER)},initialValue:!0},displayIndentGuides:{set:function(e){this.$textLayer.setDisplayIndentGuides(e)&&this.$loop.schedule(this.CHANGE_TEXT)},initialValue:!0},highlightGutterLine:{set:function(e){if(!this.$gutterLineHighlight){this.$gutterLineHighlight=i.createElement("div"),this.$gutterLineHighlight.className="ace_gutter-active-line",this.$gutter.appendChild(this.$gutterLineHighlight);return}this.$gutterLineHighlight.style.display=e?"":"none",this.$cursorLayer.$pixelPos&&this.$updateGutterLineHighlight()},initialValue:!1,value:!0},hScrollBarAlwaysVisible:{set:function(e){(!this.$hScrollBarAlwaysVisible||!this.$horizScroll)&&this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:!1},vScrollBarAlwaysVisible:{set:function(e){(!this.$vScrollBarAlwaysVisible||!this.$vScroll)&&this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:!1},fontSize:{set:function(e){typeof e=="number"&&(e+="px"),this.container.style.fontSize=e,this.updateFontSize()},initialValue:12},fontFamily:{set:function(e){this.container.style.fontFamily=e,this.updateFontSize()}},maxLines:{set:function(e){this.updateFull()}},minLines:{set:function(e){this.updateFull()}},maxPixelHeight:{set:function(e){this.updateFull()},initialValue:0},scrollPastEnd:{set:function(e){e=+e||0;if(this.$scrollPastEnd==e)return;this.$scrollPastEnd=e,this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:0,handlesSet:!0},fixedWidthGutter:{set:function(e){this.$gutterLayer.$fixedWidth=!!e,this.$loop.schedule(this.CHANGE_GUTTER)}},theme:{set:function(e){this.setTheme(e)},get:function(){return this.$themeId||this.theme},initialValue:"./theme/textmate",handlesSet:!0}}),t.VirtualRenderer=g}),define("ace/worker/worker_client",["require","exports","module","ace/lib/oop","ace/lib/net","ace/lib/event_emitter","ace/config"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/net"),s=e("../lib/event_emitter").EventEmitter,o=e("../config"),u=function(t,n,r,i){this.$sendDeltaQueue=this.$sendDeltaQueue.bind(this),this.changeListener=this.changeListener.bind(this),this.onMessage=this.onMessage.bind(this),e.nameToUrl&&!e.toUrl&&(e.toUrl=e.nameToUrl);if(o.get("packaged")||!e.toUrl)i=i||o.moduleUrl(n,"worker");else{var s=this.$normalizePath;i=i||s(e.toUrl("ace/worker/worker.js",null,"_"));var u={};t.forEach(function(t){u[t]=s(e.toUrl(t,null,"_").replace(/(\.js)?(\?.*)?$/,""))})}try{this.$worker=new Worker(i)}catch(a){if(!(a instanceof window.DOMException))throw a;var f=this.$workerBlob(i),l=window.URL||window.webkitURL,c=l.createObjectURL(f);this.$worker=new Worker(c),l.revokeObjectURL(c)}this.$worker.postMessage({init:!0,tlns:u,module:n,classname:r}),this.callbackId=1,this.callbacks={},this.$worker.onmessage=this.onMessage};(function(){r.implement(this,s),this.onMessage=function(e){var t=e.data;switch(t.type){case"event":this._signal(t.name,{data:t.data});break;case"call":var n=this.callbacks[t.id];n&&(n(t.data),delete this.callbacks[t.id]);break;case"error":this.reportError(t.data);break;case"log":window.console&&console.log&&console.log.apply(console,t.data)}},this.reportError=function(e){window.console&&console.error&&console.error(e)},this.$normalizePath=function(e){return i.qualifyURL(e)},this.terminate=function(){this._signal("terminate",{}),this.deltaQueue=null,this.$worker.terminate(),this.$worker=null,this.$doc&&this.$doc.off("change",this.changeListener),this.$doc=null},this.send=function(e,t){this.$worker.postMessage({command:e,args:t})},this.call=function(e,t,n){if(n){var r=this.callbackId++;this.callbacks[r]=n,t.push(r)}this.send(e,t)},this.emit=function(e,t){try{this.$worker.postMessage({event:e,data:{data:t.data}})}catch(n){console.error(n.stack)}},this.attachToDocument=function(e){this.$doc&&this.terminate(),this.$doc=e,this.call("setValue",[e.getValue()]),e.on("change",this.changeListener)},this.changeListener=function(e){this.deltaQueue||(this.deltaQueue=[],setTimeout(this.$sendDeltaQueue,0)),e.action=="insert"?this.deltaQueue.push(e.start,e.lines):this.deltaQueue.push(e.start,e.end)},this.$sendDeltaQueue=function(){var e=this.deltaQueue;if(!e)return;this.deltaQueue=null,e.length>50&&e.length>this.$doc.getLength()>>1?this.call("setValue",[this.$doc.getValue()]):this.emit("change",{data:e})},this.$workerBlob=function(e){var t="importScripts('"+i.qualifyURL(e)+"');";try{return new Blob([t],{type:"application/javascript"})}catch(n){var r=window.BlobBuilder||window.WebKitBlobBuilder||window.MozBlobBuilder,s=new r;return s.append(t),s.getBlob("application/javascript")}}}).call(u.prototype);var a=function(e,t,n){this.$sendDeltaQueue=this.$sendDeltaQueue.bind(this),this.changeListener=this.changeListener.bind(this),this.callbackId=1,this.callbacks={},this.messageBuffer=[];var r=null,i=!1,u=Object.create(s),a=this;this.$worker={},this.$worker.terminate=function(){},this.$worker.postMessage=function(e){a.messageBuffer.push(e),r&&(i?setTimeout(f):f())},this.setEmitSync=function(e){i=e};var f=function(){var e=a.messageBuffer.shift();e.command?r[e.command].apply(r,e.args):e.event&&u._signal(e.event,e.data)};u.postMessage=function(e){a.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.messageBuffer.length)f()})};a.prototype=u.prototype,t.UIWorkerClient=a,t.WorkerClient=u}),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.ranges[0]&&this.fromOrientedRange(this.ranges[0])},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.selectionLead),s=this.session.documentToScreenPosition(this.selectionAnchor),o=this.rectangularRangeBlock(r,s);o.forEach(this.addRange,this)}},this.rectangularRangeBlock=function(e,t,n){var r=[],s=e.column0)d--;if(d>0){var m=0;while(r[m].isEmpty())m++}for(var g=d;g>=m;g--)r[g].isEmpty()&&r.splice(g,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();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}})}),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)}),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;}';var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)}),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}),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; }","")}),define("ace/ace",["require","exports","module","ace/lib/fixoldbrowsers","ace/lib/dom","ace/lib/event","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("./editor").Editor,o=e("./edit_session").EditSession,u=e("./undomanager").UndoManager,a=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){if(typeof e=="string"){var n=e;e=document.getElementById(n);if(!e)throw new Error("ace.edit can't find div #"+n)}if(e&&e.env&&e.env.editor instanceof s)return e.env.editor;var o="";if(e&&/input|textarea/i.test(e.tagName)){var u=e;o=u.value,e=r.createElement("pre"),u.parentNode.replaceChild(e,u)}else e&&(o=r.getInnerText(e),e.innerHTML="");var f=t.createEditSession(o),l=new s(new a(e));l.setSession(f);var c={document:f,editor:l,onResize:l.resize.bind(l,null)};return u&&(c.textarea=u),i.addListener(window,"resize",c.onResize),l.on("destroy",function(){i.removeListener(window,"resize",c.onResize),c.editor.container.env=null}),l.container.env=l.env=c,l},t.createEditSession=function(e,t){var n=new o(e,t);return n.setUndoManager(new u),n},t.EditSession=o,t.UndoManager=u,t.version="1.2.6"}); + (function() { + window.require(["ace/ace"], function(a) { + if (a) { + a.config.init(true); + a.define = window.define; + } + if (!window.ace) + window.ace = a; + for (var key in a) if (a.hasOwnProperty(key)) + window.ace[key] = a[key]; + }); + })(); + \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/ext-elastic_tabstops_lite.js b/public/themes/pterodactyl/vendor/ace/ext-elastic_tabstops_lite.js new file mode 100644 index 0000000..756e0e1 --- /dev/null +++ b/public/themes/pterodactyl/vendor/ace/ext-elastic_tabstops_lite.js @@ -0,0 +1,5 @@ +define("ace/ext/elastic_tabstops_lite",["require","exports","module","ace/editor","ace/config"],function(e,t,n){"use strict";var r=function(e){this.$editor=e;var t=this,n=[],r=!1;this.onAfterExec=function(){r=!1,t.processRows(n),n=[]},this.onExec=function(){r=!0},this.onChange=function(e){r&&(n.indexOf(e.start.row)==-1&&n.push(e.start.row),e.end.row!=e.start.row&&n.push(e.end.row))}};(function(){this.processRows=function(e){this.$inChange=!0;var t=[];for(var n=0,r=e.length;n-1)continue;var s=this.$findCellWidthsForBlock(i),o=this.$setBlockCellWidthsToMax(s.cellWidths),u=s.firstRow;for(var a=0,f=o.length;a=0){n=this.$cellWidthsForRow(r);if(n.length==0)break;t.unshift(n),r--}var i=r+1;r=e;var s=this.$editor.session.getLength();while(r0&&(this.$editor.session.getDocument().insertInLine({row:e,column:f+1},Array(l+1).join(" ")+" "),this.$editor.session.getDocument().removeInLine(e,f,f+1),r+=l),l<0&&p>=-l&&(this.$editor.session.getDocument().removeInLine(e,f+l,f),r+=l)}},this.$izip_longest=function(e){if(!e[0])return[];var t=e[0].length,n=e.length;for(var r=1;rt&&(t=i)}var s=[];for(var o=0;o=t.length?t.length:e.length,r=[];for(var i=0;i\s+/g,">"),l=function(e,t,n){var i=r.createElement("div");i.innerHTML=f,this.element=i.firstChild,this.$init(),this.setEditor(e)};(function(){this.setEditor=function(e){e.searchBox=this,e.container.appendChild(this.element),this.editor=e},this.$initElements=function(e){this.searchBox=e.querySelector(".ace_search_form"),this.replaceBox=e.querySelector(".ace_replace_form"),this.searchOptions=e.querySelector(".ace_search_options"),this.regExpOption=e.querySelector("[action=toggleRegexpMode]"),this.caseSensitiveOption=e.querySelector("[action=toggleCaseSensitive]"),this.wholeWordOption=e.querySelector("[action=toggleWholeWords]"),this.searchInput=this.searchBox.querySelector(".ace_search_field"),this.replaceInput=this.replaceBox.querySelector(".ace_search_field")},this.$init=function(){var e=this.element;this.$initElements(e);var t=this;s.addListener(e,"mousedown",function(e){setTimeout(function(){t.activeInput.focus()},0),s.stopPropagation(e)}),s.addListener(e,"click",function(e){var n=e.target||e.srcElement,r=n.getAttribute("action");r&&t[r]?t[r]():t.$searchBarKb.commands[r]&&t.$searchBarKb.commands[r].exec(t),s.stopPropagation(e)}),s.addCommandKeyListener(e,function(e,n,r){var i=a.keyCodeToString(r),o=t.$searchBarKb.findKeyCommand(n,i);o&&o.exec&&(o.exec(t),s.stopEvent(e))}),this.$onChange=i.delayedCall(function(){t.find(!1,!1)}),s.addListener(this.searchInput,"input",function(){t.$onChange.schedule(20)}),s.addListener(this.searchInput,"focus",function(){t.activeInput=t.searchInput,t.searchInput.value&&t.highlight()}),s.addListener(this.replaceInput,"focus",function(){t.activeInput=t.replaceInput,t.searchInput.value&&t.highlight()})},this.$closeSearchBarKb=new u([{bindKey:"Esc",name:"closeSearchBar",exec:function(e){e.searchBox.hide()}}]),this.$searchBarKb=new u,this.$searchBarKb.bindKeys({"Ctrl-f|Command-f":function(e){var t=e.isReplace=!e.isReplace;e.replaceBox.style.display=t?"":"none",e.searchInput.focus()},"Ctrl-H|Command-Option-F":function(e){e.replaceBox.style.display="",e.replaceInput.focus()},"Ctrl-G|Command-G":function(e){e.findNext()},"Ctrl-Shift-G|Command-Shift-G":function(e){e.findPrev()},esc:function(e){setTimeout(function(){e.hide()})},Return:function(e){e.activeInput==e.replaceInput&&e.replace(),e.findNext()},"Shift-Return":function(e){e.activeInput==e.replaceInput&&e.replace(),e.findPrev()},"Alt-Return":function(e){e.activeInput==e.replaceInput&&e.replaceAll(),e.findAll()},Tab:function(e){(e.activeInput==e.replaceInput?e.searchInput:e.replaceInput).focus()}}),this.$searchBarKb.addCommands([{name:"toggleRegexpMode",bindKey:{win:"Alt-R|Alt-/",mac:"Ctrl-Alt-R|Ctrl-Alt-/"},exec:function(e){e.regExpOption.checked=!e.regExpOption.checked,e.$syncOptions()}},{name:"toggleCaseSensitive",bindKey:{win:"Alt-C|Alt-I",mac:"Ctrl-Alt-R|Ctrl-Alt-I"},exec:function(e){e.caseSensitiveOption.checked=!e.caseSensitiveOption.checked,e.$syncOptions()}},{name:"toggleWholeWords",bindKey:{win:"Alt-B|Alt-W",mac:"Ctrl-Alt-B|Ctrl-Alt-W"},exec:function(e){e.wholeWordOption.checked=!e.wholeWordOption.checked,e.$syncOptions()}}]),this.$syncOptions=function(){r.setCssClass(this.regExpOption,"checked",this.regExpOption.checked),r.setCssClass(this.wholeWordOption,"checked",this.wholeWordOption.checked),r.setCssClass(this.caseSensitiveOption,"checked",this.caseSensitiveOption.checked),this.find(!1,!1)},this.highlight=function(e){this.editor.session.highlight(e||this.editor.$search.$options.re),this.editor.renderer.updateBackMarkers()},this.find=function(e,t,n){var i=this.editor.find(this.searchInput.value,{skipCurrent:e,backwards:t,wrap:!0,regExp:this.regExpOption.checked,caseSensitive:this.caseSensitiveOption.checked,wholeWord:this.wholeWordOption.checked,preventScroll:n}),s=!i&&this.searchInput.value;r.setCssClass(this.searchBox,"ace_nomatch",s),this.editor._emit("findSearchBox",{match:!s}),this.highlight()},this.findNext=function(){this.find(!0,!1)},this.findPrev=function(){this.find(!0,!0)},this.findAll=function(){var e=this.editor.findAll(this.searchInput.value,{regExp:this.regExpOption.checked,caseSensitive:this.caseSensitiveOption.checked,wholeWord:this.wholeWordOption.checked}),t=!e&&this.searchInput.value;r.setCssClass(this.searchBox,"ace_nomatch",t),this.editor._emit("findSearchBox",{match:!t}),this.highlight(),this.hide()},this.replace=function(){this.editor.getReadOnly()||this.editor.replace(this.replaceInput.value)},this.replaceAndFindNext=function(){this.editor.getReadOnly()||(this.editor.replace(this.replaceInput.value),this.findNext())},this.replaceAll=function(){this.editor.getReadOnly()||this.editor.replaceAll(this.replaceInput.value)},this.hide=function(){this.element.style.display="none",this.editor.keyBinding.removeKeyboardHandler(this.$closeSearchBarKb),this.editor.focus()},this.show=function(e,t){this.element.style.display="",this.replaceBox.style.display=t?"":"none",this.isReplace=t,e&&(this.searchInput.value=e),this.find(!1,!1,!0),this.searchInput.focus(),this.searchInput.select(),this.editor.keyBinding.addKeyboardHandler(this.$closeSearchBarKb)},this.isFocused=function(){var e=document.activeElement;return e==this.searchInput||e==this.replaceInput}}).call(l.prototype),t.SearchBox=l,t.Search=function(e,t){var n=e.searchBox||new l(e);n.show(e.session.getTextRange(),t)}}),define("ace/ext/old_ie",["require","exports","module","ace/lib/useragent","ace/tokenizer","ace/ext/searchbox","ace/mode/text"],function(require,exports,module){"use strict";function patch(obj,name,regexp,replacement){eval("obj['"+name+"']="+obj[name].toString().replace(regexp,replacement))}var MAX_TOKEN_COUNT=1e3,useragent=require("../lib/useragent"),TokenizerModule=require("../tokenizer");useragent.isIE&&useragent.isIE<10&&window.top.document.compatMode==="BackCompat"&&(useragent.isOldIE=!0);if(typeof document!="undefined"&&!document.documentElement.querySelector){useragent.isOldIE=!0;var qs=function(e,t){if(t.charAt(0)==".")var n=t.slice(1);else var r=t.match(/(\w+)=(\w+)/),i=r&&r[1],s=r&&r[2];for(var o=0;o\s+/g,">"),l=function(e,t,n){var i=r.createElement("div");i.innerHTML=f,this.element=i.firstChild,this.$init(),this.setEditor(e)};(function(){this.setEditor=function(e){e.searchBox=this,e.container.appendChild(this.element),this.editor=e},this.$initElements=function(e){this.searchBox=e.querySelector(".ace_search_form"),this.replaceBox=e.querySelector(".ace_replace_form"),this.searchOptions=e.querySelector(".ace_search_options"),this.regExpOption=e.querySelector("[action=toggleRegexpMode]"),this.caseSensitiveOption=e.querySelector("[action=toggleCaseSensitive]"),this.wholeWordOption=e.querySelector("[action=toggleWholeWords]"),this.searchInput=this.searchBox.querySelector(".ace_search_field"),this.replaceInput=this.replaceBox.querySelector(".ace_search_field")},this.$init=function(){var e=this.element;this.$initElements(e);var t=this;s.addListener(e,"mousedown",function(e){setTimeout(function(){t.activeInput.focus()},0),s.stopPropagation(e)}),s.addListener(e,"click",function(e){var n=e.target||e.srcElement,r=n.getAttribute("action");r&&t[r]?t[r]():t.$searchBarKb.commands[r]&&t.$searchBarKb.commands[r].exec(t),s.stopPropagation(e)}),s.addCommandKeyListener(e,function(e,n,r){var i=a.keyCodeToString(r),o=t.$searchBarKb.findKeyCommand(n,i);o&&o.exec&&(o.exec(t),s.stopEvent(e))}),this.$onChange=i.delayedCall(function(){t.find(!1,!1)}),s.addListener(this.searchInput,"input",function(){t.$onChange.schedule(20)}),s.addListener(this.searchInput,"focus",function(){t.activeInput=t.searchInput,t.searchInput.value&&t.highlight()}),s.addListener(this.replaceInput,"focus",function(){t.activeInput=t.replaceInput,t.searchInput.value&&t.highlight()})},this.$closeSearchBarKb=new u([{bindKey:"Esc",name:"closeSearchBar",exec:function(e){e.searchBox.hide()}}]),this.$searchBarKb=new u,this.$searchBarKb.bindKeys({"Ctrl-f|Command-f":function(e){var t=e.isReplace=!e.isReplace;e.replaceBox.style.display=t?"":"none",e.searchInput.focus()},"Ctrl-H|Command-Option-F":function(e){e.replaceBox.style.display="",e.replaceInput.focus()},"Ctrl-G|Command-G":function(e){e.findNext()},"Ctrl-Shift-G|Command-Shift-G":function(e){e.findPrev()},esc:function(e){setTimeout(function(){e.hide()})},Return:function(e){e.activeInput==e.replaceInput&&e.replace(),e.findNext()},"Shift-Return":function(e){e.activeInput==e.replaceInput&&e.replace(),e.findPrev()},"Alt-Return":function(e){e.activeInput==e.replaceInput&&e.replaceAll(),e.findAll()},Tab:function(e){(e.activeInput==e.replaceInput?e.searchInput:e.replaceInput).focus()}}),this.$searchBarKb.addCommands([{name:"toggleRegexpMode",bindKey:{win:"Alt-R|Alt-/",mac:"Ctrl-Alt-R|Ctrl-Alt-/"},exec:function(e){e.regExpOption.checked=!e.regExpOption.checked,e.$syncOptions()}},{name:"toggleCaseSensitive",bindKey:{win:"Alt-C|Alt-I",mac:"Ctrl-Alt-R|Ctrl-Alt-I"},exec:function(e){e.caseSensitiveOption.checked=!e.caseSensitiveOption.checked,e.$syncOptions()}},{name:"toggleWholeWords",bindKey:{win:"Alt-B|Alt-W",mac:"Ctrl-Alt-B|Ctrl-Alt-W"},exec:function(e){e.wholeWordOption.checked=!e.wholeWordOption.checked,e.$syncOptions()}}]),this.$syncOptions=function(){r.setCssClass(this.regExpOption,"checked",this.regExpOption.checked),r.setCssClass(this.wholeWordOption,"checked",this.wholeWordOption.checked),r.setCssClass(this.caseSensitiveOption,"checked",this.caseSensitiveOption.checked),this.find(!1,!1)},this.highlight=function(e){this.editor.session.highlight(e||this.editor.$search.$options.re),this.editor.renderer.updateBackMarkers()},this.find=function(e,t,n){var i=this.editor.find(this.searchInput.value,{skipCurrent:e,backwards:t,wrap:!0,regExp:this.regExpOption.checked,caseSensitive:this.caseSensitiveOption.checked,wholeWord:this.wholeWordOption.checked,preventScroll:n}),s=!i&&this.searchInput.value;r.setCssClass(this.searchBox,"ace_nomatch",s),this.editor._emit("findSearchBox",{match:!s}),this.highlight()},this.findNext=function(){this.find(!0,!1)},this.findPrev=function(){this.find(!0,!0)},this.findAll=function(){var e=this.editor.findAll(this.searchInput.value,{regExp:this.regExpOption.checked,caseSensitive:this.caseSensitiveOption.checked,wholeWord:this.wholeWordOption.checked}),t=!e&&this.searchInput.value;r.setCssClass(this.searchBox,"ace_nomatch",t),this.editor._emit("findSearchBox",{match:!t}),this.highlight(),this.hide()},this.replace=function(){this.editor.getReadOnly()||this.editor.replace(this.replaceInput.value)},this.replaceAndFindNext=function(){this.editor.getReadOnly()||(this.editor.replace(this.replaceInput.value),this.findNext())},this.replaceAll=function(){this.editor.getReadOnly()||this.editor.replaceAll(this.replaceInput.value)},this.hide=function(){this.element.style.display="none",this.editor.keyBinding.removeKeyboardHandler(this.$closeSearchBarKb),this.editor.focus()},this.show=function(e,t){this.element.style.display="",this.replaceBox.style.display=t?"":"none",this.isReplace=t,e&&(this.searchInput.value=e),this.find(!1,!1,!0),this.searchInput.focus(),this.searchInput.select(),this.editor.keyBinding.addKeyboardHandler(this.$closeSearchBarKb)},this.isFocused=function(){var e=document.activeElement;return e==this.searchInput||e==this.replaceInput}}).call(l.prototype),t.SearchBox=l,t.Search=function(e,t){var n=e.searchBox||new l(e);n.show(e.session.getTextRange(),t)}}); + (function() { + window.require(["ace/ext/searchbox"], function() {}); + })(); + \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/ext-settings_menu.js b/public/themes/pterodactyl/vendor/ace/ext-settings_menu.js new file mode 100644 index 0000000..62a28c8 --- /dev/null +++ b/public/themes/pterodactyl/vendor/ace/ext-settings_menu.js @@ -0,0 +1,5 @@ +define("ace/ext/menu_tools/element_generator",["require","exports","module"],function(e,t,n){"use strict";n.exports.createOption=function(t){var n,r=document.createElement("option");for(n in t)t.hasOwnProperty(n)&&(n==="selected"?r.setAttribute(n,t[n]):r[n]=t[n]);return r},n.exports.createCheckbox=function(t,n,r){var i=document.createElement("input");return i.setAttribute("type","checkbox"),i.setAttribute("id",t),i.setAttribute("name",t),i.setAttribute("value",n),i.setAttribute("class",r),n&&i.setAttribute("checked","checked"),i},n.exports.createInput=function(t,n,r){var i=document.createElement("input");return i.setAttribute("type","text"),i.setAttribute("id",t),i.setAttribute("name",t),i.setAttribute("value",n),i.setAttribute("class",r),i},n.exports.createLabel=function(t,n){var r=document.createElement("label");return r.setAttribute("for",n),r.textContent=t,r},n.exports.createSelection=function(t,r,i){var s=document.createElement("select");return s.setAttribute("id",t),s.setAttribute("name",t),s.setAttribute("class",i),r.forEach(function(e){s.appendChild(n.exports.createOption(e))}),s}}),define("ace/ext/modelist",["require","exports","module"],function(e,t,n){"use strict";function i(e){var t=a.text,n=e.split(/[\/\\]/).pop();for(var i=0;i 0!";if(e==this.$splits)return;if(e>this.$splits){while(this.$splitse)t=this.$editors[this.$splits-1],this.$container.removeChild(t.container),this.$splits--;this.resize()},this.getSplits=function(){return this.$splits},this.getEditor=function(e){return this.$editors[e]},this.getCurrentEditor=function(){return this.$cEditor},this.focus=function(){this.$cEditor.focus()},this.blur=function(){this.$cEditor.blur()},this.setTheme=function(e){this.$editors.forEach(function(t){t.setTheme(e)})},this.setKeyboardHandler=function(e){this.$editors.forEach(function(t){t.setKeyboardHandler(e)})},this.forEach=function(e,t){this.$editors.forEach(e,t)},this.$fontSize="",this.setFontSize=function(e){this.$fontSize=e,this.forEach(function(t){t.setFontSize(e)})},this.$cloneSession=function(e){var t=new a(e.getDocument(),e.getMode()),n=e.getUndoManager();if(n){var r=new l(n,t);t.setUndoManager(r)}return t.$informUndoManager=i.delayedCall(function(){t.$deltas=[]}),t.setTabSize(e.getTabSize()),t.setUseSoftTabs(e.getUseSoftTabs()),t.setOverwrite(e.getOverwrite()),t.setBreakpoints(e.getBreakpoints()),t.setUseWrapMode(e.getUseWrapMode()),t.setUseWorker(e.getUseWorker()),t.setWrapLimitRange(e.$wrapLimitRange.min,e.$wrapLimitRange.max),t.$foldData=e.$cloneFoldData(),t},this.setSession=function(e,t){var n;t==null?n=this.$cEditor:n=this.$editors[t];var r=this.$editors.some(function(t){return t.session===e});return r&&(e=this.$cloneSession(e)),n.setSession(e),e},this.getOrientation=function(){return this.$orientation},this.setOrientation=function(e){if(this.$orientation==e)return;this.$orientation=e,this.resize()},this.resize=function(){var e=this.$container.clientWidth,t=this.$container.clientHeight,n;if(this.$orientation==this.BESIDE){var r=e/this.$splits;for(var i=0;i"),o||l.push(""),f.$renderLine(l,h,!0,!1),l.push("\n");var p="
"+"
"+l.join("")+"
"+"
";return f.destroy(),{css:s+n.cssText,html:p,session:u}},n.exports=f,n.exports.highlight=f}); + (function() { + window.require(["ace/ext/static_highlight"], function() {}); + })(); + \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/ext-textarea.js b/public/themes/pterodactyl/vendor/ace/ext-textarea.js new file mode 100644 index 0000000..56b6eac --- /dev/null +++ b/public/themes/pterodactyl/vendor/ace/ext-textarea.js @@ -0,0 +1,5 @@ +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;}';var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)}),define("ace/ext/textarea",["require","exports","module","ace/lib/event","ace/lib/useragent","ace/lib/net","ace/ace","ace/theme/textmate"],function(e,t,n){"use strict";function a(e,t){for(var n in t)e.style[n]=t[n]}function f(e,t){if(e.type!="textarea")throw new Error("Textarea required!");var n=e.parentNode,i=document.createElement("div"),s=function(){var t="position:relative;";["margin-top","margin-left","margin-right","margin-bottom"].forEach(function(n){t+=n+":"+u(e,i,n)+";"});var n=u(e,i,"width")||e.clientWidth+"px",r=u(e,i,"height")||e.clientHeight+"px";t+="height:"+r+";width:"+n+";",t+="display:inline-block;",i.setAttribute("style",t)};r.addListener(window,"resize",s),s(),n.insertBefore(i,e.nextSibling);while(n!==document){if(n.tagName.toUpperCase()==="FORM"){var o=n.onsubmit;n.onsubmit=function(n){e.value=t(),o&&o.call(this,n)};break}n=n.parentNode}return i}function l(t,n,r){s.loadScript(t,function(){e([n],r)})}function c(e,t,n,r,i,s){function a(e){return e==="true"||e==1}var o=e.getSession(),u=e.renderer;return s=s||l,e.setDisplaySettings=function(t){t==null&&(t=n.style.display=="none"),t?(n.style.display="block",n.hideButton.focus(),e.on("focus",function r(){e.removeListener("focus",r),n.style.display="none"})):e.focus()},e.$setOption=e.setOption,e.$getOption=e.getOption,e.setOption=function(t,n){switch(t){case"mode":e.$setOption("mode","ace/mode/"+n);break;case"theme":e.$setOption("theme","ace/theme/"+n);break;case"keybindings":switch(n){case"vim":e.setKeyboardHandler("ace/keyboard/vim");break;case"emacs":e.setKeyboardHandler("ace/keyboard/emacs");break;default:e.setKeyboardHandler(null)}break;case"softWrap":case"fontSize":e.$setOption(t,n);break;default:e.$setOption(t,a(n))}},e.getOption=function(t){switch(t){case"mode":return e.$getOption("mode").substr("ace/mode/".length);case"theme":return e.$getOption("theme").substr("ace/theme/".length);case"keybindings":var n=e.getKeyboardHandler();switch(n&&n.$id){case"ace/keyboard/vim":return"vim";case"ace/keyboard/emacs":return"emacs";default:return"ace"}break;default:return e.$getOption(t)}},e.setOptions(i),e}function h(e,n,i){function f(e,t,n,r){if(!n){e.push("");return}e.push("")}var s=null,o={mode:"Mode:",wrap:"Soft Wrap:",theme:"Theme:",fontSize:"Font Size:",showGutter:"Display Gutter:",keybindings:"Keyboard",showPrintMargin:"Show Print Margin:",useSoftTabs:"Use Soft Tabs:",showInvisibles:"Show Invisibles"},u={mode:{text:"Plain",javascript:"JavaScript",xml:"XML",html:"HTML",css:"CSS",scss:"SCSS",python:"Python",php:"PHP",java:"Java",ruby:"Ruby",c_cpp:"C/C++",coffee:"CoffeeScript",json:"json",perl:"Perl",clojure:"Clojure",ocaml:"OCaml",csharp:"C#",haxe:"haXe",svg:"SVG",textile:"Textile",groovy:"Groovy",liquid:"Liquid",Scala:"Scala"},theme:{clouds:"Clouds",clouds_midnight:"Clouds Midnight",cobalt:"Cobalt",crimson_editor:"Crimson Editor",dawn:"Dawn",eclipse:"Eclipse",idle_fingers:"Idle Fingers",kr_theme:"Kr Theme",merbivore:"Merbivore",merbivore_soft:"Merbivore Soft",mono_industrial:"Mono Industrial",monokai:"Monokai",pastel_on_dark:"Pastel On Dark",solarized_dark:"Solarized Dark",solarized_light:"Solarized Light",textmate:"Textmate",twilight:"Twilight",vibrant_ink:"Vibrant Ink"},showGutter:s,fontSize:{"10px":"10px","11px":"11px","12px":"12px","14px":"14px","16px":"16px"},wrap:{off:"Off",40:"40",80:"80",free:"Free"},keybindings:{ace:"ace",vim:"vim",emacs:"emacs"},showPrintMargin:s,useSoftTabs:s,showInvisibles:s},a=[];a.push("");for(var l in t.defaultOptions)a.push(""),a.push("");a.push("
SettingValue
",o[l],""),f(a,l,u[l],i.getOption(l)),a.push("
"),e.innerHTML=a.join("");var c=function(e){var t=e.currentTarget;i.setOption(t.title,t.value)},h=function(e){var t=e.currentTarget;i.setOption(t.title,t.checked)},p=e.getElementsByTagName("select");for(var d=0;d0&&!(s%l)&&!(f%l)&&(r[l]=(r[l]||0)+1),n[f]=(n[f]||0)+1}s=f}while(up.score&&(p={score:v,length:u})}if(p.score&&p.score>1.4)var m=p.length;if(i>d+1){if(m==1||di+1)return{ch:" ",length:m}},t.detectIndentation=function(e){var n=e.getLines(0,1e3),r=t.$detectIndentation(n)||{};return r.ch&&e.setUseSoftTabs(r.ch==" "),r.length&&e.setTabSize(r.length),r},t.trimTrailingSpace=function(e,t){var n=e.getDocument(),r=n.getAllLines(),i=t?-1:0;for(var s=0,o=r.length;si&&n.removeInLine(s,a,u.length)}},t.convertIndentation=function(e,t,n){var i=e.getTabString()[0],s=e.getTabSize();n||(n=s),t||(t=i);var o=t==" "?t:r.stringRepeat(t,n),u=e.doc,a=u.getAllLines(),f={},l={};for(var c=0,h=a.length;c30&&this.$data.shift()},append:function(e){var t=this.$data.length-1,n=this.$data[t]||"";e&&(n+=e),n&&(this.$data[t]=n)},get:function(e){return e=e||1,this.$data.slice(this.$data.length-e,this.$data.length).reverse().join("\n")},pop:function(){return this.$data.length>1&&this.$data.pop(),this.get()},rotate:function(){return this.$data.unshift(this.$data.pop()),this.get()}}}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/keybinding-vim.js b/public/themes/pterodactyl/vendor/ace/keybinding-vim.js new file mode 100644 index 0000000..9900e9f --- /dev/null +++ b/public/themes/pterodactyl/vendor/ace/keybinding-vim.js @@ -0,0 +1 @@ +define("ace/keyboard/vim",["require","exports","module","ace/range","ace/lib/event_emitter","ace/lib/dom","ace/lib/oop","ace/lib/keys","ace/lib/event","ace/search","ace/lib/useragent","ace/search_highlight","ace/commands/multi_select_commands","ace/mode/text","ace/multi_select"],function(e,t,n){"use strict";function r(){function t(e){return typeof e!="object"?e+"":"line"in e?e.line+":"+e.ch:"anchor"in e?t(e.anchor)+"->"+t(e.head):Array.isArray(e)?"["+e.map(function(e){return t(e)})+"]":JSON.stringify(e)}var e="";for(var n=0;n"):!1}function M(e){var t=e.state.vim;return t.onPasteFn||(t.onPasteFn=function(){t.insertMode||(e.setCursor(St(e.getCursor(),0,1)),yt.enterInsertMode(e,{},t))}),t.onPasteFn}function H(e,t){var n=[];for(var r=e;r=e.firstLine()&&t<=e.lastLine()}function U(e){return/^[a-z]$/.test(e)}function z(e){return"()[]{}".indexOf(e)!=-1}function W(e){return _.test(e)}function X(e){return/^[A-Z]$/.test(e)}function V(e){return/^\s*$/.test(e)}function $(e,t){for(var n=0;n"){var n=t.length-11,r=e.slice(0,n),i=t.slice(0,n);return r==i&&e.length>n?"full":i.indexOf(r)==0?"partial":!1}return e==t?"full":t.indexOf(e)==0?"partial":!1}function Ct(e){var t=/^.*(<[\w\-]+>)$/.exec(e),n=t?t[1]:e.slice(-1);if(n.length>1)switch(n){case"":n="\n";break;case"":n=" ";break;default:}return n}function kt(e,t,n){return function(){for(var r=0;r2&&(t=Mt.apply(undefined,Array.prototype.slice.call(arguments,1))),Ot(e,t)?e:t}function _t(e,t){return arguments.length>2&&(t=_t.apply(undefined,Array.prototype.slice.call(arguments,1))),Ot(e,t)?t:e}function Dt(e,t,n){var r=Ot(e,t),i=Ot(t,n);return r&&i}function Pt(e,t){return e.getLine(t).length}function Ht(e){return e.trim?e.trim():e.replace(/^\s+|\s+$/g,"")}function Bt(e){return e.replace(/([.?*+$\[\]\/\\(){}|\-])/g,"\\$1")}function jt(e,t,n){var r=Pt(e,t),i=(new Array(n-r+1)).join(" ");e.setCursor(E(t,r)),e.replaceRange(i,e.getCursor())}function Ft(e,t){var n=[],r=e.listSelections(),i=Lt(e.clipPos(t)),s=!At(t,i),o=e.getCursor("head"),u=qt(r,o),a=At(r[u].head,r[u].anchor),f=r.length-1,l=f-u>u?f:0,c=r[l].anchor,h=Math.min(c.line,i.line),p=Math.max(c.line,i.line),d=c.ch,v=i.ch,m=r[l].head.ch-d,g=v-d;m>0&&g<=0?(d++,s||v--):m<0&&g>=0?(d--,a||v++):m<0&&g==-1&&(d--,v++);for(var y=h;y<=p;y++){var b={anchor:new E(y,d),head:new E(y,v)};n.push(b)}return u=i.line==p?n.length-1:0,e.setSelections(n),t.ch=v,c.ch=d,c}function It(e,t,n){var r=[];for(var i=0;ia&&(i.line=a),i.ch=Pt(e,i.line)}else i.ch=0,s.ch=Pt(e,s.line);return{ranges:[{anchor:s,head:i}],primary:0}}if(n=="block"){var f=Math.min(s.line,i.line),l=Math.min(s.ch,i.ch),c=Math.max(s.line,i.line),h=Math.max(s.ch,i.ch)+1,p=c-f+1,d=i.line==f?0:p-1,v=[];for(var m=0;m0&&s&&V(s);s=i.pop())n.line--,n.ch=0;s?(n.line--,n.ch=Pt(e,n.line)):n.ch=0}}function Kt(e,t,n){t.ch=0,n.ch=0,n.line++}function Qt(e){if(!e)return 0;var t=e.search(/\S/);return t==-1?e.length:t}function Gt(e,t,n,r,i){var s=Vt(e),o=e.getLine(s.line),u=s.ch,a=i?D[0]:P[0];while(!a(o.charAt(u))){u++;if(u>=o.length)return null}r?a=P[0]:(a=D[0],a(o.charAt(u))||(a=D[1]));var f=u,l=u;while(a(o.charAt(f))&&f=0)l--;l++;if(t){var c=f;while(/\s/.test(o.charAt(f))&&f0)l--;l||(l=h)}}return{start:E(s.line,l),end:E(s.line,f)}}function Yt(e,t,n){At(t,n)||nt.jumpList.add(e,t,n)}function Zt(e,t){nt.lastChararacterSearch.increment=e,nt.lastChararacterSearch.forward=t.forward,nt.lastChararacterSearch.selectedCharacter=t.selectedCharacter}function nn(e,t,n,r){var i=Lt(e.getCursor()),s=n?1:-1,o=n?e.lineCount():-1,u=i.ch,a=i.line,f=e.getLine(a),l={lineText:f,nextCh:f.charAt(u),lastCh:null,index:u,symb:r,reverseSymb:(n?{")":"(","}":"{"}:{"(":")","{":"}"})[r],forward:n,depth:0,curMoveThrough:!1},c=en[r];if(!c)return i;var h=tn[c].init,p=tn[c].isComplete;h&&h(l);while(a!==o&&t){l.index+=s,l.nextCh=l.lineText.charAt(l.index);if(!l.nextCh){a+=s,l.lineText=e.getLine(a)||"";if(s>0)l.index=0;else{var d=l.lineText.length;l.index=d>0?d-1:0}l.nextCh=l.lineText.charAt(l.index)}p(l)&&(i.line=a,i.ch=l.index,t--)}return l.nextCh||l.curMoveThrough?E(a,l.index):i}function rn(e,t,n,r,i){var s=t.line,o=t.ch,u=e.getLine(s),a=n?1:-1,f=r?P:D;if(i&&u==""){s+=a,u=e.getLine(s);if(!R(e,s))return null;o=n?0:u.length}for(;;){if(i&&u=="")return{from:0,to:0,line:s};var l=a>0?u.length:-1,c=l,h=l;while(o!=l){var p=!1;for(var d=0;d0?0:u.length}throw new Error("The impossible happened.")}function sn(e,t,n,r,i,s){var o=Lt(t),u=[];(r&&!i||!r&&i)&&n++;var a=!r||!i;for(var f=0;f0?1:-1;var n=e.ace.session.getFoldLine(t);n&&t+r>n.start.row&&t+r0?n.end.row:n.start.row)-t)}var s=t.line,o=e.firstLine(),u=e.lastLine(),a,f,l=s;if(r){while(o<=l&&l<=u&&n>0)p(l),h(l,r)&&n--,l+=r;return new E(l,0)}var d=e.state.vim;if(d.visualLine&&h(s,1,!0)){var v=d.sel.anchor;h(v.line,-1,!0)&&(!i||v.line!=s)&&(s+=1)}var m=c(s);for(l=s;l<=u&&n;l++)h(l,1,!0)&&(!i||c(l)!=m)&&n--;f=new E(l,0),l>u&&!m?m=!0:i=!1;for(l=s;l>o;l--)if(!i||c(l)==m||l==s)if(h(l,-1,!0))break;return a=new E(l,0),{start:a,end:f}}function cn(e,t,n,r){var i=t,s,o,u={"(":/[()]/,")":/[()]/,"[":/[[\]]/,"]":/[[\]]/,"{":/[{}]/,"}":/[{}]/}[n],a={"(":"(",")":"(","[":"[","]":"[","{":"{","}":"{"}[n],f=e.getLine(i.line).charAt(i.ch),l=f===a?1:0;s=e.scanForBracket(E(i.line,i.ch+l),-1,null,{bracketRegex:u}),o=e.scanForBracket(E(i.line,i.ch+l),1,null,{bracketRegex:u});if(!s||!o)return{start:i,end:i};s=s.pos,o=o.pos;if(s.line==o.line&&s.ch>o.ch||s.line>o.line){var c=s;s=o,o=c}return r?o.ch+=1:s.ch+=1,{start:s,end:o}}function hn(e,t,n,r){var i=Lt(t),s=e.getLine(i.line),o=s.split(""),u,a,f,l,c=o.indexOf(n);i.ch-1&&!u;f--)o[f]==n&&(u=f+1);if(u&&!a)for(f=u,l=o.length;f'+t+"",{bottom:!0,duration:5e3}):alert(t)}function Nn(e,t){var n="";return e&&(n+=''+e+""),n+=' ',t&&(n+='',n+=t,n+=""),n}function kn(e,t){var n=(t.prefix||"")+" "+(t.desc||""),r=Nn(t.prefix,t.desc);vn(e,r,n,t.onClose,t)}function Ln(e,t){if(e instanceof RegExp&&t instanceof RegExp){var n=["global","multiline","ignoreCase","source"];for(var r=0;r=t&&e<=n:e==t}function Hn(e){var t=e.ace.renderer;return{top:t.getFirstFullyVisibleRow(),bottom:t.getLastFullyVisibleRow()}}function In(e,t,n,r,i,s,o,u,a){function c(){e.operation(function(){while(!f)h(),p();d()})}function h(){var t=e.getRange(s.from(),s.to()),n=t.replace(o,u);s.replace(n)}function p(){while(s.findNext()&&Pn(s.from(),r,i)){if(!n&&l&&s.from().line==l.line)continue;e.scrollIntoView(s.from(),30),e.setSelection(s.from(),s.to()),l=s.from(),f=!1;return}f=!0}function d(t){t&&t(),e.focus();if(l){e.setCursor(l);var n=e.state.vim;n.exMode=!1,n.lastHPos=n.lastHSPos=l.ch}a&&a()}function m(t,n,r){v.e_stop(t);var i=v.keyName(t);switch(i){case"Y":h(),p();break;case"N":p();break;case"A":var s=a;a=undefined,e.operation(c),a=s;break;case"L":h();case"Q":case"Esc":case"Ctrl-C":case"Ctrl-[":d(r)}return f&&d(r),!0}e.state.vim.exMode=!0;var f=!1,l=s.from();p();if(f){Tn(e,"No matches for "+o.source);return}if(!t){c(),a&&a();return}kn(e,{prefix:"replace with "+u+" (y/n/a/q/l)",onKeyDown:m})}function qn(e){var t=e.state.vim,n=nt.macroModeState,r=nt.registerController.getRegister("."),i=n.isPlaying,s=n.lastInsertModeChanges,o=[];if(!i){var u=s.inVisualBlock?t.lastSelection.visualBlock.height:1,a=s.changes,o=[],f=0;while(f1&&(Zn(e,t,t.insertModeRepeat-1,!0),t.lastEditInputState.repeatOverride=t.insertModeRepeat),delete t.insertModeRepeat,t.insertMode=!1,e.setCursor(e.getCursor().line,e.getCursor().ch-1),e.setOption("keyMap","vim"),e.setOption("disableInput",!0),e.toggleOverwrite(!1),r.setText(s.changes.join("")),v.signal(e,"vim-mode-change",{mode:"normal"}),n.isRecording&&Xn(n)}function Rn(e){b.unshift(e)}function Un(e,t,n,r,i){var s={keys:e,type:t};s[t]=n,s[t+"Args"]=r;for(var o in i)s[o]=i[o];Rn(s)}function zn(e,t,n,r){var i=nt.registerController.getRegister(r);if(r==":"){i.keyBuffer[0]&&Fn.processCommand(e,i.keyBuffer[0]),n.isPlaying=!1;return}var s=i.keyBuffer,o=0;n.isPlaying=!0,n.replaySearchQueries=i.searchQueries.slice(0);for(var u=0;u|<\w+>|./.exec(a),l=f[0],a=a.substring(f.index+l.length),v.Vim.handleKey(e,l,"macro");if(t.insertMode){var c=i.insertModeChanges[o++].changes;nt.macroModeState.lastInsertModeChanges.changes=c,er(e,c,1),qn(e)}}}n.isPlaying=!1}function Wn(e,t){if(e.isPlaying)return;var n=e.latestRegister,r=nt.registerController.getRegister(n);r&&r.pushText(t)}function Xn(e){if(e.isPlaying)return;var t=e.latestRegister,n=nt.registerController.getRegister(t);n&&n.pushInsertModeChanges&&n.pushInsertModeChanges(e.lastInsertModeChanges)}function Vn(e,t){if(e.isPlaying)return;var n=e.latestRegister,r=nt.registerController.getRegister(n);r&&r.pushSearchQuery&&r.pushSearchQuery(t)}function $n(e,t){var n=nt.macroModeState,r=n.lastInsertModeChanges;if(!n.isPlaying)while(t){r.expectCursorActivityForChange=!0;if(t.origin=="+input"||t.origin=="paste"||t.origin===undefined){var i=t.text.join("\n");r.maybeReset&&(r.changes=[],r.maybeReset=!1),r.changes.push(i)}t=t.next}}function Jn(e){var t=e.state.vim;if(t.insertMode){var n=nt.macroModeState;if(n.isPlaying)return;var r=n.lastInsertModeChanges;r.expectCursorActivityForChange?r.expectCursorActivityForChange=!1:r.maybeReset=!0}else e.curOp.isVimOp||Qn(e,t);t.visualMode&&Kn(e)}function Kn(e){var t=e.state.vim,n=wt(e,Lt(t.sel.head)),r=St(n,0,1);t.fakeCursor&&t.fakeCursor.clear(),t.fakeCursor=e.markText(n,r,{className:"cm-animate-fat-cursor"})}function Qn(e,t){var n=e.getCursor("anchor"),r=e.getCursor("head");t.visualMode&&!e.somethingSelected()?$t(e,!1):!t.visualMode&&!t.insertMode&&e.somethingSelected()&&(t.visualMode=!0,t.visualLine=!1,v.signal(e,"vim-mode-change",{mode:"visual"}));if(t.visualMode){var i=Ot(r,n)?0:-1,s=Ot(r,n)?-1:0;r=St(r,0,i),n=St(n,0,s),t.sel={anchor:n,head:r},an(e,t,"<",Mt(r,n)),an(e,t,">",_t(r,n))}else t.insertMode||(t.lastHPos=e.getCursor().ch)}function Gn(e){this.keyName=e}function Yn(e){function i(){return n.maybeReset&&(n.changes=[],n.maybeReset=!1),n.changes.push(new Gn(r)),!0}var t=nt.macroModeState,n=t.lastInsertModeChanges,r=v.keyName(e);if(!r)return;(r.indexOf("Delete")!=-1||r.indexOf("Backspace")!=-1)&&v.lookupKey(r,"vim-insert",i)}function Zn(e,t,n,r){function u(){s?ht.processAction(e,t,t.lastEditActionCommand):ht.evalInput(e,t)}function a(n){if(i.lastInsertModeChanges.changes.length>0){n=t.lastEditActionCommand?n:1;var r=i.lastInsertModeChanges;er(e,r.changes,n)}}var i=nt.macroModeState;i.isPlaying=!0;var s=!!t.lastEditActionCommand,o=t.inputState;t.inputState=t.lastEditInputState;if(s&&t.lastEditActionCommand.interlaceInsertRepeat)for(var f=0;f1&&t[0]=="n"&&(t=t.replace("numpad","")),t=tr[t]||t;var r="";return n.ctrlKey&&(r+="C-"),n.altKey&&(r+="A-"),n.shiftKey&&(r+="S-"),r+=t,r.length>1&&(r="<"+r+">"),r}function ir(e){var t=new e.constructor;return Object.keys(e).forEach(function(n){var r=e[n];Array.isArray(r)?r=r.slice():r&&typeof r=="object"&&r.constructor!=Object&&(r=ir(r)),t[n]=r}),e.sel&&(t.sel={head:e.sel.head&&Lt(e.sel.head),anchor:e.sel.anchor&&Lt(e.sel.anchor)}),t}function sr(e,t,n){var r=!1,i=S.maybeInitVimState_(e),s=i.visualBlock||i.wasInVisualBlock;i.wasInVisualBlock&&!e.ace.inMultiSelectMode?i.wasInVisualBlock=!1:e.ace.inMultiSelectMode&&i.visualBlock&&(i.wasInVisualBlock=!0);if(t==""&&!i.insertMode&&!i.visualMode&&e.ace.inMultiSelectMode)e.ace.exitMultiSelectMode();else if(s||!e.ace.inMultiSelectMode||e.ace.inVirtualSelectionMode)r=S.handleKey(e,t,n);else{var o=ir(i);e.operation(function(){e.ace.forEachSelection(function(){var i=e.ace.selection;e.state.vim.lastHPos=i.$desiredColumn==null?i.lead.column:i.$desiredColumn;var s=e.getCursor("head"),u=e.getCursor("anchor"),a=Ot(s,u)?0:-1,f=Ot(s,u)?-1:0;s=St(s,0,a),u=St(u,0,f),e.state.vim.sel.head=s,e.state.vim.sel.anchor=u,r=rr(e,t,n),i.$desiredColumn=e.state.vim.lastHPos==-1?null:e.state.vim.lastHPos,e.virtualSelectionMode()&&(e.state.vim=ir(o))}),e.curOp.cursorActivity&&!r&&(e.curOp.cursorActivity=!1)},!0)}return r}function ar(e,t){t.off("beforeEndOperation",ar);var n=t.state.cm.vimCmd;n&&t.execCommand(n.exec?n:n.name,n.args),t.curOp=t.prevOp}var i=e("../range").Range,s=e("../lib/event_emitter").EventEmitter,o=e("../lib/dom"),u=e("../lib/oop"),a=e("../lib/keys"),f=e("../lib/event"),l=e("../search").Search,c=e("../lib/useragent"),h=e("../search_highlight").SearchHighlight,p=e("../commands/multi_select_commands"),d=e("../mode/text").Mode.prototype.tokenRe;e("../multi_select");var v=function(e){this.ace=e,this.state={},this.marks={},this.$uid=0,this.onChange=this.onChange.bind(this),this.onSelectionChange=this.onSelectionChange.bind(this),this.onBeforeEndOperation=this.onBeforeEndOperation.bind(this),this.ace.on("change",this.onChange),this.ace.on("changeSelection",this.onSelectionChange),this.ace.on("beforeEndOperation",this.onBeforeEndOperation)};v.Pos=function(e,t){if(!(this instanceof E))return new E(e,t);this.line=e,this.ch=t},v.defineOption=function(e,t,n){},v.commands={redo:function(e){e.ace.redo()},undo:function(e){e.ace.undo()},newlineAndIndent:function(e){e.ace.insert("\n")}},v.keyMap={},v.addClass=v.rmClass=v.e_stop=function(){},v.keyName=function(e){if(e.key)return e.key;var t=a[e.keyCode]||"";return t.length==1&&(t=t.toUpperCase()),t=f.getModifierString(e).replace(/(^|-)\w/g,function(e){return e.toUpperCase()})+t,t},v.keyMap["default"]=function(e){return function(t){var n=t.ace.commands.commandKeyBinding[e.toLowerCase()];return n&&t.ace.execCommand(n)!==!1}},v.lookupKey=function fr(e,t,n){typeof t=="string"&&(t=v.keyMap[t]);var r=typeof t=="function"?t(e):t[e];if(r===!1)return"nothing";if(r==="...")return"multi";if(r!=null&&n(r))return"handled";if(t.fallthrough){if(!Array.isArray(t.fallthrough))return fr(e,t.fallthrough,n);for(var i=0;i0){a.row+=s,a.column+=a.row==r.row?o:0;continue}!t&&l<=0&&(a.row=n.row,a.column=n.column,l===0&&(a.bias=1))}};var e=function(e,t,n,r){this.cm=e,this.id=t,this.row=n,this.column=r,e.marks[this.id]=this};e.prototype.clear=function(){delete this.cm.marks[this.id]},e.prototype.find=function(){return g(this)},this.setBookmark=function(t,n){var r=new e(this,this.$uid++,t.line,t.ch);if(!n||!n.insertLeft)r.$insertRight=!0;return this.marks[r.id]=r,r},this.moveH=function(e,t){if(t=="char"){var n=this.ace.selection;n.clearSelection(),n.moveCursorBy(0,e)}},this.findPosV=function(e,t,n,r){if(n=="page"){var i=this.ace.renderer,s=i.layerConfig;t*=Math.floor(s.height/s.lineHeight),n="line"}if(n=="line"){var o=this.ace.session.documentToScreenPosition(e.line,e.ch);r!=null&&(o.column=r),o.row+=t,o.row=Math.min(Math.max(0,o.row),this.ace.session.getScreenLength()-1);var u=this.ace.session.screenToDocumentPosition(o.row,o.column);return g(u)}debugger},this.charCoords=function(e,t){if(t=="div"||!t){var n=this.ace.session.documentToScreenPosition(e.line,e.ch);return{left:n.column,top:n.row}}if(t=="local"){var r=this.ace.renderer,n=this.ace.session.documentToScreenPosition(e.line,e.ch),i=r.layerConfig.lineHeight,s=r.layerConfig.characterWidth,o=i*n.row;return{left:n.column*s,top:o,bottom:o+i}}},this.coordsChar=function(e,t){var n=this.ace.renderer;if(t=="local"){var r=Math.max(0,Math.floor(e.top/n.lineHeight)),i=Math.max(0,Math.floor(e.left/n.characterWidth)),s=n.session.screenToDocumentPosition(r,i);return g(s)}if(t=="div")throw"not implemented"},this.getSearchCursor=function(e,t,n){var r=!1,i=!1;e instanceof RegExp&&!e.global&&(r=!e.ignoreCase,e=e.source,i=!0);var s=new l;t.ch==undefined&&(t.ch=Number.MAX_VALUE);var o={row:t.line,column:t.ch},u=this,a=null;return{findNext:function(){return this.find(!1)},findPrevious:function(){return this.find(!0)},find:function(t){s.setOptions({needle:e,caseSensitive:r,wrap:!1,backwards:t,regExp:i,start:a||o});var n=s.find(u.ace.session);return n&&n.isEmpty()&&u.getLine(n.start.row).length==n.start.column&&(s.$options.start=n,n=s.find(u.ace.session)),a=n,a},from:function(){return a&&g(a.start)},to:function(){return a&&g(a.end)},replace:function(e){a&&(a.end=u.ace.session.doc.replace(a,e))}}},this.scrollTo=function(e,t){var n=this.ace.renderer,r=n.layerConfig,i=r.maxHeight;i-=(n.$size.scrollerHeight-n.lineHeight)*n.$scrollPastEnd,t!=null&&this.ace.session.setScrollTop(Math.max(0,Math.min(t,i))),e!=null&&this.ace.session.setScrollLeft(Math.max(0,Math.min(e,r.width)))},this.scrollInfo=function(){return 0},this.scrollIntoView=function(e,t){if(e){var n=this.ace.renderer,r={top:0,bottom:t};n.scrollCursorIntoView(m(e),n.lineHeight*2/n.$size.scrollerHeight,r)}},this.getLine=function(e){return this.ace.session.getLine(e)},this.getRange=function(e,t){return this.ace.session.getTextRange(new i(e.line,e.ch,t.line,t.ch))},this.replaceRange=function(e,t,n){return n||(n=t),this.ace.session.replace(new i(t.line,t.ch,n.line,n.ch),e)},this.replaceSelections=function(e){var t=this.ace.selection;if(this.ace.inVirtualSelectionMode){this.ace.session.replace(t.getRange(),e[0]||"");return}t.inVirtualSelectionMode=!0;var n=t.rangeList.ranges;n.length||(n=[this.ace.multiSelect.getRange()]);for(var r=n.length;r--;)this.ace.session.replace(n[r],e[r]||"");t.inVirtualSelectionMode=!1},this.getSelection=function(){return this.ace.getSelectedText()},this.getSelections=function(){return this.listSelections().map(function(e){return this.getRange(e.anchor,e.head)},this)},this.getInputField=function(){return this.ace.textInput.getElement()},this.getWrapperElement=function(){return this.ace.containter};var t={indentWithTabs:"useSoftTabs",indentUnit:"tabSize",tabSize:"tabSize",firstLineNumber:"firstLineNumber",readOnly:"readOnly"};this.setOption=function(e,n){this.state[e]=n;switch(e){case"indentWithTabs":e=t[e],n=!n;break;default:e=t[e]}e&&this.ace.setOption(e,n)},this.getOption=function(e,n){var r=t[e];r&&(n=this.ace.getOption(r));switch(e){case"indentWithTabs":return e=t[e],!n}return r?n:this.state[e]},this.toggleOverwrite=function(e){return this.state.overwrite=e,this.ace.setOverwrite(e)},this.addOverlay=function(e){if(!this.$searchHighlight||!this.$searchHighlight.session){var t=new h(null,"ace_highlight-marker","text"),n=this.ace.session.addDynamicMarker(t);t.id=n.id,t.session=this.ace.session,t.destroy=function(e){t.session.off("change",t.updateOnChange),t.session.off("changeEditor",t.destroy),t.session.removeMarker(t.id),t.session=null},t.updateOnChange=function(e){var n=e.start.row;n==e.end.row?t.cache[n]=undefined:t.cache.splice(n,t.cache.length)},t.session.on("changeEditor",t.destroy),t.session.on("change",t.updateOnChange)}var r=new RegExp(e.query.source,"gmi");this.$searchHighlight=e.highlight=t,this.$searchHighlight.setRegexp(r),this.ace.renderer.updateBackMarkers()},this.removeOverlay=function(e){this.$searchHighlight&&this.$searchHighlight.session&&this.$searchHighlight.destroy()},this.getScrollInfo=function(){var e=this.ace.renderer,t=e.layerConfig;return{left:e.scrollLeft,top:e.scrollTop,height:t.maxHeight,width:t.width,clientHeight:t.height,clientWidth:t.width}},this.getValue=function(){return this.ace.getValue()},this.setValue=function(e){return this.ace.setValue(e)},this.getTokenTypeAt=function(e){var t=this.ace.session.getTokenAt(e.line,e.ch);return t&&/comment|string/.test(t.type)?"string":""},this.findMatchingBracket=function(e){var t=this.ace.session.findMatchingBracket(m(e));return{to:t&&g(t)}},this.indentLine=function(e,t){t===!0?this.ace.session.indentRows(e,e," "):t===!1&&this.ace.session.outdentRows(new i(e,0,e,0))},this.indexFromPos=function(e){return this.ace.session.doc.positionToIndex(m(e))},this.posFromIndex=function(e){return g(this.ace.session.doc.indexToPosition(e))},this.focus=function(e){return this.ace.focus()},this.blur=function(e){return this.ace.blur()},this.defaultTextHeight=function(e){return this.ace.renderer.layerConfig.lineHeight},this.scanForBracket=function(e,t,n,r){var i=r.bracketRegex.source;if(t==1)var s=this.ace.session.$findClosingBracket(i.slice(1,2),m(e),/paren|text/);else var s=this.ace.session.$findOpeningBracket(i.slice(-2,-1),{row:e.line,column:e.ch+1},/paren|text/);return s&&{pos:g(s)}},this.refresh=function(){return this.ace.resize(!0)},this.getMode=function(){return{name:this.getOption("mode")}}}.call(v.prototype);var y=v.StringStream=function(e,t){this.pos=this.start=0,this.string=e,this.tabSize=t||8,this.lastColumnPos=this.lastColumnValue=0,this.lineStart=0};y.prototype={eol:function(){return this.pos>=this.string.length},sol:function(){return this.pos==this.lineStart},peek:function(){return this.string.charAt(this.pos)||undefined},next:function(){if(this.post},eatSpace:function(){var e=this.pos;while(/[\s\u00a0]/.test(this.string.charAt(this.pos)))++this.pos;return this.pos>e},skipToEnd:function(){this.pos=this.string.length},skipTo:function(e){var t=this.string.indexOf(e,this.pos);if(t>-1)return this.pos=t,!0},backUp:function(e){this.pos-=e},column:function(){throw"not implemented"},indentation:function(){throw"not implemented"},match:function(e,t,n){if(typeof e!="string"){var s=this.string.slice(this.pos).match(e);return s&&s.index>0?null:(s&&t!==!1&&(this.pos+=s[0].length),s)}var r=function(e){return n?e.toLowerCase():e},i=this.string.substr(this.pos,e.length);if(r(i)==r(e))return t!==!1&&(this.pos+=e.length),!0},current:function(){return this.string.slice(this.start,this.pos)},hideFirstChars:function(e,t){this.lineStart+=e;try{return t()}finally{this.lineStart-=e}}},v.defineExtension=function(e,t){v.prototype[e]=t},o.importCssString(".normal-mode .ace_cursor{ border: 1px solid red; background-color: red; opacity: 0.5;}.normal-mode .ace_hidden-cursors .ace_cursor{ background-color: transparent;}.ace_dialog { position: absolute; left: 0; right: 0; background: white; z-index: 15; padding: .1em .8em; overflow: hidden; color: #333;}.ace_dialog-top { border-bottom: 1px solid #eee; top: 0;}.ace_dialog-bottom { border-top: 1px solid #eee; bottom: 0;}.ace_dialog input { border: none; outline: none; background: transparent; width: 20em; color: inherit; font-family: monospace;}","vimMode"),function(){function e(e,t,n){var r=e.ace.container,i;return i=r.appendChild(document.createElement("div")),n?i.className="ace_dialog ace_dialog-bottom":i.className="ace_dialog ace_dialog-top",typeof t=="string"?i.innerHTML=t:i.appendChild(t),i}function t(e,t){e.state.currentNotificationClose&&e.state.currentNotificationClose(),e.state.currentNotificationClose=t}v.defineExtension("openDialog",function(n,r,i){function a(e){if(typeof e=="string")f.value=e;else{if(o)return;o=!0,s.parentNode.removeChild(s),u.focus(),i.onClose&&i.onClose(s)}}if(this.virtualSelectionMode())return;i||(i={}),t(this,null);var s=e(this,n,i.bottom),o=!1,u=this,f=s.getElementsByTagName("input")[0],l;if(f)i.value&&(f.value=i.value,i.select!==!1&&f.select()),i.onInput&&v.on(f,"input",function(e){i.onInput(e,f.value,a)}),i.onKeyUp&&v.on(f,"keyup",function(e){i.onKeyUp(e,f.value,a)}),v.on(f,"keydown",function(e){if(i&&i.onKeyDown&&i.onKeyDown(e,f.value,a))return;if(e.keyCode==27||i.closeOnEnter!==!1&&e.keyCode==13)f.blur(),v.e_stop(e),a();e.keyCode==13&&r(f.value)}),i.closeOnBlur!==!1&&v.on(f,"blur",a),f.focus();else if(l=s.getElementsByTagName("button")[0])v.on(l,"click",function(){a(),u.focus()}),i.closeOnBlur!==!1&&v.on(l,"blur",a),l.focus();return a}),v.defineExtension("openNotification",function(n,r){function a(){if(s)return;s=!0,clearTimeout(o),i.parentNode.removeChild(i)}if(this.virtualSelectionMode())return;t(this,a);var i=e(this,n,r&&r.bottom),s=!1,o,u=r&&typeof r.duration!="undefined"?r.duration:5e3;return v.on(i,"click",function(e){v.e_preventDefault(e),a()}),u&&(o=setTimeout(a,u)),a})}();var b=[{keys:"",type:"keyToKey",toKeys:"h"},{keys:"",type:"keyToKey",toKeys:"l"},{keys:"",type:"keyToKey",toKeys:"k"},{keys:"",type:"keyToKey",toKeys:"j"},{keys:"",type:"keyToKey",toKeys:"l"},{keys:"",type:"keyToKey",toKeys:"h",context:"normal"},{keys:"",type:"keyToKey",toKeys:"W"},{keys:"",type:"keyToKey",toKeys:"B",context:"normal"},{keys:"",type:"keyToKey",toKeys:"w"},{keys:"",type:"keyToKey",toKeys:"b",context:"normal"},{keys:"",type:"keyToKey",toKeys:"j"},{keys:"",type:"keyToKey",toKeys:"k"},{keys:"",type:"keyToKey",toKeys:""},{keys:"",type:"keyToKey",toKeys:""},{keys:"",type:"keyToKey",toKeys:"",context:"insert"},{keys:"",type:"keyToKey",toKeys:"",context:"insert"},{keys:"s",type:"keyToKey",toKeys:"cl",context:"normal"},{keys:"s",type:"keyToKey",toKeys:"c",context:"visual"},{keys:"S",type:"keyToKey",toKeys:"cc",context:"normal"},{keys:"S",type:"keyToKey",toKeys:"VdO",context:"visual"},{keys:"",type:"keyToKey",toKeys:"0"},{keys:"",type:"keyToKey",toKeys:"$"},{keys:"",type:"keyToKey",toKeys:""},{keys:"",type:"keyToKey",toKeys:""},{keys:"",type:"keyToKey",toKeys:"j^",context:"normal"},{keys:"H",type:"motion",motion:"moveToTopLine",motionArgs:{linewise:!0,toJumplist:!0}},{keys:"M",type:"motion",motion:"moveToMiddleLine",motionArgs:{linewise:!0,toJumplist:!0}},{keys:"L",type:"motion",motion:"moveToBottomLine",motionArgs:{linewise:!0,toJumplist:!0}},{keys:"h",type:"motion",motion:"moveByCharacters",motionArgs:{forward:!1}},{keys:"l",type:"motion",motion:"moveByCharacters",motionArgs:{forward:!0}},{keys:"j",type:"motion",motion:"moveByLines",motionArgs:{forward:!0,linewise:!0}},{keys:"k",type:"motion",motion:"moveByLines",motionArgs:{forward:!1,linewise:!0}},{keys:"gj",type:"motion",motion:"moveByDisplayLines",motionArgs:{forward:!0}},{keys:"gk",type:"motion",motion:"moveByDisplayLines",motionArgs:{forward:!1}},{keys:"w",type:"motion",motion:"moveByWords",motionArgs:{forward:!0,wordEnd:!1}},{keys:"W",type:"motion",motion:"moveByWords",motionArgs:{forward:!0,wordEnd:!1,bigWord:!0}},{keys:"e",type:"motion",motion:"moveByWords",motionArgs:{forward:!0,wordEnd:!0,inclusive:!0}},{keys:"E",type:"motion",motion:"moveByWords",motionArgs:{forward:!0,wordEnd:!0,bigWord:!0,inclusive:!0}},{keys:"b",type:"motion",motion:"moveByWords",motionArgs:{forward:!1,wordEnd:!1}},{keys:"B",type:"motion",motion:"moveByWords",motionArgs:{forward:!1,wordEnd:!1,bigWord:!0}},{keys:"ge",type:"motion",motion:"moveByWords",motionArgs:{forward:!1,wordEnd:!0,inclusive:!0}},{keys:"gE",type:"motion",motion:"moveByWords",motionArgs:{forward:!1,wordEnd:!0,bigWord:!0,inclusive:!0}},{keys:"{",type:"motion",motion:"moveByParagraph",motionArgs:{forward:!1,toJumplist:!0}},{keys:"}",type:"motion",motion:"moveByParagraph",motionArgs:{forward:!0,toJumplist:!0}},{keys:"",type:"motion",motion:"moveByPage",motionArgs:{forward:!0}},{keys:"",type:"motion",motion:"moveByPage",motionArgs:{forward:!1}},{keys:"",type:"motion",motion:"moveByScroll",motionArgs:{forward:!0,explicitRepeat:!0}},{keys:"",type:"motion",motion:"moveByScroll",motionArgs:{forward:!1,explicitRepeat:!0}},{keys:"gg",type:"motion",motion:"moveToLineOrEdgeOfDocument",motionArgs:{forward:!1,explicitRepeat:!0,linewise:!0,toJumplist:!0}},{keys:"G",type:"motion",motion:"moveToLineOrEdgeOfDocument",motionArgs:{forward:!0,explicitRepeat:!0,linewise:!0,toJumplist:!0}},{keys:"0",type:"motion",motion:"moveToStartOfLine"},{keys:"^",type:"motion",motion:"moveToFirstNonWhiteSpaceCharacter"},{keys:"+",type:"motion",motion:"moveByLines",motionArgs:{forward:!0,toFirstChar:!0}},{keys:"-",type:"motion",motion:"moveByLines",motionArgs:{forward:!1,toFirstChar:!0}},{keys:"_",type:"motion",motion:"moveByLines",motionArgs:{forward:!0,toFirstChar:!0,repeatOffset:-1}},{keys:"$",type:"motion",motion:"moveToEol",motionArgs:{inclusive:!0}},{keys:"%",type:"motion",motion:"moveToMatchedSymbol",motionArgs:{inclusive:!0,toJumplist:!0}},{keys:"f",type:"motion",motion:"moveToCharacter",motionArgs:{forward:!0,inclusive:!0}},{keys:"F",type:"motion",motion:"moveToCharacter",motionArgs:{forward:!1}},{keys:"t",type:"motion",motion:"moveTillCharacter",motionArgs:{forward:!0,inclusive:!0}},{keys:"T",type:"motion",motion:"moveTillCharacter",motionArgs:{forward:!1}},{keys:";",type:"motion",motion:"repeatLastCharacterSearch",motionArgs:{forward:!0}},{keys:",",type:"motion",motion:"repeatLastCharacterSearch",motionArgs:{forward:!1}},{keys:"'",type:"motion",motion:"goToMark",motionArgs:{toJumplist:!0,linewise:!0}},{keys:"`",type:"motion",motion:"goToMark",motionArgs:{toJumplist:!0}},{keys:"]`",type:"motion",motion:"jumpToMark",motionArgs:{forward:!0}},{keys:"[`",type:"motion",motion:"jumpToMark",motionArgs:{forward:!1}},{keys:"]'",type:"motion",motion:"jumpToMark",motionArgs:{forward:!0,linewise:!0}},{keys:"['",type:"motion",motion:"jumpToMark",motionArgs:{forward:!1,linewise:!0}},{keys:"]p",type:"action",action:"paste",isEdit:!0,actionArgs:{after:!0,isEdit:!0,matchIndent:!0}},{keys:"[p",type:"action",action:"paste",isEdit:!0,actionArgs:{after:!1,isEdit:!0,matchIndent:!0}},{keys:"]",type:"motion",motion:"moveToSymbol",motionArgs:{forward:!0,toJumplist:!0}},{keys:"[",type:"motion",motion:"moveToSymbol",motionArgs:{forward:!1,toJumplist:!0}},{keys:"|",type:"motion",motion:"moveToColumn"},{keys:"o",type:"motion",motion:"moveToOtherHighlightedEnd",context:"visual"},{keys:"O",type:"motion",motion:"moveToOtherHighlightedEnd",motionArgs:{sameLine:!0},context:"visual"},{keys:"d",type:"operator",operator:"delete"},{keys:"y",type:"operator",operator:"yank"},{keys:"c",type:"operator",operator:"change"},{keys:">",type:"operator",operator:"indent",operatorArgs:{indentRight:!0}},{keys:"<",type:"operator",operator:"indent",operatorArgs:{indentRight:!1}},{keys:"g~",type:"operator",operator:"changeCase"},{keys:"gu",type:"operator",operator:"changeCase",operatorArgs:{toLower:!0},isEdit:!0},{keys:"gU",type:"operator",operator:"changeCase",operatorArgs:{toLower:!1},isEdit:!0},{keys:"n",type:"motion",motion:"findNext",motionArgs:{forward:!0,toJumplist:!0}},{keys:"N",type:"motion",motion:"findNext",motionArgs:{forward:!1,toJumplist:!0}},{keys:"x",type:"operatorMotion",operator:"delete",motion:"moveByCharacters",motionArgs:{forward:!0},operatorMotionArgs:{visualLine:!1}},{keys:"X",type:"operatorMotion",operator:"delete",motion:"moveByCharacters",motionArgs:{forward:!1},operatorMotionArgs:{visualLine:!0}},{keys:"D",type:"operatorMotion",operator:"delete",motion:"moveToEol",motionArgs:{inclusive:!0},context:"normal"},{keys:"D",type:"operator",operator:"delete",operatorArgs:{linewise:!0},context:"visual"},{keys:"Y",type:"operatorMotion",operator:"yank",motion:"moveToEol",motionArgs:{inclusive:!0},context:"normal"},{keys:"Y",type:"operator",operator:"yank",operatorArgs:{linewise:!0},context:"visual"},{keys:"C",type:"operatorMotion",operator:"change",motion:"moveToEol",motionArgs:{inclusive:!0},context:"normal"},{keys:"C",type:"operator",operator:"change",operatorArgs:{linewise:!0},context:"visual"},{keys:"~",type:"operatorMotion",operator:"changeCase",motion:"moveByCharacters",motionArgs:{forward:!0},operatorArgs:{shouldMoveCursor:!0},context:"normal"},{keys:"~",type:"operator",operator:"changeCase",context:"visual"},{keys:"",type:"operatorMotion",operator:"delete",motion:"moveByWords",motionArgs:{forward:!1,wordEnd:!1},context:"insert"},{keys:"",type:"action",action:"jumpListWalk",actionArgs:{forward:!0}},{keys:"",type:"action",action:"jumpListWalk",actionArgs:{forward:!1}},{keys:"",type:"action",action:"scroll",actionArgs:{forward:!0,linewise:!0}},{keys:"",type:"action",action:"scroll",actionArgs:{forward:!1,linewise:!0}},{keys:"a",type:"action",action:"enterInsertMode",isEdit:!0,actionArgs:{insertAt:"charAfter"},context:"normal"},{keys:"A",type:"action",action:"enterInsertMode",isEdit:!0,actionArgs:{insertAt:"eol"},context:"normal"},{keys:"A",type:"action",action:"enterInsertMode",isEdit:!0,actionArgs:{insertAt:"endOfSelectedArea"},context:"visual"},{keys:"i",type:"action",action:"enterInsertMode",isEdit:!0,actionArgs:{insertAt:"inplace"},context:"normal"},{keys:"I",type:"action",action:"enterInsertMode",isEdit:!0,actionArgs:{insertAt:"firstNonBlank"},context:"normal"},{keys:"I",type:"action",action:"enterInsertMode",isEdit:!0,actionArgs:{insertAt:"startOfSelectedArea"},context:"visual"},{keys:"o",type:"action",action:"newLineAndEnterInsertMode",isEdit:!0,interlaceInsertRepeat:!0,actionArgs:{after:!0},context:"normal"},{keys:"O",type:"action",action:"newLineAndEnterInsertMode",isEdit:!0,interlaceInsertRepeat:!0,actionArgs:{after:!1},context:"normal"},{keys:"v",type:"action",action:"toggleVisualMode"},{keys:"V",type:"action",action:"toggleVisualMode",actionArgs:{linewise:!0}},{keys:"",type:"action",action:"toggleVisualMode",actionArgs:{blockwise:!0}},{keys:"",type:"action",action:"toggleVisualMode",actionArgs:{blockwise:!0}},{keys:"gv",type:"action",action:"reselectLastSelection"},{keys:"J",type:"action",action:"joinLines",isEdit:!0},{keys:"p",type:"action",action:"paste",isEdit:!0,actionArgs:{after:!0,isEdit:!0}},{keys:"P",type:"action",action:"paste",isEdit:!0,actionArgs:{after:!1,isEdit:!0}},{keys:"r",type:"action",action:"replace",isEdit:!0},{keys:"@",type:"action",action:"replayMacro"},{keys:"q",type:"action",action:"enterMacroRecordMode"},{keys:"R",type:"action",action:"enterInsertMode",isEdit:!0,actionArgs:{replace:!0}},{keys:"u",type:"action",action:"undo",context:"normal"},{keys:"u",type:"operator",operator:"changeCase",operatorArgs:{toLower:!0},context:"visual",isEdit:!0},{keys:"U",type:"operator",operator:"changeCase",operatorArgs:{toLower:!1},context:"visual",isEdit:!0},{keys:"",type:"action",action:"redo"},{keys:"m",type:"action",action:"setMark"},{keys:'"',type:"action",action:"setRegister"},{keys:"zz",type:"action",action:"scrollToCursor",actionArgs:{position:"center"}},{keys:"z.",type:"action",action:"scrollToCursor",actionArgs:{position:"center"},motion:"moveToFirstNonWhiteSpaceCharacter"},{keys:"zt",type:"action",action:"scrollToCursor",actionArgs:{position:"top"}},{keys:"z",type:"action",action:"scrollToCursor",actionArgs:{position:"top"},motion:"moveToFirstNonWhiteSpaceCharacter"},{keys:"z-",type:"action",action:"scrollToCursor",actionArgs:{position:"bottom"}},{keys:"zb",type:"action",action:"scrollToCursor",actionArgs:{position:"bottom"},motion:"moveToFirstNonWhiteSpaceCharacter"},{keys:".",type:"action",action:"repeatLastEdit"},{keys:"",type:"action",action:"incrementNumberToken",isEdit:!0,actionArgs:{increase:!0,backtrack:!1}},{keys:"",type:"action",action:"incrementNumberToken",isEdit:!0,actionArgs:{increase:!1,backtrack:!1}},{keys:"a",type:"motion",motion:"textObjectManipulation"},{keys:"i",type:"motion",motion:"textObjectManipulation",motionArgs:{textObjectInner:!0}},{keys:"/",type:"search",searchArgs:{forward:!0,querySrc:"prompt",toJumplist:!0}},{keys:"?",type:"search",searchArgs:{forward:!1,querySrc:"prompt",toJumplist:!0}},{keys:"*",type:"search",searchArgs:{forward:!0,querySrc:"wordUnderCursor",wholeWordOnly:!0,toJumplist:!0}},{keys:"#",type:"search",searchArgs:{forward:!1,querySrc:"wordUnderCursor",wholeWordOnly:!0,toJumplist:!0}},{keys:"g*",type:"search",searchArgs:{forward:!0,querySrc:"wordUnderCursor",toJumplist:!0}},{keys:"g#",type:"search",searchArgs:{forward:!1,querySrc:"wordUnderCursor",toJumplist:!0}},{keys:":",type:"ex"}],w=[{name:"colorscheme",shortName:"colo"},{name:"map"},{name:"imap",shortName:"im"},{name:"nmap",shortName:"nm"},{name:"vmap",shortName:"vm"},{name:"unmap"},{name:"write",shortName:"w"},{name:"undo",shortName:"u"},{name:"redo",shortName:"red"},{name:"set",shortName:"se"},{name:"set",shortName:"se"},{name:"setlocal",shortName:"setl"},{name:"setglobal",shortName:"setg"},{name:"sort",shortName:"sor"},{name:"substitute",shortName:"s",possiblyAsync:!0},{name:"nohlsearch",shortName:"noh"},{name:"delmarks",shortName:"delm"},{name:"registers",shortName:"reg",excludeFromCommandHistory:!0},{name:"global",shortName:"g"}],E=v.Pos,S=function(){return st};v.defineOption("vimMode",!1,function(e,t,n){t&&e.getOption("keyMap")!="vim"?e.setOption("keyMap","vim"):!t&&n!=v.Init&&/^vim/.test(e.getOption("keyMap"))&&e.setOption("keyMap","default")});var L={Shift:"S",Ctrl:"C",Alt:"A",Cmd:"D",Mod:"A"},A={Enter:"CR",Backspace:"BS",Delete:"Del"},_=/[\d]/,D=[v.isWordChar,function(e){return e&&!v.isWordChar(e)&&!/\s/.test(e)}],P=[function(e){return/\S/.test(e)}],B=H(65,26),j=H(97,26),F=H(48,10),I=[].concat(B,j,F,["<",">"]),q=[].concat(B,j,F,["-",'"',".",":","/"]),J={};K("filetype",undefined,"string",["ft"],function(e,t){if(t===undefined)return;if(e===undefined){var n=t.getOption("mode");return n=="null"?"":n}var n=e==""?"null":e;t.setOption("mode",n)});var Y=function(){function s(s,o,u){function l(n){var r=++t%e,o=i[r];o&&o.clear(),i[r]=s.setBookmark(n)}var a=t%e,f=i[a];if(f){var c=f.find();c&&!At(c,o)&&l(o)}else l(o);l(u),n=t,r=t-e+1,r<0&&(r=0)}function o(s,o){t+=o,t>n?t=n:t0?1:-1,f,l=s.getCursor();do{t+=a,u=i[(e+t)%e];if(u&&(f=u.find())&&!At(l,f))break}while(tr)}return u}var e=100,t=-1,n=0,r=0,i=new Array(e);return{cachedCursor:undefined,add:s,move:o}},Z=function(e){return e?{changes:e.changes,expectCursorActivityForChange:e.expectCursorActivityForChange}:{changes:[],expectCursorActivityForChange:!1}};et.prototype={exitMacroRecordMode:function(){var e=nt.macroModeState;e.onRecordingDone&&e.onRecordingDone(),e.onRecordingDone=undefined,e.isRecording=!1},enterMacroRecordMode:function(e,t){var n=nt.registerController.getRegister(t);n&&(n.clear(),this.latestRegister=t,e.openDialog&&(this.onRecordingDone=e.openDialog("(recording)["+t+"]",null,{bottom:!0})),this.isRecording=!0)}};var nt,it,st={buildKeyMap:function(){},getRegisterController:function(){return nt.registerController},resetVimGlobalState_:rt,getVimGlobalState_:function(){return nt},maybeInitVimState_:tt,suppressErrorLogging:!1,InsertModeKey:Gn,map:function(e,t,n){Fn.map(e,t,n)},unmap:function(e,t){Fn.unmap(e,t)},setOption:Q,getOption:G,defineOption:K,defineEx:function(e,t,n){if(!t)t=e;else if(e.indexOf(t)!==0)throw new Error('(Vim.defineEx) "'+t+'" is not a prefix of "'+e+'", command not registered');jn[e]=n,Fn.commandMap_[t]={name:e,shortName:t,type:"api"}},handleKey:function(e,t,n){var r=this.findKey(e,t,n);if(typeof r=="function")return r()},findKey:function(e,t,n){function i(){var r=nt.macroModeState;if(r.isRecording){if(t=="q")return r.exitMacroRecordMode(),ut(e),!0;n!="mapping"&&Wn(r,t)}}function s(){if(t=="")return ut(e),r.visualMode?$t(e):r.insertMode&&qn(e),!0}function o(n){var r;while(n)r=/<\w+-.+?>|<\w+>|./.exec(n),t=r[0],n=n.substring(r.index+t.length),v.Vim.handleKey(e,t,"mapping")}function u(){if(s())return!0;var n=r.inputState.keyBuffer=r.inputState.keyBuffer+t,i=t.length==1,o=ht.matchCommand(n,b,r.inputState,"insert");while(n.length>1&&o.type!="full"){var n=r.inputState.keyBuffer=n.slice(1),u=ht.matchCommand(n,b,r.inputState,"insert");u.type!="none"&&(o=u)}if(o.type=="none")return ut(e),!1;if(o.type=="partial")return it&&window.clearTimeout(it),it=window.setTimeout(function(){r.insertMode&&r.inputState.keyBuffer&&ut(e)},G("insertModeEscKeysTimeout")),!i;it&&window.clearTimeout(it);if(i){var a=e.listSelections();for(var f=0;f0||this.motionRepeat.length>0)e=1,this.prefixRepeat.length>0&&(e*=parseInt(this.prefixRepeat.join(""),10)),this.motionRepeat.length>0&&(e*=parseInt(this.motionRepeat.join(""),10));return e},at.prototype={setText:function(e,t,n){this.keyBuffer=[e||""],this.linewise=!!t,this.blockwise=!!n},pushText:function(e,t){t&&(this.linewise||this.keyBuffer.push("\n"),this.linewise=!0),this.keyBuffer.push(e)},pushInsertModeChanges:function(e){this.insertModeChanges.push(Z(e))},pushSearchQuery:function(e){this.searchQueries.push(e)},clear:function(){this.keyBuffer=[],this.insertModeChanges=[],this.searchQueries=[],this.linewise=!1},toString:function(){return this.keyBuffer.join("")}},lt.prototype={pushText:function(e,t,n,r,i){r&&n.charAt(0)=="\n"&&(n=n.slice(1)+"\n"),r&&n.charAt(n.length-1)!=="\n"&&(n+="\n");var s=this.isValidRegister(e)?this.getRegister(e):null;if(!s){switch(t){case"yank":this.registers[0]=new at(n,r,i);break;case"delete":case"change":n.indexOf("\n")==-1?this.registers["-"]=new at(n,r):(this.shiftNumericRegisters_(),this.registers[1]=new at(n,r))}this.unnamedRegister.setText(n,r,i);return}var o=X(e);o?s.pushText(n,r):s.setText(n,r,i),this.unnamedRegister.setText(s.toString(),r)},getRegister:function(e){return this.isValidRegister(e)?(e=e.toLowerCase(),this.registers[e]||(this.registers[e]=new at),this.registers[e]):this.unnamedRegister},isValidRegister:function(e){return e&&$(e,q)},shiftNumericRegisters_:function(){for(var e=9;e>=2;e--)this.registers[e]=this.getRegister(""+(e-1))}},ct.prototype={nextMatch:function(e,t){var n=this.historyBuffer,r=t?-1:1;this.initialPrefix===null&&(this.initialPrefix=e);for(var i=this.iterator+r;t?i>=0:i=n.length)return this.iterator=n.length,this.initialPrefix;if(i<0)return e},pushInput:function(e){var t=this.historyBuffer.indexOf(e);t>-1&&this.historyBuffer.splice(t,1),e.length&&this.historyBuffer.push(e)},reset:function(){this.initialPrefix=null,this.iterator=this.historyBuffer.length}};var ht={matchCommand:function(e,t,n,r){var i=Tt(e,t,r,n);if(!i.full&&!i.partial)return{type:"none"};if(!i.full&&i.partial)return{type:"partial"};var s;for(var o=0;o"&&(n.selectedCharacter=Ct(e)),{type:"full",command:s}},processCommand:function(e,t,n){t.inputState.repeatOverride=n.repeatOverride;switch(n.type){case"motion":this.processMotion(e,t,n);break;case"operator":this.processOperator(e,t,n);break;case"operatorMotion":this.processOperatorMotion(e,t,n);break;case"action":this.processAction(e,t,n);break;case"search":this.processSearch(e,t,n);break;case"ex":case"keyToEx":this.processEx(e,t,n);break;default:}},processMotion:function(e,t,n){t.inputState.motion=n.motion,t.inputState.motionArgs=Et(n.motionArgs),this.evalInput(e,t)},processOperator:function(e,t,n){var r=t.inputState;if(r.operator){if(r.operator==n.operator){r.motion="expandToLine",r.motionArgs={linewise:!0},this.evalInput(e,t);return}ut(e)}r.operator=n.operator,r.operatorArgs=Et(n.operatorArgs),t.visualMode&&this.evalInput(e,t)},processOperatorMotion:function(e,t,n){var r=t.visualMode,i=Et(n.operatorMotionArgs);i&&r&&i.visualLine&&(t.visualLine=!0),this.processOperator(e,t,n),r||this.processMotion(e,t,n)},processAction:function(e,t,n){var r=t.inputState,i=r.getRepeat(),s=!!i,o=Et(n.actionArgs)||{};r.selectedCharacter&&(o.selectedCharacter=r.selectedCharacter),n.operator&&this.processOperator(e,t,n),n.motion&&this.processMotion(e,t,n),(n.motion||n.operator)&&this.evalInput(e,t),o.repeat=i||1,o.repeatIsExplicit=s,o.registerName=r.registerName,ut(e),t.lastMotion=null,n.isEdit&&this.recordLastEdit(t,r,n),yt[n.action](e,o,t)},processSearch:function(e,t,n){function a(r,i,s){nt.searchHistoryController.pushInput(r),nt.searchHistoryController.reset();try{An(e,r,i,s)}catch(o){Tn(e,"Invalid regex: "+r),ut(e);return}ht.processMotion(e,t,{type:"motion",motion:"findNext",motionArgs:{forward:!0,toJumplist:n.searchArgs.toJumplist}})}function f(t){e.scrollTo(u.left,u.top),a(t,!0,!0);var n=nt.macroModeState;n.isRecording&&Vn(n,t)}function l(t,n,i){var s=v.keyName(t),o;s=="Up"||s=="Down"?(o=s=="Up"?!0:!1,n=nt.searchHistoryController.nextMatch(n,o)||"",i(n)):s!="Left"&&s!="Right"&&s!="Ctrl"&&s!="Alt"&&s!="Shift"&&nt.searchHistoryController.reset();var a;try{a=An(e,n,!0,!0)}catch(t){}a?e.scrollIntoView(_n(e,!r,a),30):(Dn(e),e.scrollTo(u.left,u.top))}function c(t,n,r){var i=v.keyName(t);i=="Esc"||i=="Ctrl-C"||i=="Ctrl-["||i=="Backspace"&&n==""?(nt.searchHistoryController.pushInput(n),nt.searchHistoryController.reset(),An(e,o),Dn(e),e.scrollTo(u.left,u.top),v.e_stop(t),ut(e),r(),e.focus()):i=="Ctrl-U"&&(v.e_stop(t),r(""))}if(!e.getSearchCursor)return;var r=n.searchArgs.forward,i=n.searchArgs.wholeWordOnly;dn(e).setReversed(!r);var s=r?"/":"?",o=dn(e).getQuery(),u=e.getScrollInfo();switch(n.searchArgs.querySrc){case"prompt":var h=nt.macroModeState;if(h.isPlaying){var p=h.replaySearchQueries.shift();a(p,!0,!1)}else kn(e,{onClose:f,prefix:s,desc:Cn,onKeyUp:l,onKeyDown:c});break;case"wordUnderCursor":var d=Gt(e,!1,!0,!1,!0),m=!0;d||(d=Gt(e,!1,!0,!1,!1),m=!1);if(!d)return;var p=e.getLine(d.start.line).substring(d.start.ch,d.end.ch);m&&i?p="\\b"+p+"\\b":p=Bt(p),nt.jumpList.cachedCursor=e.getCursor(),e.setCursor(d.start),a(p,!0,!1)}},processEx:function(e,t,n){function r(t){nt.exCommandHistoryController.pushInput(t),nt.exCommandHistoryController.reset(),Fn.processCommand(e,t)}function i(t,n,r){var i=v.keyName(t),s;if(i=="Esc"||i=="Ctrl-C"||i=="Ctrl-["||i=="Backspace"&&n=="")nt.exCommandHistoryController.pushInput(n),nt.exCommandHistoryController.reset(),v.e_stop(t),ut(e),r(),e.focus();i=="Up"||i=="Down"?(s=i=="Up"?!0:!1,n=nt.exCommandHistoryController.nextMatch(n,s)||"",r(n)):i=="Ctrl-U"?(v.e_stop(t),r("")):i!="Left"&&i!="Right"&&i!="Ctrl"&&i!="Alt"&&i!="Shift"&&nt.exCommandHistoryController.reset()}n.type=="keyToEx"?Fn.processCommand(e,n.exArgs.input):t.visualMode?kn(e,{onClose:r,prefix:":",value:"'<,'>",onKeyDown:i}):kn(e,{onClose:r,prefix:":",onKeyDown:i})},evalInput:function(e,t){var n=t.inputState,r=n.motion,i=n.motionArgs||{},s=n.operator,o=n.operatorArgs||{},u=n.registerName,a=t.sel,f=Lt(t.visualMode?wt(e,a.head):e.getCursor("head")),l=Lt(t.visualMode?wt(e,a.anchor):e.getCursor("anchor")),c=Lt(f),h=Lt(l),p,d,v;s&&this.recordLastEdit(t,n),n.repeatOverride!==undefined?v=n.repeatOverride:v=n.getRepeat();if(v>0&&i.explicitRepeat)i.repeatIsExplicit=!0;else if(i.noRepeat||!i.explicitRepeat&&v===0)v=1,i.repeatIsExplicit=!1;n.selectedCharacter&&(i.selectedCharacter=o.selectedCharacter=n.selectedCharacter),i.repeat=v,ut(e);if(r){var m=pt[r](e,f,i,t);t.lastMotion=pt[r];if(!m)return;if(i.toJumplist){!s&&e.ace.curOp!=null&&(e.ace.curOp.command.scrollIntoView="center-animate");var g=nt.jumpList,y=g.cachedCursor;y?(Yt(e,y,m),delete g.cachedCursor):Yt(e,f,m)}m instanceof Array?(d=m[0],p=m[1]):p=m,p||(p=Lt(f));if(t.visualMode){if(!t.visualBlock||p.ch!==Infinity)p=wt(e,p,t.visualBlock);d&&(d=wt(e,d,!0)),d=d||h,a.anchor=d,a.head=p,Wt(e),an(e,t,"<",Ot(d,p)?d:p),an(e,t,">",Ot(d,p)?p:d)}else s||(p=wt(e,p),e.setCursor(p.line,p.ch))}if(s){if(o.lastSel){d=h;var b=o.lastSel,w=Math.abs(b.head.line-b.anchor.line),S=Math.abs(b.head.ch-b.anchor.ch);b.visualLine?p=E(h.line+w,h.ch):b.visualBlock?p=E(h.line+w,h.ch+S):b.head.line==b.anchor.line?p=E(h.line,h.ch+S):p=E(h.line+w,h.ch),t.visualMode=!0,t.visualLine=b.visualLine,t.visualBlock=b.visualBlock,a=t.sel={anchor:d,head:p},Wt(e)}else t.visualMode&&(o.lastSel={anchor:Lt(a.anchor),head:Lt(a.head),visualBlock:t.visualBlock,visualLine:t.visualLine});var x,T,N,C,k;if(t.visualMode){x=Mt(a.head,a.anchor),T=_t(a.head,a.anchor),N=t.visualLine||o.linewise,C=t.visualBlock?"block":N?"line":"char",k=Xt(e,{anchor:x,head:T},C);if(N){var L=k.ranges;if(C=="block")for(var A=0;Af&&i.line==f)return;var l=e.ace.session.getFoldLine(u);return l&&(n.forward?u>l.start.row&&(u=l.end.row+1):u=l.start.row),n.toFirstChar&&(s=Qt(e.getLine(u)),r.lastHPos=s),r.lastHSPos=e.charCoords(E(u,s),"div").left,E(u,s)},moveByDisplayLines:function(e,t,n,r){var i=t;switch(r.lastMotion){case this.moveByDisplayLines:case this.moveByScroll:case this.moveByLines:case this.moveToColumn:case this.moveToEol:break;default:r.lastHSPos=e.charCoords(i,"div").left}var s=n.repeat,o=e.findPosV(i,n.forward?s:-s,"line",r.lastHSPos);if(o.hitSide)if(n.forward)var u=e.charCoords(o,"div"),a={top:u.top+8,left:r.lastHSPos},o=e.coordsChar(a,"div");else{var f=e.charCoords(E(e.firstLine(),0),"div");f.left=r.lastHSPos,o=e.coordsChar(f,"div")}return r.lastHPos=o.ch,o},moveByPage:function(e,t,n){var r=t,i=n.repeat;return e.findPosV(r,n.forward?i:-i,"page")},moveByParagraph:function(e,t,n){var r=n.forward?1:-1;return ln(e,t,n.repeat,r)},moveByScroll:function(e,t,n,r){var i=e.getScrollInfo(),s=null,o=n.repeat;o||(o=i.clientHeight/(2*e.defaultTextHeight()));var u=e.charCoords(t,"local");n.repeat=o;var s=pt.moveByDisplayLines(e,t,n,r);if(!s)return null;var a=e.charCoords(s,"local");return e.scrollTo(null,i.top+a.top-u.top),s},moveByWords:function(e,t,n){return sn(e,t,n.repeat,!!n.forward,!!n.wordEnd,!!n.bigWord)},moveTillCharacter:function(e,t,n){var r=n.repeat,i=on(e,r,n.forward,n.selectedCharacter),s=n.forward?-1:1;return Zt(s,n),i?(i.ch+=s,i):null},moveToCharacter:function(e,t,n){var r=n.repeat;return Zt(0,n),on(e,r,n.forward,n.selectedCharacter)||t},moveToSymbol:function(e,t,n){var r=n.repeat;return nn(e,r,n.forward,n.selectedCharacter)||t},moveToColumn:function(e,t,n,r){var i=n.repeat;return r.lastHPos=i-1,r.lastHSPos=e.charCoords(t,"div").left,un(e,i)},moveToEol:function(e,t,n,r){var i=t;r.lastHPos=Infinity;var s=E(i.line+n.repeat-1,Infinity),o=e.clipPos(s);return o.ch--,r.lastHSPos=e.charCoords(o,"div").left,s},moveToFirstNonWhiteSpaceCharacter:function(e,t){var n=t;return E(n.line,Qt(e.getLine(n.line)))},moveToMatchedSymbol:function(e,t){var n=t,r=n.line,i=n.ch,s=e.getLine(r),o;do{o=s.charAt(i++);if(o&&z(o)){var u=e.getTokenTypeAt(E(r,i));if(u!=="string"&&u!=="comment")break}}while(o);if(o){var a=e.findMatchingBracket(E(r,i));return a.to}return n},moveToStartOfLine:function(e,t){return E(t.line,0)},moveToLineOrEdgeOfDocument:function(e,t,n){var r=n.forward?e.lastLine():e.firstLine();return n.repeatIsExplicit&&(r=n.repeat-e.getOption("firstLineNumber")),E(r,Qt(e.getLine(r)))},textObjectManipulation:function(e,t,n,r){var i={"(":")",")":"(","{":"}","}":"{","[":"]","]":"["},s={"'":!0,'"':!0},o=n.selectedCharacter;o=="b"?o="(":o=="B"&&(o="{");var u=!n.textObjectInner,a;if(i[o])a=cn(e,t,o,u);else if(s[o])a=hn(e,t,o,u);else if(o==="W")a=Gt(e,u,!0,!0);else if(o==="w")a=Gt(e,u,!0,!1);else{if(o!=="p")return null;a=ln(e,t,n.repeat,0,u),n.linewise=!0;if(r.visualMode)r.visualLine||(r.visualLine=!0);else{var f=r.inputState.operatorArgs;f&&(f.linewise=!0),a.end.line--}}return e.state.vim.visualMode?zt(e,a.start,a.end):[a.start,a.end]},repeatLastCharacterSearch:function(e,t,n){var r=nt.lastChararacterSearch,i=n.repeat,s=n.forward===r.forward,o=(r.increment?1:0)*(s?-1:1);e.moveH(-o,"char"),n.inclusive=s?!0:!1;var u=on(e,i,s,r.selectedCharacter);return u?(u.ch+=o,u):(e.moveH(o,"char"),t)}},mt={change:function(e,t,n){var r,i,s=e.state.vim;nt.macroModeState.lastInsertModeChanges.inVisualBlock=s.visualBlock;if(!s.visualMode){var o=n[0].anchor,u=n[0].head;i=e.getRange(o,u);var a=s.lastEditInputState||{};if(a.motion=="moveByWords"&&!V(i)){var f=/\s+$/.exec(i);f&&a.motionArgs&&a.motionArgs.forward&&(u=St(u,0,-f[0].length),i=i.slice(0,-f[0].length))}var l=new E(o.line-1,Number.MAX_VALUE),c=e.firstLine()==e.lastLine();u.line>e.lastLine()&&t.linewise&&!c?e.replaceRange("",l,u):e.replaceRange("",o,u),t.linewise&&(c||(e.setCursor(l),v.commands.newlineAndIndent(e)),o.ch=Number.MAX_VALUE),r=o}else{i=e.getSelection();var h=vt("",n.length);e.replaceSelections(h),r=Mt(n[0].head,n[0].anchor)}nt.registerController.pushText(t.registerName,"change",i,t.linewise,n.length>1),yt.enterInsertMode(e,{head:r},e.state.vim)},"delete":function(e,t,n){var r,i,s=e.state.vim;if(!s.visualBlock){var o=n[0].anchor,u=n[0].head;t.linewise&&u.line!=e.firstLine()&&o.line==e.lastLine()&&o.line==u.line-1&&(o.line==e.firstLine()?o.ch=0:o=E(o.line-1,Pt(e,o.line-1))),i=e.getRange(o,u),e.replaceRange("",o,u),r=o,t.linewise&&(r=pt.moveToFirstNonWhiteSpaceCharacter(e,o))}else{i=e.getSelection();var a=vt("",n.length);e.replaceSelections(a),r=n[0].anchor}return nt.registerController.pushText(t.registerName,"delete",i,t.linewise,s.visualBlock),wt(e,r)},indent:function(e,t,n){var r=e.state.vim,i=n[0].anchor.line,s=r.visualBlock?n[n.length-1].anchor.line:n[0].head.line,o=r.visualMode?t.repeat:1;t.linewise&&s--;for(var u=i;u<=s;u++)for(var a=0;af.top?(a.line+=(u-f.top)/i,a.line=Math.ceil(a.line),e.setCursor(a),f=e.charCoords(a,"local"),e.scrollTo(null,f.top)):e.scrollTo(null,u);else{var l=u+e.getScrollInfo().clientHeight;l=i.anchor.line?s=St(i.head,0,1):s=E(i.anchor.line,0);else if(r=="inplace"&&n.visualMode)return;e.setOption("keyMap","vim-insert"),e.setOption("disableInput",!1),t&&t.replace?(e.toggleOverwrite(!0),e.setOption("keyMap","vim-replace"),v.signal(e,"vim-mode-change",{mode:"replace"})):(e.setOption("keyMap","vim-insert"),v.signal(e,"vim-mode-change",{mode:"insert"})),nt.macroModeState.isPlaying||(e.on("change",$n),v.on(e.getInputField(),"keydown",Yn)),n.visualMode&&$t(e),It(e,s,o)},toggleVisualMode:function(e,t,n){var r=t.repeat,i=e.getCursor(),s;n.visualMode?n.visualLine^t.linewise||n.visualBlock^t.blockwise?(n.visualLine=!!t.linewise,n.visualBlock=!!t.blockwise,v.signal(e,"vim-mode-change",{mode:"visual",subMode:n.visualLine?"linewise":n.visualBlock?"blockwise":""}),Wt(e)):$t(e):(n.visualMode=!0,n.visualLine=!!t.linewise,n.visualBlock=!!t.blockwise,s=wt(e,E(i.line,i.ch+r-1),!0),n.sel={anchor:i,head:s},v.signal(e,"vim-mode-change",{mode:"visual",subMode:n.visualLine?"linewise":n.visualBlock?"blockwise":""}),Wt(e),an(e,n,"<",Mt(i,s)),an(e,n,">",_t(i,s)))},reselectLastSelection:function(e,t,n){var r=n.lastSelection;n.visualMode&&Ut(e,n);if(r){var i=r.anchorMark.find(),s=r.headMark.find();if(!i||!s)return;n.sel={anchor:i,head:s},n.visualMode=!0,n.visualLine=r.visualLine,n.visualBlock=r.visualBlock,Wt(e),an(e,n,"<",Mt(i,s)),an(e,n,">",_t(i,s)),v.signal(e,"vim-mode-change",{mode:"visual",subMode:n.visualLine?"linewise":n.visualBlock?"blockwise":""})}},joinLines:function(e,t,n){var r,i;if(n.visualMode){r=e.getCursor("anchor"),i=e.getCursor("head");if(Ot(i,r)){var s=i;i=r,r=s}i.ch=Pt(e,i.line)-1}else{var o=Math.max(t.repeat,2);r=e.getCursor(),i=wt(e,E(r.line+o-1,Infinity))}var u=0;for(var a=r.line;a1)var s=Array(t.repeat+1).join(s);var p=i.linewise,d=i.blockwise;if(p&&!d)n.visualMode?s=n.visualLine?s.slice(0,-1):"\n"+s.slice(0,s.length-1)+"\n":t.after?(s="\n"+s.slice(0,s.length-1),r.ch=Pt(e,r.line)):r.ch=0;else{if(d){s=s.split("\n");for(var v=0;ve.lastLine()&&e.replaceRange("\n",E(C,0));var k=Pt(e,C);ka.length&&(s=a.length),o=E(i.line,s)}if(r=="\n")n.visualMode||e.replaceRange("",i,o),(v.commands.newlineAndIndentContinueComment||v.commands.newlineAndIndent)(e);else{var f=e.getRange(i,o);f=f.replace(/[^\n]/g,r);if(n.visualBlock){var l=(new Array(e.getOption("tabSize")+1)).join(" ");f=e.getSelection(),f=f.replace(/\t/g,l).replace(/[^\n]/g,r).split("\n"),e.replaceSelections(f)}else e.replaceRange(f,i,o);n.visualMode?(i=Ot(u[0].anchor,u[0].head)?u[0].anchor:u[0].head,e.setCursor(i),$t(e,!1)):e.setCursor(St(o,0,-1))}},incrementNumberToken:function(e,t){var n=e.getCursor(),r=e.getLine(n.line),i=/-?\d+/g,s,o,u,a,f;while((s=i.exec(r))!==null){f=s[0],o=s.index,u=o+f.length;if(n.ch=1)return!0}else e.nextCh===e.reverseSymb&&e.depth--;return!1}},section:{init:function(e){e.curMoveThrough=!0,e.symb=(e.forward?"]":"[")===e.symb?"{":"}"},isComplete:function(e){return e.index===0&&e.nextCh===e.symb}},comment:{isComplete:function(e){var t=e.lastCh==="*"&&e.nextCh==="/";return e.lastCh=e.nextCh,t}},method:{init:function(e){e.symb=e.symb==="m"?"{":"}",e.reverseSymb=e.symb==="{"?"}":"{"},isComplete:function(e){return e.nextCh===e.symb?!0:!1}},preprocess:{init:function(e){e.index=0},isComplete:function(e){if(e.nextCh==="#"){var t=e.lineText.match(/#(\w+)/)[1];if(t==="endif"){if(e.forward&&e.depth===0)return!0;e.depth++}else if(t==="if"){if(!e.forward&&e.depth===0)return!0;e.depth--}if(t==="else"&&e.depth===0)return!0}return!1}}};K("pcre",!0,"boolean"),pn.prototype={getQuery:function(){return nt.query},setQuery:function(e){nt.query=e},getOverlay:function(){return this.searchOverlay},setOverlay:function(e){this.searchOverlay=e},isReversed:function(){return nt.isReversed},setReversed:function(e){nt.isReversed=e},getScrollbarAnnotate:function(){return this.annotate},setScrollbarAnnotate:function(e){this.annotate=e}};var bn={"\\n":"\n","\\r":"\r","\\t":" "},En={"\\/":"/","\\\\":"\\","\\n":"\n","\\r":"\r","\\t":" "},Cn="(Javascript regexp)",Bn=function(){this.buildCommandMap_()};Bn.prototype={processCommand:function(e,t,n){var r=this;e.operation(function(){e.curOp.isVimOp=!0,r._processCommand(e,t,n)})},_processCommand:function(e,t,n){var r=e.state.vim,i=nt.registerController.getRegister(":"),s=i.toString();r.visualMode&&$t(e);var o=new v.StringStream(t);i.setText(t);var u=n||{};u.input=t;try{this.parseInput_(e,o,u)}catch(a){throw Tn(e,a),a}var f,l;if(!u.commandName)u.line!==undefined&&(l="move");else{f=this.matchCommand_(u.commandName);if(f){l=f.name,f.excludeFromCommandHistory&&i.setText(s),this.parseCommandArgs_(o,u,f);if(f.type=="exToKey"){for(var c=0;c0;t--){var n=e.substring(0,t);if(this.commandMap_[n]){var r=this.commandMap_[n];if(r.name.indexOf(e)===0)return r}}return null},buildCommandMap_:function(){this.commandMap_={};for(var e=0;e
";if(!n)for(var s in r){var o=r[s].toString();o.length&&(i+='"'+s+" "+o+"
")}else{var s;n=n.join("");for(var u=0;u"}}Tn(e,i)},sort:function(e,t){function o(){if(t.argString){var e=new v.StringStream(t.argString);e.eat("!")&&(n=!0);if(e.eol())return;if(!e.eatSpace())return"Invalid arguments";var o=e.match(/[a-z]+/);if(o){o=o[0],r=o.indexOf("i")!=-1,i=o.indexOf("u")!=-1;var u=o.indexOf("d")!=-1&&1,a=o.indexOf("x")!=-1&&1,f=o.indexOf("o")!=-1&&1;if(u+a+f>1)return"Invalid arguments";s=u&&"decimal"||a&&"hex"||f&&"octal"}if(e.match(/\/.*\//))return"patterns not supported"}}function b(e,t){if(n){var i;i=e,e=t,t=i}r&&(e=e.toLowerCase(),t=t.toLowerCase());var o=s&&p.exec(e),u=s&&p.exec(t);return o?(o=parseInt((o[1]+o[2]).toLowerCase(),d),u=parseInt((u[1]+u[2]).toLowerCase(),d),o-u):e")}if(!u){Tn(e,c);return}var d=0,v=function(){if(d=f){Tn(e,"Invalid argument: "+t.argString.substring(i));return}for(var l=0;l<=f-a;l++){var c=String.fromCharCode(a+l);delete n.marks[c]}}else delete n.marks[s]}}},Fn=new Bn;v.keyMap.vim={attach:C,detach:N,call:k},K("insertModeEscKeysTimeout",200,"number"),v.keyMap["vim-insert"]={"Ctrl-N":"autocomplete","Ctrl-P":"autocomplete",Enter:function(e){var t=v.commands.newlineAndIndentContinueComment||v.commands.newlineAndIndent;t(e)},fallthrough:["default"],attach:C,detach:N,call:k},v.keyMap["vim-replace"]={Backspace:"goCharLeft",fallthrough:["vim-insert"],attach:C,detach:N,call:k},rt(),v.Vim=S(),S=v.Vim;var tr={"return":"CR",backspace:"BS","delete":"Del",esc:"Esc",left:"Left",right:"Right",up:"Up",down:"Down",space:"Space",home:"Home",end:"End",pageup:"PageUp",pagedown:"PageDown",enter:"CR"},rr=S.handleKey.bind(S);S.handleKey=function(e,t,n){return e.operation(function(){return rr(e,t,n)},!0)},t.CodeMirror=v;var or=S.maybeInitVimState_;t.handler={$id:"ace/keyboard/vim",drawCursor:function(e,t,n,r,s){var o=this.state.vim||{},u=n.characterWidth,a=n.lineHeight,f=t.top,l=t.left;if(!o.insertMode){var c=r.cursor?i.comparePoints(r.cursor,r.start)<=0:s.selection.isBackwards()||s.selection.isEmpty();!c&&l>u&&(l-=u)}!o.insertMode&&o.status&&(a/=2,f+=a),e.left=l+"px",e.top=f+"px",e.width=u+"px",e.height=a+"px"},handleKeyboard:function(e,t,n,r,i){var s=e.editor,o=s.state.cm,u=or(o);if(r==-1)return;if(n=="c"&&t==1){if(!c.isMac&&s.getCopyText())return s.once("copy",function(){s.selection.clearSelection()}),{command:"null",passEvent:!0}}else u.insertMode||c.isMac&&this.handleMacRepeat(e,t,n)&&(t=-1,n=e.inputChar);if(t==-1||t&1||t===0&&n.length>1){var a=u.insertMode,f=nr(t,n,i||{});u.status==null&&(u.status="");var l=sr(o,f,"user");u=or(o),l&&u.status!=null?u.status+=f:u.status==null&&(u.status=""),o._signal("changeStatus");if(!l&&(t!=-1||a))return;return{command:"null",passEvent:!l}}},attach:function(e){e.state||(e.state={});var t=new v(e);e.state.cm=t,e.$vimModeHandler=this,v.keyMap.vim.attach(t),or(t).status=null,t.on("vim-command-done",function(){if(t.virtualSelectionMode())return;or(t).status=null,t.ace._signal("changeStatus"),t.ace.session.markUndoGroup()}),t.on("changeStatus",function(){t.ace.renderer.updateCursor(),t.ace._signal("changeStatus")}),t.on("vim-mode-change",function(){if(t.virtualSelectionMode())return;t.ace.renderer.setStyle("normal-mode",!or(t).insertMode),t._signal("changeStatus")}),t.ace.renderer.setStyle("normal-mode",!or(t).insertMode),e.renderer.$cursorLayer.drawCursor=this.drawCursor.bind(t),this.updateMacCompositionHandlers(e,!0)},detach:function(e){var t=e.state.cm;v.keyMap.vim.detach(t),t.destroy(),e.state.cm=null,e.$vimModeHandler=null,e.renderer.$cursorLayer.drawCursor=null,e.renderer.setStyle("normal-mode",!1),this.updateMacCompositionHandlers(e,!1)},getStatusText:function(e){var t=e.state.cm,n=or(t);if(n.insertMode)return"INSERT";var r="";return n.visualMode&&(r+="VISUAL",n.visualLine&&(r+=" LINE"),n.visualBlock&&(r+=" BLOCK")),n.status&&(r+=(r?" ":"")+n.status),r},handleMacRepeat:function(e,t,n){if(t==-1)e.inputChar=n,e.lastEvent="input";else if(e.inputChar&&e.$lastHash==t&&e.$lastKey==n){if(e.lastEvent=="input")e.lastEvent="input1";else if(e.lastEvent=="input1")return!0}else e.$lastHash=t,e.$lastKey=n,e.lastEvent="keypress"},updateMacCompositionHandlers:function(e,t){var n=function(t){var n=e.state.cm,r=or(n);if(!r.insertMode){var i=this.textInput.getElement();i.blur(),i.focus(),i.value=t}else this.onCompositionUpdateOrig(t)},r=function(t){var n=e.state.cm,r=or(n);r.insertMode||this.onCompositionStartOrig(t)};t?e.onCompositionUpdateOrig||(e.onCompositionUpdateOrig=e.onCompositionUpdate,e.onCompositionUpdate=n,e.onCompositionStartOrig=e.onCompositionStart,e.onCompositionStart=r):e.onCompositionUpdateOrig&&(e.onCompositionUpdate=e.onCompositionUpdateOrig,e.onCompositionUpdateOrig=null,e.onCompositionStart=e.onCompositionStartOrig,e.onCompositionStartOrig=null)}};var ur={getText:function(e,t){return(Math.abs(e.selection.lead.row-t)||t+1+(t<9?"\u00b7":""))+""},getWidth:function(e,t,n){return e.getLength().toString().length*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)},detach:function(e){e.renderer.$gutterLayer.$renderer=null,e.off("changeSelection",this.update)}};S.defineOption({name:"wrap",set:function(e,t){t&&t.ace.setOption("wrap",e)},type:"boolean"},!1),S.defineEx("write","w",function(){console.log(":write is not implemented")}),b.push({keys:"zc",type:"action",action:"fold",actionArgs:{open:!1}},{keys:"zC",type:"action",action:"fold",actionArgs:{open:!1,all:!0}},{keys:"zo",type:"action",action:"fold",actionArgs:{open:!0}},{keys:"zO",type:"action",action:"fold",actionArgs:{open:!0,all:!0}},{keys:"za",type:"action",action:"fold",actionArgs:{toggle:!0}},{keys:"zA",type:"action",action:"fold",actionArgs:{toggle:!0,all:!0}},{keys:"zf",type:"action",action:"fold",actionArgs:{open:!0,all:!0}},{keys:"zd",type:"action",action:"fold",actionArgs:{open:!0,all:!0}},{keys:"",type:"action",action:"aceCommand",actionArgs:{name:"addCursorAbove"}},{keys:"",type:"action",action:"aceCommand",actionArgs:{name:"addCursorBelow"}},{keys:"",type:"action",action:"aceCommand",actionArgs:{name:"addCursorAboveSkipCurrent"}},{keys:"",type:"action",action:"aceCommand",actionArgs:{name:"addCursorBelowSkipCurrent"}},{keys:"",type:"action",action:"aceCommand",actionArgs:{name:"selectMoreBefore"}},{keys:"",type:"action",action:"aceCommand",actionArgs:{name:"selectMoreAfter"}},{keys:"",type:"action",action:"aceCommand",actionArgs:{name:"selectNextBefore"}},{keys:"",type:"action",action:"aceCommand",actionArgs:{name:"selectNextAfter"}}),yt.aceCommand=function(e,t,n){e.vimCmd=t,e.ace.inVirtualSelectionMode?e.ace.on("beforeEndOperation",ar):ar(null,e.ace)},yt.fold=function(e,t,n){e.ace.execCommand(["toggleFoldWidget","toggleFoldWidget","foldOther","unfoldall"][(t.all?2:0)+(t.open?1:0)])},t.handler.defaultKeymap=b,t.handler.actions=yt,t.Vim=S,S.map("Y","yy","normal")}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-assembly_x86.js b/public/themes/pterodactyl/vendor/ace/mode-assembly_x86.js new file mode 100644 index 0000000..1ed120a --- /dev/null +++ b/public/themes/pterodactyl/vendor/ace/mode-assembly_x86.js @@ -0,0 +1 @@ +define("ace/mode/assembly_x86_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"keyword.control.assembly",regex:"\\b(?:aaa|aad|aam|aas|adc|add|addpd|addps|addsd|addss|addsubpd|addsubps|aesdec|aesdeclast|aesenc|aesenclast|aesimc|aeskeygenassist|and|andpd|andps|andnpd|andnps|arpl|blendpd|blendps|blendvpd|blendvps|bound|bsf|bsr|bswap|bt|btc|btr|bts|cbw|cwde|cdqe|clc|cld|cflush|clts|cmc|cmov(?:n?e|ge?|ae?|le?|be?|n?o|n?z)|cmp|cmppd|cmpps|cmps|cnpsb|cmpsw|cmpsd|cmpsq|cmpss|cmpxchg|cmpxchg8b|cmpxchg16b|comisd|comiss|cpuid|crc32|cvtdq2pd|cvtdq2ps|cvtpd2dq|cvtpd2pi|cvtpd2ps|cvtpi2pd|cvtpi2ps|cvtps2dq|cvtps2pd|cvtps2pi|cvtsd2si|cvtsd2ss|cvts2sd|cvtsi2ss|cvtss2sd|cvtss2si|cvttpd2dq|cvtpd2pi|cvttps2dq|cvttps2pi|cvttps2dq|cvttps2pi|cvttsd2si|cvttss2si|cwd|cdq|cqo|daa|das|dec|div|divpd|divps|divsd|divss|dppd|dpps|emms|enter|extractps|f2xm1|fabs|fadd|faddp|fiadd|fbld|fbstp|fchs|fclex|fnclex|fcmov(?:n?e|ge?|ae?|le?|be?|n?o|n?z)|fcom|fcmop|fcompp|fcomi|fcomip|fucomi|fucomip|fcos|fdecstp|fdiv|fdivp|fidiv|fdivr|fdivrp|fidivr|ffree|ficom|ficomp|fild|fincstp|finit|fnint|fist|fistp|fisttp|fld|fld1|fldl2t|fldl2e|fldpi|fldlg2|fldln2|fldz|fldcw|fldenv|fmul|fmulp|fimul|fnop|fpatan|fprem|fprem1|fptan|frndint|frstor|fsave|fnsave|fscale|fsin|fsincos|fsqrt|fst|fstp|fstcw|fnstcw|fstenv|fnstenv|fsts|fnstsw|fsub|fsubp|fisub|fsubr|fsubrp|fisubr|ftst|fucom|fucomp|fucompp|fxam|fxch|fxrstor|fxsave|fxtract|fyl2x|fyl2xp1|haddpd|haddps|husbpd|hsubps|idiv|imul|in|inc|ins|insb|insw|insd|insertps|int|into|invd|invplg|invpcid|iret|iretd|iretq|lahf|lar|lddqu|ldmxcsr|lds|les|lfs|lgs|lss|lea|leave|lfence|lgdt|lidt|llgdt|lmsw|lock|lods|lodsb|lodsw|lodsd|lodsq|lsl|ltr|maskmovdqu|maskmovq|maxpd|maxps|maxsd|maxss|mfence|minpd|minps|minsd|minss|monitor|mov|movapd|movaps|movbe|movd|movq|movddup|movdqa|movdqu|movq2q|movhlps|movhpd|movhps|movlhps|movlpd|movlps|movmskpd|movmskps|movntdqa|movntdq|movnti|movntpd|movntps|movntq|movq|movq2dq|movs|movsb|movsw|movsd|movsq|movsd|movshdup|movsldup|movss|movsx|movsxd|movupd|movups|movzx|mpsadbw|mul|mulpd|mulps|mulsd|mulss|mwait|neg|not|or|orpd|orps|out|outs|outsb|outsw|outsd|pabsb|pabsw|pabsd|packsswb|packssdw|packusdw|packuswbpaddb|paddw|paddd|paddq|paddsb|paddsw|paddusb|paddusw|palignr|pand|pandn|pause|pavgb|pavgw|pblendvb|pblendw|pclmulqdq|pcmpeqb|pcmpeqw|pcmpeqd|pcmpeqq|pcmpestri|pcmpestrm|pcmptb|pcmptgw|pcmpgtd|pcmpgtq|pcmpistri|pcmpisrm|pextrb|pextrd|pextrq|pextrw|phaddw|phaddd|phaddsw|phinposuw|phsubw|phsubd|phsubsw|pinsrb|pinsrd|pinsrq|pinsrw|pmaddubsw|pmadddwd|pmaxsb|pmaxsd|pmaxsw|pmaxsw|pmaxub|pmaxud|pmaxuw|pminsb|pminsd|pminsw|pminub|pminud|pminuw|pmovmskb|pmovsx|pmovzx|pmuldq|pmulhrsw|pmulhuw|pmulhw|pmulld|pmullw|pmuludw|pop|popa|popad|popcnt|popf|popfd|popfq|por|prefetch|psadbw|pshufb|pshufd|pshufhw|pshuflw|pshufw|psignb|psignw|psignd|pslldq|psllw|pslld|psllq|psraw|psrad|psrldq|psrlw|psrld|psrlq|psubb|psubw|psubd|psubq|psubsb|psubsw|psubusb|psubusw|test|ptest|punpckhbw|punpckhwd|punpckhdq|punpckhddq|punpcklbw|punpcklwd|punpckldq|punpckldqd|push|pusha|pushad|pushf|pushfd|pxor|prcl|rcr|rol|ror|rcpps|rcpss|rdfsbase|rdgsbase|rdmsr|rdpmc|rdrand|rdtsc|rdtscp|rep|repe|repz|repne|repnz|roundpd|roundps|roundsd|roundss|rsm|rsqrps|rsqrtss|sahf|sal|sar|shl|shr|sbb|scas|scasb|scasw|scasd|set(?:n?e|ge?|ae?|le?|be?|n?o|n?z)|sfence|sgdt|shld|shrd|shufpd|shufps|sidt|sldt|smsw|sqrtpd|sqrtps|sqrtsd|sqrtss|stc|std|stmxcsr|stos|stosb|stosw|stosd|stosq|str|sub|subpd|subps|subsd|subss|swapgs|syscall|sysenter|sysexit|sysret|teset|ucomisd|ucomiss|ud2|unpckhpd|unpckhps|unpcklpd|unpcklps|vbroadcast|vcvtph2ps|vcvtp2sph|verr|verw|vextractf128|vinsertf128|vmaskmov|vpermilpd|vpermilps|vperm2f128|vtestpd|vtestps|vzeroall|vzeroupper|wait|fwait|wbinvd|wrfsbase|wrgsbase|wrmsr|xadd|xchg|xgetbv|xlat|xlatb|xor|xorpd|xorps|xrstor|xsave|xsaveopt|xsetbv|lzcnt|extrq|insertq|movntsd|movntss|vfmaddpd|vfmaddps|vfmaddsd|vfmaddss|vfmaddsubbpd|vfmaddsubps|vfmsubaddpd|vfmsubaddps|vfmsubpd|vfmsubps|vfmsubsd|vfnmaddpd|vfnmaddps|vfnmaddsd|vfnmaddss|vfnmsubpd|vfnmusbps|vfnmusbsd|vfnmusbss|cvt|xor|cli|sti|hlt|nop|lock|wait|enter|leave|ret|loop(?:n?e|n?z)?|call|j(?:mp|n?e|ge?|ae?|le?|be?|n?o|n?z))\\b",caseInsensitive:!0},{token:"variable.parameter.register.assembly",regex:"\\b(?:CS|DS|ES|FS|GS|SS|RAX|EAX|RBX|EBX|RCX|ECX|RDX|EDX|RCX|RIP|EIP|IP|RSP|ESP|SP|RSI|ESI|SI|RDI|EDI|DI|RFLAGS|EFLAGS|FLAGS|R8-15|(?:Y|X)MM(?:[0-9]|10|11|12|13|14|15)|(?:A|B|C|D)(?:X|H|L)|CR(?:[0-4]|DR(?:[0-7]|TR6|TR7|EFER)))\\b",caseInsensitive:!0},{token:"constant.character.decimal.assembly",regex:"\\b[0-9]+\\b"},{token:"constant.character.hexadecimal.assembly",regex:"\\b0x[A-F0-9]+\\b",caseInsensitive:!0},{token:"constant.character.hexadecimal.assembly",regex:"\\b[A-F0-9]+h\\b",caseInsensitive:!0},{token:"string.assembly",regex:/'([^\\']|\\.)*'/},{token:"string.assembly",regex:/"([^\\"]|\\.)*"/},{token:"support.function.directive.assembly",regex:"^\\[",push:[{token:"support.function.directive.assembly",regex:"\\]$",next:"pop"},{defaultToken:"support.function.directive.assembly"}]},{token:["support.function.directive.assembly","support.function.directive.assembly","entity.name.function.assembly"],regex:"(^struc)( )([_a-zA-Z][_a-zA-Z0-9]*)"},{token:"support.function.directive.assembly",regex:"^endstruc\\b"},{token:["support.function.directive.assembly","entity.name.function.assembly","support.function.directive.assembly","constant.character.assembly"],regex:"^(%macro )([_a-zA-Z][_a-zA-Z0-9]*)( )([0-9]+)"},{token:"support.function.directive.assembly",regex:"^%endmacro"},{token:["text","support.function.directive.assembly","text","entity.name.function.assembly"],regex:"(\\s*)(%define|%xdefine|%idefine|%undef|%assign|%defstr|%strcat|%strlen|%substr|%00|%0|%rotate|%rep|%endrep|%include|\\$\\$|\\$|%unmacro|%if|%elif|%else|%endif|%(?:el)?ifdef|%(?:el)?ifmacro|%(?:el)?ifctx|%(?:el)?ifidn|%(?:el)?ifidni|%(?:el)?ifid|%(?:el)?ifnum|%(?:el)?ifstr|%(?:el)?iftoken|%(?:el)?ifempty|%(?:el)?ifenv|%pathsearch|%depend|%use|%push|%pop|%repl|%arg|%stacksize|%local|%error|%warning|%fatal|%line|%!|%comment|%endcomment|__NASM_VERSION_ID__|__NASM_VER__|__FILE__|__LINE__|__BITS__|__OUTPUT_FORMAT__|__DATE__|__TIME__|__DATE_NUM__|_TIME__NUM__|__UTC_DATE__|__UTC_TIME__|__UTC_DATE_NUM__|__UTC_TIME_NUM__|__POSIX_TIME__|__PASS__|ISTRUC|AT|IEND|BITS 16|BITS 32|BITS 64|USE16|USE32|__SECT__|ABSOLUTE|EXTERN|GLOBAL|COMMON|CPU|FLOAT)\\b( ?)((?:[_a-zA-Z][_a-zA-Z0-9]*)?)",caseInsensitive:!0},{token:"support.function.directive.assembly",regex:"\\b(?:d[bwdqtoy]|res[bwdqto]|equ|times|align|alignb|sectalign|section|ptr|byte|word|dword|qword|incbin)\\b",caseInsensitive:!0},{token:"entity.name.function.assembly",regex:"^\\s*%%[\\w.]+?:$"},{token:"entity.name.function.assembly",regex:"^\\s*%\\$[\\w.]+?:$"},{token:"entity.name.function.assembly",regex:"^[\\w.]+?:"},{token:"entity.name.function.assembly",regex:"^[\\w.]+?\\b"},{token:"comment.assembly",regex:";.*$"}]},this.normalizeRules()};s.metaData={fileTypes:["asm"],name:"Assembly x86",scopeName:"source.assembly"},r.inherits(s,i),t.AssemblyX86HighlightRules=s}),define("ace/mode/folding/coffee",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode","ace/range"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("./fold_mode").FoldMode,s=e("../../range").Range,o=t.FoldMode=function(){};r.inherits(o,i),function(){this.getFoldWidgetRange=function(e,t,n){var r=this.indentationBlock(e,n);if(r)return r;var i=/\S/,o=e.getLine(n),u=o.search(i);if(u==-1||o[u]!="#")return;var a=o.length,f=e.getLength(),l=n,c=n;while(++nl){var p=e.getLine(c).length;return new s(l,a,c,p)}},this.getFoldWidget=function(e,t,n){var r=e.getLine(n),i=r.search(/\S/),s=e.getLine(n+1),o=e.getLine(n-1),u=o.search(/\S/),a=s.search(/\S/);if(i==-1)return e.foldWidgets[n-1]=u!=-1&&u>=|>>>=|<>|&&|\|\||\?:|[*%\/+\-&\^|~!<>=]=?/},{token:"punctuation.operator",regex:"\\?|\\:|\\,|\\;|\\."},{token:"paren.lparen",regex:"[[({]"},{token:"paren.rparen",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],comment:[{token:"comment",regex:".*?\\*\\/",next:"start"},{token:"comment",regex:".+"}],singleLineComment:[{token:"comment",regex:/\\$/,next:"singleLineComment"},{token:"comment",regex:/$/,next:"start"},{defaultToken:"comment"}],directive:[{token:"constant.other.multiline",regex:/\\/},{token:"constant.other.multiline",regex:/.*\\/},{token:"constant.other",regex:"\\s*<.+?>",next:"start"},{token:"constant.other",regex:'\\s*["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]',next:"start"},{token:"constant.other",regex:"\\s*['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']",next:"start"},{token:"constant.other",regex:/[^\\\/]+/,next:"start"}]},this.embedRules(i,"doc-",[i.getEndRule("start")]),this.normalizeRules()};r.inherits(u,s),t.c_cppHighlightRules=u}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/c_cpp",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/c_cpp_highlight_rules","ace/mode/matching_brace_outdent","ace/range","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./c_cpp_highlight_rules").c_cppHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../range").Range,a=e("./behaviour/cstyle").CstyleBehaviour,f=e("./folding/cstyle").FoldMode,l=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new a,this.foldingRules=new f};r.inherits(l,i),function(){this.lineCommentStart="//",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens,o=i.state;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"){var u=t.match(/^.*[\{\(\[]\s*$/);u&&(r+=n)}else if(e=="doc-start"){if(o=="start")return"";var u=t.match(/^\s*(\/?)\*/);u&&(u[1]&&(r+=" "),r+="* ")}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.$id="ace/mode/c_cpp"}.call(l.prototype),t.Mode=l}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-coffee.js b/public/themes/pterodactyl/vendor/ace/mode-coffee.js new file mode 100644 index 0000000..82005fd --- /dev/null +++ b/public/themes/pterodactyl/vendor/ace/mode-coffee.js @@ -0,0 +1 @@ +define("ace/mode/coffee_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";function s(){var e="[$A-Za-z_\\x7f-\\uffff][$\\w\\x7f-\\uffff]*",t="this|throw|then|try|typeof|super|switch|return|break|by|continue|catch|class|in|instanceof|is|isnt|if|else|extends|for|own|finally|function|while|when|new|no|not|delete|debugger|do|loop|of|off|or|on|unless|until|and|yes",n="true|false|null|undefined|NaN|Infinity",r="case|const|default|function|var|void|with|enum|export|implements|interface|let|package|private|protected|public|static|yield",i="Array|Boolean|Date|Function|Number|Object|RegExp|ReferenceError|String|Error|EvalError|InternalError|RangeError|ReferenceError|StopIteration|SyntaxError|TypeError|URIError|ArrayBuffer|Float32Array|Float64Array|Int16Array|Int32Array|Int8Array|Uint16Array|Uint32Array|Uint8Array|Uint8ClampedArray",s="Math|JSON|isNaN|isFinite|parseInt|parseFloat|encodeURI|encodeURIComponent|decodeURI|decodeURIComponent|String|",o="window|arguments|prototype|document",u=this.createKeywordMapper({keyword:t,"constant.language":n,"invalid.illegal":r,"language.support.class":i,"language.support.function":s,"variable.language":o},"identifier"),a={token:["paren.lparen","variable.parameter","paren.rparen","text","storage.type"],regex:/(?:(\()((?:"[^")]*?"|'[^')]*?'|\/[^\/)]*?\/|[^()"'\/])*?)(\))(\s*))?([\-=]>)/.source},f=/\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|[0-2][0-7]{0,2}|3[0-6][0-7]?|37[0-7]?|[4-7][0-7]?|.)/;this.$rules={start:[{token:"constant.numeric",regex:"(?:0x[\\da-fA-F]+|(?:\\d+(?:\\.\\d+)?|\\.\\d+)(?:[eE][+-]?\\d+)?)"},{stateName:"qdoc",token:"string",regex:"'''",next:[{token:"string",regex:"'''",next:"start"},{token:"constant.language.escape",regex:f},{defaultToken:"string"}]},{stateName:"qqdoc",token:"string",regex:'"""',next:[{token:"string",regex:'"""',next:"start"},{token:"paren.string",regex:"#{",push:"start"},{token:"constant.language.escape",regex:f},{defaultToken:"string"}]},{stateName:"qstring",token:"string",regex:"'",next:[{token:"string",regex:"'",next:"start"},{token:"constant.language.escape",regex:f},{defaultToken:"string"}]},{stateName:"qqstring",token:"string.start",regex:'"',next:[{token:"string.end",regex:'"',next:"start"},{token:"paren.string",regex:"#{",push:"start"},{token:"constant.language.escape",regex:f},{defaultToken:"string"}]},{stateName:"js",token:"string",regex:"`",next:[{token:"string",regex:"`",next:"start"},{token:"constant.language.escape",regex:f},{defaultToken:"string"}]},{regex:"[{}]",onMatch:function(e,t,n){this.next="";if(e=="{"&&n.length)return n.unshift("start",t),"paren";if(e=="}"&&n.length){n.shift(),this.next=n.shift()||"";if(this.next.indexOf("string")!=-1)return"paren.string"}return"paren"}},{token:"string.regex",regex:"///",next:"heregex"},{token:"string.regex",regex:/(?:\/(?![\s=])[^[\/\n\\]*(?:(?:\\[\s\S]|\[[^\]\n\\]*(?:\\[\s\S][^\]\n\\]*)*])[^[\/\n\\]*)*\/)(?:[imgy]{0,4})(?!\w)/},{token:"comment",regex:"###(?!#)",next:"comment"},{token:"comment",regex:"#.*"},{token:["punctuation.operator","text","identifier"],regex:"(\\.)(\\s*)("+r+")"},{token:"punctuation.operator",regex:"\\.{1,3}"},{token:["keyword","text","language.support.class","text","keyword","text","language.support.class"],regex:"(class)(\\s+)("+e+")(?:(\\s+)(extends)(\\s+)("+e+"))?"},{token:["entity.name.function","text","keyword.operator","text"].concat(a.token),regex:"("+e+")(\\s*)([=:])(\\s*)"+a.regex},a,{token:"variable",regex:"@(?:"+e+")?"},{token:u,regex:e},{token:"punctuation.operator",regex:"\\,|\\."},{token:"storage.type",regex:"[\\-=]>"},{token:"keyword.operator",regex:"(?:[-+*/%<>&|^!?=]=|>>>=?|\\-\\-|\\+\\+|::|&&=|\\|\\|=|<<=|>>=|\\?\\.|\\.{2,3}|[!*+-=><])"},{token:"paren.lparen",regex:"[({[]"},{token:"paren.rparen",regex:"[\\]})]"},{token:"text",regex:"\\s+"}],heregex:[{token:"string.regex",regex:".*?///[imgy]{0,4}",next:"start"},{token:"comment.regex",regex:"\\s+(?:#.*)?"},{token:"string.regex",regex:"\\S+"}],comment:[{token:"comment",regex:"###",next:"start"},{defaultToken:"comment"}]},this.normalizeRules()}var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules;r.inherits(s,i),t.CoffeeHighlightRules=s}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define("ace/mode/folding/coffee",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode","ace/range"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("./fold_mode").FoldMode,s=e("../../range").Range,o=t.FoldMode=function(){};r.inherits(o,i),function(){this.getFoldWidgetRange=function(e,t,n){var r=this.indentationBlock(e,n);if(r)return r;var i=/\S/,o=e.getLine(n),u=o.search(i);if(u==-1||o[u]!="#")return;var a=o.length,f=e.getLength(),l=n,c=n;while(++nl){var p=e.getLine(c).length;return new s(l,a,c,p)}},this.getFoldWidget=function(e,t,n){var r=e.getLine(n),i=r.search(/\S/),s=e.getLine(n+1),o=e.getLine(n-1),u=o.search(/\S/),a=s.search(/\S/);if(i==-1)return e.foldWidgets[n-1]=u!=-1&&u|\b(?:else|try|(?:swi|ca)tch(?:\s+[$A-Za-z_\x7f-\uffff][$\w\x7f-\uffff]*)?|finally))\s*$|^\s*(else\b\s*)?(?:if|for|while|loop)\b(?!.*\bthen\b)/;this.lineCommentStart="#",this.blockComment={start:"###",end:"###"},this.getNextLineIndent=function(t,n,r){var i=this.$getIndent(n),s=this.getTokenizer().getLineTokens(n,t).tokens;return(!s.length||s[s.length-1].type!=="comment")&&t==="start"&&e.test(n)&&(i+=r),i},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.createWorker=function(e){var t=new a(["ace"],"ace/mode/coffee_worker","Worker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/coffee"}.call(l.prototype),t.Mode=l}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-csharp.js b/public/themes/pterodactyl/vendor/ace/mode-csharp.js new file mode 100644 index 0000000..040bca7 --- /dev/null +++ b/public/themes/pterodactyl/vendor/ace/mode-csharp.js @@ -0,0 +1 @@ +define("ace/mode/doc_comment_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},s.getTagRule(),{defaultToken:"comment.doc",caseInsensitive:!0}]}};r.inherits(s,i),s.getTagRule=function(e){return{token:"comment.doc.tag.storage.type",regex:"\\b(?:TODO|FIXME|XXX|HACK)\\b"}},s.getStartRule=function(e){return{token:"comment.doc",regex:"\\/\\*(?=\\*)",next:e}},s.getEndRule=function(e){return{token:"comment.doc",regex:"\\*\\/",next:e}},t.DocCommentHighlightRules=s}),define("ace/mode/csharp_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./text_highlight_rules").TextHighlightRules,o=function(){var e=this.createKeywordMapper({"variable.language":"this",keyword:"abstract|event|new|struct|as|explicit|null|switch|base|extern|object|this|bool|false|operator|throw|break|finally|out|true|byte|fixed|override|try|case|float|params|typeof|catch|for|private|uint|char|foreach|protected|ulong|checked|goto|public|unchecked|class|if|readonly|unsafe|const|implicit|ref|ushort|continue|in|return|using|decimal|int|sbyte|virtual|default|interface|sealed|volatile|delegate|internal|short|void|do|is|sizeof|while|double|lock|stackalloc|else|long|static|enum|namespace|string|var|dynamic","constant.language":"null|true|false"},"identifier");this.$rules={start:[{token:"comment",regex:"\\/\\/.*$"},i.getStartRule("doc-start"),{token:"comment",regex:"\\/\\*",next:"comment"},{token:"string",regex:/'(?:.|\\(:?u[\da-fA-F]+|x[\da-fA-F]+|[tbrf'"n]))'/},{token:"string",start:'"',end:'"|$',next:[{token:"constant.language.escape",regex:/\\(:?u[\da-fA-F]+|x[\da-fA-F]+|[tbrf'"n])/},{token:"invalid",regex:/\\./}]},{token:"string",start:'@"',end:'"',next:[{token:"constant.language.escape",regex:'""'}]},{token:"string",start:/\$"/,end:'"|$',next:[{token:"constant.language.escape",regex:/\\(:?$)|{{/},{token:"constant.language.escape",regex:/\\(:?u[\da-fA-F]+|x[\da-fA-F]+|[tbrf'"n])/},{token:"invalid",regex:/\\./}]},{token:"constant.numeric",regex:"0[xX][0-9a-fA-F]+\\b"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:"constant.language.boolean",regex:"(?:true|false)\\b"},{token:e,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"!|\\$|%|&|\\*|\\-\\-|\\-|\\+\\+|\\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\\|\\||\\?\\:|\\*=|%=|\\+=|\\-=|&=|\\^=|\\b(?:in|instanceof|new|delete|typeof|void)"},{token:"keyword",regex:"^\\s*#(if|else|elif|endif|define|undef|warning|error|line|region|endregion|pragma)"},{token:"punctuation.operator",regex:"\\?|\\:|\\,|\\;|\\."},{token:"paren.lparen",regex:"[[({]"},{token:"paren.rparen",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],comment:[{token:"comment",regex:".*?\\*\\/",next:"start"},{token:"comment",regex:".+"}]},this.embedRules(i,"doc-",[i.getEndRule("start")]),this.normalizeRules()};r.inherits(o,s),t.CSharpHighlightRules=o}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/folding/csharp",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./cstyle").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.usingRe=/^\s*using \S/,this.getFoldWidgetRangeBase=this.getFoldWidgetRange,this.getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=this.getFoldWidgetBase(e,t,n);if(!r){var i=e.getLine(n);if(/^\s*#region\b/.test(i))return"start";var s=this.usingRe;if(s.test(i)){var o=e.getLine(n-1),u=e.getLine(n+1);if(!s.test(o)&&s.test(u))return"start"}}return r},this.getFoldWidgetRange=function(e,t,n){var r=this.getFoldWidgetRangeBase(e,t,n);if(r)return r;var i=e.getLine(n);if(this.usingRe.test(i))return this.getUsingStatementBlock(e,i,n);if(/^\s*#region\b/.test(i))return this.getRegionBlock(e,i,n)},this.getUsingStatementBlock=function(e,t,n){var r=t.match(this.usingRe)[0].length-1,s=e.getLength(),o=n,u=n;while(++no){var a=e.getLine(u).length;return new i(o,r,u,a)}},this.getRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*#(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/csharp",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/csharp_highlight_rules","ace/mode/matching_brace_outdent","ace/mode/behaviour/cstyle","ace/mode/folding/csharp"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./csharp_highlight_rules").CSharpHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("./behaviour/cstyle").CstyleBehaviour,a=e("./folding/csharp").FoldMode,f=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new u,this.foldingRules=new a};r.inherits(f,i),function(){this.lineCommentStart="//",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().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},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.createWorker=function(e){return null},this.$id="ace/mode/csharp"}.call(f.prototype),t.Mode=f}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-css.js b/public/themes/pterodactyl/vendor/ace/mode-css.js new file mode 100644 index 0000000..b41ecdc --- /dev/null +++ b/public/themes/pterodactyl/vendor/ace/mode-css.js @@ -0,0 +1 @@ +define("ace/mode/css_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text_highlight_rules").TextHighlightRules,o=t.supportType="align-content|align-items|align-self|all|animation|animation-delay|animation-direction|animation-duration|animation-fill-mode|animation-iteration-count|animation-name|animation-play-state|animation-timing-function|backface-visibility|background|background-attachment|background-blend-mode|background-clip|background-color|background-image|background-origin|background-position|background-repeat|background-size|border|border-bottom|border-bottom-color|border-bottom-left-radius|border-bottom-right-radius|border-bottom-style|border-bottom-width|border-collapse|border-color|border-image|border-image-outset|border-image-repeat|border-image-slice|border-image-source|border-image-width|border-left|border-left-color|border-left-style|border-left-width|border-radius|border-right|border-right-color|border-right-style|border-right-width|border-spacing|border-style|border-top|border-top-color|border-top-left-radius|border-top-right-radius|border-top-style|border-top-width|border-width|bottom|box-shadow|box-sizing|caption-side|clear|clip|color|column-count|column-fill|column-gap|column-rule|column-rule-color|column-rule-style|column-rule-width|column-span|column-width|columns|content|counter-increment|counter-reset|cursor|direction|display|empty-cells|filter|flex|flex-basis|flex-direction|flex-flow|flex-grow|flex-shrink|flex-wrap|float|font|font-family|font-size|font-size-adjust|font-stretch|font-style|font-variant|font-weight|hanging-punctuation|height|justify-content|left|letter-spacing|line-height|list-style|list-style-image|list-style-position|list-style-type|margin|margin-bottom|margin-left|margin-right|margin-top|max-height|max-width|min-height|min-width|nav-down|nav-index|nav-left|nav-right|nav-up|opacity|order|outline|outline-color|outline-offset|outline-style|outline-width|overflow|overflow-x|overflow-y|padding|padding-bottom|padding-left|padding-right|padding-top|page-break-after|page-break-before|page-break-inside|perspective|perspective-origin|position|quotes|resize|right|tab-size|table-layout|text-align|text-align-last|text-decoration|text-decoration-color|text-decoration-line|text-decoration-style|text-indent|text-justify|text-overflow|text-shadow|text-transform|top|transform|transform-origin|transform-style|transition|transition-delay|transition-duration|transition-property|transition-timing-function|unicode-bidi|vertical-align|visibility|white-space|width|word-break|word-spacing|word-wrap|z-index",u=t.supportFunction="rgb|rgba|url|attr|counter|counters",a=t.supportConstant="absolute|after-edge|after|all-scroll|all|alphabetic|always|antialiased|armenian|auto|avoid-column|avoid-page|avoid|balance|baseline|before-edge|before|below|bidi-override|block-line-height|block|bold|bolder|border-box|both|bottom|box|break-all|break-word|capitalize|caps-height|caption|center|central|char|circle|cjk-ideographic|clone|close-quote|col-resize|collapse|column|consider-shifts|contain|content-box|cover|crosshair|cubic-bezier|dashed|decimal-leading-zero|decimal|default|disabled|disc|disregard-shifts|distribute-all-lines|distribute-letter|distribute-space|distribute|dotted|double|e-resize|ease-in|ease-in-out|ease-out|ease|ellipsis|end|exclude-ruby|fill|fixed|georgian|glyphs|grid-height|groove|hand|hanging|hebrew|help|hidden|hiragana-iroha|hiragana|horizontal|icon|ideograph-alpha|ideograph-numeric|ideograph-parenthesis|ideograph-space|ideographic|inactive|include-ruby|inherit|initial|inline-block|inline-box|inline-line-height|inline-table|inline|inset|inside|inter-ideograph|inter-word|invert|italic|justify|katakana-iroha|katakana|keep-all|last|left|lighter|line-edge|line-through|line|linear|list-item|local|loose|lower-alpha|lower-greek|lower-latin|lower-roman|lowercase|lr-tb|ltr|mathematical|max-height|max-size|medium|menu|message-box|middle|move|n-resize|ne-resize|newspaper|no-change|no-close-quote|no-drop|no-open-quote|no-repeat|none|normal|not-allowed|nowrap|nw-resize|oblique|open-quote|outset|outside|overline|padding-box|page|pointer|pre-line|pre-wrap|pre|preserve-3d|progress|relative|repeat-x|repeat-y|repeat|replaced|reset-size|ridge|right|round|row-resize|rtl|s-resize|scroll|se-resize|separate|slice|small-caps|small-caption|solid|space|square|start|static|status-bar|step-end|step-start|steps|stretch|strict|sub|super|sw-resize|table-caption|table-cell|table-column-group|table-column|table-footer-group|table-header-group|table-row-group|table-row|table|tb-rl|text-after-edge|text-before-edge|text-bottom|text-size|text-top|text|thick|thin|transparent|underline|upper-alpha|upper-latin|upper-roman|uppercase|use-script|vertical-ideographic|vertical-text|visible|w-resize|wait|whitespace|z-index|zero",f=t.supportConstantColor="aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow",l=t.supportConstantFonts="arial|century|comic|courier|cursive|fantasy|garamond|georgia|helvetica|impact|lucida|symbol|system|tahoma|times|trebuchet|utopia|verdana|webdings|sans-serif|serif|monospace",c=t.numRe="\\-?(?:(?:[0-9]+)|(?:[0-9]*\\.[0-9]+))",h=t.pseudoElements="(\\:+)\\b(after|before|first-letter|first-line|moz-selection|selection)\\b",p=t.pseudoClasses="(:)\\b(active|checked|disabled|empty|enabled|first-child|first-of-type|focus|hover|indeterminate|invalid|last-child|last-of-type|link|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|only-child|only-of-type|required|root|target|valid|visited)\\b",d=function(){var e=this.createKeywordMapper({"support.function":u,"support.constant":a,"support.type":o,"support.constant.color":f,"support.constant.fonts":l},"text",!0);this.$rules={start:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"@.*?{",push:"media"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],media:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"\\}",next:"pop"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],comment:[{token:"comment",regex:"\\*\\/",next:"pop"},{defaultToken:"comment"}],ruleset:[{token:"paren.rparen",regex:"\\}",next:"pop"},{token:"comment",regex:"\\/\\*",push:"comment"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:["constant.numeric","keyword"],regex:"("+c+")(ch|cm|deg|em|ex|fr|gd|grad|Hz|in|kHz|mm|ms|pc|pt|px|rad|rem|s|turn|vh|vm|vw|%)"},{token:"constant.numeric",regex:c},{token:"constant.numeric",regex:"#[a-f0-9]{6}"},{token:"constant.numeric",regex:"#[a-f0-9]{3}"},{token:["punctuation","entity.other.attribute-name.pseudo-element.css"],regex:h},{token:["punctuation","entity.other.attribute-name.pseudo-class.css"],regex:p},{token:["support.function","string","support.function"],regex:"(url\\()(.*)(\\))"},{token:e,regex:"\\-?[a-zA-Z_][a-zA-Z0-9_\\-]*"},{caseInsensitive:!0}]},this.normalizeRules()};r.inherits(d,s),t.CssHighlightRules=d}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define("ace/mode/css_completions",["require","exports","module"],function(e,t,n){"use strict";var r={background:{"#$0":1},"background-color":{"#$0":1,transparent:1,fixed:1},"background-image":{"url('/$0')":1},"background-repeat":{repeat:1,"repeat-x":1,"repeat-y":1,"no-repeat":1,inherit:1},"background-position":{bottom:2,center:2,left:2,right:2,top:2,inherit:2},"background-attachment":{scroll:1,fixed:1},"background-size":{cover:1,contain:1},"background-clip":{"border-box":1,"padding-box":1,"content-box":1},"background-origin":{"border-box":1,"padding-box":1,"content-box":1},border:{"solid $0":1,"dashed $0":1,"dotted $0":1,"#$0":1},"border-color":{"#$0":1},"border-style":{solid:2,dashed:2,dotted:2,"double":2,groove:2,hidden:2,inherit:2,inset:2,none:2,outset:2,ridged:2},"border-collapse":{collapse:1,separate:1},bottom:{px:1,em:1,"%":1},clear:{left:1,right:1,both:1,none:1},color:{"#$0":1,"rgb(#$00,0,0)":1},cursor:{"default":1,pointer:1,move:1,text:1,wait:1,help:1,progress:1,"n-resize":1,"ne-resize":1,"e-resize":1,"se-resize":1,"s-resize":1,"sw-resize":1,"w-resize":1,"nw-resize":1},display:{none:1,block:1,inline:1,"inline-block":1,"table-cell":1},"empty-cells":{show:1,hide:1},"float":{left:1,right:1,none:1},"font-family":{Arial:2,"Comic Sans MS":2,Consolas:2,"Courier New":2,Courier:2,Georgia:2,Monospace:2,"Sans-Serif":2,"Segoe UI":2,Tahoma:2,"Times New Roman":2,"Trebuchet MS":2,Verdana:1},"font-size":{px:1,em:1,"%":1},"font-weight":{bold:1,normal:1},"font-style":{italic:1,normal:1},"font-variant":{normal:1,"small-caps":1},height:{px:1,em:1,"%":1},left:{px:1,em:1,"%":1},"letter-spacing":{normal:1},"line-height":{normal:1},"list-style-type":{none:1,disc:1,circle:1,square:1,decimal:1,"decimal-leading-zero":1,"lower-roman":1,"upper-roman":1,"lower-greek":1,"lower-latin":1,"upper-latin":1,georgian:1,"lower-alpha":1,"upper-alpha":1},margin:{px:1,em:1,"%":1},"margin-right":{px:1,em:1,"%":1},"margin-left":{px:1,em:1,"%":1},"margin-top":{px:1,em:1,"%":1},"margin-bottom":{px:1,em:1,"%":1},"max-height":{px:1,em:1,"%":1},"max-width":{px:1,em:1,"%":1},"min-height":{px:1,em:1,"%":1},"min-width":{px:1,em:1,"%":1},overflow:{hidden:1,visible:1,auto:1,scroll:1},"overflow-x":{hidden:1,visible:1,auto:1,scroll:1},"overflow-y":{hidden:1,visible:1,auto:1,scroll:1},padding:{px:1,em:1,"%":1},"padding-top":{px:1,em:1,"%":1},"padding-right":{px:1,em:1,"%":1},"padding-bottom":{px:1,em:1,"%":1},"padding-left":{px:1,em:1,"%":1},"page-break-after":{auto:1,always:1,avoid:1,left:1,right:1},"page-break-before":{auto:1,always:1,avoid:1,left:1,right:1},position:{absolute:1,relative:1,fixed:1,"static":1},right:{px:1,em:1,"%":1},"table-layout":{fixed:1,auto:1},"text-decoration":{none:1,underline:1,"line-through":1,blink:1},"text-align":{left:1,right:1,center:1,justify:1},"text-transform":{capitalize:1,uppercase:1,lowercase:1,none:1},top:{px:1,em:1,"%":1},"vertical-align":{top:1,bottom:1},visibility:{hidden:1,visible:1},"white-space":{nowrap:1,normal:1,pre:1,"pre-line":1,"pre-wrap":1},width:{px:1,em:1,"%":1},"word-spacing":{normal:1},filter:{"alpha(opacity=$0100)":1},"text-shadow":{"$02px 2px 2px #777":1},"text-overflow":{"ellipsis-word":1,clip:1,ellipsis:1},"-moz-border-radius":1,"-moz-border-radius-topright":1,"-moz-border-radius-bottomright":1,"-moz-border-radius-topleft":1,"-moz-border-radius-bottomleft":1,"-webkit-border-radius":1,"-webkit-border-top-right-radius":1,"-webkit-border-top-left-radius":1,"-webkit-border-bottom-right-radius":1,"-webkit-border-bottom-left-radius":1,"-moz-box-shadow":1,"-webkit-box-shadow":1,transform:{"rotate($00deg)":1,"skew($00deg)":1},"-moz-transform":{"rotate($00deg)":1,"skew($00deg)":1},"-webkit-transform":{"rotate($00deg)":1,"skew($00deg)":1}},i=function(){};(function(){this.completionsDefined=!1,this.defineCompletions=function(){if(document){var e=document.createElement("c").style;for(var t in e){if(typeof e[t]!="string")continue;var n=t.replace(/[A-Z]/g,function(e){return"-"+e.toLowerCase()});r.hasOwnProperty(n)||(r[n]=1)}}this.completionsDefined=!0},this.getCompletions=function(e,t,n,r){this.completionsDefined||this.defineCompletions();var i=t.getTokenAt(n.row,n.column);if(!i)return[];if(e==="ruleset"){var s=t.getLine(n.row).substr(0,n.column);return/:[^;]+$/.test(s)?(/([\w\-]+):[^:]*$/.test(s),this.getPropertyValueCompletions(e,t,n,r)):this.getPropertyCompletions(e,t,n,r)}return[]},this.getPropertyCompletions=function(e,t,n,i){var s=Object.keys(r);return s.map(function(e){return{caption:e,snippet:e+": $0",meta:"property",score:Number.MAX_VALUE}})},this.getPropertyValueCompletions=function(e,t,n,i){var s=t.getLine(n.row).substr(0,n.column),o=(/([\w\-]+):[^:]*$/.exec(s)||{})[1];if(!o)return[];var u=[];return o in r&&typeof r[o]=="object"&&(u=Object.keys(r[o])),u.map(function(e){return{caption:e,snippet:e,meta:"property value",score:Number.MAX_VALUE}})}}).call(i.prototype),t.CssCompletions=i}),define("ace/mode/behaviour/css",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/mode/behaviour/cstyle","ace/token_iterator"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("./cstyle").CstyleBehaviour,o=e("../../token_iterator").TokenIterator,u=function(){this.inherit(s),this.add("colon","insertion",function(e,t,n,r,i){if(i===":"){var s=n.getCursorPosition(),u=new o(r,s.row,s.column),a=u.getCurrentToken();a&&a.value.match(/\s+/)&&(a=u.stepBackward());if(a&&a.type==="support.type"){var f=r.doc.getLine(s.row),l=f.substring(s.column,s.column+1);if(l===":")return{text:"",selection:[1,1]};if(!f.substring(s.column).match(/^\s*;/))return{text:":;",selection:[1,1]}}}}),this.add("colon","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s===":"){var u=n.getCursorPosition(),a=new o(r,u.row,u.column),f=a.getCurrentToken();f&&f.value.match(/\s+/)&&(f=a.stepBackward());if(f&&f.type==="support.type"){var l=r.doc.getLine(i.start.row),c=l.substring(i.end.column,i.end.column+1);if(c===";")return i.end.column++,i}}}),this.add("semicolon","insertion",function(e,t,n,r,i){if(i===";"){var s=n.getCursorPosition(),o=r.doc.getLine(s.row),u=o.substring(s.column,s.column+1);if(u===";")return{text:"",selection:[1,1]}}})};r.inherits(u,s),t.CssBehaviour=u}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/css",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/css_highlight_rules","ace/mode/matching_brace_outdent","ace/worker/worker_client","ace/mode/css_completions","ace/mode/behaviour/css","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./css_highlight_rules").CssHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../worker/worker_client").WorkerClient,a=e("./css_completions").CssCompletions,f=e("./behaviour/css").CssBehaviour,l=e("./folding/cstyle").FoldMode,c=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new f,this.$completer=new a,this.foldingRules=new l};r.inherits(c,i),function(){this.foldingRules="cStyle",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e).tokens;if(i.length&&i[i.length-1].type=="comment")return r;var s=t.match(/^.*\{\s*$/);return s&&(r+=n),r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.getCompletions=function(e,t,n,r){return this.$completer.getCompletions(e,t,n,r)},this.createWorker=function(e){var t=new u(["ace"],"ace/mode/css_worker","Worker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/css"}.call(c.prototype),t.Mode=c}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-golang.js b/public/themes/pterodactyl/vendor/ace/mode-golang.js new file mode 100644 index 0000000..0bec98a --- /dev/null +++ b/public/themes/pterodactyl/vendor/ace/mode-golang.js @@ -0,0 +1 @@ +define("ace/mode/doc_comment_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},s.getTagRule(),{defaultToken:"comment.doc",caseInsensitive:!0}]}};r.inherits(s,i),s.getTagRule=function(e){return{token:"comment.doc.tag.storage.type",regex:"\\b(?:TODO|FIXME|XXX|HACK)\\b"}},s.getStartRule=function(e){return{token:"comment.doc",regex:"\\/\\*(?=\\*)",next:e}},s.getEndRule=function(e){return{token:"comment.doc",regex:"\\*\\/",next:e}},t.DocCommentHighlightRules=s}),define("ace/mode/golang_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./text_highlight_rules").TextHighlightRules,o=function(){var e="else|break|case|return|goto|if|const|select|continue|struct|default|switch|for|range|func|import|package|chan|defer|fallthrough|go|interface|map|range|select|type|var",t="string|uint8|uint16|uint32|uint64|int8|int16|int32|int64|float32|float64|complex64|complex128|byte|rune|uint|int|uintptr|bool|error",n="new|close|cap|copy|panic|panicln|print|println|len|make|delete|real|recover|imag|append",r="nil|true|false|iota",s=this.createKeywordMapper({keyword:e,"constant.language":r,"support.function":n,"support.type":t},""),o="\\\\(?:[0-7]{3}|x\\h{2}|u{4}|U\\h{6}|[abfnrtv'\"\\\\])".replace(/\\h/g,"[a-fA-F\\d]");this.$rules={start:[{token:"comment",regex:"\\/\\/.*$"},i.getStartRule("doc-start"),{token:"comment.start",regex:"\\/\\*",next:"comment"},{token:"string",regex:/"(?:[^"\\]|\\.)*?"/},{token:"string",regex:"`",next:"bqstring"},{token:"constant.numeric",regex:"'(?:[^\\'\ud800-\udbff]|[\ud800-\udbff][\udc00-\udfff]|"+o.replace('"',"")+")'"},{token:"constant.numeric",regex:"0[xX][0-9a-fA-F]+\\b"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:["keyword","text","entity.name.function"],regex:"(func)(\\s+)([a-zA-Z_$][a-zA-Z0-9_$]*)\\b"},{token:function(e){return e[e.length-1]=="("?[{type:s(e.slice(0,-1))||"support.function",value:e.slice(0,-1)},{type:"paren.lparen",value:e.slice(-1)}]:s(e)||"identifier"},regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b\\(?"},{token:"keyword.operator",regex:"!|\\$|%|&|\\*|\\-\\-|\\-|\\+\\+|\\+|~|==|=|!=|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\\|\\||\\?\\:|\\*=|%=|\\+=|\\-=|&=|\\^="},{token:"punctuation.operator",regex:"\\?|\\:|\\,|\\;|\\."},{token:"paren.lparen",regex:"[[({]"},{token:"paren.rparen",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],comment:[{token:"comment.end",regex:"\\*\\/",next:"start"},{defaultToken:"comment"}],bqstring:[{token:"string",regex:"`",next:"start"},{defaultToken:"string"}]},this.embedRules(i,"doc-",[i.getEndRule("start")])};r.inherits(o,s),t.GolangHighlightRules=o}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/golang",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/golang_highlight_rules","ace/mode/matching_brace_outdent","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle"],function(e,t,n){var r=e("../lib/oop"),i=e("./text").Mode,s=e("./golang_highlight_rules").GolangHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("./behaviour/cstyle").CstyleBehaviour,a=e("./folding/cstyle").FoldMode,f=function(){this.HighlightRules=s,this.$outdent=new o,this.foldingRules=new a,this.$behaviour=new u};r.inherits(f,i),function(){this.lineCommentStart="//",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens,o=i.state;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"){var u=t.match(/^.*[\{\(\[]\s*$/);u&&(r+=n)}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.$id="ace/mode/golang"}.call(f.prototype),t.Mode=f}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-haml.js b/public/themes/pterodactyl/vendor/ace/mode-haml.js new file mode 100644 index 0000000..23eee53 --- /dev/null +++ b/public/themes/pterodactyl/vendor/ace/mode-haml.js @@ -0,0 +1 @@ +define("ace/mode/css_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text_highlight_rules").TextHighlightRules,o=t.supportType="align-content|align-items|align-self|all|animation|animation-delay|animation-direction|animation-duration|animation-fill-mode|animation-iteration-count|animation-name|animation-play-state|animation-timing-function|backface-visibility|background|background-attachment|background-blend-mode|background-clip|background-color|background-image|background-origin|background-position|background-repeat|background-size|border|border-bottom|border-bottom-color|border-bottom-left-radius|border-bottom-right-radius|border-bottom-style|border-bottom-width|border-collapse|border-color|border-image|border-image-outset|border-image-repeat|border-image-slice|border-image-source|border-image-width|border-left|border-left-color|border-left-style|border-left-width|border-radius|border-right|border-right-color|border-right-style|border-right-width|border-spacing|border-style|border-top|border-top-color|border-top-left-radius|border-top-right-radius|border-top-style|border-top-width|border-width|bottom|box-shadow|box-sizing|caption-side|clear|clip|color|column-count|column-fill|column-gap|column-rule|column-rule-color|column-rule-style|column-rule-width|column-span|column-width|columns|content|counter-increment|counter-reset|cursor|direction|display|empty-cells|filter|flex|flex-basis|flex-direction|flex-flow|flex-grow|flex-shrink|flex-wrap|float|font|font-family|font-size|font-size-adjust|font-stretch|font-style|font-variant|font-weight|hanging-punctuation|height|justify-content|left|letter-spacing|line-height|list-style|list-style-image|list-style-position|list-style-type|margin|margin-bottom|margin-left|margin-right|margin-top|max-height|max-width|min-height|min-width|nav-down|nav-index|nav-left|nav-right|nav-up|opacity|order|outline|outline-color|outline-offset|outline-style|outline-width|overflow|overflow-x|overflow-y|padding|padding-bottom|padding-left|padding-right|padding-top|page-break-after|page-break-before|page-break-inside|perspective|perspective-origin|position|quotes|resize|right|tab-size|table-layout|text-align|text-align-last|text-decoration|text-decoration-color|text-decoration-line|text-decoration-style|text-indent|text-justify|text-overflow|text-shadow|text-transform|top|transform|transform-origin|transform-style|transition|transition-delay|transition-duration|transition-property|transition-timing-function|unicode-bidi|vertical-align|visibility|white-space|width|word-break|word-spacing|word-wrap|z-index",u=t.supportFunction="rgb|rgba|url|attr|counter|counters",a=t.supportConstant="absolute|after-edge|after|all-scroll|all|alphabetic|always|antialiased|armenian|auto|avoid-column|avoid-page|avoid|balance|baseline|before-edge|before|below|bidi-override|block-line-height|block|bold|bolder|border-box|both|bottom|box|break-all|break-word|capitalize|caps-height|caption|center|central|char|circle|cjk-ideographic|clone|close-quote|col-resize|collapse|column|consider-shifts|contain|content-box|cover|crosshair|cubic-bezier|dashed|decimal-leading-zero|decimal|default|disabled|disc|disregard-shifts|distribute-all-lines|distribute-letter|distribute-space|distribute|dotted|double|e-resize|ease-in|ease-in-out|ease-out|ease|ellipsis|end|exclude-ruby|fill|fixed|georgian|glyphs|grid-height|groove|hand|hanging|hebrew|help|hidden|hiragana-iroha|hiragana|horizontal|icon|ideograph-alpha|ideograph-numeric|ideograph-parenthesis|ideograph-space|ideographic|inactive|include-ruby|inherit|initial|inline-block|inline-box|inline-line-height|inline-table|inline|inset|inside|inter-ideograph|inter-word|invert|italic|justify|katakana-iroha|katakana|keep-all|last|left|lighter|line-edge|line-through|line|linear|list-item|local|loose|lower-alpha|lower-greek|lower-latin|lower-roman|lowercase|lr-tb|ltr|mathematical|max-height|max-size|medium|menu|message-box|middle|move|n-resize|ne-resize|newspaper|no-change|no-close-quote|no-drop|no-open-quote|no-repeat|none|normal|not-allowed|nowrap|nw-resize|oblique|open-quote|outset|outside|overline|padding-box|page|pointer|pre-line|pre-wrap|pre|preserve-3d|progress|relative|repeat-x|repeat-y|repeat|replaced|reset-size|ridge|right|round|row-resize|rtl|s-resize|scroll|se-resize|separate|slice|small-caps|small-caption|solid|space|square|start|static|status-bar|step-end|step-start|steps|stretch|strict|sub|super|sw-resize|table-caption|table-cell|table-column-group|table-column|table-footer-group|table-header-group|table-row-group|table-row|table|tb-rl|text-after-edge|text-before-edge|text-bottom|text-size|text-top|text|thick|thin|transparent|underline|upper-alpha|upper-latin|upper-roman|uppercase|use-script|vertical-ideographic|vertical-text|visible|w-resize|wait|whitespace|z-index|zero",f=t.supportConstantColor="aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow",l=t.supportConstantFonts="arial|century|comic|courier|cursive|fantasy|garamond|georgia|helvetica|impact|lucida|symbol|system|tahoma|times|trebuchet|utopia|verdana|webdings|sans-serif|serif|monospace",c=t.numRe="\\-?(?:(?:[0-9]+)|(?:[0-9]*\\.[0-9]+))",h=t.pseudoElements="(\\:+)\\b(after|before|first-letter|first-line|moz-selection|selection)\\b",p=t.pseudoClasses="(:)\\b(active|checked|disabled|empty|enabled|first-child|first-of-type|focus|hover|indeterminate|invalid|last-child|last-of-type|link|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|only-child|only-of-type|required|root|target|valid|visited)\\b",d=function(){var e=this.createKeywordMapper({"support.function":u,"support.constant":a,"support.type":o,"support.constant.color":f,"support.constant.fonts":l},"text",!0);this.$rules={start:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"@.*?{",push:"media"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],media:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"\\}",next:"pop"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],comment:[{token:"comment",regex:"\\*\\/",next:"pop"},{defaultToken:"comment"}],ruleset:[{token:"paren.rparen",regex:"\\}",next:"pop"},{token:"comment",regex:"\\/\\*",push:"comment"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:["constant.numeric","keyword"],regex:"("+c+")(ch|cm|deg|em|ex|fr|gd|grad|Hz|in|kHz|mm|ms|pc|pt|px|rad|rem|s|turn|vh|vm|vw|%)"},{token:"constant.numeric",regex:c},{token:"constant.numeric",regex:"#[a-f0-9]{6}"},{token:"constant.numeric",regex:"#[a-f0-9]{3}"},{token:["punctuation","entity.other.attribute-name.pseudo-element.css"],regex:h},{token:["punctuation","entity.other.attribute-name.pseudo-class.css"],regex:p},{token:["support.function","string","support.function"],regex:"(url\\()(.*)(\\))"},{token:e,regex:"\\-?[a-zA-Z_][a-zA-Z0-9_\\-]*"},{caseInsensitive:!0}]},this.normalizeRules()};r.inherits(d,s),t.CssHighlightRules=d}),define("ace/mode/doc_comment_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},s.getTagRule(),{defaultToken:"comment.doc",caseInsensitive:!0}]}};r.inherits(s,i),s.getTagRule=function(e){return{token:"comment.doc.tag.storage.type",regex:"\\b(?:TODO|FIXME|XXX|HACK)\\b"}},s.getStartRule=function(e){return{token:"comment.doc",regex:"\\/\\*(?=\\*)",next:e}},s.getEndRule=function(e){return{token:"comment.doc",regex:"\\*\\/",next:e}},t.DocCommentHighlightRules=s}),define("ace/mode/javascript_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";function a(){var e=o.replace("\\d","\\d\\-"),t={onMatch:function(e,t,n){var r=e.charAt(1)=="/"?2:1;if(r==1)t!=this.nextState?n.unshift(this.next,this.nextState,0):n.unshift(this.next),n[2]++;else if(r==2&&t==this.nextState){n[1]--;if(!n[1]||n[1]<0)n.shift(),n.shift()}return[{type:"meta.tag.punctuation."+(r==1?"":"end-")+"tag-open.xml",value:e.slice(0,r)},{type:"meta.tag.tag-name.xml",value:e.substr(r)}]},regex:"",onMatch:function(e,t,n){return t==n[0]&&n.shift(),e.length==2&&(n[0]==this.nextState&&n[1]--,(!n[1]||n[1]<0)&&n.splice(0,2)),this.next=n[0]||"start",[{type:this.token,value:e}]},nextState:"jsx"},n,f("jsxAttributes"),{token:"entity.other.attribute-name.xml",regex:e},{token:"keyword.operator.attribute-equals.xml",regex:"="},{token:"text.tag-whitespace.xml",regex:"\\s+"},{token:"string.attribute-value.xml",regex:"'",stateName:"jsx_attr_q",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',stateName:"jsx_attr_qq",push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},t],this.$rules.reference=[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}]}function f(e){return[{token:"comment",regex:/\/\*/,next:[i.getTagRule(),{token:"comment",regex:"\\*\\/",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]},{token:"comment",regex:"\\/\\/",next:[i.getTagRule(),{token:"comment",regex:"$|^",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]}]}var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./text_highlight_rules").TextHighlightRules,o="[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*",u=function(e){var t=this.createKeywordMapper({"variable.language":"Array|Boolean|Date|Function|Iterator|Number|Object|RegExp|String|Proxy|Namespace|QName|XML|XMLList|ArrayBuffer|Float32Array|Float64Array|Int16Array|Int32Array|Int8Array|Uint16Array|Uint32Array|Uint8Array|Uint8ClampedArray|Error|EvalError|InternalError|RangeError|ReferenceError|StopIteration|SyntaxError|TypeError|URIError|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|eval|isFinite|isNaN|parseFloat|parseInt|JSON|Math|this|arguments|prototype|window|document",keyword:"const|yield|import|get|set|async|await|break|case|catch|continue|default|delete|do|else|finally|for|function|if|in|of|instanceof|new|return|switch|throw|try|typeof|let|var|while|with|debugger|__parent__|__count__|escape|unescape|with|__proto__|class|enum|extends|super|export|implements|private|public|interface|package|protected|static","storage.type":"const|let|var|function","constant.language":"null|Infinity|NaN|undefined","support.function":"alert","constant.language.boolean":"true|false"},"identifier"),n="case|do|else|finally|in|instanceof|return|throw|try|typeof|yield|void",r="\\\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|u{[0-9a-fA-F]{1,6}}|[0-2][0-7]{0,2}|3[0-7][0-7]?|[4-7][0-7]?|.)";this.$rules={no_regex:[i.getStartRule("doc-start"),f("no_regex"),{token:"string",regex:"'(?=.)",next:"qstring"},{token:"string",regex:'"(?=.)',next:"qqstring"},{token:"constant.numeric",regex:/0(?:[xX][0-9a-fA-F]+|[bB][01]+)\b/},{token:"constant.numeric",regex:/[+-]?\d[\d_]*(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/},{token:["storage.type","punctuation.operator","support.function","punctuation.operator","entity.name.function","text","keyword.operator"],regex:"("+o+")(\\.)(prototype)(\\.)("+o+")(\\s*)(=)",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s+)(\\w+)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","text","entity.name.function","text","paren.lparen"],regex:"(function)(\\s+)("+o+")(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","punctuation.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["text","text","storage.type","text","paren.lparen"],regex:"(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:"keyword",regex:"(?:"+n+")\\b",next:"start"},{token:["support.constant"],regex:/that\b/},{token:["storage.type","punctuation.operator","support.function.firebug"],regex:/(console)(\.)(warn|info|log|error|time|trace|timeEnd|assert)\b/},{token:t,regex:o},{token:"punctuation.operator",regex:/[.](?![.])/,next:"property"},{token:"keyword.operator",regex:/--|\+\+|\.{3}|===|==|=|!=|!==|<+=?|>+=?|!|&&|\|\||\?:|[!$%&*+\-~\/^]=?/,next:"start"},{token:"punctuation.operator",regex:/[?:,;.]/,next:"start"},{token:"paren.lparen",regex:/[\[({]/,next:"start"},{token:"paren.rparen",regex:/[\])}]/},{token:"comment",regex:/^#!.*$/}],property:[{token:"text",regex:"\\s+"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(?:(\\s+)(\\w+))?(\\s*)(\\()",next:"function_arguments"},{token:"punctuation.operator",regex:/[.](?![.])/},{token:"support.function",regex:/(s(?:h(?:ift|ow(?:Mod(?:elessDialog|alDialog)|Help))|croll(?:X|By(?:Pages|Lines)?|Y|To)?|t(?:op|rike)|i(?:n|zeToContent|debar|gnText)|ort|u(?:p|b(?:str(?:ing)?)?)|pli(?:ce|t)|e(?:nd|t(?:Re(?:sizable|questHeader)|M(?:i(?:nutes|lliseconds)|onth)|Seconds|Ho(?:tKeys|urs)|Year|Cursor|Time(?:out)?|Interval|ZOptions|Date|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Date|FullYear)|FullYear|Active)|arch)|qrt|lice|avePreferences|mall)|h(?:ome|andleEvent)|navigate|c(?:har(?:CodeAt|At)|o(?:s|n(?:cat|textual|firm)|mpile)|eil|lear(?:Timeout|Interval)?|a(?:ptureEvents|ll)|reate(?:StyleSheet|Popup|EventObject))|t(?:o(?:GMTString|S(?:tring|ource)|U(?:TCString|pperCase)|Lo(?:caleString|werCase))|est|a(?:n|int(?:Enabled)?))|i(?:s(?:NaN|Finite)|ndexOf|talics)|d(?:isableExternalCapture|ump|etachEvent)|u(?:n(?:shift|taint|escape|watch)|pdateCommands)|j(?:oin|avaEnabled)|p(?:o(?:p|w)|ush|lugins.refresh|a(?:ddings|rse(?:Int|Float)?)|r(?:int|ompt|eference))|e(?:scape|nableExternalCapture|val|lementFromPoint|x(?:p|ec(?:Script|Command)?))|valueOf|UTC|queryCommand(?:State|Indeterm|Enabled|Value)|f(?:i(?:nd|le(?:ModifiedDate|Size|CreatedDate|UpdatedDate)|xed)|o(?:nt(?:size|color)|rward)|loor|romCharCode)|watch|l(?:ink|o(?:ad|g)|astIndexOf)|a(?:sin|nchor|cos|t(?:tachEvent|ob|an(?:2)?)|pply|lert|b(?:s|ort))|r(?:ou(?:nd|teEvents)|e(?:size(?:By|To)|calc|turnValue|place|verse|l(?:oad|ease(?:Capture|Events)))|andom)|g(?:o|et(?:ResponseHeader|M(?:i(?:nutes|lliseconds)|onth)|Se(?:conds|lection)|Hours|Year|Time(?:zoneOffset)?|Da(?:y|te)|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Da(?:y|te)|FullYear)|FullYear|A(?:ttention|llResponseHeaders)))|m(?:in|ove(?:B(?:y|elow)|To(?:Absolute)?|Above)|ergeAttributes|a(?:tch|rgins|x))|b(?:toa|ig|o(?:ld|rderWidths)|link|ack))\b(?=\()/},{token:"support.function.dom",regex:/(s(?:ub(?:stringData|mit)|plitText|e(?:t(?:NamedItem|Attribute(?:Node)?)|lect))|has(?:ChildNodes|Feature)|namedItem|c(?:l(?:ick|o(?:se|neNode))|reate(?:C(?:omment|DATASection|aption)|T(?:Head|extNode|Foot)|DocumentFragment|ProcessingInstruction|E(?:ntityReference|lement)|Attribute))|tabIndex|i(?:nsert(?:Row|Before|Cell|Data)|tem)|open|delete(?:Row|C(?:ell|aption)|T(?:Head|Foot)|Data)|focus|write(?:ln)?|a(?:dd|ppend(?:Child|Data))|re(?:set|place(?:Child|Data)|move(?:NamedItem|Child|Attribute(?:Node)?)?)|get(?:NamedItem|Element(?:sBy(?:Name|TagName|ClassName)|ById)|Attribute(?:Node)?)|blur)\b(?=\()/},{token:"support.constant",regex:/(s(?:ystemLanguage|cr(?:ipts|ollbars|een(?:X|Y|Top|Left))|t(?:yle(?:Sheets)?|atus(?:Text|bar)?)|ibling(?:Below|Above)|ource|uffixes|e(?:curity(?:Policy)?|l(?:ection|f)))|h(?:istory|ost(?:name)?|as(?:h|Focus))|y|X(?:MLDocument|SLDocument)|n(?:ext|ame(?:space(?:s|URI)|Prop))|M(?:IN_VALUE|AX_VALUE)|c(?:haracterSet|o(?:n(?:structor|trollers)|okieEnabled|lorDepth|mp(?:onents|lete))|urrent|puClass|l(?:i(?:p(?:boardData)?|entInformation)|osed|asses)|alle(?:e|r)|rypto)|t(?:o(?:olbar|p)|ext(?:Transform|Indent|Decoration|Align)|ags)|SQRT(?:1_2|2)|i(?:n(?:ner(?:Height|Width)|put)|ds|gnoreCase)|zIndex|o(?:scpu|n(?:readystatechange|Line)|uter(?:Height|Width)|p(?:sProfile|ener)|ffscreenBuffering)|NEGATIVE_INFINITY|d(?:i(?:splay|alog(?:Height|Top|Width|Left|Arguments)|rectories)|e(?:scription|fault(?:Status|Ch(?:ecked|arset)|View)))|u(?:ser(?:Profile|Language|Agent)|n(?:iqueID|defined)|pdateInterval)|_content|p(?:ixelDepth|ort|ersonalbar|kcs11|l(?:ugins|atform)|a(?:thname|dding(?:Right|Bottom|Top|Left)|rent(?:Window|Layer)?|ge(?:X(?:Offset)?|Y(?:Offset)?))|r(?:o(?:to(?:col|type)|duct(?:Sub)?|mpter)|e(?:vious|fix)))|e(?:n(?:coding|abledPlugin)|x(?:ternal|pando)|mbeds)|v(?:isibility|endor(?:Sub)?|Linkcolor)|URLUnencoded|P(?:I|OSITIVE_INFINITY)|f(?:ilename|o(?:nt(?:Size|Family|Weight)|rmName)|rame(?:s|Element)|gColor)|E|whiteSpace|l(?:i(?:stStyleType|n(?:eHeight|kColor))|o(?:ca(?:tion(?:bar)?|lName)|wsrc)|e(?:ngth|ft(?:Context)?)|a(?:st(?:M(?:odified|atch)|Index|Paren)|yer(?:s|X)|nguage))|a(?:pp(?:MinorVersion|Name|Co(?:deName|re)|Version)|vail(?:Height|Top|Width|Left)|ll|r(?:ity|guments)|Linkcolor|bove)|r(?:ight(?:Context)?|e(?:sponse(?:XML|Text)|adyState))|global|x|m(?:imeTypes|ultiline|enubar|argin(?:Right|Bottom|Top|Left))|L(?:N(?:10|2)|OG(?:10E|2E))|b(?:o(?:ttom|rder(?:Width|RightWidth|BottomWidth|Style|Color|TopWidth|LeftWidth))|ufferDepth|elow|ackground(?:Color|Image)))\b/},{token:"identifier",regex:o},{regex:"",token:"empty",next:"no_regex"}],start:[i.getStartRule("doc-start"),f("start"),{token:"string.regexp",regex:"\\/",next:"regex"},{token:"text",regex:"\\s+|^$",next:"start"},{token:"empty",regex:"",next:"no_regex"}],regex:[{token:"regexp.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"string.regexp",regex:"/[sxngimy]*",next:"no_regex"},{token:"invalid",regex:/\{\d+\b,?\d*\}[+*]|[+*$^?][+*]|[$^][?]|\?{3,}/},{token:"constant.language.escape",regex:/\(\?[:=!]|\)|\{\d+\b,?\d*\}|[+*]\?|[()$^+*?.]/},{token:"constant.language.delimiter",regex:/\|/},{token:"constant.language.escape",regex:/\[\^?/,next:"regex_character_class"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp"}],regex_character_class:[{token:"regexp.charclass.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"constant.language.escape",regex:"]",next:"regex"},{token:"constant.language.escape",regex:"-"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp.charachterclass"}],function_arguments:[{token:"variable.parameter",regex:o},{token:"punctuation.operator",regex:"[, ]+"},{token:"punctuation.operator",regex:"$"},{token:"empty",regex:"",next:"no_regex"}],qqstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qqstring"},{token:"string",regex:'"|$',next:"no_regex"},{defaultToken:"string"}],qstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qstring"},{token:"string",regex:"'|$",next:"no_regex"},{defaultToken:"string"}]};if(!e||!e.noES6)this.$rules.no_regex.unshift({regex:"[{}]",onMatch:function(e,t,n){this.next=e=="{"?this.nextState:"";if(e=="{"&&n.length)n.unshift("start",t);else if(e=="}"&&n.length){n.shift(),this.next=n.shift();if(this.next.indexOf("string")!=-1||this.next.indexOf("jsx")!=-1)return"paren.quasi.end"}return e=="{"?"paren.lparen":"paren.rparen"},nextState:"start"},{token:"string.quasi.start",regex:/`/,push:[{token:"constant.language.escape",regex:r},{token:"paren.quasi.start",regex:/\${/,push:"start"},{token:"string.quasi.end",regex:/`/,next:"pop"},{defaultToken:"string.quasi"}]}),(!e||e.jsx!=0)&&a.call(this);this.embedRules(i,"doc-",[i.getEndRule("no_regex")]),this.normalizeRules()};r.inherits(u,s),t.JavaScriptHighlightRules=u}),define("ace/mode/xml_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(e){var t="[_:a-zA-Z\u00c0-\uffff][-_:.a-zA-Z0-9\u00c0-\uffff]*";this.$rules={start:[{token:"string.cdata.xml",regex:"<\\!\\[CDATA\\[",next:"cdata"},{token:["punctuation.xml-decl.xml","keyword.xml-decl.xml"],regex:"(<\\?)(xml)(?=[\\s])",next:"xml_decl",caseInsensitive:!0},{token:["punctuation.instruction.xml","keyword.instruction.xml"],regex:"(<\\?)("+t+")",next:"processing_instruction"},{token:"comment.xml",regex:"<\\!--",next:"comment"},{token:["xml-pe.doctype.xml","xml-pe.doctype.xml"],regex:"(<\\!)(DOCTYPE)(?=[\\s])",next:"doctype",caseInsensitive:!0},{include:"tag"},{token:"text.end-tag-open.xml",regex:"",next:"start"}],processing_instruction:[{token:"punctuation.instruction.xml",regex:"\\?>",next:"start"},{defaultToken:"instruction.xml"}],doctype:[{include:"whitespace"},{include:"string"},{token:"xml-pe.doctype.xml",regex:">",next:"start"},{token:"xml-pe.xml",regex:"[-_a-zA-Z0-9:]+"},{token:"punctuation.int-subset",regex:"\\[",push:"int_subset"}],int_subset:[{token:"text.xml",regex:"\\s+"},{token:"punctuation.int-subset.xml",regex:"]",next:"pop"},{token:["punctuation.markup-decl.xml","keyword.markup-decl.xml"],regex:"(<\\!)("+t+")",push:[{token:"text",regex:"\\s+"},{token:"punctuation.markup-decl.xml",regex:">",next:"pop"},{include:"string"}]}],cdata:[{token:"string.cdata.xml",regex:"\\]\\]>",next:"start"},{token:"text.xml",regex:"\\s+"},{token:"text.xml",regex:"(?:[^\\]]|\\](?!\\]>))+"}],comment:[{token:"comment.xml",regex:"-->",next:"start"},{defaultToken:"comment.xml"}],reference:[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}],attr_reference:[{token:"constant.language.escape.reference.attribute-value.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}],tag:[{token:["meta.tag.punctuation.tag-open.xml","meta.tag.punctuation.end-tag-open.xml","meta.tag.tag-name.xml"],regex:"(?:(<)|(",next:"start"}]}],tag_whitespace:[{token:"text.tag-whitespace.xml",regex:"\\s+"}],whitespace:[{token:"text.whitespace.xml",regex:"\\s+"}],string:[{token:"string.xml",regex:"'",push:[{token:"string.xml",regex:"'",next:"pop"},{defaultToken:"string.xml"}]},{token:"string.xml",regex:'"',push:[{token:"string.xml",regex:'"',next:"pop"},{defaultToken:"string.xml"}]}],attributes:[{token:"entity.other.attribute-name.xml",regex:"(?:"+t+":)?"+t+""},{token:"keyword.operator.attribute-equals.xml",regex:"="},{include:"tag_whitespace"},{include:"attribute_value"}],attribute_value:[{token:"string.attribute-value.xml",regex:"'",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"attr_reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"attr_reference"},{defaultToken:"string.attribute-value.xml"}]}]},this.constructor===s&&this.normalizeRules()};(function(){this.embedTagRules=function(e,t,n){this.$rules.tag.unshift({token:["meta.tag.punctuation.tag-open.xml","meta.tag."+n+".tag-name.xml"],regex:"(<)("+n+"(?=\\s|>|$))",next:[{include:"attributes"},{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",next:t+"start"}]}),this.$rules[n+"-end"]=[{include:"attributes"},{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",next:"start",onMatch:function(e,t,n){return n.splice(0),this.token}}],this.embedRules(e,t,[{token:["meta.tag.punctuation.end-tag-open.xml","meta.tag."+n+".tag-name.xml"],regex:"(|$))",next:n+"-end"},{token:"string.cdata.xml",regex:"<\\!\\[CDATA\\["},{token:"string.cdata.xml",regex:"\\]\\]>"}])}}).call(i.prototype),r.inherits(s,i),t.XmlHighlightRules=s}),define("ace/mode/html_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/css_highlight_rules","ace/mode/javascript_highlight_rules","ace/mode/xml_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./css_highlight_rules").CssHighlightRules,o=e("./javascript_highlight_rules").JavaScriptHighlightRules,u=e("./xml_highlight_rules").XmlHighlightRules,a=i.createMap({a:"anchor",button:"form",form:"form",img:"image",input:"form",label:"form",option:"form",script:"script",select:"form",textarea:"form",style:"style",table:"table",tbody:"table",td:"table",tfoot:"table",th:"table",tr:"table"}),f=function(){u.call(this),this.addRules({attributes:[{include:"tag_whitespace"},{token:"entity.other.attribute-name.xml",regex:"[-_a-zA-Z0-9:.]+"},{token:"keyword.operator.attribute-equals.xml",regex:"=",push:[{include:"tag_whitespace"},{token:"string.unquoted.attribute-value.html",regex:"[^<>='\"`\\s]+",next:"pop"},{token:"empty",regex:"",next:"pop"}]},{include:"attribute_value"}],tag:[{token:function(e,t){var n=a[t];return["meta.tag.punctuation."+(e=="<"?"":"end-")+"tag-open.xml","meta.tag"+(n?"."+n:"")+".tag-name.xml"]},regex:"(",next:"start"}]}),this.embedTagRules(s,"css-","style"),this.embedTagRules((new o({jsx:!1})).getRules(),"js-","script"),this.constructor===f&&this.normalizeRules()};r.inherits(f,u),t.HtmlHighlightRules=f}),define("ace/mode/ruby_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=t.constantOtherSymbol={token:"constant.other.symbol.ruby",regex:"[:](?:[A-Za-z_]|[@$](?=[a-zA-Z0-9_]))[a-zA-Z0-9_]*[!=?]?"},o=t.qString={token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},u=t.qqString={token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},a=t.tString={token:"string",regex:"[`](?:(?:\\\\.)|(?:[^'\\\\]))*?[`]"},f=t.constantNumericHex={token:"constant.numeric",regex:"0[xX][0-9a-fA-F](?:[0-9a-fA-F]|_(?=[0-9a-fA-F]))*\\b"},l=t.constantNumericFloat={token:"constant.numeric",regex:"[+-]?\\d(?:\\d|_(?=\\d))*(?:(?:\\.\\d(?:\\d|_(?=\\d))*)?(?:[eE][+-]?\\d+)?)?\\b"},c=function(){var e="abort|Array|assert|assert_equal|assert_not_equal|assert_same|assert_not_same|assert_nil|assert_not_nil|assert_match|assert_no_match|assert_in_delta|assert_throws|assert_raise|assert_nothing_raised|assert_instance_of|assert_kind_of|assert_respond_to|assert_operator|assert_send|assert_difference|assert_no_difference|assert_recognizes|assert_generates|assert_response|assert_redirected_to|assert_template|assert_select|assert_select_email|assert_select_rjs|assert_select_encoded|css_select|at_exit|attr|attr_writer|attr_reader|attr_accessor|attr_accessible|autoload|binding|block_given?|callcc|caller|catch|chomp|chomp!|chop|chop!|defined?|delete_via_redirect|eval|exec|exit|exit!|fail|Float|flunk|follow_redirect!|fork|form_for|form_tag|format|gets|global_variables|gsub|gsub!|get_via_redirect|host!|https?|https!|include|Integer|lambda|link_to|link_to_unless_current|link_to_function|link_to_remote|load|local_variables|loop|open|open_session|p|print|printf|proc|putc|puts|post_via_redirect|put_via_redirect|raise|rand|raw|readline|readlines|redirect?|request_via_redirect|require|scan|select|set_trace_func|sleep|split|sprintf|srand|String|stylesheet_link_tag|syscall|system|sub|sub!|test|throw|trace_var|trap|untrace_var|atan2|cos|exp|frexp|ldexp|log|log10|sin|sqrt|tan|render|javascript_include_tag|csrf_meta_tag|label_tag|text_field_tag|submit_tag|check_box_tag|content_tag|radio_button_tag|text_area_tag|password_field_tag|hidden_field_tag|fields_for|select_tag|options_for_select|options_from_collection_for_select|collection_select|time_zone_select|select_date|select_time|select_datetime|date_select|time_select|datetime_select|select_year|select_month|select_day|select_hour|select_minute|select_second|file_field_tag|file_field|respond_to|skip_before_filter|around_filter|after_filter|verify|protect_from_forgery|rescue_from|helper_method|redirect_to|before_filter|send_data|send_file|validates_presence_of|validates_uniqueness_of|validates_length_of|validates_format_of|validates_acceptance_of|validates_associated|validates_exclusion_of|validates_inclusion_of|validates_numericality_of|validates_with|validates_each|authenticate_or_request_with_http_basic|authenticate_or_request_with_http_digest|filter_parameter_logging|match|get|post|resources|redirect|scope|assert_routing|translate|localize|extract_locale_from_tld|caches_page|expire_page|caches_action|expire_action|cache|expire_fragment|expire_cache_for|observe|cache_sweeper|has_many|has_one|belongs_to|has_and_belongs_to_many",t="alias|and|BEGIN|begin|break|case|class|def|defined|do|else|elsif|END|end|ensure|__FILE__|finally|for|gem|if|in|__LINE__|module|next|not|or|private|protected|public|redo|rescue|retry|return|super|then|undef|unless|until|when|while|yield",n="true|TRUE|false|FALSE|nil|NIL|ARGF|ARGV|DATA|ENV|RUBY_PLATFORM|RUBY_RELEASE_DATE|RUBY_VERSION|STDERR|STDIN|STDOUT|TOPLEVEL_BINDING",r="$DEBUG|$defout|$FILENAME|$LOAD_PATH|$SAFE|$stdin|$stdout|$stderr|$VERBOSE|$!|root_url|flash|session|cookies|params|request|response|logger|self",i=this.$keywords=this.createKeywordMapper({keyword:t,"constant.language":n,"variable.language":r,"support.function":e,"invalid.deprecated":"debugger"},"identifier");this.$rules={start:[{token:"comment",regex:"#.*$"},{token:"comment",regex:"^=begin(?:$|\\s.*$)",next:"comment"},{token:"string.regexp",regex:"[/](?:(?:\\[(?:\\\\]|[^\\]])+\\])|(?:\\\\/|[^\\]/]))*[/]\\w*\\s*(?=[).,;]|$)"},[{regex:"[{}]",onMatch:function(e,t,n){this.next=e=="{"?this.nextState:"";if(e=="{"&&n.length)return n.unshift("start",t),"paren.lparen";if(e=="}"&&n.length){n.shift(),this.next=n.shift();if(this.next.indexOf("string")!=-1)return"paren.end"}return e=="{"?"paren.lparen":"paren.rparen"},nextState:"start"},{token:"string.start",regex:/"/,push:[{token:"constant.language.escape",regex:/\\(?:[nsrtvfbae'"\\]|c.|C-.|M-.(?:\\C-.)?|[0-7]{3}|x[\da-fA-F]{2}|u[\da-fA-F]{4})/},{token:"paren.start",regex:/#{/,push:"start"},{token:"string.end",regex:/"/,next:"pop"},{defaultToken:"string"}]},{token:"string.start",regex:/`/,push:[{token:"constant.language.escape",regex:/\\(?:[nsrtvfbae'"\\]|c.|C-.|M-.(?:\\C-.)?|[0-7]{3}|x[\da-fA-F]{2}|u[\da-fA-F]{4})/},{token:"paren.start",regex:/#{/,push:"start"},{token:"string.end",regex:/`/,next:"pop"},{defaultToken:"string"}]},{token:"string.start",regex:/'/,push:[{token:"constant.language.escape",regex:/\\['\\]/},{token:"string.end",regex:/'/,next:"pop"},{defaultToken:"string"}]}],{token:"text",regex:"::"},{token:"variable.instance",regex:"@{1,2}[a-zA-Z_\\d]+"},{token:"support.class",regex:"[A-Z][a-zA-Z_\\d]+"},s,f,l,{token:"constant.language.boolean",regex:"(?:true|false)\\b"},{token:i,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"punctuation.separator.key-value",regex:"=>"},{stateName:"heredoc",onMatch:function(e,t,n){var r=e[2]=="-"?"indentedHeredoc":"heredoc",i=e.split(this.splitRegex);return n.push(r,i[3]),[{type:"constant",value:i[1]},{type:"string",value:i[2]},{type:"support.class",value:i[3]},{type:"string",value:i[4]}]},regex:"(<<-?)(['\"`]?)([\\w]+)(['\"`]?)",rules:{heredoc:[{onMatch:function(e,t,n){return e===n[1]?(n.shift(),n.shift(),this.next=n[0]||"start","support.class"):(this.next="","string")},regex:".*$",next:"start"}],indentedHeredoc:[{token:"string",regex:"^ +"},{onMatch:function(e,t,n){return e===n[1]?(n.shift(),n.shift(),this.next=n[0]||"start","support.class"):(this.next="","string")},regex:".*$",next:"start"}]}},{regex:"$",token:"empty",next:function(e,t){return t[0]==="heredoc"||t[0]==="indentedHeredoc"?t[0]:e}},{token:"string.character",regex:"\\B\\?."},{token:"keyword.operator",regex:"!|\\$|%|&|\\*|\\-\\-|\\-|\\+\\+|\\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\\|\\||\\?\\:|\\*=|%=|\\+=|\\-=|&=|\\^=|\\b(?:in|instanceof|new|delete|typeof|void)"},{token:"paren.lparen",regex:"[[({]"},{token:"paren.rparen",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],comment:[{token:"comment",regex:"^=end(?:$|\\s.*$)",next:"start"},{token:"comment",regex:".+"}]},this.normalizeRules()};r.inherits(c,i),t.RubyHighlightRules=c}),define("ace/mode/haml_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/html_highlight_rules","ace/mode/ruby_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./html_highlight_rules").HtmlHighlightRules,s=e("./ruby_highlight_rules"),o=s.RubyHighlightRules,u=function(){i.call(this),this.$rules.start.unshift({token:"punctuation.section.comment",regex:/^\s*\/.*/},{token:"string.quoted.double",regex:"==.+?=="},{token:"keyword.other.doctype",regex:"^!!!\\s*(?:[a-zA-Z0-9-_]+)?"},s.qString,s.qqString,s.tString,{token:"character.escape.haml",regex:"^\\s*\\\\."},{token:"text",regex:/^\s*/,next:"tag_single"},s.constantNumericHex,s.constantNumericFloat,s.constantOtherSymbol,{token:"text",regex:"=|-|~",next:"embedded_ruby"}),this.$rules.tag_single=[{token:"meta.tag.haml",regex:/(%[\w:\-]+)/},{token:"keyword.attribute-name.class.haml",regex:"\\.[\\w-]+"},{token:"keyword.attribute-name.id.haml",regex:"#[\\w-]+"},{token:"punctuation.section",regex:"\\{",next:"section"},s.constantOtherSymbol,{token:"text",regex:/\s/,next:"start"},{token:"empty",regex:"$|(?!\\.|#|\\{|\\[|=|-|~|\\/)",next:"start"}],this.$rules.section=[s.constantOtherSymbol,s.qString,s.qqString,s.tString,s.constantNumericHex,s.constantNumericFloat,{token:"punctuation.section",regex:"\\}",next:"start"}],this.$rules.embedded_ruby=[s.constantNumericHex,s.constantNumericFloat,{token:"support.class",regex:"[A-Z][a-zA-Z_\\d]+"},{token:(new o).getKeywords(),regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:["keyword","text","text"],regex:"(?:do|\\{)(?: \\|[^|]+\\|)?$",next:"start"},{token:["text"],regex:"^$",next:"start"},{token:["text"],regex:"^(?!.*\\|\\s*$)",next:"start"}],this.normalizeRules()};r.inherits(u,i),t.HamlHighlightRules=u}),define("ace/mode/folding/coffee",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode","ace/range"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("./fold_mode").FoldMode,s=e("../../range").Range,o=t.FoldMode=function(){};r.inherits(o,i),function(){this.getFoldWidgetRange=function(e,t,n){var r=this.indentationBlock(e,n);if(r)return r;var i=/\S/,o=e.getLine(n),u=o.search(i);if(u==-1||o[u]!="#")return;var a=o.length,f=e.getLength(),l=n,c=n;while(++nl){var p=e.getLine(c).length;return new s(l,a,c,p)}},this.getFoldWidget=function(e,t,n){var r=e.getLine(n),i=r.search(/\S/),s=e.getLine(n+1),o=e.getLine(n-1),u=o.search(/\S/),a=s.search(/\S/);if(i==-1)return e.foldWidgets[n-1]=u!=-1&&u",onMatch:function(e,t,n){return t==n[0]&&n.shift(),e.length==2&&(n[0]==this.nextState&&n[1]--,(!n[1]||n[1]<0)&&n.splice(0,2)),this.next=n[0]||"start",[{type:this.token,value:e}]},nextState:"jsx"},n,f("jsxAttributes"),{token:"entity.other.attribute-name.xml",regex:e},{token:"keyword.operator.attribute-equals.xml",regex:"="},{token:"text.tag-whitespace.xml",regex:"\\s+"},{token:"string.attribute-value.xml",regex:"'",stateName:"jsx_attr_q",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',stateName:"jsx_attr_qq",push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},t],this.$rules.reference=[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}]}function f(e){return[{token:"comment",regex:/\/\*/,next:[i.getTagRule(),{token:"comment",regex:"\\*\\/",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]},{token:"comment",regex:"\\/\\/",next:[i.getTagRule(),{token:"comment",regex:"$|^",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]}]}var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./text_highlight_rules").TextHighlightRules,o="[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*",u=function(e){var t=this.createKeywordMapper({"variable.language":"Array|Boolean|Date|Function|Iterator|Number|Object|RegExp|String|Proxy|Namespace|QName|XML|XMLList|ArrayBuffer|Float32Array|Float64Array|Int16Array|Int32Array|Int8Array|Uint16Array|Uint32Array|Uint8Array|Uint8ClampedArray|Error|EvalError|InternalError|RangeError|ReferenceError|StopIteration|SyntaxError|TypeError|URIError|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|eval|isFinite|isNaN|parseFloat|parseInt|JSON|Math|this|arguments|prototype|window|document",keyword:"const|yield|import|get|set|async|await|break|case|catch|continue|default|delete|do|else|finally|for|function|if|in|of|instanceof|new|return|switch|throw|try|typeof|let|var|while|with|debugger|__parent__|__count__|escape|unescape|with|__proto__|class|enum|extends|super|export|implements|private|public|interface|package|protected|static","storage.type":"const|let|var|function","constant.language":"null|Infinity|NaN|undefined","support.function":"alert","constant.language.boolean":"true|false"},"identifier"),n="case|do|else|finally|in|instanceof|return|throw|try|typeof|yield|void",r="\\\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|u{[0-9a-fA-F]{1,6}}|[0-2][0-7]{0,2}|3[0-7][0-7]?|[4-7][0-7]?|.)";this.$rules={no_regex:[i.getStartRule("doc-start"),f("no_regex"),{token:"string",regex:"'(?=.)",next:"qstring"},{token:"string",regex:'"(?=.)',next:"qqstring"},{token:"constant.numeric",regex:/0(?:[xX][0-9a-fA-F]+|[bB][01]+)\b/},{token:"constant.numeric",regex:/[+-]?\d[\d_]*(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/},{token:["storage.type","punctuation.operator","support.function","punctuation.operator","entity.name.function","text","keyword.operator"],regex:"("+o+")(\\.)(prototype)(\\.)("+o+")(\\s*)(=)",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s+)(\\w+)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","text","entity.name.function","text","paren.lparen"],regex:"(function)(\\s+)("+o+")(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","punctuation.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["text","text","storage.type","text","paren.lparen"],regex:"(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:"keyword",regex:"(?:"+n+")\\b",next:"start"},{token:["support.constant"],regex:/that\b/},{token:["storage.type","punctuation.operator","support.function.firebug"],regex:/(console)(\.)(warn|info|log|error|time|trace|timeEnd|assert)\b/},{token:t,regex:o},{token:"punctuation.operator",regex:/[.](?![.])/,next:"property"},{token:"keyword.operator",regex:/--|\+\+|\.{3}|===|==|=|!=|!==|<+=?|>+=?|!|&&|\|\||\?:|[!$%&*+\-~\/^]=?/,next:"start"},{token:"punctuation.operator",regex:/[?:,;.]/,next:"start"},{token:"paren.lparen",regex:/[\[({]/,next:"start"},{token:"paren.rparen",regex:/[\])}]/},{token:"comment",regex:/^#!.*$/}],property:[{token:"text",regex:"\\s+"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(?:(\\s+)(\\w+))?(\\s*)(\\()",next:"function_arguments"},{token:"punctuation.operator",regex:/[.](?![.])/},{token:"support.function",regex:/(s(?:h(?:ift|ow(?:Mod(?:elessDialog|alDialog)|Help))|croll(?:X|By(?:Pages|Lines)?|Y|To)?|t(?:op|rike)|i(?:n|zeToContent|debar|gnText)|ort|u(?:p|b(?:str(?:ing)?)?)|pli(?:ce|t)|e(?:nd|t(?:Re(?:sizable|questHeader)|M(?:i(?:nutes|lliseconds)|onth)|Seconds|Ho(?:tKeys|urs)|Year|Cursor|Time(?:out)?|Interval|ZOptions|Date|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Date|FullYear)|FullYear|Active)|arch)|qrt|lice|avePreferences|mall)|h(?:ome|andleEvent)|navigate|c(?:har(?:CodeAt|At)|o(?:s|n(?:cat|textual|firm)|mpile)|eil|lear(?:Timeout|Interval)?|a(?:ptureEvents|ll)|reate(?:StyleSheet|Popup|EventObject))|t(?:o(?:GMTString|S(?:tring|ource)|U(?:TCString|pperCase)|Lo(?:caleString|werCase))|est|a(?:n|int(?:Enabled)?))|i(?:s(?:NaN|Finite)|ndexOf|talics)|d(?:isableExternalCapture|ump|etachEvent)|u(?:n(?:shift|taint|escape|watch)|pdateCommands)|j(?:oin|avaEnabled)|p(?:o(?:p|w)|ush|lugins.refresh|a(?:ddings|rse(?:Int|Float)?)|r(?:int|ompt|eference))|e(?:scape|nableExternalCapture|val|lementFromPoint|x(?:p|ec(?:Script|Command)?))|valueOf|UTC|queryCommand(?:State|Indeterm|Enabled|Value)|f(?:i(?:nd|le(?:ModifiedDate|Size|CreatedDate|UpdatedDate)|xed)|o(?:nt(?:size|color)|rward)|loor|romCharCode)|watch|l(?:ink|o(?:ad|g)|astIndexOf)|a(?:sin|nchor|cos|t(?:tachEvent|ob|an(?:2)?)|pply|lert|b(?:s|ort))|r(?:ou(?:nd|teEvents)|e(?:size(?:By|To)|calc|turnValue|place|verse|l(?:oad|ease(?:Capture|Events)))|andom)|g(?:o|et(?:ResponseHeader|M(?:i(?:nutes|lliseconds)|onth)|Se(?:conds|lection)|Hours|Year|Time(?:zoneOffset)?|Da(?:y|te)|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Da(?:y|te)|FullYear)|FullYear|A(?:ttention|llResponseHeaders)))|m(?:in|ove(?:B(?:y|elow)|To(?:Absolute)?|Above)|ergeAttributes|a(?:tch|rgins|x))|b(?:toa|ig|o(?:ld|rderWidths)|link|ack))\b(?=\()/},{token:"support.function.dom",regex:/(s(?:ub(?:stringData|mit)|plitText|e(?:t(?:NamedItem|Attribute(?:Node)?)|lect))|has(?:ChildNodes|Feature)|namedItem|c(?:l(?:ick|o(?:se|neNode))|reate(?:C(?:omment|DATASection|aption)|T(?:Head|extNode|Foot)|DocumentFragment|ProcessingInstruction|E(?:ntityReference|lement)|Attribute))|tabIndex|i(?:nsert(?:Row|Before|Cell|Data)|tem)|open|delete(?:Row|C(?:ell|aption)|T(?:Head|Foot)|Data)|focus|write(?:ln)?|a(?:dd|ppend(?:Child|Data))|re(?:set|place(?:Child|Data)|move(?:NamedItem|Child|Attribute(?:Node)?)?)|get(?:NamedItem|Element(?:sBy(?:Name|TagName|ClassName)|ById)|Attribute(?:Node)?)|blur)\b(?=\()/},{token:"support.constant",regex:/(s(?:ystemLanguage|cr(?:ipts|ollbars|een(?:X|Y|Top|Left))|t(?:yle(?:Sheets)?|atus(?:Text|bar)?)|ibling(?:Below|Above)|ource|uffixes|e(?:curity(?:Policy)?|l(?:ection|f)))|h(?:istory|ost(?:name)?|as(?:h|Focus))|y|X(?:MLDocument|SLDocument)|n(?:ext|ame(?:space(?:s|URI)|Prop))|M(?:IN_VALUE|AX_VALUE)|c(?:haracterSet|o(?:n(?:structor|trollers)|okieEnabled|lorDepth|mp(?:onents|lete))|urrent|puClass|l(?:i(?:p(?:boardData)?|entInformation)|osed|asses)|alle(?:e|r)|rypto)|t(?:o(?:olbar|p)|ext(?:Transform|Indent|Decoration|Align)|ags)|SQRT(?:1_2|2)|i(?:n(?:ner(?:Height|Width)|put)|ds|gnoreCase)|zIndex|o(?:scpu|n(?:readystatechange|Line)|uter(?:Height|Width)|p(?:sProfile|ener)|ffscreenBuffering)|NEGATIVE_INFINITY|d(?:i(?:splay|alog(?:Height|Top|Width|Left|Arguments)|rectories)|e(?:scription|fault(?:Status|Ch(?:ecked|arset)|View)))|u(?:ser(?:Profile|Language|Agent)|n(?:iqueID|defined)|pdateInterval)|_content|p(?:ixelDepth|ort|ersonalbar|kcs11|l(?:ugins|atform)|a(?:thname|dding(?:Right|Bottom|Top|Left)|rent(?:Window|Layer)?|ge(?:X(?:Offset)?|Y(?:Offset)?))|r(?:o(?:to(?:col|type)|duct(?:Sub)?|mpter)|e(?:vious|fix)))|e(?:n(?:coding|abledPlugin)|x(?:ternal|pando)|mbeds)|v(?:isibility|endor(?:Sub)?|Linkcolor)|URLUnencoded|P(?:I|OSITIVE_INFINITY)|f(?:ilename|o(?:nt(?:Size|Family|Weight)|rmName)|rame(?:s|Element)|gColor)|E|whiteSpace|l(?:i(?:stStyleType|n(?:eHeight|kColor))|o(?:ca(?:tion(?:bar)?|lName)|wsrc)|e(?:ngth|ft(?:Context)?)|a(?:st(?:M(?:odified|atch)|Index|Paren)|yer(?:s|X)|nguage))|a(?:pp(?:MinorVersion|Name|Co(?:deName|re)|Version)|vail(?:Height|Top|Width|Left)|ll|r(?:ity|guments)|Linkcolor|bove)|r(?:ight(?:Context)?|e(?:sponse(?:XML|Text)|adyState))|global|x|m(?:imeTypes|ultiline|enubar|argin(?:Right|Bottom|Top|Left))|L(?:N(?:10|2)|OG(?:10E|2E))|b(?:o(?:ttom|rder(?:Width|RightWidth|BottomWidth|Style|Color|TopWidth|LeftWidth))|ufferDepth|elow|ackground(?:Color|Image)))\b/},{token:"identifier",regex:o},{regex:"",token:"empty",next:"no_regex"}],start:[i.getStartRule("doc-start"),f("start"),{token:"string.regexp",regex:"\\/",next:"regex"},{token:"text",regex:"\\s+|^$",next:"start"},{token:"empty",regex:"",next:"no_regex"}],regex:[{token:"regexp.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"string.regexp",regex:"/[sxngimy]*",next:"no_regex"},{token:"invalid",regex:/\{\d+\b,?\d*\}[+*]|[+*$^?][+*]|[$^][?]|\?{3,}/},{token:"constant.language.escape",regex:/\(\?[:=!]|\)|\{\d+\b,?\d*\}|[+*]\?|[()$^+*?.]/},{token:"constant.language.delimiter",regex:/\|/},{token:"constant.language.escape",regex:/\[\^?/,next:"regex_character_class"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp"}],regex_character_class:[{token:"regexp.charclass.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"constant.language.escape",regex:"]",next:"regex"},{token:"constant.language.escape",regex:"-"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp.charachterclass"}],function_arguments:[{token:"variable.parameter",regex:o},{token:"punctuation.operator",regex:"[, ]+"},{token:"punctuation.operator",regex:"$"},{token:"empty",regex:"",next:"no_regex"}],qqstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qqstring"},{token:"string",regex:'"|$',next:"no_regex"},{defaultToken:"string"}],qstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qstring"},{token:"string",regex:"'|$",next:"no_regex"},{defaultToken:"string"}]};if(!e||!e.noES6)this.$rules.no_regex.unshift({regex:"[{}]",onMatch:function(e,t,n){this.next=e=="{"?this.nextState:"";if(e=="{"&&n.length)n.unshift("start",t);else if(e=="}"&&n.length){n.shift(),this.next=n.shift();if(this.next.indexOf("string")!=-1||this.next.indexOf("jsx")!=-1)return"paren.quasi.end"}return e=="{"?"paren.lparen":"paren.rparen"},nextState:"start"},{token:"string.quasi.start",regex:/`/,push:[{token:"constant.language.escape",regex:r},{token:"paren.quasi.start",regex:/\${/,push:"start"},{token:"string.quasi.end",regex:/`/,next:"pop"},{defaultToken:"string.quasi"}]}),(!e||e.jsx!=0)&&a.call(this);this.embedRules(i,"doc-",[i.getEndRule("no_regex")]),this.normalizeRules()};r.inherits(u,s),t.JavaScriptHighlightRules=u}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/javascript",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/javascript_highlight_rules","ace/mode/matching_brace_outdent","ace/worker/worker_client","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./javascript_highlight_rules").JavaScriptHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../worker/worker_client").WorkerClient,a=e("./behaviour/cstyle").CstyleBehaviour,f=e("./folding/cstyle").FoldMode,l=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new a,this.foldingRules=new f};r.inherits(l,i),function(){this.lineCommentStart="//",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens,o=i.state;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"||e=="no_regex"){var u=t.match(/^.*(?:\bcase\b.*:|[\{\(\[])\s*$/);u&&(r+=n)}else if(e=="doc-start"){if(o=="start"||o=="no_regex")return"";var u=t.match(/^\s*(\/?)\*/);u&&(u[1]&&(r+=" "),r+="* ")}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.createWorker=function(e){var t=new u(["ace"],"ace/mode/javascript_worker","JavaScriptWorker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/javascript"}.call(l.prototype),t.Mode=l}),define("ace/mode/css_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text_highlight_rules").TextHighlightRules,o=t.supportType="align-content|align-items|align-self|all|animation|animation-delay|animation-direction|animation-duration|animation-fill-mode|animation-iteration-count|animation-name|animation-play-state|animation-timing-function|backface-visibility|background|background-attachment|background-blend-mode|background-clip|background-color|background-image|background-origin|background-position|background-repeat|background-size|border|border-bottom|border-bottom-color|border-bottom-left-radius|border-bottom-right-radius|border-bottom-style|border-bottom-width|border-collapse|border-color|border-image|border-image-outset|border-image-repeat|border-image-slice|border-image-source|border-image-width|border-left|border-left-color|border-left-style|border-left-width|border-radius|border-right|border-right-color|border-right-style|border-right-width|border-spacing|border-style|border-top|border-top-color|border-top-left-radius|border-top-right-radius|border-top-style|border-top-width|border-width|bottom|box-shadow|box-sizing|caption-side|clear|clip|color|column-count|column-fill|column-gap|column-rule|column-rule-color|column-rule-style|column-rule-width|column-span|column-width|columns|content|counter-increment|counter-reset|cursor|direction|display|empty-cells|filter|flex|flex-basis|flex-direction|flex-flow|flex-grow|flex-shrink|flex-wrap|float|font|font-family|font-size|font-size-adjust|font-stretch|font-style|font-variant|font-weight|hanging-punctuation|height|justify-content|left|letter-spacing|line-height|list-style|list-style-image|list-style-position|list-style-type|margin|margin-bottom|margin-left|margin-right|margin-top|max-height|max-width|min-height|min-width|nav-down|nav-index|nav-left|nav-right|nav-up|opacity|order|outline|outline-color|outline-offset|outline-style|outline-width|overflow|overflow-x|overflow-y|padding|padding-bottom|padding-left|padding-right|padding-top|page-break-after|page-break-before|page-break-inside|perspective|perspective-origin|position|quotes|resize|right|tab-size|table-layout|text-align|text-align-last|text-decoration|text-decoration-color|text-decoration-line|text-decoration-style|text-indent|text-justify|text-overflow|text-shadow|text-transform|top|transform|transform-origin|transform-style|transition|transition-delay|transition-duration|transition-property|transition-timing-function|unicode-bidi|vertical-align|visibility|white-space|width|word-break|word-spacing|word-wrap|z-index",u=t.supportFunction="rgb|rgba|url|attr|counter|counters",a=t.supportConstant="absolute|after-edge|after|all-scroll|all|alphabetic|always|antialiased|armenian|auto|avoid-column|avoid-page|avoid|balance|baseline|before-edge|before|below|bidi-override|block-line-height|block|bold|bolder|border-box|both|bottom|box|break-all|break-word|capitalize|caps-height|caption|center|central|char|circle|cjk-ideographic|clone|close-quote|col-resize|collapse|column|consider-shifts|contain|content-box|cover|crosshair|cubic-bezier|dashed|decimal-leading-zero|decimal|default|disabled|disc|disregard-shifts|distribute-all-lines|distribute-letter|distribute-space|distribute|dotted|double|e-resize|ease-in|ease-in-out|ease-out|ease|ellipsis|end|exclude-ruby|fill|fixed|georgian|glyphs|grid-height|groove|hand|hanging|hebrew|help|hidden|hiragana-iroha|hiragana|horizontal|icon|ideograph-alpha|ideograph-numeric|ideograph-parenthesis|ideograph-space|ideographic|inactive|include-ruby|inherit|initial|inline-block|inline-box|inline-line-height|inline-table|inline|inset|inside|inter-ideograph|inter-word|invert|italic|justify|katakana-iroha|katakana|keep-all|last|left|lighter|line-edge|line-through|line|linear|list-item|local|loose|lower-alpha|lower-greek|lower-latin|lower-roman|lowercase|lr-tb|ltr|mathematical|max-height|max-size|medium|menu|message-box|middle|move|n-resize|ne-resize|newspaper|no-change|no-close-quote|no-drop|no-open-quote|no-repeat|none|normal|not-allowed|nowrap|nw-resize|oblique|open-quote|outset|outside|overline|padding-box|page|pointer|pre-line|pre-wrap|pre|preserve-3d|progress|relative|repeat-x|repeat-y|repeat|replaced|reset-size|ridge|right|round|row-resize|rtl|s-resize|scroll|se-resize|separate|slice|small-caps|small-caption|solid|space|square|start|static|status-bar|step-end|step-start|steps|stretch|strict|sub|super|sw-resize|table-caption|table-cell|table-column-group|table-column|table-footer-group|table-header-group|table-row-group|table-row|table|tb-rl|text-after-edge|text-before-edge|text-bottom|text-size|text-top|text|thick|thin|transparent|underline|upper-alpha|upper-latin|upper-roman|uppercase|use-script|vertical-ideographic|vertical-text|visible|w-resize|wait|whitespace|z-index|zero",f=t.supportConstantColor="aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow",l=t.supportConstantFonts="arial|century|comic|courier|cursive|fantasy|garamond|georgia|helvetica|impact|lucida|symbol|system|tahoma|times|trebuchet|utopia|verdana|webdings|sans-serif|serif|monospace",c=t.numRe="\\-?(?:(?:[0-9]+)|(?:[0-9]*\\.[0-9]+))",h=t.pseudoElements="(\\:+)\\b(after|before|first-letter|first-line|moz-selection|selection)\\b",p=t.pseudoClasses="(:)\\b(active|checked|disabled|empty|enabled|first-child|first-of-type|focus|hover|indeterminate|invalid|last-child|last-of-type|link|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|only-child|only-of-type|required|root|target|valid|visited)\\b",d=function(){var e=this.createKeywordMapper({"support.function":u,"support.constant":a,"support.type":o,"support.constant.color":f,"support.constant.fonts":l},"text",!0);this.$rules={start:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"@.*?{",push:"media"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],media:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"\\}",next:"pop"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],comment:[{token:"comment",regex:"\\*\\/",next:"pop"},{defaultToken:"comment"}],ruleset:[{token:"paren.rparen",regex:"\\}",next:"pop"},{token:"comment",regex:"\\/\\*",push:"comment"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:["constant.numeric","keyword"],regex:"("+c+")(ch|cm|deg|em|ex|fr|gd|grad|Hz|in|kHz|mm|ms|pc|pt|px|rad|rem|s|turn|vh|vm|vw|%)"},{token:"constant.numeric",regex:c},{token:"constant.numeric",regex:"#[a-f0-9]{6}"},{token:"constant.numeric",regex:"#[a-f0-9]{3}"},{token:["punctuation","entity.other.attribute-name.pseudo-element.css"],regex:h},{token:["punctuation","entity.other.attribute-name.pseudo-class.css"],regex:p},{token:["support.function","string","support.function"],regex:"(url\\()(.*)(\\))"},{token:e,regex:"\\-?[a-zA-Z_][a-zA-Z0-9_\\-]*"},{caseInsensitive:!0}]},this.normalizeRules()};r.inherits(d,s),t.CssHighlightRules=d}),define("ace/mode/css_completions",["require","exports","module"],function(e,t,n){"use strict";var r={background:{"#$0":1},"background-color":{"#$0":1,transparent:1,fixed:1},"background-image":{"url('/$0')":1},"background-repeat":{repeat:1,"repeat-x":1,"repeat-y":1,"no-repeat":1,inherit:1},"background-position":{bottom:2,center:2,left:2,right:2,top:2,inherit:2},"background-attachment":{scroll:1,fixed:1},"background-size":{cover:1,contain:1},"background-clip":{"border-box":1,"padding-box":1,"content-box":1},"background-origin":{"border-box":1,"padding-box":1,"content-box":1},border:{"solid $0":1,"dashed $0":1,"dotted $0":1,"#$0":1},"border-color":{"#$0":1},"border-style":{solid:2,dashed:2,dotted:2,"double":2,groove:2,hidden:2,inherit:2,inset:2,none:2,outset:2,ridged:2},"border-collapse":{collapse:1,separate:1},bottom:{px:1,em:1,"%":1},clear:{left:1,right:1,both:1,none:1},color:{"#$0":1,"rgb(#$00,0,0)":1},cursor:{"default":1,pointer:1,move:1,text:1,wait:1,help:1,progress:1,"n-resize":1,"ne-resize":1,"e-resize":1,"se-resize":1,"s-resize":1,"sw-resize":1,"w-resize":1,"nw-resize":1},display:{none:1,block:1,inline:1,"inline-block":1,"table-cell":1},"empty-cells":{show:1,hide:1},"float":{left:1,right:1,none:1},"font-family":{Arial:2,"Comic Sans MS":2,Consolas:2,"Courier New":2,Courier:2,Georgia:2,Monospace:2,"Sans-Serif":2,"Segoe UI":2,Tahoma:2,"Times New Roman":2,"Trebuchet MS":2,Verdana:1},"font-size":{px:1,em:1,"%":1},"font-weight":{bold:1,normal:1},"font-style":{italic:1,normal:1},"font-variant":{normal:1,"small-caps":1},height:{px:1,em:1,"%":1},left:{px:1,em:1,"%":1},"letter-spacing":{normal:1},"line-height":{normal:1},"list-style-type":{none:1,disc:1,circle:1,square:1,decimal:1,"decimal-leading-zero":1,"lower-roman":1,"upper-roman":1,"lower-greek":1,"lower-latin":1,"upper-latin":1,georgian:1,"lower-alpha":1,"upper-alpha":1},margin:{px:1,em:1,"%":1},"margin-right":{px:1,em:1,"%":1},"margin-left":{px:1,em:1,"%":1},"margin-top":{px:1,em:1,"%":1},"margin-bottom":{px:1,em:1,"%":1},"max-height":{px:1,em:1,"%":1},"max-width":{px:1,em:1,"%":1},"min-height":{px:1,em:1,"%":1},"min-width":{px:1,em:1,"%":1},overflow:{hidden:1,visible:1,auto:1,scroll:1},"overflow-x":{hidden:1,visible:1,auto:1,scroll:1},"overflow-y":{hidden:1,visible:1,auto:1,scroll:1},padding:{px:1,em:1,"%":1},"padding-top":{px:1,em:1,"%":1},"padding-right":{px:1,em:1,"%":1},"padding-bottom":{px:1,em:1,"%":1},"padding-left":{px:1,em:1,"%":1},"page-break-after":{auto:1,always:1,avoid:1,left:1,right:1},"page-break-before":{auto:1,always:1,avoid:1,left:1,right:1},position:{absolute:1,relative:1,fixed:1,"static":1},right:{px:1,em:1,"%":1},"table-layout":{fixed:1,auto:1},"text-decoration":{none:1,underline:1,"line-through":1,blink:1},"text-align":{left:1,right:1,center:1,justify:1},"text-transform":{capitalize:1,uppercase:1,lowercase:1,none:1},top:{px:1,em:1,"%":1},"vertical-align":{top:1,bottom:1},visibility:{hidden:1,visible:1},"white-space":{nowrap:1,normal:1,pre:1,"pre-line":1,"pre-wrap":1},width:{px:1,em:1,"%":1},"word-spacing":{normal:1},filter:{"alpha(opacity=$0100)":1},"text-shadow":{"$02px 2px 2px #777":1},"text-overflow":{"ellipsis-word":1,clip:1,ellipsis:1},"-moz-border-radius":1,"-moz-border-radius-topright":1,"-moz-border-radius-bottomright":1,"-moz-border-radius-topleft":1,"-moz-border-radius-bottomleft":1,"-webkit-border-radius":1,"-webkit-border-top-right-radius":1,"-webkit-border-top-left-radius":1,"-webkit-border-bottom-right-radius":1,"-webkit-border-bottom-left-radius":1,"-moz-box-shadow":1,"-webkit-box-shadow":1,transform:{"rotate($00deg)":1,"skew($00deg)":1},"-moz-transform":{"rotate($00deg)":1,"skew($00deg)":1},"-webkit-transform":{"rotate($00deg)":1,"skew($00deg)":1}},i=function(){};(function(){this.completionsDefined=!1,this.defineCompletions=function(){if(document){var e=document.createElement("c").style;for(var t in e){if(typeof e[t]!="string")continue;var n=t.replace(/[A-Z]/g,function(e){return"-"+e.toLowerCase()});r.hasOwnProperty(n)||(r[n]=1)}}this.completionsDefined=!0},this.getCompletions=function(e,t,n,r){this.completionsDefined||this.defineCompletions();var i=t.getTokenAt(n.row,n.column);if(!i)return[];if(e==="ruleset"){var s=t.getLine(n.row).substr(0,n.column);return/:[^;]+$/.test(s)?(/([\w\-]+):[^:]*$/.test(s),this.getPropertyValueCompletions(e,t,n,r)):this.getPropertyCompletions(e,t,n,r)}return[]},this.getPropertyCompletions=function(e,t,n,i){var s=Object.keys(r);return s.map(function(e){return{caption:e,snippet:e+": $0",meta:"property",score:Number.MAX_VALUE}})},this.getPropertyValueCompletions=function(e,t,n,i){var s=t.getLine(n.row).substr(0,n.column),o=(/([\w\-]+):[^:]*$/.exec(s)||{})[1];if(!o)return[];var u=[];return o in r&&typeof r[o]=="object"&&(u=Object.keys(r[o])),u.map(function(e){return{caption:e,snippet:e,meta:"property value",score:Number.MAX_VALUE}})}}).call(i.prototype),t.CssCompletions=i}),define("ace/mode/behaviour/css",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/mode/behaviour/cstyle","ace/token_iterator"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("./cstyle").CstyleBehaviour,o=e("../../token_iterator").TokenIterator,u=function(){this.inherit(s),this.add("colon","insertion",function(e,t,n,r,i){if(i===":"){var s=n.getCursorPosition(),u=new o(r,s.row,s.column),a=u.getCurrentToken();a&&a.value.match(/\s+/)&&(a=u.stepBackward());if(a&&a.type==="support.type"){var f=r.doc.getLine(s.row),l=f.substring(s.column,s.column+1);if(l===":")return{text:"",selection:[1,1]};if(!f.substring(s.column).match(/^\s*;/))return{text:":;",selection:[1,1]}}}}),this.add("colon","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s===":"){var u=n.getCursorPosition(),a=new o(r,u.row,u.column),f=a.getCurrentToken();f&&f.value.match(/\s+/)&&(f=a.stepBackward());if(f&&f.type==="support.type"){var l=r.doc.getLine(i.start.row),c=l.substring(i.end.column,i.end.column+1);if(c===";")return i.end.column++,i}}}),this.add("semicolon","insertion",function(e,t,n,r,i){if(i===";"){var s=n.getCursorPosition(),o=r.doc.getLine(s.row),u=o.substring(s.column,s.column+1);if(u===";")return{text:"",selection:[1,1]}}})};r.inherits(u,s),t.CssBehaviour=u}),define("ace/mode/css",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/css_highlight_rules","ace/mode/matching_brace_outdent","ace/worker/worker_client","ace/mode/css_completions","ace/mode/behaviour/css","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./css_highlight_rules").CssHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../worker/worker_client").WorkerClient,a=e("./css_completions").CssCompletions,f=e("./behaviour/css").CssBehaviour,l=e("./folding/cstyle").FoldMode,c=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new f,this.$completer=new a,this.foldingRules=new l};r.inherits(c,i),function(){this.foldingRules="cStyle",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e).tokens;if(i.length&&i[i.length-1].type=="comment")return r;var s=t.match(/^.*\{\s*$/);return s&&(r+=n),r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.getCompletions=function(e,t,n,r){return this.$completer.getCompletions(e,t,n,r)},this.createWorker=function(e){var t=new u(["ace"],"ace/mode/css_worker","Worker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/css"}.call(c.prototype),t.Mode=c}),define("ace/mode/xml_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(e){var t="[_:a-zA-Z\u00c0-\uffff][-_:.a-zA-Z0-9\u00c0-\uffff]*";this.$rules={start:[{token:"string.cdata.xml",regex:"<\\!\\[CDATA\\[",next:"cdata"},{token:["punctuation.xml-decl.xml","keyword.xml-decl.xml"],regex:"(<\\?)(xml)(?=[\\s])",next:"xml_decl",caseInsensitive:!0},{token:["punctuation.instruction.xml","keyword.instruction.xml"],regex:"(<\\?)("+t+")",next:"processing_instruction"},{token:"comment.xml",regex:"<\\!--",next:"comment"},{token:["xml-pe.doctype.xml","xml-pe.doctype.xml"],regex:"(<\\!)(DOCTYPE)(?=[\\s])",next:"doctype",caseInsensitive:!0},{include:"tag"},{token:"text.end-tag-open.xml",regex:"",next:"start"}],processing_instruction:[{token:"punctuation.instruction.xml",regex:"\\?>",next:"start"},{defaultToken:"instruction.xml"}],doctype:[{include:"whitespace"},{include:"string"},{token:"xml-pe.doctype.xml",regex:">",next:"start"},{token:"xml-pe.xml",regex:"[-_a-zA-Z0-9:]+"},{token:"punctuation.int-subset",regex:"\\[",push:"int_subset"}],int_subset:[{token:"text.xml",regex:"\\s+"},{token:"punctuation.int-subset.xml",regex:"]",next:"pop"},{token:["punctuation.markup-decl.xml","keyword.markup-decl.xml"],regex:"(<\\!)("+t+")",push:[{token:"text",regex:"\\s+"},{token:"punctuation.markup-decl.xml",regex:">",next:"pop"},{include:"string"}]}],cdata:[{token:"string.cdata.xml",regex:"\\]\\]>",next:"start"},{token:"text.xml",regex:"\\s+"},{token:"text.xml",regex:"(?:[^\\]]|\\](?!\\]>))+"}],comment:[{token:"comment.xml",regex:"-->",next:"start"},{defaultToken:"comment.xml"}],reference:[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}],attr_reference:[{token:"constant.language.escape.reference.attribute-value.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}],tag:[{token:["meta.tag.punctuation.tag-open.xml","meta.tag.punctuation.end-tag-open.xml","meta.tag.tag-name.xml"],regex:"(?:(<)|(",next:"start"}]}],tag_whitespace:[{token:"text.tag-whitespace.xml",regex:"\\s+"}],whitespace:[{token:"text.whitespace.xml",regex:"\\s+"}],string:[{token:"string.xml",regex:"'",push:[{token:"string.xml",regex:"'",next:"pop"},{defaultToken:"string.xml"}]},{token:"string.xml",regex:'"',push:[{token:"string.xml",regex:'"',next:"pop"},{defaultToken:"string.xml"}]}],attributes:[{token:"entity.other.attribute-name.xml",regex:"(?:"+t+":)?"+t+""},{token:"keyword.operator.attribute-equals.xml",regex:"="},{include:"tag_whitespace"},{include:"attribute_value"}],attribute_value:[{token:"string.attribute-value.xml",regex:"'",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"attr_reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"attr_reference"},{defaultToken:"string.attribute-value.xml"}]}]},this.constructor===s&&this.normalizeRules()};(function(){this.embedTagRules=function(e,t,n){this.$rules.tag.unshift({token:["meta.tag.punctuation.tag-open.xml","meta.tag."+n+".tag-name.xml"],regex:"(<)("+n+"(?=\\s|>|$))",next:[{include:"attributes"},{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",next:t+"start"}]}),this.$rules[n+"-end"]=[{include:"attributes"},{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",next:"start",onMatch:function(e,t,n){return n.splice(0),this.token}}],this.embedRules(e,t,[{token:["meta.tag.punctuation.end-tag-open.xml","meta.tag."+n+".tag-name.xml"],regex:"(|$))",next:n+"-end"},{token:"string.cdata.xml",regex:"<\\!\\[CDATA\\["},{token:"string.cdata.xml",regex:"\\]\\]>"}])}}).call(i.prototype),r.inherits(s,i),t.XmlHighlightRules=s}),define("ace/mode/html_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/css_highlight_rules","ace/mode/javascript_highlight_rules","ace/mode/xml_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./css_highlight_rules").CssHighlightRules,o=e("./javascript_highlight_rules").JavaScriptHighlightRules,u=e("./xml_highlight_rules").XmlHighlightRules,a=i.createMap({a:"anchor",button:"form",form:"form",img:"image",input:"form",label:"form",option:"form",script:"script",select:"form",textarea:"form",style:"style",table:"table",tbody:"table",td:"table",tfoot:"table",th:"table",tr:"table"}),f=function(){u.call(this),this.addRules({attributes:[{include:"tag_whitespace"},{token:"entity.other.attribute-name.xml",regex:"[-_a-zA-Z0-9:.]+"},{token:"keyword.operator.attribute-equals.xml",regex:"=",push:[{include:"tag_whitespace"},{token:"string.unquoted.attribute-value.html",regex:"[^<>='\"`\\s]+",next:"pop"},{token:"empty",regex:"",next:"pop"}]},{include:"attribute_value"}],tag:[{token:function(e,t){var n=a[t];return["meta.tag.punctuation."+(e=="<"?"":"end-")+"tag-open.xml","meta.tag"+(n?"."+n:"")+".tag-name.xml"]},regex:"(",next:"start"}]}),this.embedTagRules(s,"css-","style"),this.embedTagRules((new o({jsx:!1})).getRules(),"js-","script"),this.constructor===f&&this.normalizeRules()};r.inherits(f,u),t.HtmlHighlightRules=f}),define("ace/mode/behaviour/xml",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";function u(e,t){return e.type.lastIndexOf(t+".xml")>-1}var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),a=function(){this.add("string_dquotes","insertion",function(e,t,n,r,i){if(i=='"'||i=="'"){var o=i,a=r.doc.getTextRange(n.getSelectionRange());if(a!==""&&a!=="'"&&a!='"'&&n.getWrapBehavioursEnabled())return{text:o+a+o,selection:!1};var f=n.getCursorPosition(),l=r.doc.getLine(f.row),c=l.substring(f.column,f.column+1),h=new s(r,f.row,f.column),p=h.getCurrentToken();if(c==o&&(u(p,"attribute-value")||u(p,"string")))return{text:"",selection:[1,1]};p||(p=h.stepBackward());if(!p)return;while(u(p,"tag-whitespace")||u(p,"whitespace"))p=h.stepBackward();var d=!c||c.match(/\s/);if(u(p,"attribute-equals")&&(d||c==">")||u(p,"decl-attribute-equals")&&(d||c=="?"))return{text:o+o,selection:[1,1]}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&(s=='"'||s=="'")){var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==s)return i.end.column++,i}}),this.add("autoclosing","insertion",function(e,t,n,r,i){if(i==">"){var o=n.getSelectionRange().start,a=new s(r,o.row,o.column),f=a.getCurrentToken()||a.stepBackward();if(!f||!(u(f,"tag-name")||u(f,"tag-whitespace")||u(f,"attribute-name")||u(f,"attribute-equals")||u(f,"attribute-value")))return;if(u(f,"reference.attribute-value"))return;if(u(f,"attribute-value")){var l=f.value.charAt(0);if(l=='"'||l=="'"){var c=f.value.charAt(f.value.length-1),h=a.getCurrentTokenColumn()+f.value.length;if(h>o.column||h==o.column&&l!=c)return}}while(!u(f,"tag-name")){f=a.stepBackward();if(f.value=="<"){f=a.stepForward();break}}var p=a.getCurrentTokenRow(),d=a.getCurrentTokenColumn();if(u(a.stepBackward(),"end-tag-open"))return;var v=f.value;p==o.row&&(v=v.substring(0,o.column-d));if(this.voidElements.hasOwnProperty(v.toLowerCase()))return;return{text:">",selection:[1,1]}}}),this.add("autoindent","insertion",function(e,t,n,r,i){if(i=="\n"){var o=n.getCursorPosition(),u=r.getLine(o.row),a=new s(r,o.row,o.column),f=a.getCurrentToken();if(f&&f.type.indexOf("tag-close")!==-1){if(f.value=="/>")return;while(f&&f.type.indexOf("tag-name")===-1)f=a.stepBackward();if(!f)return;var l=f.value,c=a.getCurrentTokenRow();f=a.stepBackward();if(!f||f.type.indexOf("end-tag")!==-1)return;if(this.voidElements&&!this.voidElements[l]){var h=r.getTokenAt(o.row,o.column+1),u=r.getLine(c),p=this.$getIndent(u),d=p+r.getTabString();return h&&h.value==="-1}var r=e("../../lib/oop"),i=e("../../lib/lang"),s=e("../../range").Range,o=e("./fold_mode").FoldMode,u=e("../../token_iterator").TokenIterator,a=t.FoldMode=function(e,t){o.call(this),this.voidElements=e||{},this.optionalEndTags=r.mixin({},this.voidElements),t&&r.mixin(this.optionalEndTags,t)};r.inherits(a,o);var f=function(){this.tagName="",this.closing=!1,this.selfClosing=!1,this.start={row:0,column:0},this.end={row:0,column:0}};(function(){this.getFoldWidget=function(e,t,n){var r=this._getFirstTagInLine(e,n);return r?r.closing||!r.tagName&&r.selfClosing?t=="markbeginend"?"end":"":!r.tagName||r.selfClosing||this.voidElements.hasOwnProperty(r.tagName.toLowerCase())?"":this._findEndTagInLine(e,n,r.tagName,r.end.column)?"":"start":""},this._getFirstTagInLine=function(e,t){var n=e.getTokens(t),r=new f;for(var i=0;i";break}}return r}if(l(s,"tag-close"))return r.selfClosing=s.value=="/>",r;r.start.column+=s.value.length}return null},this._findEndTagInLine=function(e,t,n,r){var i=e.getTokens(t),s=0;for(var o=0;o",n.end.row=e.getCurrentTokenRow(),n.end.column=e.getCurrentTokenColumn()+t.value.length,e.stepForward(),n;while(t=e.stepForward());return null},this._readTagBackward=function(e){var t=e.getCurrentToken();if(!t)return null;var n=new f;do{if(l(t,"tag-open"))return n.closing=l(t,"end-tag-open"),n.start.row=e.getCurrentTokenRow(),n.start.column=e.getCurrentTokenColumn(),e.stepBackward(),n;l(t,"tag-name")?n.tagName=t.value:l(t,"tag-close")&&(n.selfClosing=t.value=="/>",n.end.row=e.getCurrentTokenRow(),n.end.column=e.getCurrentTokenColumn()+t.value.length)}while(t=e.stepBackward());return null},this._pop=function(e,t){while(e.length){var n=e[e.length-1];if(!t||n.tagName==t.tagName)return e.pop();if(this.optionalEndTags.hasOwnProperty(n.tagName)){e.pop();continue}return null}},this.getFoldWidgetRange=function(e,t,n){var r=this._getFirstTagInLine(e,n);if(!r)return null;var i=r.closing||r.selfClosing,o=[],a;if(!i){var f=new u(e,n,r.start.column),l={row:n,column:r.start.column+r.tagName.length+2};r.start.row==r.end.row&&(l.column=r.end.column);while(a=this._readTagForward(f)){if(a.selfClosing){if(!o.length)return a.start.column+=a.tagName.length+2,a.end.column-=2,s.fromPoints(a.start,a.end);continue}if(a.closing){this._pop(o,a);if(o.length==0)return s.fromPoints(l,a.start)}else o.push(a)}}else{var f=new u(e,n,r.end.column),c={row:n,column:r.start.column};while(a=this._readTagBackward(f)){if(a.selfClosing){if(!o.length)return a.start.column+=a.tagName.length+2,a.end.column-=2,s.fromPoints(a.start,a.end);continue}if(!a.closing){this._pop(o,a);if(o.length==0)return a.start.column+=a.tagName.length+2,a.start.row==a.end.row&&a.start.column-1}function l(e,t){var n=new r(e,t.row,t.column),i=n.getCurrentToken();while(i&&!f(i,"tag-name"))i=n.stepBackward();if(i)return i.value}function c(e,t){var n=new r(e,t.row,t.column),i=n.getCurrentToken();while(i&&!f(i,"attribute-name"))i=n.stepBackward();if(i)return i.value}var r=e("../token_iterator").TokenIterator,i=["accesskey","class","contenteditable","contextmenu","dir","draggable","dropzone","hidden","id","inert","itemid","itemprop","itemref","itemscope","itemtype","lang","spellcheck","style","tabindex","title","translate"],s=["onabort","onblur","oncancel","oncanplay","oncanplaythrough","onchange","onclick","onclose","oncontextmenu","oncuechange","ondblclick","ondrag","ondragend","ondragenter","ondragleave","ondragover","ondragstart","ondrop","ondurationchange","onemptied","onended","onerror","onfocus","oninput","oninvalid","onkeydown","onkeypress","onkeyup","onload","onloadeddata","onloadedmetadata","onloadstart","onmousedown","onmousemove","onmouseout","onmouseover","onmouseup","onmousewheel","onpause","onplay","onplaying","onprogress","onratechange","onreset","onscroll","onseeked","onseeking","onselect","onshow","onstalled","onsubmit","onsuspend","ontimeupdate","onvolumechange","onwaiting"],o=i.concat(s),u={html:{manifest:1},head:{},title:{},base:{href:1,target:1},link:{href:1,hreflang:1,rel:{stylesheet:1,icon:1},media:{all:1,screen:1,print:1},type:{"text/css":1,"image/png":1,"image/jpeg":1,"image/gif":1},sizes:1},meta:{"http-equiv":{"content-type":1},name:{description:1,keywords:1},content:{"text/html; charset=UTF-8":1},charset:1},style:{type:1,media:{all:1,screen:1,print:1},scoped:1},script:{charset:1,type:{"text/javascript":1},src:1,defer:1,async:1},noscript:{href:1},body:{onafterprint:1,onbeforeprint:1,onbeforeunload:1,onhashchange:1,onmessage:1,onoffline:1,onpopstate:1,onredo:1,onresize:1,onstorage:1,onundo:1,onunload:1},section:{},nav:{},article:{pubdate:1},aside:{},h1:{},h2:{},h3:{},h4:{},h5:{},h6:{},header:{},footer:{},address:{},main:{},p:{},hr:{},pre:{},blockquote:{cite:1},ol:{start:1,reversed:1},ul:{},li:{value:1},dl:{},dt:{},dd:{},figure:{},figcaption:{},div:{},a:{href:1,target:{_blank:1,top:1},ping:1,rel:{nofollow:1,alternate:1,author:1,bookmark:1,help:1,license:1,next:1,noreferrer:1,prefetch:1,prev:1,search:1,tag:1},media:1,hreflang:1,type:1},em:{},strong:{},small:{},s:{},cite:{},q:{cite:1},dfn:{},abbr:{},data:{},time:{datetime:1},code:{},"var":{},samp:{},kbd:{},sub:{},sup:{},i:{},b:{},u:{},mark:{},ruby:{},rt:{},rp:{},bdi:{},bdo:{},span:{},br:{},wbr:{},ins:{cite:1,datetime:1},del:{cite:1,datetime:1},img:{alt:1,src:1,height:1,width:1,usemap:1,ismap:1},iframe:{name:1,src:1,height:1,width:1,sandbox:{"allow-same-origin":1,"allow-top-navigation":1,"allow-forms":1,"allow-scripts":1},seamless:{seamless:1}},embed:{src:1,height:1,width:1,type:1},object:{param:1,data:1,type:1,height:1,width:1,usemap:1,name:1,form:1,classid:1},param:{name:1,value:1},video:{src:1,autobuffer:1,autoplay:{autoplay:1},loop:{loop:1},controls:{controls:1},width:1,height:1,poster:1,muted:{muted:1},preload:{auto:1,metadata:1,none:1}},audio:{src:1,autobuffer:1,autoplay:{autoplay:1},loop:{loop:1},controls:{controls:1},muted:{muted:1},preload:{auto:1,metadata:1,none:1}},source:{src:1,type:1,media:1},track:{kind:1,src:1,srclang:1,label:1,"default":1},canvas:{width:1,height:1},map:{name:1},area:{shape:1,coords:1,href:1,hreflang:1,alt:1,target:1,media:1,rel:1,ping:1,type:1},svg:{},math:{},table:{summary:1},caption:{},colgroup:{span:1},col:{span:1},tbody:{},thead:{},tfoot:{},tr:{},td:{headers:1,rowspan:1,colspan:1},th:{headers:1,rowspan:1,colspan:1,scope:1},form:{"accept-charset":1,action:1,autocomplete:1,enctype:{"multipart/form-data":1,"application/x-www-form-urlencoded":1},method:{get:1,post:1},name:1,novalidate:1,target:{_blank:1,top:1}},fieldset:{disabled:1,form:1,name:1},legend:{},label:{form:1,"for":1},input:{type:{text:1,password:1,hidden:1,checkbox:1,submit:1,radio:1,file:1,button:1,reset:1,image:31,color:1,date:1,datetime:1,"datetime-local":1,email:1,month:1,number:1,range:1,search:1,tel:1,time:1,url:1,week:1},accept:1,alt:1,autocomplete:{on:1,off:1},autofocus:{autofocus:1},checked:{checked:1},disabled:{disabled:1},form:1,formaction:1,formenctype:{"application/x-www-form-urlencoded":1,"multipart/form-data":1,"text/plain":1},formmethod:{get:1,post:1},formnovalidate:{formnovalidate:1},formtarget:{_blank:1,_self:1,_parent:1,_top:1},height:1,list:1,max:1,maxlength:1,min:1,multiple:{multiple:1},name:1,pattern:1,placeholder:1,readonly:{readonly:1},required:{required:1},size:1,src:1,step:1,width:1,files:1,value:1},button:{autofocus:1,disabled:{disabled:1},form:1,formaction:1,formenctype:1,formmethod:1,formnovalidate:1,formtarget:1,name:1,value:1,type:{button:1,submit:1}},select:{autofocus:1,disabled:1,form:1,multiple:{multiple:1},name:1,size:1,readonly:{readonly:1}},datalist:{},optgroup:{disabled:1,label:1},option:{disabled:1,selected:1,label:1,value:1},textarea:{autofocus:{autofocus:1},disabled:{disabled:1},form:1,maxlength:1,name:1,placeholder:1,readonly:{readonly:1},required:{required:1},rows:1,cols:1,wrap:{on:1,off:1,hard:1,soft:1}},keygen:{autofocus:1,challenge:{challenge:1},disabled:{disabled:1},form:1,keytype:{rsa:1,dsa:1,ec:1},name:1},output:{"for":1,form:1,name:1},progress:{value:1,max:1},meter:{value:1,min:1,max:1,low:1,high:1,optimum:1},details:{open:1},summary:{},command:{type:1,label:1,icon:1,disabled:1,checked:1,radiogroup:1,command:1},menu:{type:1,label:1},dialog:{open:1}},a=Object.keys(u),h=function(){};(function(){this.getCompletions=function(e,t,n,r){var i=t.getTokenAt(n.row,n.column);if(!i)return[];if(f(i,"tag-name")||f(i,"tag-open")||f(i,"end-tag-open"))return this.getTagCompletions(e,t,n,r);if(f(i,"tag-whitespace")||f(i,"attribute-name"))return this.getAttributeCompletions(e,t,n,r);if(f(i,"attribute-value"))return this.getAttributeValueCompletions(e,t,n,r);var s=t.getLine(n.row).substr(0,n.column);return/&[a-z]*$/i.test(s)?this.getHTMLEntityCompletions(e,t,n,r):[]},this.getTagCompletions=function(e,t,n,r){return a.map(function(e){return{value:e,meta:"tag",score:Number.MAX_VALUE}})},this.getAttributeCompletions=function(e,t,n,r){var i=l(t,n);if(!i)return[];var s=o;return i in u&&(s=s.concat(Object.keys(u[i]))),s.map(function(e){return{caption:e,snippet:e+'="$0"',meta:"attribute",score:Number.MAX_VALUE}})},this.getAttributeValueCompletions=function(e,t,n,r){var i=l(t,n),s=c(t,n);if(!i)return[];var o=[];return i in u&&s in u[i]&&typeof u[i][s]=="object"&&(o=Object.keys(u[i][s])),o.map(function(e){return{caption:e,snippet:e,meta:"attribute value",score:Number.MAX_VALUE}})},this.getHTMLEntityCompletions=function(e,t,n,r){var i=["Aacute;","aacute;","Acirc;","acirc;","acute;","AElig;","aelig;","Agrave;","agrave;","alefsym;","Alpha;","alpha;","amp;","and;","ang;","Aring;","aring;","asymp;","Atilde;","atilde;","Auml;","auml;","bdquo;","Beta;","beta;","brvbar;","bull;","cap;","Ccedil;","ccedil;","cedil;","cent;","Chi;","chi;","circ;","clubs;","cong;","copy;","crarr;","cup;","curren;","Dagger;","dagger;","dArr;","darr;","deg;","Delta;","delta;","diams;","divide;","Eacute;","eacute;","Ecirc;","ecirc;","Egrave;","egrave;","empty;","emsp;","ensp;","Epsilon;","epsilon;","equiv;","Eta;","eta;","ETH;","eth;","Euml;","euml;","euro;","exist;","fnof;","forall;","frac12;","frac14;","frac34;","frasl;","Gamma;","gamma;","ge;","gt;","hArr;","harr;","hearts;","hellip;","Iacute;","iacute;","Icirc;","icirc;","iexcl;","Igrave;","igrave;","image;","infin;","int;","Iota;","iota;","iquest;","isin;","Iuml;","iuml;","Kappa;","kappa;","Lambda;","lambda;","lang;","laquo;","lArr;","larr;","lceil;","ldquo;","le;","lfloor;","lowast;","loz;","lrm;","lsaquo;","lsquo;","lt;","macr;","mdash;","micro;","middot;","minus;","Mu;","mu;","nabla;","nbsp;","ndash;","ne;","ni;","not;","notin;","nsub;","Ntilde;","ntilde;","Nu;","nu;","Oacute;","oacute;","Ocirc;","ocirc;","OElig;","oelig;","Ograve;","ograve;","oline;","Omega;","omega;","Omicron;","omicron;","oplus;","or;","ordf;","ordm;","Oslash;","oslash;","Otilde;","otilde;","otimes;","Ouml;","ouml;","para;","part;","permil;","perp;","Phi;","phi;","Pi;","pi;","piv;","plusmn;","pound;","Prime;","prime;","prod;","prop;","Psi;","psi;","quot;","radic;","rang;","raquo;","rArr;","rarr;","rceil;","rdquo;","real;","reg;","rfloor;","Rho;","rho;","rlm;","rsaquo;","rsquo;","sbquo;","Scaron;","scaron;","sdot;","sect;","shy;","Sigma;","sigma;","sigmaf;","sim;","spades;","sub;","sube;","sum;","sup;","sup1;","sup2;","sup3;","supe;","szlig;","Tau;","tau;","there4;","Theta;","theta;","thetasym;","thinsp;","THORN;","thorn;","tilde;","times;","trade;","Uacute;","uacute;","uArr;","uarr;","Ucirc;","ucirc;","Ugrave;","ugrave;","uml;","upsih;","Upsilon;","upsilon;","Uuml;","uuml;","weierp;","Xi;","xi;","Yacute;","yacute;","yen;","Yuml;","yuml;","Zeta;","zeta;","zwj;","zwnj;"];return i.map(function(e){return{caption:e,snippet:e,meta:"html entity",score:Number.MAX_VALUE}})}}).call(h.prototype),t.HtmlCompletions=h}),define("ace/mode/html",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text","ace/mode/javascript","ace/mode/css","ace/mode/html_highlight_rules","ace/mode/behaviour/xml","ace/mode/folding/html","ace/mode/html_completions","ace/worker/worker_client"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text").Mode,o=e("./javascript").Mode,u=e("./css").Mode,a=e("./html_highlight_rules").HtmlHighlightRules,f=e("./behaviour/xml").XmlBehaviour,l=e("./folding/html").FoldMode,c=e("./html_completions").HtmlCompletions,h=e("../worker/worker_client").WorkerClient,p=["area","base","br","col","embed","hr","img","input","keygen","link","meta","menuitem","param","source","track","wbr"],d=["li","dt","dd","p","rt","rp","optgroup","option","colgroup","td","th"],v=function(e){this.fragmentContext=e&&e.fragmentContext,this.HighlightRules=a,this.$behaviour=new f,this.$completer=new c,this.createModeDelegates({"js-":o,"css-":u}),this.foldingRules=new l(this.voidElements,i.arrayToMap(d))};r.inherits(v,s),function(){this.blockComment={start:""},this.voidElements=i.arrayToMap(p),this.getNextLineIndent=function(e,t,n){return this.$getIndent(t)},this.checkOutdent=function(e,t,n){return!1},this.getCompletions=function(e,t,n,r){return this.$completer.getCompletions(e,t,n,r)},this.createWorker=function(e){if(this.constructor!=v)return;var t=new h(["ace"],"ace/mode/html_worker","Worker");return t.attachToDocument(e.getDocument()),this.fragmentContext&&t.call("setOptions",[{context:this.fragmentContext}]),t.on("error",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/html"}.call(v.prototype),t.Mode=v}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-ini.js b/public/themes/pterodactyl/vendor/ace/mode-ini.js new file mode 100644 index 0000000..3826b3c --- /dev/null +++ b/public/themes/pterodactyl/vendor/ace/mode-ini.js @@ -0,0 +1 @@ +define("ace/mode/ini_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s="\\\\(?:[\\\\0abtrn;#=:]|x[a-fA-F\\d]{4})",o=function(){this.$rules={start:[{token:"punctuation.definition.comment.ini",regex:"#.*",push_:[{token:"comment.line.number-sign.ini",regex:"$|^",next:"pop"},{defaultToken:"comment.line.number-sign.ini"}]},{token:"punctuation.definition.comment.ini",regex:";.*",push_:[{token:"comment.line.semicolon.ini",regex:"$|^",next:"pop"},{defaultToken:"comment.line.semicolon.ini"}]},{token:["keyword.other.definition.ini","text","punctuation.separator.key-value.ini"],regex:"\\b([a-zA-Z0-9_.-]+)\\b(\\s*)(=)"},{token:["punctuation.definition.entity.ini","constant.section.group-title.ini","punctuation.definition.entity.ini"],regex:"^(\\[)(.*?)(\\])"},{token:"punctuation.definition.string.begin.ini",regex:"'",push:[{token:"punctuation.definition.string.end.ini",regex:"'",next:"pop"},{token:"constant.language.escape",regex:s},{defaultToken:"string.quoted.single.ini"}]},{token:"punctuation.definition.string.begin.ini",regex:'"',push:[{token:"constant.language.escape",regex:s},{token:"punctuation.definition.string.end.ini",regex:'"',next:"pop"},{defaultToken:"string.quoted.double.ini"}]}]},this.normalizeRules()};o.metaData={fileTypes:["ini","conf"],keyEquivalent:"^~I",name:"Ini",scopeName:"source.ini"},r.inherits(o,i),t.IniHighlightRules=o}),define("ace/mode/folding/ini",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(){};r.inherits(o,s),function(){this.foldingStartMarker=/^\s*\[([^\])]*)]\s*(?:$|[;#])/,this.getFoldWidgetRange=function(e,t,n){var r=this.foldingStartMarker,s=e.getLine(n),o=s.match(r);if(!o)return;var u=o[1]+".",a=s.length,f=e.getLength(),l=n,c=n;while(++nl){var h=e.getLine(c).length;return new i(l,a,c,h)}}}.call(o.prototype)}),define("ace/mode/ini",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/ini_highlight_rules","ace/mode/folding/ini"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./ini_highlight_rules").IniHighlightRules,o=e("./folding/ini").FoldMode,u=function(){this.HighlightRules=s,this.foldingRules=new o,this.$behaviour=this.$defaultBehaviour};r.inherits(u,i),function(){this.lineCommentStart=";",this.blockComment=null,this.$id="ace/mode/ini"}.call(u.prototype),t.Mode=u}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-java.js b/public/themes/pterodactyl/vendor/ace/mode-java.js new file mode 100644 index 0000000..cdddb9b --- /dev/null +++ b/public/themes/pterodactyl/vendor/ace/mode-java.js @@ -0,0 +1 @@ +define("ace/mode/doc_comment_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},s.getTagRule(),{defaultToken:"comment.doc",caseInsensitive:!0}]}};r.inherits(s,i),s.getTagRule=function(e){return{token:"comment.doc.tag.storage.type",regex:"\\b(?:TODO|FIXME|XXX|HACK)\\b"}},s.getStartRule=function(e){return{token:"comment.doc",regex:"\\/\\*(?=\\*)",next:e}},s.getEndRule=function(e){return{token:"comment.doc",regex:"\\*\\/",next:e}},t.DocCommentHighlightRules=s}),define("ace/mode/javascript_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";function a(){var e=o.replace("\\d","\\d\\-"),t={onMatch:function(e,t,n){var r=e.charAt(1)=="/"?2:1;if(r==1)t!=this.nextState?n.unshift(this.next,this.nextState,0):n.unshift(this.next),n[2]++;else if(r==2&&t==this.nextState){n[1]--;if(!n[1]||n[1]<0)n.shift(),n.shift()}return[{type:"meta.tag.punctuation."+(r==1?"":"end-")+"tag-open.xml",value:e.slice(0,r)},{type:"meta.tag.tag-name.xml",value:e.substr(r)}]},regex:"",onMatch:function(e,t,n){return t==n[0]&&n.shift(),e.length==2&&(n[0]==this.nextState&&n[1]--,(!n[1]||n[1]<0)&&n.splice(0,2)),this.next=n[0]||"start",[{type:this.token,value:e}]},nextState:"jsx"},n,f("jsxAttributes"),{token:"entity.other.attribute-name.xml",regex:e},{token:"keyword.operator.attribute-equals.xml",regex:"="},{token:"text.tag-whitespace.xml",regex:"\\s+"},{token:"string.attribute-value.xml",regex:"'",stateName:"jsx_attr_q",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',stateName:"jsx_attr_qq",push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},t],this.$rules.reference=[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}]}function f(e){return[{token:"comment",regex:/\/\*/,next:[i.getTagRule(),{token:"comment",regex:"\\*\\/",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]},{token:"comment",regex:"\\/\\/",next:[i.getTagRule(),{token:"comment",regex:"$|^",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]}]}var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./text_highlight_rules").TextHighlightRules,o="[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*",u=function(e){var t=this.createKeywordMapper({"variable.language":"Array|Boolean|Date|Function|Iterator|Number|Object|RegExp|String|Proxy|Namespace|QName|XML|XMLList|ArrayBuffer|Float32Array|Float64Array|Int16Array|Int32Array|Int8Array|Uint16Array|Uint32Array|Uint8Array|Uint8ClampedArray|Error|EvalError|InternalError|RangeError|ReferenceError|StopIteration|SyntaxError|TypeError|URIError|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|eval|isFinite|isNaN|parseFloat|parseInt|JSON|Math|this|arguments|prototype|window|document",keyword:"const|yield|import|get|set|async|await|break|case|catch|continue|default|delete|do|else|finally|for|function|if|in|of|instanceof|new|return|switch|throw|try|typeof|let|var|while|with|debugger|__parent__|__count__|escape|unescape|with|__proto__|class|enum|extends|super|export|implements|private|public|interface|package|protected|static","storage.type":"const|let|var|function","constant.language":"null|Infinity|NaN|undefined","support.function":"alert","constant.language.boolean":"true|false"},"identifier"),n="case|do|else|finally|in|instanceof|return|throw|try|typeof|yield|void",r="\\\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|u{[0-9a-fA-F]{1,6}}|[0-2][0-7]{0,2}|3[0-7][0-7]?|[4-7][0-7]?|.)";this.$rules={no_regex:[i.getStartRule("doc-start"),f("no_regex"),{token:"string",regex:"'(?=.)",next:"qstring"},{token:"string",regex:'"(?=.)',next:"qqstring"},{token:"constant.numeric",regex:/0(?:[xX][0-9a-fA-F]+|[bB][01]+)\b/},{token:"constant.numeric",regex:/[+-]?\d[\d_]*(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/},{token:["storage.type","punctuation.operator","support.function","punctuation.operator","entity.name.function","text","keyword.operator"],regex:"("+o+")(\\.)(prototype)(\\.)("+o+")(\\s*)(=)",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s+)(\\w+)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","text","entity.name.function","text","paren.lparen"],regex:"(function)(\\s+)("+o+")(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","punctuation.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["text","text","storage.type","text","paren.lparen"],regex:"(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:"keyword",regex:"(?:"+n+")\\b",next:"start"},{token:["support.constant"],regex:/that\b/},{token:["storage.type","punctuation.operator","support.function.firebug"],regex:/(console)(\.)(warn|info|log|error|time|trace|timeEnd|assert)\b/},{token:t,regex:o},{token:"punctuation.operator",regex:/[.](?![.])/,next:"property"},{token:"keyword.operator",regex:/--|\+\+|\.{3}|===|==|=|!=|!==|<+=?|>+=?|!|&&|\|\||\?:|[!$%&*+\-~\/^]=?/,next:"start"},{token:"punctuation.operator",regex:/[?:,;.]/,next:"start"},{token:"paren.lparen",regex:/[\[({]/,next:"start"},{token:"paren.rparen",regex:/[\])}]/},{token:"comment",regex:/^#!.*$/}],property:[{token:"text",regex:"\\s+"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(?:(\\s+)(\\w+))?(\\s*)(\\()",next:"function_arguments"},{token:"punctuation.operator",regex:/[.](?![.])/},{token:"support.function",regex:/(s(?:h(?:ift|ow(?:Mod(?:elessDialog|alDialog)|Help))|croll(?:X|By(?:Pages|Lines)?|Y|To)?|t(?:op|rike)|i(?:n|zeToContent|debar|gnText)|ort|u(?:p|b(?:str(?:ing)?)?)|pli(?:ce|t)|e(?:nd|t(?:Re(?:sizable|questHeader)|M(?:i(?:nutes|lliseconds)|onth)|Seconds|Ho(?:tKeys|urs)|Year|Cursor|Time(?:out)?|Interval|ZOptions|Date|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Date|FullYear)|FullYear|Active)|arch)|qrt|lice|avePreferences|mall)|h(?:ome|andleEvent)|navigate|c(?:har(?:CodeAt|At)|o(?:s|n(?:cat|textual|firm)|mpile)|eil|lear(?:Timeout|Interval)?|a(?:ptureEvents|ll)|reate(?:StyleSheet|Popup|EventObject))|t(?:o(?:GMTString|S(?:tring|ource)|U(?:TCString|pperCase)|Lo(?:caleString|werCase))|est|a(?:n|int(?:Enabled)?))|i(?:s(?:NaN|Finite)|ndexOf|talics)|d(?:isableExternalCapture|ump|etachEvent)|u(?:n(?:shift|taint|escape|watch)|pdateCommands)|j(?:oin|avaEnabled)|p(?:o(?:p|w)|ush|lugins.refresh|a(?:ddings|rse(?:Int|Float)?)|r(?:int|ompt|eference))|e(?:scape|nableExternalCapture|val|lementFromPoint|x(?:p|ec(?:Script|Command)?))|valueOf|UTC|queryCommand(?:State|Indeterm|Enabled|Value)|f(?:i(?:nd|le(?:ModifiedDate|Size|CreatedDate|UpdatedDate)|xed)|o(?:nt(?:size|color)|rward)|loor|romCharCode)|watch|l(?:ink|o(?:ad|g)|astIndexOf)|a(?:sin|nchor|cos|t(?:tachEvent|ob|an(?:2)?)|pply|lert|b(?:s|ort))|r(?:ou(?:nd|teEvents)|e(?:size(?:By|To)|calc|turnValue|place|verse|l(?:oad|ease(?:Capture|Events)))|andom)|g(?:o|et(?:ResponseHeader|M(?:i(?:nutes|lliseconds)|onth)|Se(?:conds|lection)|Hours|Year|Time(?:zoneOffset)?|Da(?:y|te)|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Da(?:y|te)|FullYear)|FullYear|A(?:ttention|llResponseHeaders)))|m(?:in|ove(?:B(?:y|elow)|To(?:Absolute)?|Above)|ergeAttributes|a(?:tch|rgins|x))|b(?:toa|ig|o(?:ld|rderWidths)|link|ack))\b(?=\()/},{token:"support.function.dom",regex:/(s(?:ub(?:stringData|mit)|plitText|e(?:t(?:NamedItem|Attribute(?:Node)?)|lect))|has(?:ChildNodes|Feature)|namedItem|c(?:l(?:ick|o(?:se|neNode))|reate(?:C(?:omment|DATASection|aption)|T(?:Head|extNode|Foot)|DocumentFragment|ProcessingInstruction|E(?:ntityReference|lement)|Attribute))|tabIndex|i(?:nsert(?:Row|Before|Cell|Data)|tem)|open|delete(?:Row|C(?:ell|aption)|T(?:Head|Foot)|Data)|focus|write(?:ln)?|a(?:dd|ppend(?:Child|Data))|re(?:set|place(?:Child|Data)|move(?:NamedItem|Child|Attribute(?:Node)?)?)|get(?:NamedItem|Element(?:sBy(?:Name|TagName|ClassName)|ById)|Attribute(?:Node)?)|blur)\b(?=\()/},{token:"support.constant",regex:/(s(?:ystemLanguage|cr(?:ipts|ollbars|een(?:X|Y|Top|Left))|t(?:yle(?:Sheets)?|atus(?:Text|bar)?)|ibling(?:Below|Above)|ource|uffixes|e(?:curity(?:Policy)?|l(?:ection|f)))|h(?:istory|ost(?:name)?|as(?:h|Focus))|y|X(?:MLDocument|SLDocument)|n(?:ext|ame(?:space(?:s|URI)|Prop))|M(?:IN_VALUE|AX_VALUE)|c(?:haracterSet|o(?:n(?:structor|trollers)|okieEnabled|lorDepth|mp(?:onents|lete))|urrent|puClass|l(?:i(?:p(?:boardData)?|entInformation)|osed|asses)|alle(?:e|r)|rypto)|t(?:o(?:olbar|p)|ext(?:Transform|Indent|Decoration|Align)|ags)|SQRT(?:1_2|2)|i(?:n(?:ner(?:Height|Width)|put)|ds|gnoreCase)|zIndex|o(?:scpu|n(?:readystatechange|Line)|uter(?:Height|Width)|p(?:sProfile|ener)|ffscreenBuffering)|NEGATIVE_INFINITY|d(?:i(?:splay|alog(?:Height|Top|Width|Left|Arguments)|rectories)|e(?:scription|fault(?:Status|Ch(?:ecked|arset)|View)))|u(?:ser(?:Profile|Language|Agent)|n(?:iqueID|defined)|pdateInterval)|_content|p(?:ixelDepth|ort|ersonalbar|kcs11|l(?:ugins|atform)|a(?:thname|dding(?:Right|Bottom|Top|Left)|rent(?:Window|Layer)?|ge(?:X(?:Offset)?|Y(?:Offset)?))|r(?:o(?:to(?:col|type)|duct(?:Sub)?|mpter)|e(?:vious|fix)))|e(?:n(?:coding|abledPlugin)|x(?:ternal|pando)|mbeds)|v(?:isibility|endor(?:Sub)?|Linkcolor)|URLUnencoded|P(?:I|OSITIVE_INFINITY)|f(?:ilename|o(?:nt(?:Size|Family|Weight)|rmName)|rame(?:s|Element)|gColor)|E|whiteSpace|l(?:i(?:stStyleType|n(?:eHeight|kColor))|o(?:ca(?:tion(?:bar)?|lName)|wsrc)|e(?:ngth|ft(?:Context)?)|a(?:st(?:M(?:odified|atch)|Index|Paren)|yer(?:s|X)|nguage))|a(?:pp(?:MinorVersion|Name|Co(?:deName|re)|Version)|vail(?:Height|Top|Width|Left)|ll|r(?:ity|guments)|Linkcolor|bove)|r(?:ight(?:Context)?|e(?:sponse(?:XML|Text)|adyState))|global|x|m(?:imeTypes|ultiline|enubar|argin(?:Right|Bottom|Top|Left))|L(?:N(?:10|2)|OG(?:10E|2E))|b(?:o(?:ttom|rder(?:Width|RightWidth|BottomWidth|Style|Color|TopWidth|LeftWidth))|ufferDepth|elow|ackground(?:Color|Image)))\b/},{token:"identifier",regex:o},{regex:"",token:"empty",next:"no_regex"}],start:[i.getStartRule("doc-start"),f("start"),{token:"string.regexp",regex:"\\/",next:"regex"},{token:"text",regex:"\\s+|^$",next:"start"},{token:"empty",regex:"",next:"no_regex"}],regex:[{token:"regexp.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"string.regexp",regex:"/[sxngimy]*",next:"no_regex"},{token:"invalid",regex:/\{\d+\b,?\d*\}[+*]|[+*$^?][+*]|[$^][?]|\?{3,}/},{token:"constant.language.escape",regex:/\(\?[:=!]|\)|\{\d+\b,?\d*\}|[+*]\?|[()$^+*?.]/},{token:"constant.language.delimiter",regex:/\|/},{token:"constant.language.escape",regex:/\[\^?/,next:"regex_character_class"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp"}],regex_character_class:[{token:"regexp.charclass.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"constant.language.escape",regex:"]",next:"regex"},{token:"constant.language.escape",regex:"-"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp.charachterclass"}],function_arguments:[{token:"variable.parameter",regex:o},{token:"punctuation.operator",regex:"[, ]+"},{token:"punctuation.operator",regex:"$"},{token:"empty",regex:"",next:"no_regex"}],qqstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qqstring"},{token:"string",regex:'"|$',next:"no_regex"},{defaultToken:"string"}],qstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qstring"},{token:"string",regex:"'|$",next:"no_regex"},{defaultToken:"string"}]};if(!e||!e.noES6)this.$rules.no_regex.unshift({regex:"[{}]",onMatch:function(e,t,n){this.next=e=="{"?this.nextState:"";if(e=="{"&&n.length)n.unshift("start",t);else if(e=="}"&&n.length){n.shift(),this.next=n.shift();if(this.next.indexOf("string")!=-1||this.next.indexOf("jsx")!=-1)return"paren.quasi.end"}return e=="{"?"paren.lparen":"paren.rparen"},nextState:"start"},{token:"string.quasi.start",regex:/`/,push:[{token:"constant.language.escape",regex:r},{token:"paren.quasi.start",regex:/\${/,push:"start"},{token:"string.quasi.end",regex:/`/,next:"pop"},{defaultToken:"string.quasi"}]}),(!e||e.jsx!=0)&&a.call(this);this.embedRules(i,"doc-",[i.getEndRule("no_regex")]),this.normalizeRules()};r.inherits(u,s),t.JavaScriptHighlightRules=u}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/javascript",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/javascript_highlight_rules","ace/mode/matching_brace_outdent","ace/worker/worker_client","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./javascript_highlight_rules").JavaScriptHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../worker/worker_client").WorkerClient,a=e("./behaviour/cstyle").CstyleBehaviour,f=e("./folding/cstyle").FoldMode,l=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new a,this.foldingRules=new f};r.inherits(l,i),function(){this.lineCommentStart="//",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens,o=i.state;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"||e=="no_regex"){var u=t.match(/^.*(?:\bcase\b.*:|[\{\(\[])\s*$/);u&&(r+=n)}else if(e=="doc-start"){if(o=="start"||o=="no_regex")return"";var u=t.match(/^\s*(\/?)\*/);u&&(u[1]&&(r+=" "),r+="* ")}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.createWorker=function(e){var t=new u(["ace"],"ace/mode/javascript_worker","JavaScriptWorker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/javascript"}.call(l.prototype),t.Mode=l}),define("ace/mode/java_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./text_highlight_rules").TextHighlightRules,o=function(){var e="abstract|continue|for|new|switch|assert|default|goto|package|synchronized|boolean|do|if|private|this|break|double|implements|protected|throw|byte|else|import|public|throws|case|enum|instanceof|return|transient|catch|extends|int|short|try|char|final|interface|static|void|class|finally|long|strictfp|volatile|const|float|native|super|while",t="null|Infinity|NaN|undefined",n="AbstractMethodError|AssertionError|ClassCircularityError|ClassFormatError|Deprecated|EnumConstantNotPresentException|ExceptionInInitializerError|IllegalAccessError|IllegalThreadStateException|InstantiationError|InternalError|NegativeArraySizeException|NoSuchFieldError|Override|Process|ProcessBuilder|SecurityManager|StringIndexOutOfBoundsException|SuppressWarnings|TypeNotPresentException|UnknownError|UnsatisfiedLinkError|UnsupportedClassVersionError|VerifyError|InstantiationException|IndexOutOfBoundsException|ArrayIndexOutOfBoundsException|CloneNotSupportedException|NoSuchFieldException|IllegalArgumentException|NumberFormatException|SecurityException|Void|InheritableThreadLocal|IllegalStateException|InterruptedException|NoSuchMethodException|IllegalAccessException|UnsupportedOperationException|Enum|StrictMath|Package|Compiler|Readable|Runtime|StringBuilder|Math|IncompatibleClassChangeError|NoSuchMethodError|ThreadLocal|RuntimePermission|ArithmeticException|NullPointerException|Long|Integer|Short|Byte|Double|Number|Float|Character|Boolean|StackTraceElement|Appendable|StringBuffer|Iterable|ThreadGroup|Runnable|Thread|IllegalMonitorStateException|StackOverflowError|OutOfMemoryError|VirtualMachineError|ArrayStoreException|ClassCastException|LinkageError|NoClassDefFoundError|ClassNotFoundException|RuntimeException|Exception|ThreadDeath|Error|Throwable|System|ClassLoader|Cloneable|Class|CharSequence|Comparable|String|Object",r=this.createKeywordMapper({"variable.language":"this",keyword:e,"constant.language":t,"support.function":n},"identifier");this.$rules={start:[{token:"comment",regex:"\\/\\/.*$"},i.getStartRule("doc-start"),{token:"comment",regex:"\\/\\*",next:"comment"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"constant.numeric",regex:/0(?:[xX][0-9a-fA-F][0-9a-fA-F_]*|[bB][01][01_]*)[LlSsDdFfYy]?\b/},{token:"constant.numeric",regex:/[+-]?\d[\d_]*(?:(?:\.[\d_]*)?(?:[eE][+-]?[\d_]+)?)?[LlSsDdFfYy]?\b/},{token:"constant.language.boolean",regex:"(?:true|false)\\b"},{token:r,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"!|\\$|%|&|\\*|\\-\\-|\\-|\\+\\+|\\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\\|\\||\\?\\:|\\*=|%=|\\+=|\\-=|&=|\\^=|\\b(?:in|instanceof|new|delete|typeof|void)"},{token:"lparen",regex:"[[({]"},{token:"rparen",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],comment:[{token:"comment",regex:".*?\\*\\/",next:"start"},{token:"comment",regex:".+"}]},this.embedRules(i,"doc-",[i.getEndRule("start")])};r.inherits(o,s),t.JavaHighlightRules=o}),define("ace/mode/java",["require","exports","module","ace/lib/oop","ace/mode/javascript","ace/mode/java_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./javascript").Mode,s=e("./java_highlight_rules").JavaHighlightRules,o=function(){i.call(this),this.HighlightRules=s};r.inherits(o,i),function(){this.createWorker=function(e){return null},this.$id="ace/mode/java"}.call(o.prototype),t.Mode=o}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-javascript.js b/public/themes/pterodactyl/vendor/ace/mode-javascript.js new file mode 100644 index 0000000..0893912 --- /dev/null +++ b/public/themes/pterodactyl/vendor/ace/mode-javascript.js @@ -0,0 +1 @@ +define("ace/mode/doc_comment_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},s.getTagRule(),{defaultToken:"comment.doc",caseInsensitive:!0}]}};r.inherits(s,i),s.getTagRule=function(e){return{token:"comment.doc.tag.storage.type",regex:"\\b(?:TODO|FIXME|XXX|HACK)\\b"}},s.getStartRule=function(e){return{token:"comment.doc",regex:"\\/\\*(?=\\*)",next:e}},s.getEndRule=function(e){return{token:"comment.doc",regex:"\\*\\/",next:e}},t.DocCommentHighlightRules=s}),define("ace/mode/javascript_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";function a(){var e=o.replace("\\d","\\d\\-"),t={onMatch:function(e,t,n){var r=e.charAt(1)=="/"?2:1;if(r==1)t!=this.nextState?n.unshift(this.next,this.nextState,0):n.unshift(this.next),n[2]++;else if(r==2&&t==this.nextState){n[1]--;if(!n[1]||n[1]<0)n.shift(),n.shift()}return[{type:"meta.tag.punctuation."+(r==1?"":"end-")+"tag-open.xml",value:e.slice(0,r)},{type:"meta.tag.tag-name.xml",value:e.substr(r)}]},regex:"",onMatch:function(e,t,n){return t==n[0]&&n.shift(),e.length==2&&(n[0]==this.nextState&&n[1]--,(!n[1]||n[1]<0)&&n.splice(0,2)),this.next=n[0]||"start",[{type:this.token,value:e}]},nextState:"jsx"},n,f("jsxAttributes"),{token:"entity.other.attribute-name.xml",regex:e},{token:"keyword.operator.attribute-equals.xml",regex:"="},{token:"text.tag-whitespace.xml",regex:"\\s+"},{token:"string.attribute-value.xml",regex:"'",stateName:"jsx_attr_q",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',stateName:"jsx_attr_qq",push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},t],this.$rules.reference=[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}]}function f(e){return[{token:"comment",regex:/\/\*/,next:[i.getTagRule(),{token:"comment",regex:"\\*\\/",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]},{token:"comment",regex:"\\/\\/",next:[i.getTagRule(),{token:"comment",regex:"$|^",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]}]}var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./text_highlight_rules").TextHighlightRules,o="[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*",u=function(e){var t=this.createKeywordMapper({"variable.language":"Array|Boolean|Date|Function|Iterator|Number|Object|RegExp|String|Proxy|Namespace|QName|XML|XMLList|ArrayBuffer|Float32Array|Float64Array|Int16Array|Int32Array|Int8Array|Uint16Array|Uint32Array|Uint8Array|Uint8ClampedArray|Error|EvalError|InternalError|RangeError|ReferenceError|StopIteration|SyntaxError|TypeError|URIError|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|eval|isFinite|isNaN|parseFloat|parseInt|JSON|Math|this|arguments|prototype|window|document",keyword:"const|yield|import|get|set|async|await|break|case|catch|continue|default|delete|do|else|finally|for|function|if|in|of|instanceof|new|return|switch|throw|try|typeof|let|var|while|with|debugger|__parent__|__count__|escape|unescape|with|__proto__|class|enum|extends|super|export|implements|private|public|interface|package|protected|static","storage.type":"const|let|var|function","constant.language":"null|Infinity|NaN|undefined","support.function":"alert","constant.language.boolean":"true|false"},"identifier"),n="case|do|else|finally|in|instanceof|return|throw|try|typeof|yield|void",r="\\\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|u{[0-9a-fA-F]{1,6}}|[0-2][0-7]{0,2}|3[0-7][0-7]?|[4-7][0-7]?|.)";this.$rules={no_regex:[i.getStartRule("doc-start"),f("no_regex"),{token:"string",regex:"'(?=.)",next:"qstring"},{token:"string",regex:'"(?=.)',next:"qqstring"},{token:"constant.numeric",regex:/0(?:[xX][0-9a-fA-F]+|[bB][01]+)\b/},{token:"constant.numeric",regex:/[+-]?\d[\d_]*(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/},{token:["storage.type","punctuation.operator","support.function","punctuation.operator","entity.name.function","text","keyword.operator"],regex:"("+o+")(\\.)(prototype)(\\.)("+o+")(\\s*)(=)",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s+)(\\w+)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","text","entity.name.function","text","paren.lparen"],regex:"(function)(\\s+)("+o+")(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","punctuation.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["text","text","storage.type","text","paren.lparen"],regex:"(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:"keyword",regex:"(?:"+n+")\\b",next:"start"},{token:["support.constant"],regex:/that\b/},{token:["storage.type","punctuation.operator","support.function.firebug"],regex:/(console)(\.)(warn|info|log|error|time|trace|timeEnd|assert)\b/},{token:t,regex:o},{token:"punctuation.operator",regex:/[.](?![.])/,next:"property"},{token:"keyword.operator",regex:/--|\+\+|\.{3}|===|==|=|!=|!==|<+=?|>+=?|!|&&|\|\||\?:|[!$%&*+\-~\/^]=?/,next:"start"},{token:"punctuation.operator",regex:/[?:,;.]/,next:"start"},{token:"paren.lparen",regex:/[\[({]/,next:"start"},{token:"paren.rparen",regex:/[\])}]/},{token:"comment",regex:/^#!.*$/}],property:[{token:"text",regex:"\\s+"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(?:(\\s+)(\\w+))?(\\s*)(\\()",next:"function_arguments"},{token:"punctuation.operator",regex:/[.](?![.])/},{token:"support.function",regex:/(s(?:h(?:ift|ow(?:Mod(?:elessDialog|alDialog)|Help))|croll(?:X|By(?:Pages|Lines)?|Y|To)?|t(?:op|rike)|i(?:n|zeToContent|debar|gnText)|ort|u(?:p|b(?:str(?:ing)?)?)|pli(?:ce|t)|e(?:nd|t(?:Re(?:sizable|questHeader)|M(?:i(?:nutes|lliseconds)|onth)|Seconds|Ho(?:tKeys|urs)|Year|Cursor|Time(?:out)?|Interval|ZOptions|Date|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Date|FullYear)|FullYear|Active)|arch)|qrt|lice|avePreferences|mall)|h(?:ome|andleEvent)|navigate|c(?:har(?:CodeAt|At)|o(?:s|n(?:cat|textual|firm)|mpile)|eil|lear(?:Timeout|Interval)?|a(?:ptureEvents|ll)|reate(?:StyleSheet|Popup|EventObject))|t(?:o(?:GMTString|S(?:tring|ource)|U(?:TCString|pperCase)|Lo(?:caleString|werCase))|est|a(?:n|int(?:Enabled)?))|i(?:s(?:NaN|Finite)|ndexOf|talics)|d(?:isableExternalCapture|ump|etachEvent)|u(?:n(?:shift|taint|escape|watch)|pdateCommands)|j(?:oin|avaEnabled)|p(?:o(?:p|w)|ush|lugins.refresh|a(?:ddings|rse(?:Int|Float)?)|r(?:int|ompt|eference))|e(?:scape|nableExternalCapture|val|lementFromPoint|x(?:p|ec(?:Script|Command)?))|valueOf|UTC|queryCommand(?:State|Indeterm|Enabled|Value)|f(?:i(?:nd|le(?:ModifiedDate|Size|CreatedDate|UpdatedDate)|xed)|o(?:nt(?:size|color)|rward)|loor|romCharCode)|watch|l(?:ink|o(?:ad|g)|astIndexOf)|a(?:sin|nchor|cos|t(?:tachEvent|ob|an(?:2)?)|pply|lert|b(?:s|ort))|r(?:ou(?:nd|teEvents)|e(?:size(?:By|To)|calc|turnValue|place|verse|l(?:oad|ease(?:Capture|Events)))|andom)|g(?:o|et(?:ResponseHeader|M(?:i(?:nutes|lliseconds)|onth)|Se(?:conds|lection)|Hours|Year|Time(?:zoneOffset)?|Da(?:y|te)|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Da(?:y|te)|FullYear)|FullYear|A(?:ttention|llResponseHeaders)))|m(?:in|ove(?:B(?:y|elow)|To(?:Absolute)?|Above)|ergeAttributes|a(?:tch|rgins|x))|b(?:toa|ig|o(?:ld|rderWidths)|link|ack))\b(?=\()/},{token:"support.function.dom",regex:/(s(?:ub(?:stringData|mit)|plitText|e(?:t(?:NamedItem|Attribute(?:Node)?)|lect))|has(?:ChildNodes|Feature)|namedItem|c(?:l(?:ick|o(?:se|neNode))|reate(?:C(?:omment|DATASection|aption)|T(?:Head|extNode|Foot)|DocumentFragment|ProcessingInstruction|E(?:ntityReference|lement)|Attribute))|tabIndex|i(?:nsert(?:Row|Before|Cell|Data)|tem)|open|delete(?:Row|C(?:ell|aption)|T(?:Head|Foot)|Data)|focus|write(?:ln)?|a(?:dd|ppend(?:Child|Data))|re(?:set|place(?:Child|Data)|move(?:NamedItem|Child|Attribute(?:Node)?)?)|get(?:NamedItem|Element(?:sBy(?:Name|TagName|ClassName)|ById)|Attribute(?:Node)?)|blur)\b(?=\()/},{token:"support.constant",regex:/(s(?:ystemLanguage|cr(?:ipts|ollbars|een(?:X|Y|Top|Left))|t(?:yle(?:Sheets)?|atus(?:Text|bar)?)|ibling(?:Below|Above)|ource|uffixes|e(?:curity(?:Policy)?|l(?:ection|f)))|h(?:istory|ost(?:name)?|as(?:h|Focus))|y|X(?:MLDocument|SLDocument)|n(?:ext|ame(?:space(?:s|URI)|Prop))|M(?:IN_VALUE|AX_VALUE)|c(?:haracterSet|o(?:n(?:structor|trollers)|okieEnabled|lorDepth|mp(?:onents|lete))|urrent|puClass|l(?:i(?:p(?:boardData)?|entInformation)|osed|asses)|alle(?:e|r)|rypto)|t(?:o(?:olbar|p)|ext(?:Transform|Indent|Decoration|Align)|ags)|SQRT(?:1_2|2)|i(?:n(?:ner(?:Height|Width)|put)|ds|gnoreCase)|zIndex|o(?:scpu|n(?:readystatechange|Line)|uter(?:Height|Width)|p(?:sProfile|ener)|ffscreenBuffering)|NEGATIVE_INFINITY|d(?:i(?:splay|alog(?:Height|Top|Width|Left|Arguments)|rectories)|e(?:scription|fault(?:Status|Ch(?:ecked|arset)|View)))|u(?:ser(?:Profile|Language|Agent)|n(?:iqueID|defined)|pdateInterval)|_content|p(?:ixelDepth|ort|ersonalbar|kcs11|l(?:ugins|atform)|a(?:thname|dding(?:Right|Bottom|Top|Left)|rent(?:Window|Layer)?|ge(?:X(?:Offset)?|Y(?:Offset)?))|r(?:o(?:to(?:col|type)|duct(?:Sub)?|mpter)|e(?:vious|fix)))|e(?:n(?:coding|abledPlugin)|x(?:ternal|pando)|mbeds)|v(?:isibility|endor(?:Sub)?|Linkcolor)|URLUnencoded|P(?:I|OSITIVE_INFINITY)|f(?:ilename|o(?:nt(?:Size|Family|Weight)|rmName)|rame(?:s|Element)|gColor)|E|whiteSpace|l(?:i(?:stStyleType|n(?:eHeight|kColor))|o(?:ca(?:tion(?:bar)?|lName)|wsrc)|e(?:ngth|ft(?:Context)?)|a(?:st(?:M(?:odified|atch)|Index|Paren)|yer(?:s|X)|nguage))|a(?:pp(?:MinorVersion|Name|Co(?:deName|re)|Version)|vail(?:Height|Top|Width|Left)|ll|r(?:ity|guments)|Linkcolor|bove)|r(?:ight(?:Context)?|e(?:sponse(?:XML|Text)|adyState))|global|x|m(?:imeTypes|ultiline|enubar|argin(?:Right|Bottom|Top|Left))|L(?:N(?:10|2)|OG(?:10E|2E))|b(?:o(?:ttom|rder(?:Width|RightWidth|BottomWidth|Style|Color|TopWidth|LeftWidth))|ufferDepth|elow|ackground(?:Color|Image)))\b/},{token:"identifier",regex:o},{regex:"",token:"empty",next:"no_regex"}],start:[i.getStartRule("doc-start"),f("start"),{token:"string.regexp",regex:"\\/",next:"regex"},{token:"text",regex:"\\s+|^$",next:"start"},{token:"empty",regex:"",next:"no_regex"}],regex:[{token:"regexp.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"string.regexp",regex:"/[sxngimy]*",next:"no_regex"},{token:"invalid",regex:/\{\d+\b,?\d*\}[+*]|[+*$^?][+*]|[$^][?]|\?{3,}/},{token:"constant.language.escape",regex:/\(\?[:=!]|\)|\{\d+\b,?\d*\}|[+*]\?|[()$^+*?.]/},{token:"constant.language.delimiter",regex:/\|/},{token:"constant.language.escape",regex:/\[\^?/,next:"regex_character_class"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp"}],regex_character_class:[{token:"regexp.charclass.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"constant.language.escape",regex:"]",next:"regex"},{token:"constant.language.escape",regex:"-"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp.charachterclass"}],function_arguments:[{token:"variable.parameter",regex:o},{token:"punctuation.operator",regex:"[, ]+"},{token:"punctuation.operator",regex:"$"},{token:"empty",regex:"",next:"no_regex"}],qqstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qqstring"},{token:"string",regex:'"|$',next:"no_regex"},{defaultToken:"string"}],qstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qstring"},{token:"string",regex:"'|$",next:"no_regex"},{defaultToken:"string"}]};if(!e||!e.noES6)this.$rules.no_regex.unshift({regex:"[{}]",onMatch:function(e,t,n){this.next=e=="{"?this.nextState:"";if(e=="{"&&n.length)n.unshift("start",t);else if(e=="}"&&n.length){n.shift(),this.next=n.shift();if(this.next.indexOf("string")!=-1||this.next.indexOf("jsx")!=-1)return"paren.quasi.end"}return e=="{"?"paren.lparen":"paren.rparen"},nextState:"start"},{token:"string.quasi.start",regex:/`/,push:[{token:"constant.language.escape",regex:r},{token:"paren.quasi.start",regex:/\${/,push:"start"},{token:"string.quasi.end",regex:/`/,next:"pop"},{defaultToken:"string.quasi"}]}),(!e||e.jsx!=0)&&a.call(this);this.embedRules(i,"doc-",[i.getEndRule("no_regex")]),this.normalizeRules()};r.inherits(u,s),t.JavaScriptHighlightRules=u}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/javascript",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/javascript_highlight_rules","ace/mode/matching_brace_outdent","ace/worker/worker_client","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./javascript_highlight_rules").JavaScriptHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../worker/worker_client").WorkerClient,a=e("./behaviour/cstyle").CstyleBehaviour,f=e("./folding/cstyle").FoldMode,l=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new a,this.foldingRules=new f};r.inherits(l,i),function(){this.lineCommentStart="//",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens,o=i.state;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"||e=="no_regex"){var u=t.match(/^.*(?:\bcase\b.*:|[\{\(\[])\s*$/);u&&(r+=n)}else if(e=="doc-start"){if(o=="start"||o=="no_regex")return"";var u=t.match(/^\s*(\/?)\*/);u&&(u[1]&&(r+=" "),r+="* ")}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.createWorker=function(e){var t=new u(["ace"],"ace/mode/javascript_worker","JavaScriptWorker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/javascript"}.call(l.prototype),t.Mode=l}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-json.js b/public/themes/pterodactyl/vendor/ace/mode-json.js new file mode 100644 index 0000000..c919855 --- /dev/null +++ b/public/themes/pterodactyl/vendor/ace/mode-json.js @@ -0,0 +1 @@ +define("ace/mode/json_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"variable",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]\\s*(?=:)'},{token:"string",regex:'"',next:"string"},{token:"constant.numeric",regex:"0[xX][0-9a-fA-F]+\\b"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:"constant.language.boolean",regex:"(?:true|false)\\b"},{token:"invalid.illegal",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"invalid.illegal",regex:"\\/\\/.*$"},{token:"paren.lparen",regex:"[[({]"},{token:"paren.rparen",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],string:[{token:"constant.language.escape",regex:/\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|["\\\/bfnrt])/},{token:"string",regex:'[^"\\\\]+'},{token:"string",regex:'"',next:"start"},{token:"string",regex:"",next:"start"}]}};r.inherits(s,i),t.JsonHighlightRules=s}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/json",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/json_highlight_rules","ace/mode/matching_brace_outdent","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle","ace/worker/worker_client"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./json_highlight_rules").JsonHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("./behaviour/cstyle").CstyleBehaviour,a=e("./folding/cstyle").FoldMode,f=e("../worker/worker_client").WorkerClient,l=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new u,this.foldingRules=new a};r.inherits(l,i),function(){this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t);if(e=="start"){var i=t.match(/^.*[\{\(\[]\s*$/);i&&(r+=n)}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.createWorker=function(e){var t=new f(["ace"],"ace/mode/json_worker","JsonWorker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/json"}.call(l.prototype),t.Mode=l}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-kotlin.js b/public/themes/pterodactyl/vendor/ace/mode-kotlin.js new file mode 100644 index 0000000..462cc34 --- /dev/null +++ b/public/themes/pterodactyl/vendor/ace/mode-kotlin.js @@ -0,0 +1 @@ +define("ace/mode/kotlin_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{include:"#comments"},{token:["text","keyword.other.kotlin","text","entity.name.package.kotlin","text"],regex:/^(\s*)(package)\b(?:(\s*)([^ ;$]+)(\s*))?/},{include:"#imports"},{include:"#statements"}],"#classes":[{token:"text",regex:/(?=\s*(?:companion|class|object|interface))/,push:[{token:"text",regex:/}|(?=$)/,next:"pop"},{token:["keyword.other.kotlin","text"],regex:/\b((?:companion\s*)?)(class|object|interface)\b/,push:[{token:"text",regex:/(?=<|{|\(|:)/,next:"pop"},{token:"keyword.other.kotlin",regex:/\bobject\b/},{token:"entity.name.type.class.kotlin",regex:/\w+/}]},{token:"text",regex://,next:"pop"},{include:"#generics"}]},{token:"text",regex:/\(/,push:[{token:"text",regex:/\)/,next:"pop"},{include:"#parameters"}]},{token:"keyword.operator.declaration.kotlin",regex:/:/,push:[{token:"text",regex:/(?={|$)/,next:"pop"},{token:"entity.other.inherited-class.kotlin",regex:/\w+/},{token:"text",regex:/\(/,push:[{token:"text",regex:/\)/,next:"pop"},{include:"#expressions"}]}]},{token:"text",regex:/\{/,push:[{token:"text",regex:/\}/,next:"pop"},{include:"#statements"}]}]}],"#comments":[{token:"punctuation.definition.comment.kotlin",regex:/\/\*/,push:[{token:"punctuation.definition.comment.kotlin",regex:/\*\//,next:"pop"},{defaultToken:"comment.block.kotlin"}]},{token:["text","punctuation.definition.comment.kotlin","comment.line.double-slash.kotlin"],regex:/(\s*)(\/\/)(.*$)/}],"#constants":[{token:"constant.language.kotlin",regex:/\b(?:true|false|null|this|super)\b/},{token:"constant.numeric.kotlin",regex:/\b(?:0(?:x|X)[0-9a-fA-F]*|(?:[0-9]+\.?[0-9]*|\.[0-9]+)(?:(?:e|E)(?:\+|-)?[0-9]+)?)(?:[LlFfUuDd]|UL|ul)?\b/},{token:"constant.other.kotlin",regex:/\b[A-Z][A-Z0-9_]+\b/}],"#expressions":[{token:"text",regex:/\(/,push:[{token:"text",regex:/\)/,next:"pop"},{include:"#expressions"}]},{include:"#types"},{include:"#strings"},{include:"#constants"},{include:"#comments"},{include:"#keywords"}],"#functions":[{token:"text",regex:/(?=\s*fun)/,push:[{token:"text",regex:/}|(?=$)/,next:"pop"},{token:"keyword.other.kotlin",regex:/\bfun\b/,push:[{token:"text",regex:/(?=\()/,next:"pop"},{token:"text",regex://,next:"pop"},{include:"#generics"}]},{token:["text","entity.name.function.kotlin"],regex:/((?:[\.<\?>\w]+\.)?)(\w+)/}]},{token:"text",regex:/\(/,push:[{token:"text",regex:/\)/,next:"pop"},{include:"#parameters"}]},{token:"keyword.operator.declaration.kotlin",regex:/:/,push:[{token:"text",regex:/(?={|=|$)/,next:"pop"},{include:"#types"}]},{token:"text",regex:/\{/,push:[{token:"text",regex:/(?=\})/,next:"pop"},{include:"#statements"}]},{token:"keyword.operator.assignment.kotlin",regex:/=/,push:[{token:"text",regex:/(?=$)/,next:"pop"},{include:"#expressions"}]}]}],"#generics":[{token:"keyword.operator.declaration.kotlin",regex:/:/,push:[{token:"text",regex:/(?=,|>)/,next:"pop"},{include:"#types"}]},{include:"#keywords"},{token:"storage.type.generic.kotlin",regex:/\w+/}],"#getters-and-setters":[{token:["entity.name.function.kotlin","text"],regex:/\b(get)\b(\s*\(\s*\))/,push:[{token:"text",regex:/\}|(?=\bset\b)|$/,next:"pop"},{token:"keyword.operator.assignment.kotlin",regex:/=/,push:[{token:"text",regex:/(?=$|\bset\b)/,next:"pop"},{include:"#expressions"}]},{token:"text",regex:/\{/,push:[{token:"text",regex:/\}/,next:"pop"},{include:"#expressions"}]}]},{token:["entity.name.function.kotlin","text"],regex:/\b(set)\b(\s*)(?=\()/,push:[{token:"text",regex:/\}|(?=\bget\b)|$/,next:"pop"},{token:"text",regex:/\(/,push:[{token:"text",regex:/\)/,next:"pop"},{include:"#parameters"}]},{token:"keyword.operator.assignment.kotlin",regex:/=/,push:[{token:"text",regex:/(?=$|\bset\b)/,next:"pop"},{include:"#expressions"}]},{token:"text",regex:/\{/,push:[{token:"text",regex:/\}/,next:"pop"},{include:"#expressions"}]}]}],"#imports":[{token:["text","keyword.other.kotlin","text","keyword.other.kotlin"],regex:/^(\s*)(import)(\s+[^ $]+\s+)((?:as)?)/}],"#keywords":[{token:"storage.modifier.kotlin",regex:/\b(?:var|val|public|private|protected|abstract|final|enum|open|attribute|annotation|override|inline|var|val|vararg|lazy|in|out|internal|data|tailrec|operator|infix|const|yield|typealias|typeof)\b/},{token:"keyword.control.catch-exception.kotlin",regex:/\b(?:try|catch|finally|throw)\b/},{token:"keyword.control.kotlin",regex:/\b(?:if|else|while|for|do|return|when|where|break|continue)\b/},{token:"keyword.operator.kotlin",regex:/\b(?:in|is|as|assert)\b/},{token:"keyword.operator.comparison.kotlin",regex:/==|!=|===|!==|<=|>=|<|>/},{token:"keyword.operator.assignment.kotlin",regex:/=/},{token:"keyword.operator.declaration.kotlin",regex:/:/},{token:"keyword.operator.dot.kotlin",regex:/\./},{token:"keyword.operator.increment-decrement.kotlin",regex:/\-\-|\+\+/},{token:"keyword.operator.arithmetic.kotlin",regex:/\-|\+|\*|\/|%/},{token:"keyword.operator.arithmetic.assign.kotlin",regex:/\+=|\-=|\*=|\/=/},{token:"keyword.operator.logical.kotlin",regex:/!|&&|\|\|/},{token:"keyword.operator.range.kotlin",regex:/\.\./},{token:"punctuation.terminator.kotlin",regex:/;/}],"#namespaces":[{token:"keyword.other.kotlin",regex:/\bnamespace\b/},{token:"text",regex:/\{/,push:[{token:"text",regex:/\}/,next:"pop"},{include:"#statements"}]}],"#parameters":[{token:"keyword.operator.declaration.kotlin",regex:/:/,push:[{token:"text",regex:/(?=,|\)|=)/,next:"pop"},{include:"#types"}]},{token:"keyword.operator.declaration.kotlin",regex:/=/,push:[{token:"text",regex:/(?=,|\))/,next:"pop"},{include:"#expressions"}]},{include:"#keywords"},{token:"variable.parameter.function.kotlin",regex:/\w+/}],"#statements":[{include:"#namespaces"},{include:"#typedefs"},{include:"#classes"},{include:"#functions"},{include:"#variables"},{include:"#getters-and-setters"},{include:"#expressions"}],"#strings":[{token:"punctuation.definition.string.begin.kotlin",regex:/"""/,push:[{token:"punctuation.definition.string.end.kotlin",regex:/"""/,next:"pop"},{token:"variable.parameter.template.kotlin",regex:/\$\w+|\$\{[^\}]+\}/},{token:"constant.character.escape.kotlin",regex:/\\./},{defaultToken:"string.quoted.third.kotlin"}]},{token:"punctuation.definition.string.begin.kotlin",regex:/"/,push:[{token:"punctuation.definition.string.end.kotlin",regex:/"/,next:"pop"},{token:"variable.parameter.template.kotlin",regex:/\$\w+|\$\{[^\}]+\}/},{token:"constant.character.escape.kotlin",regex:/\\./},{defaultToken:"string.quoted.double.kotlin"}]},{token:"punctuation.definition.string.begin.kotlin",regex:/'/,push:[{token:"punctuation.definition.string.end.kotlin",regex:/'/,next:"pop"},{token:"constant.character.escape.kotlin",regex:/\\./},{defaultToken:"string.quoted.single.kotlin"}]},{token:"punctuation.definition.string.begin.kotlin",regex:/`/,push:[{token:"punctuation.definition.string.end.kotlin",regex:/`/,next:"pop"},{defaultToken:"string.quoted.single.kotlin"}]}],"#typedefs":[{token:"text",regex:/(?=\s*type)/,push:[{token:"text",regex:/(?=$)/,next:"pop"},{token:"keyword.other.kotlin",regex:/\btype\b/},{token:"text",regex://,next:"pop"},{include:"#generics"}]},{include:"#expressions"}]}],"#types":[{token:"storage.type.buildin.kotlin",regex:/\b(?:Any|Unit|String|Int|Boolean|Char|Long|Double|Float|Short|Byte|dynamic)\b/},{token:"storage.type.buildin.array.kotlin",regex:/\b(?:IntArray|BooleanArray|CharArray|LongArray|DoubleArray|FloatArray|ShortArray|ByteArray)\b/},{token:["storage.type.buildin.collection.kotlin","text"],regex:/\b(Array|List|Map)(<\b)/,push:[{token:"text",regex:/>/,next:"pop"},{include:"#types"},{include:"#keywords"}]},{token:"text",regex:/\w+/,next:"pop"},{include:"#types"},{include:"#keywords"}]},{token:["keyword.operator.tuple.kotlin","text"],regex:/(#)(\()/,push:[{token:"text",regex:/\)/,next:"pop"},{include:"#expressions"}]},{token:"text",regex:/\{/,push:[{token:"text",regex:/\}/,next:"pop"},{include:"#statements"}]},{token:"text",regex:/\(/,push:[{token:"text",regex:/\)/,next:"pop"},{include:"#types"}]},{token:"keyword.operator.declaration.kotlin",regex:/->/}],"#variables":[{token:"text",regex:/(?=\s*(?:var|val))/,push:[{token:"text",regex:/(?=:|=|$)/,next:"pop"},{token:"keyword.other.kotlin",regex:/\b(?:var|val)\b/,push:[{token:"text",regex:/(?=:|=|$)/,next:"pop"},{token:"text",regex://,next:"pop"},{include:"#generics"}]},{token:["text","entity.name.variable.kotlin"],regex:/((?:[\.<\?>\w]+\.)?)(\w+)/}]},{token:"keyword.operator.declaration.kotlin",regex:/:/,push:[{token:"text",regex:/(?==|$)/,next:"pop"},{include:"#types"},{include:"#getters-and-setters"}]},{token:"keyword.operator.assignment.kotlin",regex:/=/,push:[{token:"text",regex:/(?=$)/,next:"pop"},{include:"#expressions"},{include:"#getters-and-setters"}]}]}]},this.normalizeRules()};s.metaData={fileTypes:["kt","kts"],name:"Kotlin",scopeName:"source.Kotlin"},r.inherits(s,i),t.KotlinHighlightRules=s}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/kotlin",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/kotlin_highlight_rules","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./kotlin_highlight_rules").KotlinHighlightRules,o=e("./folding/cstyle").FoldMode,u=function(){this.HighlightRules=s,this.foldingRules=new o};r.inherits(u,i),function(){this.$id="ace/mode/kotlin"}.call(u.prototype),t.Mode=u}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-lua.js b/public/themes/pterodactyl/vendor/ace/mode-lua.js new file mode 100644 index 0000000..bd473ef --- /dev/null +++ b/public/themes/pterodactyl/vendor/ace/mode-lua.js @@ -0,0 +1 @@ +define("ace/mode/lua_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e="break|do|else|elseif|end|for|function|if|in|local|repeat|return|then|until|while|or|and|not",t="true|false|nil|_G|_VERSION",n="string|xpcall|package|tostring|print|os|unpack|require|getfenv|setmetatable|next|assert|tonumber|io|rawequal|collectgarbage|getmetatable|module|rawset|math|debug|pcall|table|newproxy|type|coroutine|_G|select|gcinfo|pairs|rawget|loadstring|ipairs|_VERSION|dofile|setfenv|load|error|loadfile|sub|upper|len|gfind|rep|find|match|char|dump|gmatch|reverse|byte|format|gsub|lower|preload|loadlib|loaded|loaders|cpath|config|path|seeall|exit|setlocale|date|getenv|difftime|remove|time|clock|tmpname|rename|execute|lines|write|close|flush|open|output|type|read|stderr|stdin|input|stdout|popen|tmpfile|log|max|acos|huge|ldexp|pi|cos|tanh|pow|deg|tan|cosh|sinh|random|randomseed|frexp|ceil|floor|rad|abs|sqrt|modf|asin|min|mod|fmod|log10|atan2|exp|sin|atan|getupvalue|debug|sethook|getmetatable|gethook|setmetatable|setlocal|traceback|setfenv|getinfo|setupvalue|getlocal|getregistry|getfenv|setn|insert|getn|foreachi|maxn|foreach|concat|sort|remove|resume|yield|status|wrap|create|running|__add|__sub|__mod|__unm|__concat|__lt|__index|__call|__gc|__metatable|__mul|__div|__pow|__len|__eq|__le|__newindex|__tostring|__mode|__tonumber",r="string|package|os|io|math|debug|table|coroutine",i="setn|foreach|foreachi|gcinfo|log10|maxn",s=this.createKeywordMapper({keyword:e,"support.function":n,"keyword.deprecated":i,"constant.library":r,"constant.language":t,"variable.language":"self"},"identifier"),o="(?:(?:[1-9]\\d*)|(?:0))",u="(?:0[xX][\\dA-Fa-f]+)",a="(?:"+o+"|"+u+")",f="(?:\\.\\d+)",l="(?:\\d+)",c="(?:(?:"+l+"?"+f+")|(?:"+l+"\\.))",h="(?:"+c+")";this.$rules={start:[{stateName:"bracketedComment",onMatch:function(e,t,n){return n.unshift(this.next,e.length-2,t),"comment"},regex:/\-\-\[=*\[/,next:[{onMatch:function(e,t,n){return e.length==n[1]?(n.shift(),n.shift(),this.next=n.shift()):this.next="","comment"},regex:/\]=*\]/,next:"start"},{defaultToken:"comment"}]},{token:"comment",regex:"\\-\\-.*$"},{stateName:"bracketedString",onMatch:function(e,t,n){return n.unshift(this.next,e.length,t),"comment"},regex:/\[=*\[/,next:[{onMatch:function(e,t,n){return e.length==n[1]?(n.shift(),n.shift(),this.next=n.shift()):this.next="","comment"},regex:/\]=*\]/,next:"start"},{defaultToken:"comment"}]},{token:"string",regex:'"(?:[^\\\\]|\\\\.)*?"'},{token:"string",regex:"'(?:[^\\\\]|\\\\.)*?'"},{token:"constant.numeric",regex:h},{token:"constant.numeric",regex:a+"\\b"},{token:s,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+|\\w+"}]},this.normalizeRules()};r.inherits(s,i),t.LuaHighlightRules=s}),define("ace/mode/folding/lua",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode","ace/range","ace/token_iterator"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("./fold_mode").FoldMode,s=e("../../range").Range,o=e("../../token_iterator").TokenIterator,u=t.FoldMode=function(){};r.inherits(u,i),function(){this.foldingStartMarker=/\b(function|then|do|repeat)\b|{\s*$|(\[=*\[)/,this.foldingStopMarker=/\bend\b|^\s*}|\]=*\]/,this.getFoldWidget=function(e,t,n){var r=e.getLine(n),i=this.foldingStartMarker.test(r),s=this.foldingStopMarker.test(r);if(i&&!s){var o=r.match(this.foldingStartMarker);if(o[1]=="then"&&/\belseif\b/.test(r))return;if(o[1]){if(e.getTokenAt(n,o.index+1).type==="keyword")return"start"}else{if(!o[2])return"start";var u=e.bgTokenizer.getState(n)||"";if(u[0]=="bracketedComment"||u[0]=="bracketedString")return"start"}}if(t!="markbeginend"||!s||i&&s)return"";var o=r.match(this.foldingStopMarker);if(o[0]==="end"){if(e.getTokenAt(n,o.index+1).type==="keyword")return"end"}else{if(o[0][0]!=="]")return"end";var u=e.bgTokenizer.getState(n-1)||"";if(u[0]=="bracketedComment"||u[0]=="bracketedString")return"end"}},this.getFoldWidgetRange=function(e,t,n){var r=e.doc.getLine(n),i=this.foldingStartMarker.exec(r);if(i)return i[1]?this.luaBlock(e,n,i.index+1):i[2]?e.getCommentFoldRange(n,i.index+1):this.openingBracketBlock(e,"{",n,i.index);var i=this.foldingStopMarker.exec(r);if(i)return i[0]==="end"&&e.getTokenAt(n,i.index+1).type==="keyword"?this.luaBlock(e,n,i.index+1):i[0][0]==="]"?e.getCommentFoldRange(n,i.index+1):this.closingBracketBlock(e,"}",n,i.index+i[0].length)},this.luaBlock=function(e,t,n){var r=new o(e,t,n),i={"function":1,"do":1,then:1,elseif:-1,end:-1,repeat:1,until:-1},u=r.getCurrentToken();if(!u||u.type!="keyword")return;var a=u.value,f=[a],l=i[a];if(!l)return;var c=l===-1?r.getCurrentTokenColumn():e.getLine(t).length,h=t;r.step=l===-1?r.stepBackward:r.stepForward;while(u=r.step()){if(u.type!=="keyword")continue;var p=l*i[u.value];if(p>0)f.unshift(u.value);else if(p<=0){f.shift();if(!f.length&&u.value!="elseif")break;p===0&&f.unshift(u.value)}}var t=r.getCurrentTokenRow();return l===-1?new s(t,e.getLine(t).length,h,c):new s(h,c,t,r.getCurrentTokenColumn())}}.call(u.prototype)}),define("ace/mode/lua",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/lua_highlight_rules","ace/mode/folding/lua","ace/range","ace/worker/worker_client"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./lua_highlight_rules").LuaHighlightRules,o=e("./folding/lua").FoldMode,u=e("../range").Range,a=e("../worker/worker_client").WorkerClient,f=function(){this.HighlightRules=s,this.foldingRules=new o,this.$behaviour=this.$defaultBehaviour};r.inherits(f,i),function(){function n(t){var n=0;for(var r=0;r0?1:0}this.lineCommentStart="--",this.blockComment={start:"--[",end:"]--"};var e={"function":1,then:1,"do":1,"else":1,elseif:1,repeat:1,end:-1,until:-1},t=["else","elseif","end","until"];this.getNextLineIndent=function(e,t,r){var i=this.$getIndent(t),s=0,o=this.getTokenizer().getLineTokens(t,e),u=o.tokens;return e=="start"&&(s=n(u)),s>0?i+r:s<0&&i.substr(i.length-r.length)==r&&!this.checkOutdent(e,t,"\n")?i.substr(0,i.length-r.length):i},this.checkOutdent=function(e,n,r){if(r!="\n"&&r!="\r"&&r!="\r\n")return!1;if(n.match(/^\s*[\)\}\]]$/))return!0;var i=this.getTokenizer().getLineTokens(n.trim(),e).tokens;return!i||!i.length?!1:i[0].type=="keyword"&&t.indexOf(i[0].value)!=-1},this.autoOutdent=function(e,t,r){var i=t.getLine(r-1),s=this.$getIndent(i).length,o=this.getTokenizer().getLineTokens(i,"start").tokens,a=t.getTabString().length,f=s+a*n(o),l=this.$getIndent(t.getLine(r)).length;if(l",onMatch:function(e,t,n){return t==n[0]&&n.shift(),e.length==2&&(n[0]==this.nextState&&n[1]--,(!n[1]||n[1]<0)&&n.splice(0,2)),this.next=n[0]||"start",[{type:this.token,value:e}]},nextState:"jsx"},n,f("jsxAttributes"),{token:"entity.other.attribute-name.xml",regex:e},{token:"keyword.operator.attribute-equals.xml",regex:"="},{token:"text.tag-whitespace.xml",regex:"\\s+"},{token:"string.attribute-value.xml",regex:"'",stateName:"jsx_attr_q",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',stateName:"jsx_attr_qq",push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},t],this.$rules.reference=[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}]}function f(e){return[{token:"comment",regex:/\/\*/,next:[i.getTagRule(),{token:"comment",regex:"\\*\\/",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]},{token:"comment",regex:"\\/\\/",next:[i.getTagRule(),{token:"comment",regex:"$|^",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]}]}var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./text_highlight_rules").TextHighlightRules,o="[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*",u=function(e){var t=this.createKeywordMapper({"variable.language":"Array|Boolean|Date|Function|Iterator|Number|Object|RegExp|String|Proxy|Namespace|QName|XML|XMLList|ArrayBuffer|Float32Array|Float64Array|Int16Array|Int32Array|Int8Array|Uint16Array|Uint32Array|Uint8Array|Uint8ClampedArray|Error|EvalError|InternalError|RangeError|ReferenceError|StopIteration|SyntaxError|TypeError|URIError|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|eval|isFinite|isNaN|parseFloat|parseInt|JSON|Math|this|arguments|prototype|window|document",keyword:"const|yield|import|get|set|async|await|break|case|catch|continue|default|delete|do|else|finally|for|function|if|in|of|instanceof|new|return|switch|throw|try|typeof|let|var|while|with|debugger|__parent__|__count__|escape|unescape|with|__proto__|class|enum|extends|super|export|implements|private|public|interface|package|protected|static","storage.type":"const|let|var|function","constant.language":"null|Infinity|NaN|undefined","support.function":"alert","constant.language.boolean":"true|false"},"identifier"),n="case|do|else|finally|in|instanceof|return|throw|try|typeof|yield|void",r="\\\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|u{[0-9a-fA-F]{1,6}}|[0-2][0-7]{0,2}|3[0-7][0-7]?|[4-7][0-7]?|.)";this.$rules={no_regex:[i.getStartRule("doc-start"),f("no_regex"),{token:"string",regex:"'(?=.)",next:"qstring"},{token:"string",regex:'"(?=.)',next:"qqstring"},{token:"constant.numeric",regex:/0(?:[xX][0-9a-fA-F]+|[bB][01]+)\b/},{token:"constant.numeric",regex:/[+-]?\d[\d_]*(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/},{token:["storage.type","punctuation.operator","support.function","punctuation.operator","entity.name.function","text","keyword.operator"],regex:"("+o+")(\\.)(prototype)(\\.)("+o+")(\\s*)(=)",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s+)(\\w+)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","text","entity.name.function","text","paren.lparen"],regex:"(function)(\\s+)("+o+")(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","punctuation.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["text","text","storage.type","text","paren.lparen"],regex:"(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:"keyword",regex:"(?:"+n+")\\b",next:"start"},{token:["support.constant"],regex:/that\b/},{token:["storage.type","punctuation.operator","support.function.firebug"],regex:/(console)(\.)(warn|info|log|error|time|trace|timeEnd|assert)\b/},{token:t,regex:o},{token:"punctuation.operator",regex:/[.](?![.])/,next:"property"},{token:"keyword.operator",regex:/--|\+\+|\.{3}|===|==|=|!=|!==|<+=?|>+=?|!|&&|\|\||\?:|[!$%&*+\-~\/^]=?/,next:"start"},{token:"punctuation.operator",regex:/[?:,;.]/,next:"start"},{token:"paren.lparen",regex:/[\[({]/,next:"start"},{token:"paren.rparen",regex:/[\])}]/},{token:"comment",regex:/^#!.*$/}],property:[{token:"text",regex:"\\s+"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(?:(\\s+)(\\w+))?(\\s*)(\\()",next:"function_arguments"},{token:"punctuation.operator",regex:/[.](?![.])/},{token:"support.function",regex:/(s(?:h(?:ift|ow(?:Mod(?:elessDialog|alDialog)|Help))|croll(?:X|By(?:Pages|Lines)?|Y|To)?|t(?:op|rike)|i(?:n|zeToContent|debar|gnText)|ort|u(?:p|b(?:str(?:ing)?)?)|pli(?:ce|t)|e(?:nd|t(?:Re(?:sizable|questHeader)|M(?:i(?:nutes|lliseconds)|onth)|Seconds|Ho(?:tKeys|urs)|Year|Cursor|Time(?:out)?|Interval|ZOptions|Date|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Date|FullYear)|FullYear|Active)|arch)|qrt|lice|avePreferences|mall)|h(?:ome|andleEvent)|navigate|c(?:har(?:CodeAt|At)|o(?:s|n(?:cat|textual|firm)|mpile)|eil|lear(?:Timeout|Interval)?|a(?:ptureEvents|ll)|reate(?:StyleSheet|Popup|EventObject))|t(?:o(?:GMTString|S(?:tring|ource)|U(?:TCString|pperCase)|Lo(?:caleString|werCase))|est|a(?:n|int(?:Enabled)?))|i(?:s(?:NaN|Finite)|ndexOf|talics)|d(?:isableExternalCapture|ump|etachEvent)|u(?:n(?:shift|taint|escape|watch)|pdateCommands)|j(?:oin|avaEnabled)|p(?:o(?:p|w)|ush|lugins.refresh|a(?:ddings|rse(?:Int|Float)?)|r(?:int|ompt|eference))|e(?:scape|nableExternalCapture|val|lementFromPoint|x(?:p|ec(?:Script|Command)?))|valueOf|UTC|queryCommand(?:State|Indeterm|Enabled|Value)|f(?:i(?:nd|le(?:ModifiedDate|Size|CreatedDate|UpdatedDate)|xed)|o(?:nt(?:size|color)|rward)|loor|romCharCode)|watch|l(?:ink|o(?:ad|g)|astIndexOf)|a(?:sin|nchor|cos|t(?:tachEvent|ob|an(?:2)?)|pply|lert|b(?:s|ort))|r(?:ou(?:nd|teEvents)|e(?:size(?:By|To)|calc|turnValue|place|verse|l(?:oad|ease(?:Capture|Events)))|andom)|g(?:o|et(?:ResponseHeader|M(?:i(?:nutes|lliseconds)|onth)|Se(?:conds|lection)|Hours|Year|Time(?:zoneOffset)?|Da(?:y|te)|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Da(?:y|te)|FullYear)|FullYear|A(?:ttention|llResponseHeaders)))|m(?:in|ove(?:B(?:y|elow)|To(?:Absolute)?|Above)|ergeAttributes|a(?:tch|rgins|x))|b(?:toa|ig|o(?:ld|rderWidths)|link|ack))\b(?=\()/},{token:"support.function.dom",regex:/(s(?:ub(?:stringData|mit)|plitText|e(?:t(?:NamedItem|Attribute(?:Node)?)|lect))|has(?:ChildNodes|Feature)|namedItem|c(?:l(?:ick|o(?:se|neNode))|reate(?:C(?:omment|DATASection|aption)|T(?:Head|extNode|Foot)|DocumentFragment|ProcessingInstruction|E(?:ntityReference|lement)|Attribute))|tabIndex|i(?:nsert(?:Row|Before|Cell|Data)|tem)|open|delete(?:Row|C(?:ell|aption)|T(?:Head|Foot)|Data)|focus|write(?:ln)?|a(?:dd|ppend(?:Child|Data))|re(?:set|place(?:Child|Data)|move(?:NamedItem|Child|Attribute(?:Node)?)?)|get(?:NamedItem|Element(?:sBy(?:Name|TagName|ClassName)|ById)|Attribute(?:Node)?)|blur)\b(?=\()/},{token:"support.constant",regex:/(s(?:ystemLanguage|cr(?:ipts|ollbars|een(?:X|Y|Top|Left))|t(?:yle(?:Sheets)?|atus(?:Text|bar)?)|ibling(?:Below|Above)|ource|uffixes|e(?:curity(?:Policy)?|l(?:ection|f)))|h(?:istory|ost(?:name)?|as(?:h|Focus))|y|X(?:MLDocument|SLDocument)|n(?:ext|ame(?:space(?:s|URI)|Prop))|M(?:IN_VALUE|AX_VALUE)|c(?:haracterSet|o(?:n(?:structor|trollers)|okieEnabled|lorDepth|mp(?:onents|lete))|urrent|puClass|l(?:i(?:p(?:boardData)?|entInformation)|osed|asses)|alle(?:e|r)|rypto)|t(?:o(?:olbar|p)|ext(?:Transform|Indent|Decoration|Align)|ags)|SQRT(?:1_2|2)|i(?:n(?:ner(?:Height|Width)|put)|ds|gnoreCase)|zIndex|o(?:scpu|n(?:readystatechange|Line)|uter(?:Height|Width)|p(?:sProfile|ener)|ffscreenBuffering)|NEGATIVE_INFINITY|d(?:i(?:splay|alog(?:Height|Top|Width|Left|Arguments)|rectories)|e(?:scription|fault(?:Status|Ch(?:ecked|arset)|View)))|u(?:ser(?:Profile|Language|Agent)|n(?:iqueID|defined)|pdateInterval)|_content|p(?:ixelDepth|ort|ersonalbar|kcs11|l(?:ugins|atform)|a(?:thname|dding(?:Right|Bottom|Top|Left)|rent(?:Window|Layer)?|ge(?:X(?:Offset)?|Y(?:Offset)?))|r(?:o(?:to(?:col|type)|duct(?:Sub)?|mpter)|e(?:vious|fix)))|e(?:n(?:coding|abledPlugin)|x(?:ternal|pando)|mbeds)|v(?:isibility|endor(?:Sub)?|Linkcolor)|URLUnencoded|P(?:I|OSITIVE_INFINITY)|f(?:ilename|o(?:nt(?:Size|Family|Weight)|rmName)|rame(?:s|Element)|gColor)|E|whiteSpace|l(?:i(?:stStyleType|n(?:eHeight|kColor))|o(?:ca(?:tion(?:bar)?|lName)|wsrc)|e(?:ngth|ft(?:Context)?)|a(?:st(?:M(?:odified|atch)|Index|Paren)|yer(?:s|X)|nguage))|a(?:pp(?:MinorVersion|Name|Co(?:deName|re)|Version)|vail(?:Height|Top|Width|Left)|ll|r(?:ity|guments)|Linkcolor|bove)|r(?:ight(?:Context)?|e(?:sponse(?:XML|Text)|adyState))|global|x|m(?:imeTypes|ultiline|enubar|argin(?:Right|Bottom|Top|Left))|L(?:N(?:10|2)|OG(?:10E|2E))|b(?:o(?:ttom|rder(?:Width|RightWidth|BottomWidth|Style|Color|TopWidth|LeftWidth))|ufferDepth|elow|ackground(?:Color|Image)))\b/},{token:"identifier",regex:o},{regex:"",token:"empty",next:"no_regex"}],start:[i.getStartRule("doc-start"),f("start"),{token:"string.regexp",regex:"\\/",next:"regex"},{token:"text",regex:"\\s+|^$",next:"start"},{token:"empty",regex:"",next:"no_regex"}],regex:[{token:"regexp.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"string.regexp",regex:"/[sxngimy]*",next:"no_regex"},{token:"invalid",regex:/\{\d+\b,?\d*\}[+*]|[+*$^?][+*]|[$^][?]|\?{3,}/},{token:"constant.language.escape",regex:/\(\?[:=!]|\)|\{\d+\b,?\d*\}|[+*]\?|[()$^+*?.]/},{token:"constant.language.delimiter",regex:/\|/},{token:"constant.language.escape",regex:/\[\^?/,next:"regex_character_class"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp"}],regex_character_class:[{token:"regexp.charclass.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"constant.language.escape",regex:"]",next:"regex"},{token:"constant.language.escape",regex:"-"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp.charachterclass"}],function_arguments:[{token:"variable.parameter",regex:o},{token:"punctuation.operator",regex:"[, ]+"},{token:"punctuation.operator",regex:"$"},{token:"empty",regex:"",next:"no_regex"}],qqstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qqstring"},{token:"string",regex:'"|$',next:"no_regex"},{defaultToken:"string"}],qstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qstring"},{token:"string",regex:"'|$",next:"no_regex"},{defaultToken:"string"}]};if(!e||!e.noES6)this.$rules.no_regex.unshift({regex:"[{}]",onMatch:function(e,t,n){this.next=e=="{"?this.nextState:"";if(e=="{"&&n.length)n.unshift("start",t);else if(e=="}"&&n.length){n.shift(),this.next=n.shift();if(this.next.indexOf("string")!=-1||this.next.indexOf("jsx")!=-1)return"paren.quasi.end"}return e=="{"?"paren.lparen":"paren.rparen"},nextState:"start"},{token:"string.quasi.start",regex:/`/,push:[{token:"constant.language.escape",regex:r},{token:"paren.quasi.start",regex:/\${/,push:"start"},{token:"string.quasi.end",regex:/`/,next:"pop"},{defaultToken:"string.quasi"}]}),(!e||e.jsx!=0)&&a.call(this);this.embedRules(i,"doc-",[i.getEndRule("no_regex")]),this.normalizeRules()};r.inherits(u,s),t.JavaScriptHighlightRules=u}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/javascript",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/javascript_highlight_rules","ace/mode/matching_brace_outdent","ace/worker/worker_client","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./javascript_highlight_rules").JavaScriptHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../worker/worker_client").WorkerClient,a=e("./behaviour/cstyle").CstyleBehaviour,f=e("./folding/cstyle").FoldMode,l=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new a,this.foldingRules=new f};r.inherits(l,i),function(){this.lineCommentStart="//",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens,o=i.state;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"||e=="no_regex"){var u=t.match(/^.*(?:\bcase\b.*:|[\{\(\[])\s*$/);u&&(r+=n)}else if(e=="doc-start"){if(o=="start"||o=="no_regex")return"";var u=t.match(/^\s*(\/?)\*/);u&&(u[1]&&(r+=" "),r+="* ")}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.createWorker=function(e){var t=new u(["ace"],"ace/mode/javascript_worker","JavaScriptWorker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/javascript"}.call(l.prototype),t.Mode=l}),define("ace/mode/xml_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(e){var t="[_:a-zA-Z\u00c0-\uffff][-_:.a-zA-Z0-9\u00c0-\uffff]*";this.$rules={start:[{token:"string.cdata.xml",regex:"<\\!\\[CDATA\\[",next:"cdata"},{token:["punctuation.xml-decl.xml","keyword.xml-decl.xml"],regex:"(<\\?)(xml)(?=[\\s])",next:"xml_decl",caseInsensitive:!0},{token:["punctuation.instruction.xml","keyword.instruction.xml"],regex:"(<\\?)("+t+")",next:"processing_instruction"},{token:"comment.xml",regex:"<\\!--",next:"comment"},{token:["xml-pe.doctype.xml","xml-pe.doctype.xml"],regex:"(<\\!)(DOCTYPE)(?=[\\s])",next:"doctype",caseInsensitive:!0},{include:"tag"},{token:"text.end-tag-open.xml",regex:"",next:"start"}],processing_instruction:[{token:"punctuation.instruction.xml",regex:"\\?>",next:"start"},{defaultToken:"instruction.xml"}],doctype:[{include:"whitespace"},{include:"string"},{token:"xml-pe.doctype.xml",regex:">",next:"start"},{token:"xml-pe.xml",regex:"[-_a-zA-Z0-9:]+"},{token:"punctuation.int-subset",regex:"\\[",push:"int_subset"}],int_subset:[{token:"text.xml",regex:"\\s+"},{token:"punctuation.int-subset.xml",regex:"]",next:"pop"},{token:["punctuation.markup-decl.xml","keyword.markup-decl.xml"],regex:"(<\\!)("+t+")",push:[{token:"text",regex:"\\s+"},{token:"punctuation.markup-decl.xml",regex:">",next:"pop"},{include:"string"}]}],cdata:[{token:"string.cdata.xml",regex:"\\]\\]>",next:"start"},{token:"text.xml",regex:"\\s+"},{token:"text.xml",regex:"(?:[^\\]]|\\](?!\\]>))+"}],comment:[{token:"comment.xml",regex:"-->",next:"start"},{defaultToken:"comment.xml"}],reference:[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}],attr_reference:[{token:"constant.language.escape.reference.attribute-value.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}],tag:[{token:["meta.tag.punctuation.tag-open.xml","meta.tag.punctuation.end-tag-open.xml","meta.tag.tag-name.xml"],regex:"(?:(<)|(",next:"start"}]}],tag_whitespace:[{token:"text.tag-whitespace.xml",regex:"\\s+"}],whitespace:[{token:"text.whitespace.xml",regex:"\\s+"}],string:[{token:"string.xml",regex:"'",push:[{token:"string.xml",regex:"'",next:"pop"},{defaultToken:"string.xml"}]},{token:"string.xml",regex:'"',push:[{token:"string.xml",regex:'"',next:"pop"},{defaultToken:"string.xml"}]}],attributes:[{token:"entity.other.attribute-name.xml",regex:"(?:"+t+":)?"+t+""},{token:"keyword.operator.attribute-equals.xml",regex:"="},{include:"tag_whitespace"},{include:"attribute_value"}],attribute_value:[{token:"string.attribute-value.xml",regex:"'",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"attr_reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"attr_reference"},{defaultToken:"string.attribute-value.xml"}]}]},this.constructor===s&&this.normalizeRules()};(function(){this.embedTagRules=function(e,t,n){this.$rules.tag.unshift({token:["meta.tag.punctuation.tag-open.xml","meta.tag."+n+".tag-name.xml"],regex:"(<)("+n+"(?=\\s|>|$))",next:[{include:"attributes"},{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",next:t+"start"}]}),this.$rules[n+"-end"]=[{include:"attributes"},{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",next:"start",onMatch:function(e,t,n){return n.splice(0),this.token}}],this.embedRules(e,t,[{token:["meta.tag.punctuation.end-tag-open.xml","meta.tag."+n+".tag-name.xml"],regex:"(|$))",next:n+"-end"},{token:"string.cdata.xml",regex:"<\\!\\[CDATA\\["},{token:"string.cdata.xml",regex:"\\]\\]>"}])}}).call(i.prototype),r.inherits(s,i),t.XmlHighlightRules=s}),define("ace/mode/behaviour/xml",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";function u(e,t){return e.type.lastIndexOf(t+".xml")>-1}var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),a=function(){this.add("string_dquotes","insertion",function(e,t,n,r,i){if(i=='"'||i=="'"){var o=i,a=r.doc.getTextRange(n.getSelectionRange());if(a!==""&&a!=="'"&&a!='"'&&n.getWrapBehavioursEnabled())return{text:o+a+o,selection:!1};var f=n.getCursorPosition(),l=r.doc.getLine(f.row),c=l.substring(f.column,f.column+1),h=new s(r,f.row,f.column),p=h.getCurrentToken();if(c==o&&(u(p,"attribute-value")||u(p,"string")))return{text:"",selection:[1,1]};p||(p=h.stepBackward());if(!p)return;while(u(p,"tag-whitespace")||u(p,"whitespace"))p=h.stepBackward();var d=!c||c.match(/\s/);if(u(p,"attribute-equals")&&(d||c==">")||u(p,"decl-attribute-equals")&&(d||c=="?"))return{text:o+o,selection:[1,1]}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&(s=='"'||s=="'")){var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==s)return i.end.column++,i}}),this.add("autoclosing","insertion",function(e,t,n,r,i){if(i==">"){var o=n.getSelectionRange().start,a=new s(r,o.row,o.column),f=a.getCurrentToken()||a.stepBackward();if(!f||!(u(f,"tag-name")||u(f,"tag-whitespace")||u(f,"attribute-name")||u(f,"attribute-equals")||u(f,"attribute-value")))return;if(u(f,"reference.attribute-value"))return;if(u(f,"attribute-value")){var l=f.value.charAt(0);if(l=='"'||l=="'"){var c=f.value.charAt(f.value.length-1),h=a.getCurrentTokenColumn()+f.value.length;if(h>o.column||h==o.column&&l!=c)return}}while(!u(f,"tag-name")){f=a.stepBackward();if(f.value=="<"){f=a.stepForward();break}}var p=a.getCurrentTokenRow(),d=a.getCurrentTokenColumn();if(u(a.stepBackward(),"end-tag-open"))return;var v=f.value;p==o.row&&(v=v.substring(0,o.column-d));if(this.voidElements.hasOwnProperty(v.toLowerCase()))return;return{text:">",selection:[1,1]}}}),this.add("autoindent","insertion",function(e,t,n,r,i){if(i=="\n"){var o=n.getCursorPosition(),u=r.getLine(o.row),a=new s(r,o.row,o.column),f=a.getCurrentToken();if(f&&f.type.indexOf("tag-close")!==-1){if(f.value=="/>")return;while(f&&f.type.indexOf("tag-name")===-1)f=a.stepBackward();if(!f)return;var l=f.value,c=a.getCurrentTokenRow();f=a.stepBackward();if(!f||f.type.indexOf("end-tag")!==-1)return;if(this.voidElements&&!this.voidElements[l]){var h=r.getTokenAt(o.row,o.column+1),u=r.getLine(c),p=this.$getIndent(u),d=p+r.getTabString();return h&&h.value==="-1}var r=e("../../lib/oop"),i=e("../../lib/lang"),s=e("../../range").Range,o=e("./fold_mode").FoldMode,u=e("../../token_iterator").TokenIterator,a=t.FoldMode=function(e,t){o.call(this),this.voidElements=e||{},this.optionalEndTags=r.mixin({},this.voidElements),t&&r.mixin(this.optionalEndTags,t)};r.inherits(a,o);var f=function(){this.tagName="",this.closing=!1,this.selfClosing=!1,this.start={row:0,column:0},this.end={row:0,column:0}};(function(){this.getFoldWidget=function(e,t,n){var r=this._getFirstTagInLine(e,n);return r?r.closing||!r.tagName&&r.selfClosing?t=="markbeginend"?"end":"":!r.tagName||r.selfClosing||this.voidElements.hasOwnProperty(r.tagName.toLowerCase())?"":this._findEndTagInLine(e,n,r.tagName,r.end.column)?"":"start":""},this._getFirstTagInLine=function(e,t){var n=e.getTokens(t),r=new f;for(var i=0;i";break}}return r}if(l(s,"tag-close"))return r.selfClosing=s.value=="/>",r;r.start.column+=s.value.length}return null},this._findEndTagInLine=function(e,t,n,r){var i=e.getTokens(t),s=0;for(var o=0;o",n.end.row=e.getCurrentTokenRow(),n.end.column=e.getCurrentTokenColumn()+t.value.length,e.stepForward(),n;while(t=e.stepForward());return null},this._readTagBackward=function(e){var t=e.getCurrentToken();if(!t)return null;var n=new f;do{if(l(t,"tag-open"))return n.closing=l(t,"end-tag-open"),n.start.row=e.getCurrentTokenRow(),n.start.column=e.getCurrentTokenColumn(),e.stepBackward(),n;l(t,"tag-name")?n.tagName=t.value:l(t,"tag-close")&&(n.selfClosing=t.value=="/>",n.end.row=e.getCurrentTokenRow(),n.end.column=e.getCurrentTokenColumn()+t.value.length)}while(t=e.stepBackward());return null},this._pop=function(e,t){while(e.length){var n=e[e.length-1];if(!t||n.tagName==t.tagName)return e.pop();if(this.optionalEndTags.hasOwnProperty(n.tagName)){e.pop();continue}return null}},this.getFoldWidgetRange=function(e,t,n){var r=this._getFirstTagInLine(e,n);if(!r)return null;var i=r.closing||r.selfClosing,o=[],a;if(!i){var f=new u(e,n,r.start.column),l={row:n,column:r.start.column+r.tagName.length+2};r.start.row==r.end.row&&(l.column=r.end.column);while(a=this._readTagForward(f)){if(a.selfClosing){if(!o.length)return a.start.column+=a.tagName.length+2,a.end.column-=2,s.fromPoints(a.start,a.end);continue}if(a.closing){this._pop(o,a);if(o.length==0)return s.fromPoints(l,a.start)}else o.push(a)}}else{var f=new u(e,n,r.end.column),c={row:n,column:r.start.column};while(a=this._readTagBackward(f)){if(a.selfClosing){if(!o.length)return a.start.column+=a.tagName.length+2,a.end.column-=2,s.fromPoints(a.start,a.end);continue}if(!a.closing){this._pop(o,a);if(o.length==0)return a.start.column+=a.tagName.length+2,a.start.row==a.end.row&&a.start.column"},this.createWorker=function(e){var t=new f(["ace"],"ace/mode/xml_worker","Worker");return t.attachToDocument(e.getDocument()),t.on("error",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/xml"}.call(l.prototype),t.Mode=l}),define("ace/mode/css_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text_highlight_rules").TextHighlightRules,o=t.supportType="align-content|align-items|align-self|all|animation|animation-delay|animation-direction|animation-duration|animation-fill-mode|animation-iteration-count|animation-name|animation-play-state|animation-timing-function|backface-visibility|background|background-attachment|background-blend-mode|background-clip|background-color|background-image|background-origin|background-position|background-repeat|background-size|border|border-bottom|border-bottom-color|border-bottom-left-radius|border-bottom-right-radius|border-bottom-style|border-bottom-width|border-collapse|border-color|border-image|border-image-outset|border-image-repeat|border-image-slice|border-image-source|border-image-width|border-left|border-left-color|border-left-style|border-left-width|border-radius|border-right|border-right-color|border-right-style|border-right-width|border-spacing|border-style|border-top|border-top-color|border-top-left-radius|border-top-right-radius|border-top-style|border-top-width|border-width|bottom|box-shadow|box-sizing|caption-side|clear|clip|color|column-count|column-fill|column-gap|column-rule|column-rule-color|column-rule-style|column-rule-width|column-span|column-width|columns|content|counter-increment|counter-reset|cursor|direction|display|empty-cells|filter|flex|flex-basis|flex-direction|flex-flow|flex-grow|flex-shrink|flex-wrap|float|font|font-family|font-size|font-size-adjust|font-stretch|font-style|font-variant|font-weight|hanging-punctuation|height|justify-content|left|letter-spacing|line-height|list-style|list-style-image|list-style-position|list-style-type|margin|margin-bottom|margin-left|margin-right|margin-top|max-height|max-width|min-height|min-width|nav-down|nav-index|nav-left|nav-right|nav-up|opacity|order|outline|outline-color|outline-offset|outline-style|outline-width|overflow|overflow-x|overflow-y|padding|padding-bottom|padding-left|padding-right|padding-top|page-break-after|page-break-before|page-break-inside|perspective|perspective-origin|position|quotes|resize|right|tab-size|table-layout|text-align|text-align-last|text-decoration|text-decoration-color|text-decoration-line|text-decoration-style|text-indent|text-justify|text-overflow|text-shadow|text-transform|top|transform|transform-origin|transform-style|transition|transition-delay|transition-duration|transition-property|transition-timing-function|unicode-bidi|vertical-align|visibility|white-space|width|word-break|word-spacing|word-wrap|z-index",u=t.supportFunction="rgb|rgba|url|attr|counter|counters",a=t.supportConstant="absolute|after-edge|after|all-scroll|all|alphabetic|always|antialiased|armenian|auto|avoid-column|avoid-page|avoid|balance|baseline|before-edge|before|below|bidi-override|block-line-height|block|bold|bolder|border-box|both|bottom|box|break-all|break-word|capitalize|caps-height|caption|center|central|char|circle|cjk-ideographic|clone|close-quote|col-resize|collapse|column|consider-shifts|contain|content-box|cover|crosshair|cubic-bezier|dashed|decimal-leading-zero|decimal|default|disabled|disc|disregard-shifts|distribute-all-lines|distribute-letter|distribute-space|distribute|dotted|double|e-resize|ease-in|ease-in-out|ease-out|ease|ellipsis|end|exclude-ruby|fill|fixed|georgian|glyphs|grid-height|groove|hand|hanging|hebrew|help|hidden|hiragana-iroha|hiragana|horizontal|icon|ideograph-alpha|ideograph-numeric|ideograph-parenthesis|ideograph-space|ideographic|inactive|include-ruby|inherit|initial|inline-block|inline-box|inline-line-height|inline-table|inline|inset|inside|inter-ideograph|inter-word|invert|italic|justify|katakana-iroha|katakana|keep-all|last|left|lighter|line-edge|line-through|line|linear|list-item|local|loose|lower-alpha|lower-greek|lower-latin|lower-roman|lowercase|lr-tb|ltr|mathematical|max-height|max-size|medium|menu|message-box|middle|move|n-resize|ne-resize|newspaper|no-change|no-close-quote|no-drop|no-open-quote|no-repeat|none|normal|not-allowed|nowrap|nw-resize|oblique|open-quote|outset|outside|overline|padding-box|page|pointer|pre-line|pre-wrap|pre|preserve-3d|progress|relative|repeat-x|repeat-y|repeat|replaced|reset-size|ridge|right|round|row-resize|rtl|s-resize|scroll|se-resize|separate|slice|small-caps|small-caption|solid|space|square|start|static|status-bar|step-end|step-start|steps|stretch|strict|sub|super|sw-resize|table-caption|table-cell|table-column-group|table-column|table-footer-group|table-header-group|table-row-group|table-row|table|tb-rl|text-after-edge|text-before-edge|text-bottom|text-size|text-top|text|thick|thin|transparent|underline|upper-alpha|upper-latin|upper-roman|uppercase|use-script|vertical-ideographic|vertical-text|visible|w-resize|wait|whitespace|z-index|zero",f=t.supportConstantColor="aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow",l=t.supportConstantFonts="arial|century|comic|courier|cursive|fantasy|garamond|georgia|helvetica|impact|lucida|symbol|system|tahoma|times|trebuchet|utopia|verdana|webdings|sans-serif|serif|monospace",c=t.numRe="\\-?(?:(?:[0-9]+)|(?:[0-9]*\\.[0-9]+))",h=t.pseudoElements="(\\:+)\\b(after|before|first-letter|first-line|moz-selection|selection)\\b",p=t.pseudoClasses="(:)\\b(active|checked|disabled|empty|enabled|first-child|first-of-type|focus|hover|indeterminate|invalid|last-child|last-of-type|link|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|only-child|only-of-type|required|root|target|valid|visited)\\b",d=function(){var e=this.createKeywordMapper({"support.function":u,"support.constant":a,"support.type":o,"support.constant.color":f,"support.constant.fonts":l},"text",!0);this.$rules={start:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"@.*?{",push:"media"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],media:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"\\}",next:"pop"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],comment:[{token:"comment",regex:"\\*\\/",next:"pop"},{defaultToken:"comment"}],ruleset:[{token:"paren.rparen",regex:"\\}",next:"pop"},{token:"comment",regex:"\\/\\*",push:"comment"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:["constant.numeric","keyword"],regex:"("+c+")(ch|cm|deg|em|ex|fr|gd|grad|Hz|in|kHz|mm|ms|pc|pt|px|rad|rem|s|turn|vh|vm|vw|%)"},{token:"constant.numeric",regex:c},{token:"constant.numeric",regex:"#[a-f0-9]{6}"},{token:"constant.numeric",regex:"#[a-f0-9]{3}"},{token:["punctuation","entity.other.attribute-name.pseudo-element.css"],regex:h},{token:["punctuation","entity.other.attribute-name.pseudo-class.css"],regex:p},{token:["support.function","string","support.function"],regex:"(url\\()(.*)(\\))"},{token:e,regex:"\\-?[a-zA-Z_][a-zA-Z0-9_\\-]*"},{caseInsensitive:!0}]},this.normalizeRules()};r.inherits(d,s),t.CssHighlightRules=d}),define("ace/mode/css_completions",["require","exports","module"],function(e,t,n){"use strict";var r={background:{"#$0":1},"background-color":{"#$0":1,transparent:1,fixed:1},"background-image":{"url('/$0')":1},"background-repeat":{repeat:1,"repeat-x":1,"repeat-y":1,"no-repeat":1,inherit:1},"background-position":{bottom:2,center:2,left:2,right:2,top:2,inherit:2},"background-attachment":{scroll:1,fixed:1},"background-size":{cover:1,contain:1},"background-clip":{"border-box":1,"padding-box":1,"content-box":1},"background-origin":{"border-box":1,"padding-box":1,"content-box":1},border:{"solid $0":1,"dashed $0":1,"dotted $0":1,"#$0":1},"border-color":{"#$0":1},"border-style":{solid:2,dashed:2,dotted:2,"double":2,groove:2,hidden:2,inherit:2,inset:2,none:2,outset:2,ridged:2},"border-collapse":{collapse:1,separate:1},bottom:{px:1,em:1,"%":1},clear:{left:1,right:1,both:1,none:1},color:{"#$0":1,"rgb(#$00,0,0)":1},cursor:{"default":1,pointer:1,move:1,text:1,wait:1,help:1,progress:1,"n-resize":1,"ne-resize":1,"e-resize":1,"se-resize":1,"s-resize":1,"sw-resize":1,"w-resize":1,"nw-resize":1},display:{none:1,block:1,inline:1,"inline-block":1,"table-cell":1},"empty-cells":{show:1,hide:1},"float":{left:1,right:1,none:1},"font-family":{Arial:2,"Comic Sans MS":2,Consolas:2,"Courier New":2,Courier:2,Georgia:2,Monospace:2,"Sans-Serif":2,"Segoe UI":2,Tahoma:2,"Times New Roman":2,"Trebuchet MS":2,Verdana:1},"font-size":{px:1,em:1,"%":1},"font-weight":{bold:1,normal:1},"font-style":{italic:1,normal:1},"font-variant":{normal:1,"small-caps":1},height:{px:1,em:1,"%":1},left:{px:1,em:1,"%":1},"letter-spacing":{normal:1},"line-height":{normal:1},"list-style-type":{none:1,disc:1,circle:1,square:1,decimal:1,"decimal-leading-zero":1,"lower-roman":1,"upper-roman":1,"lower-greek":1,"lower-latin":1,"upper-latin":1,georgian:1,"lower-alpha":1,"upper-alpha":1},margin:{px:1,em:1,"%":1},"margin-right":{px:1,em:1,"%":1},"margin-left":{px:1,em:1,"%":1},"margin-top":{px:1,em:1,"%":1},"margin-bottom":{px:1,em:1,"%":1},"max-height":{px:1,em:1,"%":1},"max-width":{px:1,em:1,"%":1},"min-height":{px:1,em:1,"%":1},"min-width":{px:1,em:1,"%":1},overflow:{hidden:1,visible:1,auto:1,scroll:1},"overflow-x":{hidden:1,visible:1,auto:1,scroll:1},"overflow-y":{hidden:1,visible:1,auto:1,scroll:1},padding:{px:1,em:1,"%":1},"padding-top":{px:1,em:1,"%":1},"padding-right":{px:1,em:1,"%":1},"padding-bottom":{px:1,em:1,"%":1},"padding-left":{px:1,em:1,"%":1},"page-break-after":{auto:1,always:1,avoid:1,left:1,right:1},"page-break-before":{auto:1,always:1,avoid:1,left:1,right:1},position:{absolute:1,relative:1,fixed:1,"static":1},right:{px:1,em:1,"%":1},"table-layout":{fixed:1,auto:1},"text-decoration":{none:1,underline:1,"line-through":1,blink:1},"text-align":{left:1,right:1,center:1,justify:1},"text-transform":{capitalize:1,uppercase:1,lowercase:1,none:1},top:{px:1,em:1,"%":1},"vertical-align":{top:1,bottom:1},visibility:{hidden:1,visible:1},"white-space":{nowrap:1,normal:1,pre:1,"pre-line":1,"pre-wrap":1},width:{px:1,em:1,"%":1},"word-spacing":{normal:1},filter:{"alpha(opacity=$0100)":1},"text-shadow":{"$02px 2px 2px #777":1},"text-overflow":{"ellipsis-word":1,clip:1,ellipsis:1},"-moz-border-radius":1,"-moz-border-radius-topright":1,"-moz-border-radius-bottomright":1,"-moz-border-radius-topleft":1,"-moz-border-radius-bottomleft":1,"-webkit-border-radius":1,"-webkit-border-top-right-radius":1,"-webkit-border-top-left-radius":1,"-webkit-border-bottom-right-radius":1,"-webkit-border-bottom-left-radius":1,"-moz-box-shadow":1,"-webkit-box-shadow":1,transform:{"rotate($00deg)":1,"skew($00deg)":1},"-moz-transform":{"rotate($00deg)":1,"skew($00deg)":1},"-webkit-transform":{"rotate($00deg)":1,"skew($00deg)":1}},i=function(){};(function(){this.completionsDefined=!1,this.defineCompletions=function(){if(document){var e=document.createElement("c").style;for(var t in e){if(typeof e[t]!="string")continue;var n=t.replace(/[A-Z]/g,function(e){return"-"+e.toLowerCase()});r.hasOwnProperty(n)||(r[n]=1)}}this.completionsDefined=!0},this.getCompletions=function(e,t,n,r){this.completionsDefined||this.defineCompletions();var i=t.getTokenAt(n.row,n.column);if(!i)return[];if(e==="ruleset"){var s=t.getLine(n.row).substr(0,n.column);return/:[^;]+$/.test(s)?(/([\w\-]+):[^:]*$/.test(s),this.getPropertyValueCompletions(e,t,n,r)):this.getPropertyCompletions(e,t,n,r)}return[]},this.getPropertyCompletions=function(e,t,n,i){var s=Object.keys(r);return s.map(function(e){return{caption:e,snippet:e+": $0",meta:"property",score:Number.MAX_VALUE}})},this.getPropertyValueCompletions=function(e,t,n,i){var s=t.getLine(n.row).substr(0,n.column),o=(/([\w\-]+):[^:]*$/.exec(s)||{})[1];if(!o)return[];var u=[];return o in r&&typeof r[o]=="object"&&(u=Object.keys(r[o])),u.map(function(e){return{caption:e,snippet:e,meta:"property value",score:Number.MAX_VALUE}})}}).call(i.prototype),t.CssCompletions=i}),define("ace/mode/behaviour/css",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/mode/behaviour/cstyle","ace/token_iterator"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("./cstyle").CstyleBehaviour,o=e("../../token_iterator").TokenIterator,u=function(){this.inherit(s),this.add("colon","insertion",function(e,t,n,r,i){if(i===":"){var s=n.getCursorPosition(),u=new o(r,s.row,s.column),a=u.getCurrentToken();a&&a.value.match(/\s+/)&&(a=u.stepBackward());if(a&&a.type==="support.type"){var f=r.doc.getLine(s.row),l=f.substring(s.column,s.column+1);if(l===":")return{text:"",selection:[1,1]};if(!f.substring(s.column).match(/^\s*;/))return{text:":;",selection:[1,1]}}}}),this.add("colon","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s===":"){var u=n.getCursorPosition(),a=new o(r,u.row,u.column),f=a.getCurrentToken();f&&f.value.match(/\s+/)&&(f=a.stepBackward());if(f&&f.type==="support.type"){var l=r.doc.getLine(i.start.row),c=l.substring(i.end.column,i.end.column+1);if(c===";")return i.end.column++,i}}}),this.add("semicolon","insertion",function(e,t,n,r,i){if(i===";"){var s=n.getCursorPosition(),o=r.doc.getLine(s.row),u=o.substring(s.column,s.column+1);if(u===";")return{text:"",selection:[1,1]}}})};r.inherits(u,s),t.CssBehaviour=u}),define("ace/mode/css",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/css_highlight_rules","ace/mode/matching_brace_outdent","ace/worker/worker_client","ace/mode/css_completions","ace/mode/behaviour/css","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./css_highlight_rules").CssHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../worker/worker_client").WorkerClient,a=e("./css_completions").CssCompletions,f=e("./behaviour/css").CssBehaviour,l=e("./folding/cstyle").FoldMode,c=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new f,this.$completer=new a,this.foldingRules=new l};r.inherits(c,i),function(){this.foldingRules="cStyle",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e).tokens;if(i.length&&i[i.length-1].type=="comment")return r;var s=t.match(/^.*\{\s*$/);return s&&(r+=n),r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.getCompletions=function(e,t,n,r){return this.$completer.getCompletions(e,t,n,r)},this.createWorker=function(e){var t=new u(["ace"],"ace/mode/css_worker","Worker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/css"}.call(c.prototype),t.Mode=c}),define("ace/mode/html_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/css_highlight_rules","ace/mode/javascript_highlight_rules","ace/mode/xml_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./css_highlight_rules").CssHighlightRules,o=e("./javascript_highlight_rules").JavaScriptHighlightRules,u=e("./xml_highlight_rules").XmlHighlightRules,a=i.createMap({a:"anchor",button:"form",form:"form",img:"image",input:"form",label:"form",option:"form",script:"script",select:"form",textarea:"form",style:"style",table:"table",tbody:"table",td:"table",tfoot:"table",th:"table",tr:"table"}),f=function(){u.call(this),this.addRules({attributes:[{include:"tag_whitespace"},{token:"entity.other.attribute-name.xml",regex:"[-_a-zA-Z0-9:.]+"},{token:"keyword.operator.attribute-equals.xml",regex:"=",push:[{include:"tag_whitespace"},{token:"string.unquoted.attribute-value.html",regex:"[^<>='\"`\\s]+",next:"pop"},{token:"empty",regex:"",next:"pop"}]},{include:"attribute_value"}],tag:[{token:function(e,t){var n=a[t];return["meta.tag.punctuation."+(e=="<"?"":"end-")+"tag-open.xml","meta.tag"+(n?"."+n:"")+".tag-name.xml"]},regex:"(",next:"start"}]}),this.embedTagRules(s,"css-","style"),this.embedTagRules((new o({jsx:!1})).getRules(),"js-","script"),this.constructor===f&&this.normalizeRules()};r.inherits(f,u),t.HtmlHighlightRules=f}),define("ace/mode/folding/mixed",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("./fold_mode").FoldMode,s=t.FoldMode=function(e,t){this.defaultMode=e,this.subModes=t};r.inherits(s,i),function(){this.$getMode=function(e){typeof e!="string"&&(e=e[0]);for(var t in this.subModes)if(e.indexOf(t)===0)return this.subModes[t];return null},this.$tryMode=function(e,t,n,r){var i=this.$getMode(e);return i?i.getFoldWidget(t,n,r):""},this.getFoldWidget=function(e,t,n){return this.$tryMode(e.getState(n-1),e,t,n)||this.$tryMode(e.getState(n),e,t,n)||this.defaultMode.getFoldWidget(e,t,n)},this.getFoldWidgetRange=function(e,t,n){var r=this.$getMode(e.getState(n-1));if(!r||!r.getFoldWidget(e,t,n))r=this.$getMode(e.getState(n));if(!r||!r.getFoldWidget(e,t,n))r=this.defaultMode;return r.getFoldWidgetRange(e,t,n)}}.call(s.prototype)}),define("ace/mode/folding/html",["require","exports","module","ace/lib/oop","ace/mode/folding/mixed","ace/mode/folding/xml","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("./mixed").FoldMode,s=e("./xml").FoldMode,o=e("./cstyle").FoldMode,u=t.FoldMode=function(e,t){i.call(this,new s(e,t),{"js-":new o,"css-":new o})};r.inherits(u,i)}),define("ace/mode/html_completions",["require","exports","module","ace/token_iterator"],function(e,t,n){"use strict";function f(e,t){return e.type.lastIndexOf(t+".xml")>-1}function l(e,t){var n=new r(e,t.row,t.column),i=n.getCurrentToken();while(i&&!f(i,"tag-name"))i=n.stepBackward();if(i)return i.value}function c(e,t){var n=new r(e,t.row,t.column),i=n.getCurrentToken();while(i&&!f(i,"attribute-name"))i=n.stepBackward();if(i)return i.value}var r=e("../token_iterator").TokenIterator,i=["accesskey","class","contenteditable","contextmenu","dir","draggable","dropzone","hidden","id","inert","itemid","itemprop","itemref","itemscope","itemtype","lang","spellcheck","style","tabindex","title","translate"],s=["onabort","onblur","oncancel","oncanplay","oncanplaythrough","onchange","onclick","onclose","oncontextmenu","oncuechange","ondblclick","ondrag","ondragend","ondragenter","ondragleave","ondragover","ondragstart","ondrop","ondurationchange","onemptied","onended","onerror","onfocus","oninput","oninvalid","onkeydown","onkeypress","onkeyup","onload","onloadeddata","onloadedmetadata","onloadstart","onmousedown","onmousemove","onmouseout","onmouseover","onmouseup","onmousewheel","onpause","onplay","onplaying","onprogress","onratechange","onreset","onscroll","onseeked","onseeking","onselect","onshow","onstalled","onsubmit","onsuspend","ontimeupdate","onvolumechange","onwaiting"],o=i.concat(s),u={html:{manifest:1},head:{},title:{},base:{href:1,target:1},link:{href:1,hreflang:1,rel:{stylesheet:1,icon:1},media:{all:1,screen:1,print:1},type:{"text/css":1,"image/png":1,"image/jpeg":1,"image/gif":1},sizes:1},meta:{"http-equiv":{"content-type":1},name:{description:1,keywords:1},content:{"text/html; charset=UTF-8":1},charset:1},style:{type:1,media:{all:1,screen:1,print:1},scoped:1},script:{charset:1,type:{"text/javascript":1},src:1,defer:1,async:1},noscript:{href:1},body:{onafterprint:1,onbeforeprint:1,onbeforeunload:1,onhashchange:1,onmessage:1,onoffline:1,onpopstate:1,onredo:1,onresize:1,onstorage:1,onundo:1,onunload:1},section:{},nav:{},article:{pubdate:1},aside:{},h1:{},h2:{},h3:{},h4:{},h5:{},h6:{},header:{},footer:{},address:{},main:{},p:{},hr:{},pre:{},blockquote:{cite:1},ol:{start:1,reversed:1},ul:{},li:{value:1},dl:{},dt:{},dd:{},figure:{},figcaption:{},div:{},a:{href:1,target:{_blank:1,top:1},ping:1,rel:{nofollow:1,alternate:1,author:1,bookmark:1,help:1,license:1,next:1,noreferrer:1,prefetch:1,prev:1,search:1,tag:1},media:1,hreflang:1,type:1},em:{},strong:{},small:{},s:{},cite:{},q:{cite:1},dfn:{},abbr:{},data:{},time:{datetime:1},code:{},"var":{},samp:{},kbd:{},sub:{},sup:{},i:{},b:{},u:{},mark:{},ruby:{},rt:{},rp:{},bdi:{},bdo:{},span:{},br:{},wbr:{},ins:{cite:1,datetime:1},del:{cite:1,datetime:1},img:{alt:1,src:1,height:1,width:1,usemap:1,ismap:1},iframe:{name:1,src:1,height:1,width:1,sandbox:{"allow-same-origin":1,"allow-top-navigation":1,"allow-forms":1,"allow-scripts":1},seamless:{seamless:1}},embed:{src:1,height:1,width:1,type:1},object:{param:1,data:1,type:1,height:1,width:1,usemap:1,name:1,form:1,classid:1},param:{name:1,value:1},video:{src:1,autobuffer:1,autoplay:{autoplay:1},loop:{loop:1},controls:{controls:1},width:1,height:1,poster:1,muted:{muted:1},preload:{auto:1,metadata:1,none:1}},audio:{src:1,autobuffer:1,autoplay:{autoplay:1},loop:{loop:1},controls:{controls:1},muted:{muted:1},preload:{auto:1,metadata:1,none:1}},source:{src:1,type:1,media:1},track:{kind:1,src:1,srclang:1,label:1,"default":1},canvas:{width:1,height:1},map:{name:1},area:{shape:1,coords:1,href:1,hreflang:1,alt:1,target:1,media:1,rel:1,ping:1,type:1},svg:{},math:{},table:{summary:1},caption:{},colgroup:{span:1},col:{span:1},tbody:{},thead:{},tfoot:{},tr:{},td:{headers:1,rowspan:1,colspan:1},th:{headers:1,rowspan:1,colspan:1,scope:1},form:{"accept-charset":1,action:1,autocomplete:1,enctype:{"multipart/form-data":1,"application/x-www-form-urlencoded":1},method:{get:1,post:1},name:1,novalidate:1,target:{_blank:1,top:1}},fieldset:{disabled:1,form:1,name:1},legend:{},label:{form:1,"for":1},input:{type:{text:1,password:1,hidden:1,checkbox:1,submit:1,radio:1,file:1,button:1,reset:1,image:31,color:1,date:1,datetime:1,"datetime-local":1,email:1,month:1,number:1,range:1,search:1,tel:1,time:1,url:1,week:1},accept:1,alt:1,autocomplete:{on:1,off:1},autofocus:{autofocus:1},checked:{checked:1},disabled:{disabled:1},form:1,formaction:1,formenctype:{"application/x-www-form-urlencoded":1,"multipart/form-data":1,"text/plain":1},formmethod:{get:1,post:1},formnovalidate:{formnovalidate:1},formtarget:{_blank:1,_self:1,_parent:1,_top:1},height:1,list:1,max:1,maxlength:1,min:1,multiple:{multiple:1},name:1,pattern:1,placeholder:1,readonly:{readonly:1},required:{required:1},size:1,src:1,step:1,width:1,files:1,value:1},button:{autofocus:1,disabled:{disabled:1},form:1,formaction:1,formenctype:1,formmethod:1,formnovalidate:1,formtarget:1,name:1,value:1,type:{button:1,submit:1}},select:{autofocus:1,disabled:1,form:1,multiple:{multiple:1},name:1,size:1,readonly:{readonly:1}},datalist:{},optgroup:{disabled:1,label:1},option:{disabled:1,selected:1,label:1,value:1},textarea:{autofocus:{autofocus:1},disabled:{disabled:1},form:1,maxlength:1,name:1,placeholder:1,readonly:{readonly:1},required:{required:1},rows:1,cols:1,wrap:{on:1,off:1,hard:1,soft:1}},keygen:{autofocus:1,challenge:{challenge:1},disabled:{disabled:1},form:1,keytype:{rsa:1,dsa:1,ec:1},name:1},output:{"for":1,form:1,name:1},progress:{value:1,max:1},meter:{value:1,min:1,max:1,low:1,high:1,optimum:1},details:{open:1},summary:{},command:{type:1,label:1,icon:1,disabled:1,checked:1,radiogroup:1,command:1},menu:{type:1,label:1},dialog:{open:1}},a=Object.keys(u),h=function(){};(function(){this.getCompletions=function(e,t,n,r){var i=t.getTokenAt(n.row,n.column);if(!i)return[];if(f(i,"tag-name")||f(i,"tag-open")||f(i,"end-tag-open"))return this.getTagCompletions(e,t,n,r);if(f(i,"tag-whitespace")||f(i,"attribute-name"))return this.getAttributeCompletions(e,t,n,r);if(f(i,"attribute-value"))return this.getAttributeValueCompletions(e,t,n,r);var s=t.getLine(n.row).substr(0,n.column);return/&[a-z]*$/i.test(s)?this.getHTMLEntityCompletions(e,t,n,r):[]},this.getTagCompletions=function(e,t,n,r){return a.map(function(e){return{value:e,meta:"tag",score:Number.MAX_VALUE}})},this.getAttributeCompletions=function(e,t,n,r){var i=l(t,n);if(!i)return[];var s=o;return i in u&&(s=s.concat(Object.keys(u[i]))),s.map(function(e){return{caption:e,snippet:e+'="$0"',meta:"attribute",score:Number.MAX_VALUE}})},this.getAttributeValueCompletions=function(e,t,n,r){var i=l(t,n),s=c(t,n);if(!i)return[];var o=[];return i in u&&s in u[i]&&typeof u[i][s]=="object"&&(o=Object.keys(u[i][s])),o.map(function(e){return{caption:e,snippet:e,meta:"attribute value",score:Number.MAX_VALUE}})},this.getHTMLEntityCompletions=function(e,t,n,r){var i=["Aacute;","aacute;","Acirc;","acirc;","acute;","AElig;","aelig;","Agrave;","agrave;","alefsym;","Alpha;","alpha;","amp;","and;","ang;","Aring;","aring;","asymp;","Atilde;","atilde;","Auml;","auml;","bdquo;","Beta;","beta;","brvbar;","bull;","cap;","Ccedil;","ccedil;","cedil;","cent;","Chi;","chi;","circ;","clubs;","cong;","copy;","crarr;","cup;","curren;","Dagger;","dagger;","dArr;","darr;","deg;","Delta;","delta;","diams;","divide;","Eacute;","eacute;","Ecirc;","ecirc;","Egrave;","egrave;","empty;","emsp;","ensp;","Epsilon;","epsilon;","equiv;","Eta;","eta;","ETH;","eth;","Euml;","euml;","euro;","exist;","fnof;","forall;","frac12;","frac14;","frac34;","frasl;","Gamma;","gamma;","ge;","gt;","hArr;","harr;","hearts;","hellip;","Iacute;","iacute;","Icirc;","icirc;","iexcl;","Igrave;","igrave;","image;","infin;","int;","Iota;","iota;","iquest;","isin;","Iuml;","iuml;","Kappa;","kappa;","Lambda;","lambda;","lang;","laquo;","lArr;","larr;","lceil;","ldquo;","le;","lfloor;","lowast;","loz;","lrm;","lsaquo;","lsquo;","lt;","macr;","mdash;","micro;","middot;","minus;","Mu;","mu;","nabla;","nbsp;","ndash;","ne;","ni;","not;","notin;","nsub;","Ntilde;","ntilde;","Nu;","nu;","Oacute;","oacute;","Ocirc;","ocirc;","OElig;","oelig;","Ograve;","ograve;","oline;","Omega;","omega;","Omicron;","omicron;","oplus;","or;","ordf;","ordm;","Oslash;","oslash;","Otilde;","otilde;","otimes;","Ouml;","ouml;","para;","part;","permil;","perp;","Phi;","phi;","Pi;","pi;","piv;","plusmn;","pound;","Prime;","prime;","prod;","prop;","Psi;","psi;","quot;","radic;","rang;","raquo;","rArr;","rarr;","rceil;","rdquo;","real;","reg;","rfloor;","Rho;","rho;","rlm;","rsaquo;","rsquo;","sbquo;","Scaron;","scaron;","sdot;","sect;","shy;","Sigma;","sigma;","sigmaf;","sim;","spades;","sub;","sube;","sum;","sup;","sup1;","sup2;","sup3;","supe;","szlig;","Tau;","tau;","there4;","Theta;","theta;","thetasym;","thinsp;","THORN;","thorn;","tilde;","times;","trade;","Uacute;","uacute;","uArr;","uarr;","Ucirc;","ucirc;","Ugrave;","ugrave;","uml;","upsih;","Upsilon;","upsilon;","Uuml;","uuml;","weierp;","Xi;","xi;","Yacute;","yacute;","yen;","Yuml;","yuml;","Zeta;","zeta;","zwj;","zwnj;"];return i.map(function(e){return{caption:e,snippet:e,meta:"html entity",score:Number.MAX_VALUE}})}}).call(h.prototype),t.HtmlCompletions=h}),define("ace/mode/html",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text","ace/mode/javascript","ace/mode/css","ace/mode/html_highlight_rules","ace/mode/behaviour/xml","ace/mode/folding/html","ace/mode/html_completions","ace/worker/worker_client"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text").Mode,o=e("./javascript").Mode,u=e("./css").Mode,a=e("./html_highlight_rules").HtmlHighlightRules,f=e("./behaviour/xml").XmlBehaviour,l=e("./folding/html").FoldMode,c=e("./html_completions").HtmlCompletions,h=e("../worker/worker_client").WorkerClient,p=["area","base","br","col","embed","hr","img","input","keygen","link","meta","menuitem","param","source","track","wbr"],d=["li","dt","dd","p","rt","rp","optgroup","option","colgroup","td","th"],v=function(e){this.fragmentContext=e&&e.fragmentContext,this.HighlightRules=a,this.$behaviour=new f,this.$completer=new c,this.createModeDelegates({"js-":o,"css-":u}),this.foldingRules=new l(this.voidElements,i.arrayToMap(d))};r.inherits(v,s),function(){this.blockComment={start:""},this.voidElements=i.arrayToMap(p),this.getNextLineIndent=function(e,t,n){return this.$getIndent(t)},this.checkOutdent=function(e,t,n){return!1},this.getCompletions=function(e,t,n,r){return this.$completer.getCompletions(e,t,n,r)},this.createWorker=function(e){if(this.constructor!=v)return;var t=new h(["ace"],"ace/mode/html_worker","Worker");return t.attachToDocument(e.getDocument()),this.fragmentContext&&t.call("setOptions",[{context:this.fragmentContext}]),t.on("error",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/html"}.call(v.prototype),t.Mode=v}),define("ace/mode/markdown_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text_highlight_rules","ace/mode/javascript_highlight_rules","ace/mode/xml_highlight_rules","ace/mode/html_highlight_rules","ace/mode/css_highlight_rules"],function(e,t,n){"use strict";function c(e,t){return{token:"support.function",regex:"^\\s*```"+e+"\\s*$",push:t+"start"}}var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text_highlight_rules").TextHighlightRules,o=e("./javascript_highlight_rules").JavaScriptHighlightRules,u=e("./xml_highlight_rules").XmlHighlightRules,a=e("./html_highlight_rules").HtmlHighlightRules,f=e("./css_highlight_rules").CssHighlightRules,l=function(e){return"(?:[^"+i.escapeRegExp(e)+"\\\\]|\\\\.)*"},h=function(){a.call(this),this.$rules.start.unshift({token:"empty_line",regex:"^$",next:"allowBlock"},{token:"markup.heading.1",regex:"^=+(?=\\s*$)"},{token:"markup.heading.2",regex:"^\\-+(?=\\s*$)"},{token:function(e){return"markup.heading."+e.length},regex:/^#{1,6}(?=\s*[^ #]|\s+#.)/,next:"header"},c("(?:javascript|js)","jscode-"),c("xml","xmlcode-"),c("html","htmlcode-"),c("css","csscode-"),{token:"support.function",regex:"^\\s*```\\s*\\S*(?:{.*?\\})?\\s*$",next:"githubblock"},{token:"string.blockquote",regex:"^\\s*>\\s*(?:[*+-]|\\d+\\.)?\\s+",next:"blockquote"},{token:"constant",regex:"^ {0,2}(?:(?: ?\\* ?){3,}|(?: ?\\- ?){3,}|(?: ?\\_ ?){3,})\\s*$",next:"allowBlock"},{token:"markup.list",regex:"^\\s{0,3}(?:[*+-]|\\d+\\.)\\s+",next:"listblock-start"},{include:"basic"}),this.addRules({basic:[{token:"constant.language.escape",regex:/\\[\\`*_{}\[\]()#+\-.!]/},{token:"support.function",regex:"(`+)(.*?[^`])(\\1)"},{token:["text","constant","text","url","string","text"],regex:'^([ ]{0,3}\\[)([^\\]]+)(\\]:\\s*)([^ ]+)(\\s*(?:["][^"]+["])?(\\s*))$'},{token:["text","string","text","constant","text"],regex:"(\\[)("+l("]")+")(\\]\\s*\\[)("+l("]")+")(\\])"},{token:["text","string","text","markup.underline","string","text"],regex:"(\\[)("+l("]")+")(\\]\\()"+'((?:[^\\)\\s\\\\]|\\\\.|\\s(?=[^"]))*)'+'(\\s*"'+l('"')+'"\\s*)?'+"(\\))"},{token:"string.strong",regex:"([*]{2}|[_]{2}(?=\\S))(.*?\\S[*_]*)(\\1)"},{token:"string.emphasis",regex:"([*]|[_](?=\\S))(.*?\\S[*_]*)(\\1)"},{token:["text","url","text"],regex:"(<)((?:https?|ftp|dict):[^'\">\\s]+|(?:mailto:)?[-.\\w]+\\@[-a-z0-9]+(?:\\.[-a-z0-9]+)*\\.[a-z]+)(>)"}],allowBlock:[{token:"support.function",regex:"^ {4}.+",next:"allowBlock"},{token:"empty_line",regex:"^$",next:"allowBlock"},{token:"empty",regex:"",next:"start"}],header:[{regex:"$",next:"start"},{include:"basic"},{defaultToken:"heading"}],"listblock-start":[{token:"support.variable",regex:/(?:\[[ x]\])?/,next:"listblock"}],listblock:[{token:"empty_line",regex:"^$",next:"start"},{token:"markup.list",regex:"^\\s{0,3}(?:[*+-]|\\d+\\.)\\s+",next:"listblock-start"},{include:"basic",noEscape:!0},{token:"support.function",regex:"^\\s*```\\s*[a-zA-Z]*(?:{.*?\\})?\\s*$",next:"githubblock"},{defaultToken:"list"}],blockquote:[{token:"empty_line",regex:"^\\s*$",next:"start"},{token:"string.blockquote",regex:"^\\s*>\\s*(?:[*+-]|\\d+\\.)?\\s+",next:"blockquote"},{include:"basic",noEscape:!0},{defaultToken:"string.blockquote"}],githubblock:[{token:"support.function",regex:"^\\s*```",next:"start"},{token:"support.function",regex:".+"}]}),this.embedRules(o,"jscode-",[{token:"support.function",regex:"^\\s*```",next:"pop"}]),this.embedRules(a,"htmlcode-",[{token:"support.function",regex:"^\\s*```",next:"pop"}]),this.embedRules(f,"csscode-",[{token:"support.function",regex:"^\\s*```",next:"pop"}]),this.embedRules(u,"xmlcode-",[{token:"support.function",regex:"^\\s*```",next:"pop"}]),this.normalizeRules()};r.inherits(h,s),t.MarkdownHighlightRules=h}),define("ace/mode/folding/markdown",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode","ace/range"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("./fold_mode").FoldMode,s=e("../../range").Range,o=t.FoldMode=function(){};r.inherits(o,i),function(){this.foldingStartMarker=/^(?:[=-]+\s*$|#{1,6} |`{3})/,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);return this.foldingStartMarker.test(r)?r[0]=="`"?e.bgTokenizer.getState(n)=="start"?"end":"start":"start":""},this.getFoldWidgetRange=function(e,t,n){function l(t){return f=e.getTokens(t)[0],f&&f.type.lastIndexOf(c,0)===0}function h(){var e=f.value[0];return e=="="?6:e=="-"?5:7-f.value.search(/[^#]/)}var r=e.getLine(n),i=r.length,o=e.getLength(),u=n,a=n;if(!r.match(this.foldingStartMarker))return;if(r[0]=="`"){if(e.bgTokenizer.getState(n)!=="start"){while(++n0){r=e.getLine(n);if(r[0]=="`"&r.substring(0,3)=="```")break}return new s(n,r.length,u,0)}var f,c="markup.heading";if(l(n)){var p=h();while(++n=p)break}a=n-(!f||["=","-"].indexOf(f.value[0])==-1?1:2);if(a>u)while(a>u&&/^\s*$/.test(e.getLine(a)))a--;if(a>u){var v=e.getLine(a).length;return new s(u,i,a,v)}}}}.call(o.prototype)}),define("ace/mode/markdown",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/javascript","ace/mode/xml","ace/mode/html","ace/mode/markdown_highlight_rules","ace/mode/folding/markdown"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./javascript").Mode,o=e("./xml").Mode,u=e("./html").Mode,a=e("./markdown_highlight_rules").MarkdownHighlightRules,f=e("./folding/markdown").FoldMode,l=function(){this.HighlightRules=a,this.createModeDelegates({"js-":s,"xml-":o,"html-":u}),this.foldingRules=new f,this.$behaviour=this.$defaultBehaviour};r.inherits(l,i),function(){this.type="text",this.blockComment={start:""},this.getNextLineIndent=function(e,t,n){if(e=="listblock"){var r=/^(\s*)(?:([-+*])|(\d+)\.)(\s+)/.exec(t);if(!r)return"";var i=r[2];return i||(i=parseInt(r[3],10)+1+"."),r[1]+i+r[4]}return this.$getIndent(t)},this.$id="ace/mode/markdown"}.call(l.prototype),t.Mode=l}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-mysql.js b/public/themes/pterodactyl/vendor/ace/mode-mysql.js new file mode 100644 index 0000000..b8e6a8d --- /dev/null +++ b/public/themes/pterodactyl/vendor/ace/mode-mysql.js @@ -0,0 +1 @@ +define("ace/mode/doc_comment_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},s.getTagRule(),{defaultToken:"comment.doc",caseInsensitive:!0}]}};r.inherits(s,i),s.getTagRule=function(e){return{token:"comment.doc.tag.storage.type",regex:"\\b(?:TODO|FIXME|XXX|HACK)\\b"}},s.getStartRule=function(e){return{token:"comment.doc",regex:"\\/\\*(?=\\*)",next:e}},s.getEndRule=function(e){return{token:"comment.doc",regex:"\\*\\/",next:e}},t.DocCommentHighlightRules=s}),define("ace/mode/mysql_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./doc_comment_highlight_rules").DocCommentHighlightRules,o=e("./text_highlight_rules").TextHighlightRules,u=function(){function i(e){var t=e.start,n=e.escape;return{token:"string.start",regex:t,next:[{token:"constant.language.escape",regex:n},{token:"string.end",next:"start",regex:t},{defaultToken:"string"}]}}var e="alter|and|as|asc|between|count|create|delete|desc|distinct|drop|from|having|in|insert|into|is|join|like|not|on|or|order|select|set|table|union|update|values|where|accessible|action|add|after|algorithm|all|analyze|asensitive|at|authors|auto_increment|autocommit|avg|avg_row_length|before|binary|binlog|both|btree|cache|call|cascade|cascaded|case|catalog_name|chain|change|changed|character|check|checkpoint|checksum|class_origin|client_statistics|close|coalesce|code|collate|collation|collations|column|columns|comment|commit|committed|completion|concurrent|condition|connection|consistent|constraint|contains|continue|contributors|convert|cross|current_date|current_time|current_timestamp|current_user|cursor|data|database|databases|day_hour|day_microsecond|day_minute|day_second|deallocate|dec|declare|default|delay_key_write|delayed|delimiter|des_key_file|describe|deterministic|dev_pop|dev_samp|deviance|directory|disable|discard|distinctrow|div|dual|dumpfile|each|elseif|enable|enclosed|end|ends|engine|engines|enum|errors|escape|escaped|even|event|events|every|execute|exists|exit|explain|extended|fast|fetch|field|fields|first|flush|for|force|foreign|found_rows|full|fulltext|function|general|global|grant|grants|group|groupby_concat|handler|hash|help|high_priority|hosts|hour_microsecond|hour_minute|hour_second|if|ignore|ignore_server_ids|import|index|index_statistics|infile|inner|innodb|inout|insensitive|insert_method|install|interval|invoker|isolation|iterate|key|keys|kill|language|last|leading|leave|left|level|limit|linear|lines|list|load|local|localtime|localtimestamp|lock|logs|low_priority|master|master_heartbeat_period|master_ssl_verify_server_cert|masters|match|max|max_rows|maxvalue|message_text|middleint|migrate|min|min_rows|minute_microsecond|minute_second|mod|mode|modifies|modify|mutex|mysql_errno|natural|next|no|no_write_to_binlog|offline|offset|one|online|open|optimize|option|optionally|out|outer|outfile|pack_keys|parser|partition|partitions|password|phase|plugin|plugins|prepare|preserve|prev|primary|privileges|procedure|processlist|profile|profiles|purge|query|quick|range|read|read_write|reads|real|rebuild|recover|references|regexp|relaylog|release|remove|rename|reorganize|repair|repeatable|replace|require|resignal|restrict|resume|return|returns|revoke|right|rlike|rollback|rollup|row|row_format|rtree|savepoint|schedule|schema|schema_name|schemas|second_microsecond|security|sensitive|separator|serializable|server|session|share|show|signal|slave|slow|smallint|snapshot|soname|spatial|specific|sql|sql_big_result|sql_buffer_result|sql_cache|sql_calc_found_rows|sql_no_cache|sql_small_result|sqlexception|sqlstate|sqlwarning|ssl|start|starting|starts|status|std|stddev|stddev_pop|stddev_samp|storage|straight_join|subclass_origin|sum|suspend|table_name|table_statistics|tables|tablespace|temporary|terminated|to|trailing|transaction|trigger|triggers|truncate|uncommitted|undo|uninstall|unique|unlock|upgrade|usage|use|use_frm|user|user_resources|user_statistics|using|utc_date|utc_time|utc_timestamp|value|variables|varying|view|views|warnings|when|while|with|work|write|xa|xor|year_month|zerofill|begin|do|then|else|loop|repeat",t="by|bool|boolean|bit|blob|decimal|double|enum|float|long|longblob|longtext|medium|mediumblob|mediumint|mediumtext|time|timestamp|tinyblob|tinyint|tinytext|text|bigint|int|int1|int2|int3|int4|int8|integer|float|float4|float8|double|char|varbinary|varchar|varcharacter|precision|date|datetime|year|unsigned|signed|numeric|ucase|lcase|mid|len|round|rank|now|format|coalesce|ifnull|isnull|nvl",n="charset|clear|connect|edit|ego|exit|go|help|nopager|notee|nowarning|pager|print|prompt|quit|rehash|source|status|system|tee",r=this.createKeywordMapper({"support.function":t,keyword:e,constant:"false|true|null|unknown|date|time|timestamp|ODBCdotTable|zerolessFloat","variable.language":n},"identifier",!0);this.$rules={start:[{token:"comment",regex:"(?:-- |#).*$"},i({start:'"',escape:/\\[0'"bnrtZ\\%_]?/}),i({start:"'",escape:/\\[0'"bnrtZ\\%_]?/}),s.getStartRule("doc-start"),{token:"comment",regex:/\/\*/,next:"comment"},{token:"constant.numeric",regex:/0[xX][0-9a-fA-F]+|[xX]'[0-9a-fA-F]+'|0[bB][01]+|[bB]'[01]+'/},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:r,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"constant.class",regex:"@@?[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"constant.buildin",regex:"`[^`]*`"},{token:"keyword.operator",regex:"\\+|\\-|\\/|\\/\\/|%|<@>|@>|<@|&|\\^|~|<|>|<=|=>|==|!=|<>|="},{token:"paren.lparen",regex:"[\\(]"},{token:"paren.rparen",regex:"[\\)]"},{token:"text",regex:"\\s+"}],comment:[{token:"comment",regex:"\\*\\/",next:"start"},{defaultToken:"comment"}]},this.embedRules(s,"doc-",[s.getEndRule("start")]),this.normalizeRules()};r.inherits(u,o),t.MysqlHighlightRules=u}),define("ace/mode/mysql",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/mysql_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("../mode/text").Mode,s=e("./mysql_highlight_rules").MysqlHighlightRules,o=function(){this.HighlightRules=s,this.$behaviour=this.$defaultBehaviour};r.inherits(o,i),function(){this.lineCommentStart=["--","#"],this.blockComment={start:"/*",end:"*/"},this.$id="ace/mode/mysql"}.call(o.prototype),t.Mode=o}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-objectivec.js b/public/themes/pterodactyl/vendor/ace/mode-objectivec.js new file mode 100644 index 0000000..98645e5 --- /dev/null +++ b/public/themes/pterodactyl/vendor/ace/mode-objectivec.js @@ -0,0 +1 @@ +define("ace/mode/doc_comment_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},s.getTagRule(),{defaultToken:"comment.doc",caseInsensitive:!0}]}};r.inherits(s,i),s.getTagRule=function(e){return{token:"comment.doc.tag.storage.type",regex:"\\b(?:TODO|FIXME|XXX|HACK)\\b"}},s.getStartRule=function(e){return{token:"comment.doc",regex:"\\/\\*(?=\\*)",next:e}},s.getEndRule=function(e){return{token:"comment.doc",regex:"\\*\\/",next:e}},t.DocCommentHighlightRules=s}),define("ace/mode/c_cpp_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./text_highlight_rules").TextHighlightRules,o=t.cFunctions="\\b(?:hypot(?:f|l)?|s(?:scanf|ystem|nprintf|ca(?:nf|lb(?:n(?:f|l)?|ln(?:f|l)?))|i(?:n(?:h(?:f|l)?|f|l)?|gn(?:al|bit))|tr(?:s(?:tr|pn)|nc(?:py|at|mp)|c(?:spn|hr|oll|py|at|mp)|to(?:imax|d|u(?:l(?:l)?|max)|k|f|l(?:d|l)?)|error|pbrk|ftime|len|rchr|xfrm)|printf|et(?:jmp|vbuf|locale|buf)|qrt(?:f|l)?|w(?:scanf|printf)|rand)|n(?:e(?:arbyint(?:f|l)?|xt(?:toward(?:f|l)?|after(?:f|l)?))|an(?:f|l)?)|c(?:s(?:in(?:h(?:f|l)?|f|l)?|qrt(?:f|l)?)|cos(?:h(?:f)?|f|l)?|imag(?:f|l)?|t(?:ime|an(?:h(?:f|l)?|f|l)?)|o(?:s(?:h(?:f|l)?|f|l)?|nj(?:f|l)?|pysign(?:f|l)?)|p(?:ow(?:f|l)?|roj(?:f|l)?)|e(?:il(?:f|l)?|xp(?:f|l)?)|l(?:o(?:ck|g(?:f|l)?)|earerr)|a(?:sin(?:h(?:f|l)?|f|l)?|cos(?:h(?:f|l)?|f|l)?|tan(?:h(?:f|l)?|f|l)?|lloc|rg(?:f|l)?|bs(?:f|l)?)|real(?:f|l)?|brt(?:f|l)?)|t(?:ime|o(?:upper|lower)|an(?:h(?:f|l)?|f|l)?|runc(?:f|l)?|gamma(?:f|l)?|mp(?:nam|file))|i(?:s(?:space|n(?:ormal|an)|cntrl|inf|digit|u(?:nordered|pper)|p(?:unct|rint)|finite|w(?:space|c(?:ntrl|type)|digit|upper|p(?:unct|rint)|lower|al(?:num|pha)|graph|xdigit|blank)|l(?:ower|ess(?:equal|greater)?)|al(?:num|pha)|gr(?:eater(?:equal)?|aph)|xdigit|blank)|logb(?:f|l)?|max(?:div|abs))|di(?:v|fftime)|_Exit|unget(?:c|wc)|p(?:ow(?:f|l)?|ut(?:s|c(?:har)?|wc(?:har)?)|error|rintf)|e(?:rf(?:c(?:f|l)?|f|l)?|x(?:it|p(?:2(?:f|l)?|f|l|m1(?:f|l)?)?))|v(?:s(?:scanf|nprintf|canf|printf|w(?:scanf|printf))|printf|f(?:scanf|printf|w(?:scanf|printf))|w(?:scanf|printf)|a_(?:start|copy|end|arg))|qsort|f(?:s(?:canf|e(?:tpos|ek))|close|tell|open|dim(?:f|l)?|p(?:classify|ut(?:s|c|w(?:s|c))|rintf)|e(?:holdexcept|set(?:e(?:nv|xceptflag)|round)|clearexcept|testexcept|of|updateenv|r(?:aiseexcept|ror)|get(?:e(?:nv|xceptflag)|round))|flush|w(?:scanf|ide|printf|rite)|loor(?:f|l)?|abs(?:f|l)?|get(?:s|c|pos|w(?:s|c))|re(?:open|e|ad|xp(?:f|l)?)|m(?:in(?:f|l)?|od(?:f|l)?|a(?:f|l|x(?:f|l)?)?))|l(?:d(?:iv|exp(?:f|l)?)|o(?:ngjmp|cal(?:time|econv)|g(?:1(?:p(?:f|l)?|0(?:f|l)?)|2(?:f|l)?|f|l|b(?:f|l)?)?)|abs|l(?:div|abs|r(?:int(?:f|l)?|ound(?:f|l)?))|r(?:int(?:f|l)?|ound(?:f|l)?)|gamma(?:f|l)?)|w(?:scanf|c(?:s(?:s(?:tr|pn)|nc(?:py|at|mp)|c(?:spn|hr|oll|py|at|mp)|to(?:imax|d|u(?:l(?:l)?|max)|k|f|l(?:d|l)?|mbs)|pbrk|ftime|len|r(?:chr|tombs)|xfrm)|to(?:b|mb)|rtomb)|printf|mem(?:set|c(?:hr|py|mp)|move))|a(?:s(?:sert|ctime|in(?:h(?:f|l)?|f|l)?)|cos(?:h(?:f|l)?|f|l)?|t(?:o(?:i|f|l(?:l)?)|exit|an(?:h(?:f|l)?|2(?:f|l)?|f|l)?)|b(?:s|ort))|g(?:et(?:s|c(?:har)?|env|wc(?:har)?)|mtime)|r(?:int(?:f|l)?|ound(?:f|l)?|e(?:name|alloc|wind|m(?:ove|quo(?:f|l)?|ainder(?:f|l)?))|a(?:nd|ise))|b(?:search|towc)|m(?:odf(?:f|l)?|em(?:set|c(?:hr|py|mp)|move)|ktime|alloc|b(?:s(?:init|towcs|rtowcs)|towc|len|r(?:towc|len))))\\b",u=function(){var e="break|case|continue|default|do|else|for|goto|if|_Pragma|return|switch|while|catch|operator|try|throw|using",t="asm|__asm__|auto|bool|_Bool|char|_Complex|double|enum|float|_Imaginary|int|long|short|signed|struct|typedef|union|unsigned|void|class|wchar_t|template|char16_t|char32_t",n="const|extern|register|restrict|static|volatile|inline|private|protected|public|friend|explicit|virtual|export|mutable|typename|constexpr|new|delete|alignas|alignof|decltype|noexcept|thread_local",r="and|and_eq|bitand|bitor|compl|not|not_eq|or|or_eq|typeid|xor|xor_eqconst_cast|dynamic_cast|reinterpret_cast|static_cast|sizeof|namespace",s="NULL|true|false|TRUE|FALSE|nullptr",u=this.$keywords=this.createKeywordMapper({"keyword.control":e,"storage.type":t,"storage.modifier":n,"keyword.operator":r,"variable.language":"this","constant.language":s},"identifier"),a="[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*\\b",f=/\\(?:['"?\\abfnrtv]|[0-7]{1,3}|x[a-fA-F\d]{2}|u[a-fA-F\d]{4}U[a-fA-F\d]{8}|.)/.source;this.$rules={start:[{token:"comment",regex:"//$",next:"start"},{token:"comment",regex:"//",next:"singleLineComment"},i.getStartRule("doc-start"),{token:"comment",regex:"\\/\\*",next:"comment"},{token:"string",regex:"'(?:"+f+"|.)?'"},{token:"string.start",regex:'"',stateName:"qqstring",next:[{token:"string",regex:/\\\s*$/,next:"qqstring"},{token:"constant.language.escape",regex:f},{token:"constant.language.escape",regex:/%[^'"\\]/},{token:"string.end",regex:'"|$',next:"start"},{defaultToken:"string"}]},{token:"string.start",regex:'R"\\(',stateName:"rawString",next:[{token:"string.end",regex:'\\)"',next:"start"},{defaultToken:"string"}]},{token:"constant.numeric",regex:"0[xX][0-9a-fA-F]+(L|l|UL|ul|u|U|F|f|ll|LL|ull|ULL)?\\b"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?(L|l|UL|ul|u|U|F|f|ll|LL|ull|ULL)?\\b"},{token:"keyword",regex:"#\\s*(?:include|import|pragma|line|define|undef)\\b",next:"directive"},{token:"keyword",regex:"#\\s*(?:endif|if|ifdef|else|elif|ifndef)\\b"},{token:"support.function.C99.c",regex:o},{token:u,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*"},{token:"keyword.operator",regex:/--|\+\+|<<=|>>=|>>>=|<>|&&|\|\||\?:|[*%\/+\-&\^|~!<>=]=?/},{token:"punctuation.operator",regex:"\\?|\\:|\\,|\\;|\\."},{token:"paren.lparen",regex:"[[({]"},{token:"paren.rparen",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],comment:[{token:"comment",regex:".*?\\*\\/",next:"start"},{token:"comment",regex:".+"}],singleLineComment:[{token:"comment",regex:/\\$/,next:"singleLineComment"},{token:"comment",regex:/$/,next:"start"},{defaultToken:"comment"}],directive:[{token:"constant.other.multiline",regex:/\\/},{token:"constant.other.multiline",regex:/.*\\/},{token:"constant.other",regex:"\\s*<.+?>",next:"start"},{token:"constant.other",regex:'\\s*["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]',next:"start"},{token:"constant.other",regex:"\\s*['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']",next:"start"},{token:"constant.other",regex:/[^\\\/]+/,next:"start"}]},this.embedRules(i,"doc-",[i.getEndRule("start")]),this.normalizeRules()};r.inherits(u,s),t.c_cppHighlightRules=u}),define("ace/mode/objectivec_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/doc_comment_highlight_rules","ace/mode/c_cpp_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./c_cpp_highlight_rules"),o=s.c_cppHighlightRules,u=function(){var e="\\\\(?:[abefnrtv'\"?\\\\]|[0-3]\\d{1,2}|[4-7]\\d?|222|x[a-zA-Z0-9]+)",t=[{regex:"\\b_cmd\\b",token:"variable.other.selector.objc"},{regex:"\\b(?:self|super)\\b",token:"variable.language.objc"}],n=new o,r=n.getRules();this.$rules={start:[{token:"comment",regex:"\\/\\/.*$"},i.getStartRule("doc-start"),{token:"comment",regex:"\\/\\*",next:"comment"},{token:["storage.type.objc","punctuation.definition.storage.type.objc","entity.name.type.objc","text","entity.other.inherited-class.objc"],regex:"(@)(interface|protocol)(?!.+;)(\\s+[A-Za-z_][A-Za-z0-9_]*)(\\s*:\\s*)([A-Za-z]+)"},{token:["storage.type.objc"],regex:"(@end)"},{token:["storage.type.objc","entity.name.type.objc","entity.other.inherited-class.objc"],regex:"(@implementation)(\\s+[A-Za-z_][A-Za-z0-9_]*)(\\s*?::\\s*(?:[A-Za-z][A-Za-z0-9]*))?"},{token:"string.begin.objc",regex:'@"',next:"constant_NSString"},{token:"storage.type.objc",regex:"\\bid\\s*<",next:"protocol_list"},{token:"keyword.control.macro.objc",regex:"\\bNS_DURING|NS_HANDLER|NS_ENDHANDLER\\b"},{token:["punctuation.definition.keyword.objc","keyword.control.exception.objc"],regex:"(@)(try|catch|finally|throw)\\b"},{token:["punctuation.definition.keyword.objc","keyword.other.objc"],regex:"(@)(defs|encode)\\b"},{token:["storage.type.id.objc","text"],regex:"(\\bid\\b)(\\s|\\n)?"},{token:"storage.type.objc",regex:"\\bIBOutlet|IBAction|BOOL|SEL|id|unichar|IMP|Class\\b"},{token:["punctuation.definition.storage.type.objc","storage.type.objc"],regex:"(@)(class|protocol)\\b"},{token:["punctuation.definition.storage.type.objc","punctuation"],regex:"(@selector)(\\s*\\()",next:"selectors"},{token:["punctuation.definition.storage.modifier.objc","storage.modifier.objc"],regex:"(@)(synchronized|public|private|protected|package)\\b"},{token:"constant.language.objc",regex:"\\bYES|NO|Nil|nil\\b"},{token:"support.variable.foundation",regex:"\\bNSApp\\b"},{token:["support.function.cocoa.leopard"],regex:"(?:\\b)(NS(?:Rect(?:ToCGRect|FromCGRect)|MakeCollectable|S(?:tringFromProtocol|ize(?:ToCGSize|FromCGSize))|Draw(?:NinePartImage|ThreePartImage)|P(?:oint(?:ToCGPoint|FromCGPoint)|rotocolFromString)|EventMaskFromType|Value))(?:\\b)"},{token:["support.function.cocoa"],regex:"(?:\\b)(NS(?:R(?:ound(?:DownToMultipleOfPageSize|UpToMultipleOfPageSize)|un(?:CriticalAlertPanel(?:RelativeToWindow)?|InformationalAlertPanel(?:RelativeToWindow)?|AlertPanel(?:RelativeToWindow)?)|e(?:set(?:MapTable|HashTable)|c(?:ycleZone|t(?:Clip(?:List)?|F(?:ill(?:UsingOperation|List(?:UsingOperation|With(?:Grays|Colors(?:UsingOperation)?))?)?|romString))|ordAllocationEvent)|turnAddress|leaseAlertPanel|a(?:dPixel|l(?:MemoryAvailable|locateCollectable))|gisterServicesProvider)|angeFromString)|Get(?:SizeAndAlignment|CriticalAlertPanel|InformationalAlertPanel|UncaughtExceptionHandler|FileType(?:s)?|WindowServerMemory|AlertPanel)|M(?:i(?:n(?:X|Y)|d(?:X|Y))|ouseInRect|a(?:p(?:Remove|Get|Member|Insert(?:IfAbsent|KnownAbsent)?)|ke(?:R(?:ect|ange)|Size|Point)|x(?:Range|X|Y)))|B(?:itsPer(?:SampleFromDepth|PixelFromDepth)|e(?:stDepth|ep|gin(?:CriticalAlertSheet|InformationalAlertSheet|AlertSheet)))|S(?:ho(?:uldRetainWithZone|w(?:sServicesMenuItem|AnimationEffect))|tringFrom(?:R(?:ect|ange)|MapTable|S(?:ize|elector)|HashTable|Class|Point)|izeFromString|e(?:t(?:ShowsServicesMenuItem|ZoneName|UncaughtExceptionHandler|FocusRingStyle)|lectorFromString|archPathForDirectoriesInDomains)|wap(?:Big(?:ShortToHost|IntToHost|DoubleToHost|FloatToHost|Long(?:ToHost|LongToHost))|Short|Host(?:ShortTo(?:Big|Little)|IntTo(?:Big|Little)|DoubleTo(?:Big|Little)|FloatTo(?:Big|Little)|Long(?:To(?:Big|Little)|LongTo(?:Big|Little)))|Int|Double|Float|L(?:ittle(?:ShortToHost|IntToHost|DoubleToHost|FloatToHost|Long(?:ToHost|LongToHost))|ong(?:Long)?)))|H(?:ighlightRect|o(?:stByteOrder|meDirectory(?:ForUser)?)|eight|ash(?:Remove|Get|Insert(?:IfAbsent|KnownAbsent)?)|FSType(?:CodeFromFileType|OfFile))|N(?:umberOfColorComponents|ext(?:MapEnumeratorPair|HashEnumeratorItem))|C(?:o(?:n(?:tainsRect|vert(?:GlyphsToPackedGlyphs|Swapped(?:DoubleToHost|FloatToHost)|Host(?:DoubleToSwapped|FloatToSwapped)))|unt(?:MapTable|HashTable|Frames|Windows(?:ForContext)?)|py(?:M(?:emoryPages|apTableWithZone)|Bits|HashTableWithZone|Object)|lorSpaceFromDepth|mpare(?:MapTables|HashTables))|lassFromString|reate(?:MapTable(?:WithZone)?|HashTable(?:WithZone)?|Zone|File(?:namePboardType|ContentsPboardType)))|TemporaryDirectory|I(?:s(?:ControllerMarker|EmptyRect|FreedObject)|n(?:setRect|crementExtraRefCount|te(?:r(?:sect(?:sRect|ionR(?:ect|ange))|faceStyleForKey)|gralRect)))|Zone(?:Realloc|Malloc|Name|Calloc|Fr(?:omPointer|ee))|O(?:penStepRootDirectory|ffsetRect)|D(?:i(?:sableScreenUpdates|videRect)|ottedFrameRect|e(?:c(?:imal(?:Round|Multiply|S(?:tring|ubtract)|Normalize|Co(?:py|mpa(?:ct|re))|IsNotANumber|Divide|Power|Add)|rementExtraRefCountWasZero)|faultMallocZone|allocate(?:MemoryPages|Object))|raw(?:Gr(?:oove|ayBezel)|B(?:itmap|utton)|ColorTiledRects|TiledRects|DarkBezel|W(?:hiteBezel|indowBackground)|LightBezel))|U(?:serName|n(?:ionR(?:ect|ange)|registerServicesProvider)|pdateDynamicServices)|Java(?:Bundle(?:Setup|Cleanup)|Setup(?:VirtualMachine)?|Needs(?:ToLoadClasses|VirtualMachine)|ClassesF(?:orBundle|romPath)|ObjectNamedInPath|ProvidesClasses)|P(?:oint(?:InRect|FromString)|erformService|lanarFromDepth|ageSize)|E(?:n(?:d(?:MapTableEnumeration|HashTableEnumeration)|umerate(?:MapTable|HashTable)|ableScreenUpdates)|qual(?:R(?:ects|anges)|Sizes|Points)|raseRect|xtraRefCount)|F(?:ileTypeForHFSTypeCode|ullUserName|r(?:ee(?:MapTable|HashTable)|ame(?:Rect(?:WithWidth(?:UsingOperation)?)?|Address)))|Wi(?:ndowList(?:ForContext)?|dth)|Lo(?:cationInRange|g(?:v|PageSize)?)|A(?:ccessibility(?:R(?:oleDescription(?:ForUIElement)?|aiseBadArgumentException)|Unignored(?:Children(?:ForOnlyChild)?|Descendant|Ancestor)|PostNotification|ActionDescription)|pplication(?:Main|Load)|vailableWindowDepths|ll(?:MapTable(?:Values|Keys)|HashTableObjects|ocate(?:MemoryPages|Collectable|Object)))))(?:\\b)"},{token:["support.class.cocoa.leopard"],regex:"(?:\\b)(NS(?:RuleEditor|G(?:arbageCollector|radient)|MapTable|HashTable|Co(?:ndition|llectionView(?:Item)?)|T(?:oolbarItemGroup|extInputClient|r(?:eeNode|ackingArea))|InvocationOperation|Operation(?:Queue)?|D(?:ictionaryController|ockTile)|P(?:ointer(?:Functions|Array)|athC(?:o(?:ntrol(?:Delegate)?|mponentCell)|ell(?:Delegate)?)|r(?:intPanelAccessorizing|edicateEditor(?:RowTemplate)?))|ViewController|FastEnumeration|Animat(?:ionContext|ablePropertyContainer)))(?:\\b)"},{token:["support.class.cocoa"],regex:"(?:\\b)(NS(?:R(?:u(?:nLoop|ler(?:Marker|View))|e(?:sponder|cursiveLock|lativeSpecifier)|an(?:domSpecifier|geSpecifier))|G(?:etCommand|lyph(?:Generator|Storage|Info)|raphicsContext)|XML(?:Node|D(?:ocument|TD(?:Node)?)|Parser|Element)|M(?:iddleSpecifier|ov(?:ie(?:View)?|eCommand)|utable(?:S(?:tring|et)|C(?:haracterSet|opying)|IndexSet|D(?:ictionary|ata)|URLRequest|ParagraphStyle|A(?:ttributedString|rray))|e(?:ssagePort(?:NameServer)?|nu(?:Item(?:Cell)?|View)?|t(?:hodSignature|adata(?:Item|Query(?:ResultGroup|AttributeValueTuple)?)))|a(?:ch(?:BootstrapServer|Port)|trix))|B(?:itmapImageRep|ox|u(?:ndle|tton(?:Cell)?)|ezierPath|rowser(?:Cell)?)|S(?:hadow|c(?:anner|r(?:ipt(?:SuiteRegistry|C(?:o(?:ercionHandler|mmand(?:Description)?)|lassDescription)|ObjectSpecifier|ExecutionContext|WhoseTest)|oll(?:er|View)|een))|t(?:epper(?:Cell)?|atus(?:Bar|Item)|r(?:ing|eam))|imple(?:HorizontalTypesetter|CString)|o(?:cketPort(?:NameServer)?|und|rtDescriptor)|p(?:e(?:cifierTest|ech(?:Recognizer|Synthesizer)|ll(?:Server|Checker))|litView)|e(?:cureTextField(?:Cell)?|t(?:Command)?|archField(?:Cell)?|rializer|gmentedC(?:ontrol|ell))|lider(?:Cell)?|avePanel)|H(?:ost|TTP(?:Cookie(?:Storage)?|URLResponse)|elpManager)|N(?:ib(?:Con(?:nector|trolConnector)|OutletConnector)?|otification(?:Center|Queue)?|u(?:ll|mber(?:Formatter)?)|etService(?:Browser)?|ameSpecifier)|C(?:ha(?:ngeSpelling|racterSet)|o(?:n(?:stantString|nection|trol(?:ler)?|ditionLock)|d(?:ing|er)|unt(?:Command|edSet)|pying|lor(?:Space|P(?:ick(?:ing(?:Custom|Default)|er)|anel)|Well|List)?|m(?:p(?:oundPredicate|arisonPredicate)|boBox(?:Cell)?))|u(?:stomImageRep|rsor)|IImageRep|ell|l(?:ipView|o(?:seCommand|neCommand)|assDescription)|a(?:ched(?:ImageRep|URLResponse)|lendar(?:Date)?)|reateCommand)|T(?:hread|ypesetter|ime(?:Zone|r)|o(?:olbar(?:Item(?:Validations)?)?|kenField(?:Cell)?)|ext(?:Block|Storage|Container|Tab(?:le(?:Block)?)?|Input|View|Field(?:Cell)?|List|Attachment(?:Cell)?)?|a(?:sk|b(?:le(?:Header(?:Cell|View)|Column|View)|View(?:Item)?))|reeController)|I(?:n(?:dex(?:S(?:pecifier|et)|Path)|put(?:Manager|S(?:tream|erv(?:iceProvider|er(?:MouseTracker)?)))|vocation)|gnoreMisspelledWords|mage(?:Rep|Cell|View)?)|O(?:ut(?:putStream|lineView)|pen(?:GL(?:Context|Pixel(?:Buffer|Format)|View)|Panel)|bj(?:CTypeSerializationCallBack|ect(?:Controller)?))|D(?:i(?:st(?:antObject(?:Request)?|ributed(?:NotificationCenter|Lock))|ctionary|rectoryEnumerator)|ocument(?:Controller)?|e(?:serializer|cimalNumber(?:Behaviors|Handler)?|leteCommand)|at(?:e(?:Components|Picker(?:Cell)?|Formatter)?|a)|ra(?:wer|ggingInfo))|U(?:ser(?:InterfaceValidations|Defaults(?:Controller)?)|RL(?:Re(?:sponse|quest)|Handle(?:Client)?|C(?:onnection|ache|redential(?:Storage)?)|Download(?:Delegate)?|Prot(?:ocol(?:Client)?|ectionSpace)|AuthenticationChallenge(?:Sender)?)?|n(?:iqueIDSpecifier|doManager|archiver))|P(?:ipe|o(?:sitionalSpecifier|pUpButton(?:Cell)?|rt(?:Message|NameServer|Coder)?)|ICTImageRep|ersistentDocument|DFImageRep|a(?:steboard|nel|ragraphStyle|geLayout)|r(?:int(?:Info|er|Operation|Panel)|o(?:cessInfo|tocolChecker|perty(?:Specifier|ListSerialization)|gressIndicator|xy)|edicate))|E(?:numerator|vent|PSImageRep|rror|x(?:ception|istsCommand|pression))|V(?:iew(?:Animation)?|al(?:idated(?:ToobarItem|UserInterfaceItem)|ue(?:Transformer)?))|Keyed(?:Unarchiver|Archiver)|Qui(?:ckDrawView|tCommand)|F(?:ile(?:Manager|Handle|Wrapper)|o(?:nt(?:Manager|Descriptor|Panel)?|rm(?:Cell|atter)))|W(?:hoseSpecifier|indow(?:Controller)?|orkspace)|L(?:o(?:c(?:k(?:ing)?|ale)|gicalTest)|evelIndicator(?:Cell)?|ayoutManager)|A(?:ssertionHandler|nimation|ctionCell|ttributedString|utoreleasePool|TSTypesetter|ppl(?:ication|e(?:Script|Event(?:Manager|Descriptor)))|ffineTransform|lert|r(?:chiver|ray(?:Controller)?))))(?:\\b)"},{token:["support.type.cocoa.leopard"],regex:"(?:\\b)(NS(?:R(?:u(?:nLoop|ler(?:Marker|View))|e(?:sponder|cursiveLock|lativeSpecifier)|an(?:domSpecifier|geSpecifier))|G(?:etCommand|lyph(?:Generator|Storage|Info)|raphicsContext)|XML(?:Node|D(?:ocument|TD(?:Node)?)|Parser|Element)|M(?:iddleSpecifier|ov(?:ie(?:View)?|eCommand)|utable(?:S(?:tring|et)|C(?:haracterSet|opying)|IndexSet|D(?:ictionary|ata)|URLRequest|ParagraphStyle|A(?:ttributedString|rray))|e(?:ssagePort(?:NameServer)?|nu(?:Item(?:Cell)?|View)?|t(?:hodSignature|adata(?:Item|Query(?:ResultGroup|AttributeValueTuple)?)))|a(?:ch(?:BootstrapServer|Port)|trix))|B(?:itmapImageRep|ox|u(?:ndle|tton(?:Cell)?)|ezierPath|rowser(?:Cell)?)|S(?:hadow|c(?:anner|r(?:ipt(?:SuiteRegistry|C(?:o(?:ercionHandler|mmand(?:Description)?)|lassDescription)|ObjectSpecifier|ExecutionContext|WhoseTest)|oll(?:er|View)|een))|t(?:epper(?:Cell)?|atus(?:Bar|Item)|r(?:ing|eam))|imple(?:HorizontalTypesetter|CString)|o(?:cketPort(?:NameServer)?|und|rtDescriptor)|p(?:e(?:cifierTest|ech(?:Recognizer|Synthesizer)|ll(?:Server|Checker))|litView)|e(?:cureTextField(?:Cell)?|t(?:Command)?|archField(?:Cell)?|rializer|gmentedC(?:ontrol|ell))|lider(?:Cell)?|avePanel)|H(?:ost|TTP(?:Cookie(?:Storage)?|URLResponse)|elpManager)|N(?:ib(?:Con(?:nector|trolConnector)|OutletConnector)?|otification(?:Center|Queue)?|u(?:ll|mber(?:Formatter)?)|etService(?:Browser)?|ameSpecifier)|C(?:ha(?:ngeSpelling|racterSet)|o(?:n(?:stantString|nection|trol(?:ler)?|ditionLock)|d(?:ing|er)|unt(?:Command|edSet)|pying|lor(?:Space|P(?:ick(?:ing(?:Custom|Default)|er)|anel)|Well|List)?|m(?:p(?:oundPredicate|arisonPredicate)|boBox(?:Cell)?))|u(?:stomImageRep|rsor)|IImageRep|ell|l(?:ipView|o(?:seCommand|neCommand)|assDescription)|a(?:ched(?:ImageRep|URLResponse)|lendar(?:Date)?)|reateCommand)|T(?:hread|ypesetter|ime(?:Zone|r)|o(?:olbar(?:Item(?:Validations)?)?|kenField(?:Cell)?)|ext(?:Block|Storage|Container|Tab(?:le(?:Block)?)?|Input|View|Field(?:Cell)?|List|Attachment(?:Cell)?)?|a(?:sk|b(?:le(?:Header(?:Cell|View)|Column|View)|View(?:Item)?))|reeController)|I(?:n(?:dex(?:S(?:pecifier|et)|Path)|put(?:Manager|S(?:tream|erv(?:iceProvider|er(?:MouseTracker)?)))|vocation)|gnoreMisspelledWords|mage(?:Rep|Cell|View)?)|O(?:ut(?:putStream|lineView)|pen(?:GL(?:Context|Pixel(?:Buffer|Format)|View)|Panel)|bj(?:CTypeSerializationCallBack|ect(?:Controller)?))|D(?:i(?:st(?:antObject(?:Request)?|ributed(?:NotificationCenter|Lock))|ctionary|rectoryEnumerator)|ocument(?:Controller)?|e(?:serializer|cimalNumber(?:Behaviors|Handler)?|leteCommand)|at(?:e(?:Components|Picker(?:Cell)?|Formatter)?|a)|ra(?:wer|ggingInfo))|U(?:ser(?:InterfaceValidations|Defaults(?:Controller)?)|RL(?:Re(?:sponse|quest)|Handle(?:Client)?|C(?:onnection|ache|redential(?:Storage)?)|Download(?:Delegate)?|Prot(?:ocol(?:Client)?|ectionSpace)|AuthenticationChallenge(?:Sender)?)?|n(?:iqueIDSpecifier|doManager|archiver))|P(?:ipe|o(?:sitionalSpecifier|pUpButton(?:Cell)?|rt(?:Message|NameServer|Coder)?)|ICTImageRep|ersistentDocument|DFImageRep|a(?:steboard|nel|ragraphStyle|geLayout)|r(?:int(?:Info|er|Operation|Panel)|o(?:cessInfo|tocolChecker|perty(?:Specifier|ListSerialization)|gressIndicator|xy)|edicate))|E(?:numerator|vent|PSImageRep|rror|x(?:ception|istsCommand|pression))|V(?:iew(?:Animation)?|al(?:idated(?:ToobarItem|UserInterfaceItem)|ue(?:Transformer)?))|Keyed(?:Unarchiver|Archiver)|Qui(?:ckDrawView|tCommand)|F(?:ile(?:Manager|Handle|Wrapper)|o(?:nt(?:Manager|Descriptor|Panel)?|rm(?:Cell|atter)))|W(?:hoseSpecifier|indow(?:Controller)?|orkspace)|L(?:o(?:c(?:k(?:ing)?|ale)|gicalTest)|evelIndicator(?:Cell)?|ayoutManager)|A(?:ssertionHandler|nimation|ctionCell|ttributedString|utoreleasePool|TSTypesetter|ppl(?:ication|e(?:Script|Event(?:Manager|Descriptor)))|ffineTransform|lert|r(?:chiver|ray(?:Controller)?))))(?:\\b)"},{token:["support.class.quartz"],regex:"(?:\\b)(C(?:I(?:Sampler|Co(?:ntext|lor)|Image(?:Accumulator)?|PlugIn(?:Registration)?|Vector|Kernel|Filter(?:Generator|Shape)?)|A(?:Renderer|MediaTiming(?:Function)?|BasicAnimation|ScrollLayer|Constraint(?:LayoutManager)?|T(?:iledLayer|extLayer|rans(?:ition|action))|OpenGLLayer|PropertyAnimation|KeyframeAnimation|Layer|A(?:nimation(?:Group)?|ction))))(?:\\b)"},{token:["support.type.quartz"],regex:"(?:\\b)(C(?:G(?:Float|Point|Size|Rect)|IFormat|AConstraintAttribute))(?:\\b)"},{token:["support.type.cocoa"],regex:"(?:\\b)(NS(?:R(?:ect(?:Edge)?|ange)|G(?:lyph(?:Relation|LayoutMode)?|radientType)|M(?:odalSession|a(?:trixMode|p(?:Table|Enumerator)))|B(?:itmapImageFileType|orderType|uttonType|ezelStyle|ackingStoreType|rowserColumnResizingType)|S(?:cr(?:oll(?:er(?:Part|Arrow)|ArrowPosition)|eenAuxiliaryOpaque)|tringEncoding|ize|ocketNativeHandle|election(?:Granularity|Direction|Affinity)|wapped(?:Double|Float)|aveOperationType)|Ha(?:sh(?:Table|Enumerator)|ndler(?:2)?)|C(?:o(?:ntrol(?:Size|Tint)|mp(?:ositingOperation|arisonResult))|ell(?:State|Type|ImagePosition|Attribute))|T(?:hreadPrivate|ypesetterGlyphInfo|i(?:ckMarkPosition|tlePosition|meInterval)|o(?:ol(?:TipTag|bar(?:SizeMode|DisplayMode))|kenStyle)|IFFCompression|ext(?:TabType|Alignment)|ab(?:State|leViewDropOperation|ViewType)|rackingRectTag)|ImageInterpolation|Zone|OpenGL(?:ContextAuxiliary|PixelFormatAuxiliary)|D(?:ocumentChangeType|atePickerElementFlags|ra(?:werState|gOperation))|UsableScrollerParts|P(?:oint|r(?:intingPageOrder|ogressIndicator(?:Style|Th(?:ickness|readInfo))))|EventType|KeyValueObservingOptions|Fo(?:nt(?:SymbolicTraits|TraitMask|Action)|cusRingType)|W(?:indow(?:OrderingMode|Depth)|orkspace(?:IconCreationOptions|LaunchOptions)|ritingDirection)|L(?:ineBreakMode|ayout(?:Status|Direction))|A(?:nimation(?:Progress|Effect)|ppl(?:ication(?:TerminateReply|DelegateReply|PrintReply)|eEventManagerSuspensionID)|ffineTransformStruct|lertStyle)))(?:\\b)"},{token:["support.constant.cocoa"],regex:"(?:\\b)(NS(?:NotFound|Ordered(?:Ascending|Descending|Same)))(?:\\b)"},{token:["support.constant.notification.cocoa.leopard"],regex:"(?:\\b)(NS(?:MenuDidBeginTracking|ViewDidUpdateTrackingAreas)?Notification)(?:\\b)"},{token:["support.constant.notification.cocoa"],regex:"(?:\\b)(NS(?:Menu(?:Did(?:RemoveItem|SendAction|ChangeItem|EndTracking|AddItem)|WillSendAction)|S(?:ystemColorsDidChange|plitView(?:DidResizeSubviews|WillResizeSubviews))|C(?:o(?:nt(?:extHelpModeDid(?:Deactivate|Activate)|rolT(?:intDidChange|extDid(?:BeginEditing|Change|EndEditing)))|lor(?:PanelColorDidChange|ListDidChange)|mboBox(?:Selection(?:IsChanging|DidChange)|Will(?:Dismiss|PopUp)))|lassDescriptionNeededForClass)|T(?:oolbar(?:DidRemoveItem|WillAddItem)|ext(?:Storage(?:DidProcessEditing|WillProcessEditing)|Did(?:BeginEditing|Change|EndEditing)|View(?:DidChange(?:Selection|TypingAttributes)|WillChangeNotifyingTextView))|ableView(?:Selection(?:IsChanging|DidChange)|ColumnDid(?:Resize|Move)))|ImageRepRegistryDidChange|OutlineView(?:Selection(?:IsChanging|DidChange)|ColumnDid(?:Resize|Move)|Item(?:Did(?:Collapse|Expand)|Will(?:Collapse|Expand)))|Drawer(?:Did(?:Close|Open)|Will(?:Close|Open))|PopUpButton(?:CellWillPopUp|WillPopUp)|View(?:GlobalFrameDidChange|BoundsDidChange|F(?:ocusDidChange|rameDidChange))|FontSetChanged|W(?:indow(?:Did(?:Resi(?:ze|gn(?:Main|Key))|M(?:iniaturize|ove)|Become(?:Main|Key)|ChangeScreen(?:|Profile)|Deminiaturize|Update|E(?:ndSheet|xpose))|Will(?:M(?:iniaturize|ove)|BeginSheet|Close))|orkspace(?:SessionDid(?:ResignActive|BecomeActive)|Did(?:Mount|TerminateApplication|Unmount|PerformFileOperation|Wake|LaunchApplication)|Will(?:Sleep|Unmount|PowerOff|LaunchApplication)))|A(?:ntialiasThresholdChanged|ppl(?:ication(?:Did(?:ResignActive|BecomeActive|Hide|ChangeScreenParameters|U(?:nhide|pdate)|FinishLaunching)|Will(?:ResignActive|BecomeActive|Hide|Terminate|U(?:nhide|pdate)|FinishLaunching))|eEventManagerWillProcessFirstEvent)))Notification)(?:\\b)"},{token:["support.constant.cocoa.leopard"],regex:"(?:\\b)(NS(?:RuleEditor(?:RowType(?:Simple|Compound)|NestingMode(?:Si(?:ngle|mple)|Compound|List))|GradientDraws(?:BeforeStartingLocation|AfterEndingLocation)|M(?:inusSetExpressionType|a(?:chPortDeallocate(?:ReceiveRight|SendRight|None)|pTable(?:StrongMemory|CopyIn|ZeroingWeakMemory|ObjectPointerPersonality)))|B(?:oxCustom|undleExecutableArchitecture(?:X86|I386|PPC(?:64)?)|etweenPredicateOperatorType|ackgroundStyle(?:Raised|Dark|L(?:ight|owered)))|S(?:tring(?:DrawingTruncatesLastVisibleLine|EncodingConversion(?:ExternalRepresentation|AllowLossy))|ubqueryExpressionType|p(?:e(?:ech(?:SentenceBoundary|ImmediateBoundary|WordBoundary)|llingState(?:GrammarFlag|SpellingFlag))|litViewDividerStyleThi(?:n|ck))|e(?:rvice(?:RequestTimedOutError|M(?:iscellaneousError|alformedServiceDictionaryError)|InvalidPasteboardDataError|ErrorM(?:inimum|aximum)|Application(?:NotFoundError|LaunchFailedError))|gmentStyle(?:Round(?:Rect|ed)|SmallSquare|Capsule|Textured(?:Rounded|Square)|Automatic)))|H(?:UDWindowMask|ashTable(?:StrongMemory|CopyIn|ZeroingWeakMemory|ObjectPointerPersonality))|N(?:oModeColorPanel|etServiceNoAutoRename)|C(?:hangeRedone|o(?:ntainsPredicateOperatorType|l(?:orRenderingIntent(?:RelativeColorimetric|Saturation|Default|Perceptual|AbsoluteColorimetric)|lectorDisabledOption))|ellHit(?:None|ContentArea|TrackableArea|EditableTextArea))|T(?:imeZoneNameStyle(?:S(?:hort(?:Standard|DaylightSaving)|tandard)|DaylightSaving)|extFieldDatePickerStyle|ableViewSelectionHighlightStyle(?:Regular|SourceList)|racking(?:Mouse(?:Moved|EnteredAndExited)|CursorUpdate|InVisibleRect|EnabledDuringMouseDrag|A(?:ssumeInside|ctive(?:In(?:KeyWindow|ActiveApp)|WhenFirstResponder|Always))))|I(?:n(?:tersectSetExpressionType|dexedColorSpaceModel)|mageScale(?:None|Proportionally(?:Down|UpOrDown)|AxesIndependently))|Ope(?:nGLPFAAllowOfflineRenderers|rationQueue(?:DefaultMaxConcurrentOperationCount|Priority(?:High|Normal|Very(?:High|Low)|Low)))|D(?:iacriticInsensitiveSearch|ownloadsDirectory)|U(?:nionSetExpressionType|TF(?:16(?:BigEndianStringEncoding|StringEncoding|LittleEndianStringEncoding)|32(?:BigEndianStringEncoding|StringEncoding|LittleEndianStringEncoding)))|P(?:ointerFunctions(?:Ma(?:chVirtualMemory|llocMemory)|Str(?:ongMemory|uctPersonality)|C(?:StringPersonality|opyIn)|IntegerPersonality|ZeroingWeakMemory|O(?:paque(?:Memory|Personality)|bjectP(?:ointerPersonality|ersonality)))|at(?:hStyle(?:Standard|NavigationBar|PopUp)|ternColorSpaceModel)|rintPanelShows(?:Scaling|Copies|Orientation|P(?:a(?:perSize|ge(?:Range|SetupAccessory))|review)))|Executable(?:RuntimeMismatchError|NotLoadableError|ErrorM(?:inimum|aximum)|L(?:inkError|oadError)|ArchitectureMismatchError)|KeyValueObservingOption(?:Initial|Prior)|F(?:i(?:ndPanelSubstringMatchType(?:StartsWith|Contains|EndsWith|FullWord)|leRead(?:TooLargeError|UnknownStringEncodingError))|orcedOrderingSearch)|Wi(?:ndow(?:BackingLocation(?:MainMemory|Default|VideoMemory)|Sharing(?:Read(?:Only|Write)|None)|CollectionBehavior(?:MoveToActiveSpace|CanJoinAllSpaces|Default))|dthInsensitiveSearch)|AggregateExpressionType))(?:\\b)"},{token:["support.constant.cocoa"],regex:"(?:\\b)(NS(?:R(?:GB(?:ModeColorPanel|ColorSpaceModel)|ight(?:Mouse(?:D(?:own(?:Mask)?|ragged(?:Mask)?)|Up(?:Mask)?)|T(?:ext(?:Movement|Alignment)|ab(?:sBezelBorder|StopType))|ArrowFunctionKey)|ound(?:RectBezelStyle|Bankers|ed(?:BezelStyle|TokenStyle|DisclosureBezelStyle)|Down|Up|Plain|Line(?:CapStyle|JoinStyle))|un(?:StoppedResponse|ContinuesResponse|AbortedResponse)|e(?:s(?:izableWindowMask|et(?:CursorRectsRunLoopOrdering|FunctionKey))|ce(?:ssedBezelStyle|iver(?:sCantHandleCommandScriptError|EvaluationScriptError))|turnTextMovement|doFunctionKey|quiredArgumentsMissingScriptError|l(?:evancyLevelIndicatorStyle|ative(?:Before|After))|gular(?:SquareBezelStyle|ControlSize)|moveTraitFontAction)|a(?:n(?:domSubelement|geDateMode)|tingLevelIndicatorStyle|dio(?:ModeMatrix|Button)))|G(?:IFFileType|lyph(?:Below|Inscribe(?:B(?:elow|ase)|Over(?:strike|Below)|Above)|Layout(?:WithPrevious|A(?:tAPoint|gainstAPoint))|A(?:ttribute(?:BidiLevel|Soft|Inscribe|Elastic)|bove))|r(?:ooveBorder|eaterThan(?:Comparison|OrEqualTo(?:Comparison|PredicateOperatorType)|PredicateOperatorType)|a(?:y(?:ModeColorPanel|ColorSpaceModel)|dient(?:None|Con(?:cave(?:Strong|Weak)|vex(?:Strong|Weak)))|phiteControlTint)))|XML(?:N(?:o(?:tationDeclarationKind|de(?:CompactEmptyElement|IsCDATA|OptionsNone|Use(?:SingleQuotes|DoubleQuotes)|Pre(?:serve(?:NamespaceOrder|C(?:haracterReferences|DATA)|DTD|Prefixes|E(?:ntities|mptyElements)|Quotes|Whitespace|A(?:ttributeOrder|ll))|ttyPrint)|ExpandEmptyElement))|amespaceKind)|CommentKind|TextKind|InvalidKind|D(?:ocument(?:X(?:MLKind|HTMLKind|Include)|HTMLKind|T(?:idy(?:XML|HTML)|extKind)|IncludeContentTypeDeclaration|Validate|Kind)|TDKind)|P(?:arser(?:GTRequiredError|XMLDeclNot(?:StartedError|FinishedError)|Mi(?:splaced(?:XMLDeclarationError|CDATAEndStringError)|xedContentDeclNot(?:StartedError|FinishedError))|S(?:t(?:andaloneValueError|ringNot(?:StartedError|ClosedError))|paceRequiredError|eparatorRequiredError)|N(?:MTOKENRequiredError|o(?:t(?:ationNot(?:StartedError|FinishedError)|WellBalancedError)|DTDError)|amespaceDeclarationError|AMERequiredError)|C(?:haracterRef(?:In(?:DTDError|PrologError|EpilogError)|AtEOFError)|o(?:nditionalSectionNot(?:StartedError|FinishedError)|mment(?:NotFinishedError|ContainsDoubleHyphenError))|DATANotFinishedError)|TagNameMismatchError|In(?:ternalError|valid(?:HexCharacterRefError|C(?:haracter(?:RefError|InEntityError|Error)|onditionalSectionError)|DecimalCharacterRefError|URIError|Encoding(?:NameError|Error)))|OutOfMemoryError|D(?:ocumentStartError|elegateAbortedParseError|OCTYPEDeclNotFinishedError)|U(?:RI(?:RequiredError|FragmentError)|n(?:declaredEntityError|parsedEntityError|knownEncodingError|finishedTagError))|P(?:CDATARequiredError|ublicIdentifierRequiredError|arsedEntityRef(?:MissingSemiError|NoNameError|In(?:Internal(?:SubsetError|Error)|PrologError|EpilogError)|AtEOFError)|r(?:ocessingInstructionNot(?:StartedError|FinishedError)|ematureDocumentEndError))|E(?:n(?:codingNotSupportedError|tity(?:Ref(?:In(?:DTDError|PrologError|EpilogError)|erence(?:MissingSemiError|WithoutNameError)|LoopError|AtEOFError)|BoundaryError|Not(?:StartedError|FinishedError)|Is(?:ParameterError|ExternalError)|ValueRequiredError))|qualExpectedError|lementContentDeclNot(?:StartedError|FinishedError)|xt(?:ernalS(?:tandaloneEntityError|ubsetNotFinishedError)|raContentError)|mptyDocumentError)|L(?:iteralNot(?:StartedError|FinishedError)|T(?:RequiredError|SlashRequiredError)|essThanSymbolInAttributeError)|Attribute(?:RedefinedError|HasNoValueError|Not(?:StartedError|FinishedError)|ListNot(?:StartedError|FinishedError)))|rocessingInstructionKind)|E(?:ntity(?:GeneralKind|DeclarationKind|UnparsedKind|P(?:ar(?:sedKind|ameterKind)|redefined))|lement(?:Declaration(?:MixedKind|UndefinedKind|E(?:lementKind|mptyKind)|Kind|AnyKind)|Kind))|Attribute(?:N(?:MToken(?:sKind|Kind)|otationKind)|CDATAKind|ID(?:Ref(?:sKind|Kind)|Kind)|DeclarationKind|En(?:tit(?:yKind|iesKind)|umerationKind)|Kind))|M(?:i(?:n(?:XEdge|iaturizableWindowMask|YEdge|uteCalendarUnit)|terLineJoinStyle|ddleSubelement|xedState)|o(?:nthCalendarUnit|deSwitchFunctionKey|use(?:Moved(?:Mask)?|E(?:ntered(?:Mask)?|ventSubtype|xited(?:Mask)?))|veToBezierPathElement|mentary(?:ChangeButton|Push(?:Button|InButton)|Light(?:Button)?))|enuFunctionKey|a(?:c(?:intoshInterfaceStyle|OSRomanStringEncoding)|tchesPredicateOperatorType|ppedRead|x(?:XEdge|YEdge))|ACHOperatingSystem)|B(?:MPFileType|o(?:ttomTabsBezelBorder|ldFontMask|rderlessWindowMask|x(?:Se(?:condary|parator)|OldStyle|Primary))|uttLineCapStyle|e(?:zelBorder|velLineJoinStyle|low(?:Bottom|Top)|gin(?:sWith(?:Comparison|PredicateOperatorType)|FunctionKey))|lueControlTint|ack(?:spaceCharacter|tabTextMovement|ingStore(?:Retained|Buffered|Nonretained)|TabCharacter|wardsSearch|groundTab)|r(?:owser(?:NoColumnResizing|UserColumnResizing|AutoColumnResizing)|eakFunctionKey))|S(?:h(?:ift(?:JISStringEncoding|KeyMask)|ow(?:ControlGlyphs|InvisibleGlyphs)|adowlessSquareBezelStyle)|y(?:s(?:ReqFunctionKey|tem(?:D(?:omainMask|efined(?:Mask)?)|FunctionKey))|mbolStringEncoding)|c(?:a(?:nnedOption|le(?:None|ToFit|Proportionally))|r(?:oll(?:er(?:NoPart|Increment(?:Page|Line|Arrow)|Decrement(?:Page|Line|Arrow)|Knob(?:Slot)?|Arrows(?:M(?:inEnd|axEnd)|None|DefaultSetting))|Wheel(?:Mask)?|LockFunctionKey)|eenChangedEventType))|t(?:opFunctionKey|r(?:ingDrawing(?:OneShot|DisableScreenFontSubstitution|Uses(?:DeviceMetrics|FontLeading|LineFragmentOrigin))|eam(?:Status(?:Reading|NotOpen|Closed|Open(?:ing)?|Error|Writing|AtEnd)|Event(?:Has(?:BytesAvailable|SpaceAvailable)|None|OpenCompleted|E(?:ndEncountered|rrorOccurred)))))|i(?:ngle(?:DateMode|UnderlineStyle)|ze(?:DownFontAction|UpFontAction))|olarisOperatingSystem|unOSOperatingSystem|pecialPageOrder|e(?:condCalendarUnit|lect(?:By(?:Character|Paragraph|Word)|i(?:ng(?:Next|Previous)|onAffinity(?:Downstream|Upstream))|edTab|FunctionKey)|gmentSwitchTracking(?:Momentary|Select(?:One|Any)))|quareLineCapStyle|witchButton|ave(?:ToOperation|Op(?:tions(?:Yes|No|Ask)|eration)|AsOperation)|mall(?:SquareBezelStyle|C(?:ontrolSize|apsFontMask)|IconButtonBezelStyle))|H(?:ighlightModeMatrix|SBModeColorPanel|o(?:ur(?:Minute(?:SecondDatePickerElementFlag|DatePickerElementFlag)|CalendarUnit)|rizontalRuler|meFunctionKey)|TTPCookieAcceptPolicy(?:Never|OnlyFromMainDocumentDomain|Always)|e(?:lp(?:ButtonBezelStyle|KeyMask|FunctionKey)|avierFontAction)|PUXOperatingSystem)|Year(?:MonthDa(?:yDatePickerElementFlag|tePickerElementFlag)|CalendarUnit)|N(?:o(?:n(?:StandardCharacterSetFontMask|ZeroWindingRule|activatingPanelMask|LossyASCIIStringEncoding)|Border|t(?:ification(?:SuspensionBehavior(?:Hold|Coalesce|D(?:eliverImmediately|rop))|NoCoalescing|CoalescingOn(?:Sender|Name)|DeliverImmediately|PostToAllSessions)|PredicateType|EqualToPredicateOperatorType)|S(?:cr(?:iptError|ollerParts)|ubelement|pecifierError)|CellMask|T(?:itle|opLevelContainersSpecifierError|abs(?:BezelBorder|NoBorder|LineBorder))|I(?:nterfaceStyle|mage)|UnderlineStyle|FontChangeAction)|u(?:ll(?:Glyph|CellType)|m(?:eric(?:Search|PadKeyMask)|berFormatter(?:Round(?:Half(?:Down|Up|Even)|Ceiling|Down|Up|Floor)|Behavior(?:10|Default)|S(?:cientificStyle|pellOutStyle)|NoStyle|CurrencyStyle|DecimalStyle|P(?:ercentStyle|ad(?:Before(?:Suffix|Prefix)|After(?:Suffix|Prefix))))))|e(?:t(?:Service(?:BadArgumentError|NotFoundError|C(?:ollisionError|ancelledError)|TimeoutError|InvalidError|UnknownError|ActivityInProgress)|workDomainMask)|wlineCharacter|xt(?:StepInterfaceStyle|FunctionKey))|EXTSTEPStringEncoding|a(?:t(?:iveShortGlyphPacking|uralTextAlignment)|rrowFontMask))|C(?:hange(?:ReadOtherContents|GrayCell(?:Mask)?|BackgroundCell(?:Mask)?|Cleared|Done|Undone|Autosaved)|MYK(?:ModeColorPanel|ColorSpaceModel)|ircular(?:BezelStyle|Slider)|o(?:n(?:stantValueExpressionType|t(?:inuousCapacityLevelIndicatorStyle|entsCellMask|ain(?:sComparison|erSpecifierError)|rol(?:Glyph|KeyMask))|densedFontMask)|lor(?:Panel(?:RGBModeMask|GrayModeMask|HSBModeMask|C(?:MYKModeMask|olorListModeMask|ustomPaletteModeMask|rayonModeMask)|WheelModeMask|AllModesMask)|ListModeColorPanel)|reServiceDirectory|m(?:p(?:osite(?:XOR|Source(?:In|O(?:ut|ver)|Atop)|Highlight|C(?:opy|lear)|Destination(?:In|O(?:ut|ver)|Atop)|Plus(?:Darker|Lighter))|ressedFontMask)|mandKeyMask))|u(?:stom(?:SelectorPredicateOperatorType|PaletteModeColorPanel)|r(?:sor(?:Update(?:Mask)?|PointingDevice)|veToBezierPathElement))|e(?:nterT(?:extAlignment|abStopType)|ll(?:State|H(?:ighlighted|as(?:Image(?:Horizontal|OnLeftOrBottom)|OverlappingImage))|ChangesContents|Is(?:Bordered|InsetButton)|Disabled|Editable|LightsBy(?:Gray|Background|Contents)|AllowsMixedState))|l(?:ipPagination|o(?:s(?:ePathBezierPathElement|ableWindowMask)|ckAndCalendarDatePickerStyle)|ear(?:ControlTint|DisplayFunctionKey|LineFunctionKey))|a(?:seInsensitive(?:Search|PredicateOption)|n(?:notCreateScriptCommandError|cel(?:Button|TextMovement))|chesDirectory|lculation(?:NoError|Overflow|DivideByZero|Underflow|LossOfPrecision)|rriageReturnCharacter)|r(?:itical(?:Request|AlertStyle)|ayonModeColorPanel))|T(?:hick(?:SquareBezelStyle|erSquareBezelStyle)|ypesetter(?:Behavior|HorizontalTabAction|ContainerBreakAction|ZeroAdvancementAction|OriginalBehavior|ParagraphBreakAction|WhitespaceAction|L(?:ineBreakAction|atestBehavior))|i(?:ckMark(?:Right|Below|Left|Above)|tledWindowMask|meZoneDatePickerElementFlag)|o(?:olbarItemVisibilityPriority(?:Standard|High|User|Low)|pTabsBezelBorder|ggleButton)|IFF(?:Compression(?:N(?:one|EXT)|CCITTFAX(?:3|4)|OldJPEG|JPEG|PackBits|LZW)|FileType)|e(?:rminate(?:Now|Cancel|Later)|xt(?:Read(?:InapplicableDocumentTypeError|WriteErrorM(?:inimum|aximum))|Block(?:M(?:i(?:nimum(?:Height|Width)|ddleAlignment)|a(?:rgin|ximum(?:Height|Width)))|B(?:o(?:ttomAlignment|rder)|aselineAlignment)|Height|TopAlignment|P(?:ercentageValueType|adding)|Width|AbsoluteValueType)|StorageEdited(?:Characters|Attributes)|CellType|ured(?:RoundedBezelStyle|BackgroundWindowMask|SquareBezelStyle)|Table(?:FixedLayoutAlgorithm|AutomaticLayoutAlgorithm)|Field(?:RoundedBezel|SquareBezel|AndStepperDatePickerStyle)|WriteInapplicableDocumentTypeError|ListPrependEnclosingMarker))|woByteGlyphPacking|ab(?:Character|TextMovement|le(?:tP(?:oint(?:Mask|EventSubtype)?|roximity(?:Mask|EventSubtype)?)|Column(?:NoResizing|UserResizingMask|AutoresizingMask)|View(?:ReverseSequentialColumnAutoresizingStyle|GridNone|S(?:olid(?:HorizontalGridLineMask|VerticalGridLineMask)|equentialColumnAutoresizingStyle)|NoColumnAutoresizing|UniformColumnAutoresizingStyle|FirstColumnOnlyAutoresizingStyle|LastColumnOnlyAutoresizingStyle)))|rackModeMatrix)|I(?:n(?:sert(?:CharFunctionKey|FunctionKey|LineFunctionKey)|t(?:Type|ernalS(?:criptError|pecifierError))|dexSubelement|validIndexSpecifierError|formational(?:Request|AlertStyle)|PredicateOperatorType)|talicFontMask|SO(?:2022JPStringEncoding|Latin(?:1StringEncoding|2StringEncoding))|dentityMappingCharacterCollection|llegalTextMovement|mage(?:R(?:ight|ep(?:MatchesDevice|LoadStatus(?:ReadingHeader|Completed|InvalidData|Un(?:expectedEOF|knownType)|WillNeedAllData)))|Below|C(?:ellType|ache(?:BySize|Never|Default|Always))|Interpolation(?:High|None|Default|Low)|O(?:nly|verlaps)|Frame(?:Gr(?:oove|ayBezel)|Button|None|Photo)|L(?:oadStatus(?:ReadError|C(?:ompleted|ancelled)|InvalidData|UnexpectedEOF)|eft)|A(?:lign(?:Right|Bottom(?:Right|Left)?|Center|Top(?:Right|Left)?|Left)|bove)))|O(?:n(?:State|eByteGlyphPacking|OffButton|lyScrollerArrows)|ther(?:Mouse(?:D(?:own(?:Mask)?|ragged(?:Mask)?)|Up(?:Mask)?)|TextMovement)|SF1OperatingSystem|pe(?:n(?:GL(?:GO(?:Re(?:setLibrary|tainRenderers)|ClearFormatCache|FormatCacheSize)|PFA(?:R(?:obust|endererID)|M(?:inimumPolicy|ulti(?:sample|Screen)|PSafe|aximumPolicy)|BackingStore|S(?:creenMask|te(?:ncilSize|reo)|ingleRenderer|upersample|ample(?:s|Buffers|Alpha))|NoRecovery|C(?:o(?:lor(?:Size|Float)|mpliant)|losestPolicy)|OffScreen|D(?:oubleBuffer|epthSize)|PixelBuffer|VirtualScreenCount|FullScreen|Window|A(?:cc(?:umSize|elerated)|ux(?:Buffers|DepthStencil)|l(?:phaSize|lRenderers))))|StepUnicodeReservedBase)|rationNotSupportedForKeyS(?:criptError|pecifierError))|ffState|KButton|rPredicateType|bjC(?:B(?:itfield|oolType)|S(?:hortType|tr(?:ingType|uctType)|electorType)|NoType|CharType|ObjectType|DoubleType|UnionType|PointerType|VoidType|FloatType|Long(?:Type|longType)|ArrayType))|D(?:i(?:s(?:c(?:losureBezelStyle|reteCapacityLevelIndicatorStyle)|playWindowRunLoopOrdering)|acriticInsensitivePredicateOption|rect(?:Selection|PredicateModifier))|o(?:c(?:ModalWindowMask|ument(?:Directory|ationDirectory))|ubleType|wn(?:TextMovement|ArrowFunctionKey))|e(?:s(?:cendingPageOrder|ktopDirectory)|cimalTabStopType|v(?:ice(?:NColorSpaceModel|IndependentModifierFlagsMask)|eloper(?:Directory|ApplicationDirectory))|fault(?:ControlTint|TokenStyle)|lete(?:Char(?:acter|FunctionKey)|FunctionKey|LineFunctionKey)|moApplicationDirectory)|a(?:yCalendarUnit|teFormatter(?:MediumStyle|Behavior(?:10|Default)|ShortStyle|NoStyle|FullStyle|LongStyle))|ra(?:wer(?:Clos(?:ingState|edState)|Open(?:ingState|State))|gOperation(?:Generic|Move|None|Copy|Delete|Private|Every|Link|All)))|U(?:ser(?:CancelledError|D(?:irectory|omainMask)|FunctionKey)|RL(?:Handle(?:NotLoaded|Load(?:Succeeded|InProgress|Failed))|CredentialPersistence(?:None|Permanent|ForSession))|n(?:scaledWindowMask|cachedRead|i(?:codeStringEncoding|talicFontMask|fiedTitleAndToolbarWindowMask)|d(?:o(?:CloseGroupingRunLoopOrdering|FunctionKey)|e(?:finedDateComponent|rline(?:Style(?:Single|None|Thick|Double)|Pattern(?:Solid|D(?:ot|ash(?:Dot(?:Dot)?)?)))))|known(?:ColorSpaceModel|P(?:ointingDevice|ageOrder)|KeyS(?:criptError|pecifierError))|boldFontMask)|tilityWindowMask|TF8StringEncoding|p(?:dateWindowsRunLoopOrdering|TextMovement|ArrowFunctionKey))|J(?:ustifiedTextAlignment|PEG(?:2000FileType|FileType)|apaneseEUC(?:GlyphPacking|StringEncoding))|P(?:o(?:s(?:t(?:Now|erFontMask|WhenIdle|ASAP)|iti(?:on(?:Replace|Be(?:fore|ginning)|End|After)|ve(?:IntType|DoubleType|FloatType)))|pUp(?:NoArrow|ArrowAt(?:Bottom|Center))|werOffEventType|rtraitOrientation)|NGFileType|ush(?:InCell(?:Mask)?|OnPushOffButton)|e(?:n(?:TipMask|UpperSideMask|PointingDevice|LowerSideMask)|riodic(?:Mask)?)|P(?:S(?:caleField|tatus(?:Title|Field)|aveButton)|N(?:ote(?:Title|Field)|ame(?:Title|Field))|CopiesField|TitleField|ImageButton|OptionsButton|P(?:a(?:perFeedButton|ge(?:Range(?:To|From)|ChoiceMatrix))|reviewButton)|LayoutButton)|lainTextTokenStyle|a(?:useFunctionKey|ragraphSeparatorCharacter|ge(?:DownFunctionKey|UpFunctionKey))|r(?:int(?:ing(?:ReplyLater|Success|Cancelled|Failure)|ScreenFunctionKey|erTable(?:NotFound|OK|Error)|FunctionKey)|o(?:p(?:ertyList(?:XMLFormat|MutableContainers(?:AndLeaves)?|BinaryFormat|Immutable|OpenStepFormat)|rietaryStringEncoding)|gressIndicator(?:BarStyle|SpinningStyle|Preferred(?:SmallThickness|Thickness|LargeThickness|AquaThickness)))|e(?:ssedTab|vFunctionKey))|L(?:HeightForm|CancelButton|TitleField|ImageButton|O(?:KButton|rientationMatrix)|UnitsButton|PaperNameButton|WidthForm))|E(?:n(?:terCharacter|d(?:sWith(?:Comparison|PredicateOperatorType)|FunctionKey))|v(?:e(?:nOddWindingRule|rySubelement)|aluatedObjectExpressionType)|qualTo(?:Comparison|PredicateOperatorType)|ra(?:serPointingDevice|CalendarUnit|DatePickerElementFlag)|x(?:clude(?:10|QuickDrawElementsIconCreationOption)|pandedFontMask|ecuteFunctionKey))|V(?:i(?:ew(?:M(?:in(?:XMargin|YMargin)|ax(?:XMargin|YMargin))|HeightSizable|NotSizable|WidthSizable)|aPanelFontAction)|erticalRuler|a(?:lidationErrorM(?:inimum|aximum)|riableExpressionType))|Key(?:SpecifierEvaluationScriptError|Down(?:Mask)?|Up(?:Mask)?|PathExpressionType|Value(?:MinusSetMutation|SetSetMutation|Change(?:Re(?:placement|moval)|Setting|Insertion)|IntersectSetMutation|ObservingOption(?:New|Old)|UnionSetMutation|ValidationError))|QTMovie(?:NormalPlayback|Looping(?:BackAndForthPlayback|Playback))|F(?:1(?:1FunctionKey|7FunctionKey|2FunctionKey|8FunctionKey|3FunctionKey|9FunctionKey|4FunctionKey|5FunctionKey|FunctionKey|0FunctionKey|6FunctionKey)|7FunctionKey|i(?:nd(?:PanelAction(?:Replace(?:A(?:ndFind|ll(?:InSelection)?))?|S(?:howFindPanel|e(?:tFindString|lectAll(?:InSelection)?))|Next|Previous)|FunctionKey)|tPagination|le(?:Read(?:No(?:SuchFileError|PermissionError)|CorruptFileError|In(?:validFileNameError|applicableStringEncodingError)|Un(?:supportedSchemeError|knownError))|HandlingPanel(?:CancelButton|OKButton)|NoSuchFileError|ErrorM(?:inimum|aximum)|Write(?:NoPermissionError|In(?:validFileNameError|applicableStringEncodingError)|OutOfSpaceError|Un(?:supportedSchemeError|knownError))|LockingError)|xedPitchFontMask)|2(?:1FunctionKey|7FunctionKey|2FunctionKey|8FunctionKey|3FunctionKey|9FunctionKey|4FunctionKey|5FunctionKey|FunctionKey|0FunctionKey|6FunctionKey)|o(?:nt(?:Mo(?:noSpaceTrait|dernSerifsClass)|BoldTrait|S(?:ymbolicClass|criptsClass|labSerifsClass|ansSerifClass)|C(?:o(?:ndensedTrait|llectionApplicationOnlyMask)|larendonSerifsClass)|TransitionalSerifsClass|I(?:ntegerAdvancementsRenderingMode|talicTrait)|O(?:ldStyleSerifsClass|rnamentalsClass)|DefaultRenderingMode|U(?:nknownClass|IOptimizedTrait)|Panel(?:S(?:hadowEffectModeMask|t(?:andardModesMask|rikethroughEffectModeMask)|izeModeMask)|CollectionModeMask|TextColorEffectModeMask|DocumentColorEffectModeMask|UnderlineEffectModeMask|FaceModeMask|All(?:ModesMask|EffectsModeMask))|ExpandedTrait|VerticalTrait|F(?:amilyClassMask|reeformSerifsClass)|Antialiased(?:RenderingMode|IntegerAdvancementsRenderingMode))|cusRing(?:Below|Type(?:None|Default|Exterior)|Only|Above)|urByteGlyphPacking|rm(?:attingError(?:M(?:inimum|aximum))?|FeedCharacter))|8FunctionKey|unction(?:ExpressionType|KeyMask)|3(?:1FunctionKey|2FunctionKey|3FunctionKey|4FunctionKey|5FunctionKey|FunctionKey|0FunctionKey)|9FunctionKey|4FunctionKey|P(?:RevertButton|S(?:ize(?:Title|Field)|etButton)|CurrentField|Preview(?:Button|Field))|l(?:oat(?:ingPointSamplesBitmapFormat|Type)|agsChanged(?:Mask)?)|axButton|5FunctionKey|6FunctionKey)|W(?:heelModeColorPanel|indow(?:s(?:NTOperatingSystem|CP125(?:1StringEncoding|2StringEncoding|3StringEncoding|4StringEncoding|0StringEncoding)|95(?:InterfaceStyle|OperatingSystem))|M(?:iniaturizeButton|ovedEventType)|Below|CloseButton|ToolbarButton|ZoomButton|Out|DocumentIconButton|ExposedEventType|Above)|orkspaceLaunch(?:NewInstance|InhibitingBackgroundOnly|Default|PreferringClassic|WithoutA(?:ctivation|ddingToRecents)|A(?:sync|nd(?:Hide(?:Others)?|Print)|llowingClassicStartup))|eek(?:day(?:CalendarUnit|OrdinalCalendarUnit)|CalendarUnit)|a(?:ntsBidiLevels|rningAlertStyle)|r(?:itingDirection(?:RightToLeft|Natural|LeftToRight)|apCalendarComponents))|L(?:i(?:stModeMatrix|ne(?:Moves(?:Right|Down|Up|Left)|B(?:order|reakBy(?:C(?:harWrapping|lipping)|Truncating(?:Middle|Head|Tail)|WordWrapping))|S(?:eparatorCharacter|weep(?:Right|Down|Up|Left))|ToBezierPathElement|DoesntMove|arSlider)|teralSearch|kePredicateOperatorType|ghterFontAction|braryDirectory)|ocalDomainMask|e(?:ssThan(?:Comparison|OrEqualTo(?:Comparison|PredicateOperatorType)|PredicateOperatorType)|ft(?:Mouse(?:D(?:own(?:Mask)?|ragged(?:Mask)?)|Up(?:Mask)?)|T(?:ext(?:Movement|Alignment)|ab(?:sBezelBorder|StopType))|ArrowFunctionKey))|a(?:yout(?:RightToLeft|NotDone|CantFit|OutOfGlyphs|Done|LeftToRight)|ndscapeOrientation)|ABColorSpaceModel)|A(?:sc(?:iiWithDoubleByteEUCGlyphPacking|endingPageOrder)|n(?:y(?:Type|PredicateModifier|EventMask)|choredSearch|imation(?:Blocking|Nonblocking(?:Threaded)?|E(?:ffect(?:DisappearingItemDefault|Poof)|ase(?:In(?:Out)?|Out))|Linear)|dPredicateType)|t(?:Bottom|tachmentCharacter|omicWrite|Top)|SCIIStringEncoding|d(?:obe(?:GB1CharacterCollection|CNS1CharacterCollection|Japan(?:1CharacterCollection|2CharacterCollection)|Korea1CharacterCollection)|dTraitFontAction|minApplicationDirectory)|uto(?:saveOperation|Pagination)|pp(?:lication(?:SupportDirectory|D(?:irectory|e(?:fined(?:Mask)?|legateReply(?:Success|Cancel|Failure)|activatedEventType))|ActivatedEventType)|KitDefined(?:Mask)?)|l(?:ternateKeyMask|pha(?:ShiftKeyMask|NonpremultipliedBitmapFormat|FirstBitmapFormat)|ert(?:SecondButtonReturn|ThirdButtonReturn|OtherReturn|DefaultReturn|ErrorReturn|FirstButtonReturn|AlternateReturn)|l(?:ScrollerParts|DomainsMask|PredicateModifier|LibrariesDirectory|ApplicationsDirectory))|rgument(?:sWrongScriptError|EvaluationScriptError)|bove(?:Bottom|Top)|WTEventType)))(?:\\b)"},{token:"support.function.C99.c",regex:s.cFunctions},{token:n.getKeywords(),regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"punctuation.section.scope.begin.objc",regex:"\\[",next:"bracketed_content"},{token:"meta.function.objc",regex:"^(?:-|\\+)\\s*"}],constant_NSString:[{token:"constant.character.escape.objc",regex:e},{token:"invalid.illegal.unknown-escape.objc",regex:"\\\\."},{token:"string",regex:'[^"\\\\]+'},{token:"punctuation.definition.string.end",regex:'"',next:"start"}],protocol_list:[{token:"punctuation.section.scope.end.objc",regex:">",next:"start"},{token:"support.other.protocol.objc",regex:"\bNS(?:GlyphStorage|M(?:utableCopying|enuItem)|C(?:hangeSpelling|o(?:ding|pying|lorPicking(?:Custom|Default)))|T(?:oolbarItemValidations|ext(?:Input|AttachmentCell))|I(?:nputServ(?:iceProvider|erMouseTracker)|gnoreMisspelledWords)|Obj(?:CTypeSerializationCallBack|ect)|D(?:ecimalNumberBehaviors|raggingInfo)|U(?:serInterfaceValidations|RL(?:HandleClient|DownloadDelegate|ProtocolClient|AuthenticationChallengeSender))|Validated(?:ToobarItem|UserInterfaceItem)|Locking)\b"}],selectors:[{token:"support.function.any-method.name-of-parameter.objc",regex:"\\b(?:[a-zA-Z_:][\\w]*)+"},{token:"punctuation",regex:"\\)",next:"start"}],bracketed_content:[{token:"punctuation.section.scope.end.objc",regex:"]",next:"start"},{token:["support.function.any-method.objc"],regex:"(?:predicateWithFormat:| NSPredicate predicateWithFormat:)",next:"start"},{token:"support.function.any-method.objc",regex:"\\w+(?::|(?=]))",next:"start"}],bracketed_strings:[{token:"punctuation.section.scope.end.objc",regex:"]",next:"start"},{token:"keyword.operator.logical.predicate.cocoa",regex:"\\b(?:AND|OR|NOT|IN)\\b"},{token:["invalid.illegal.unknown-method.objc","punctuation.separator.arguments.objc"],regex:"\\b(\\w+)(:)"},{regex:"\\b(?:ALL|ANY|SOME|NONE)\\b",token:"constant.language.predicate.cocoa"},{regex:"\\b(?:NULL|NIL|SELF|TRUE|YES|FALSE|NO|FIRST|LAST|SIZE)\\b",token:"constant.language.predicate.cocoa"},{regex:"\\b(?:MATCHES|CONTAINS|BEGINSWITH|ENDSWITH|BETWEEN)\\b",token:"keyword.operator.comparison.predicate.cocoa"},{regex:"\\bC(?:ASEINSENSITIVE|I)\\b",token:"keyword.other.modifier.predicate.cocoa"},{regex:"\\b(?:ANYKEY|SUBQUERY|CAST|TRUEPREDICATE|FALSEPREDICATE)\\b",token:"keyword.other.predicate.cocoa"},{regex:e,token:"constant.character.escape.objc"},{regex:"\\\\.",token:"invalid.illegal.unknown-escape.objc"},{token:"string",regex:'[^"\\\\]'},{token:"punctuation.definition.string.end.objc",regex:'"',next:"predicates"}],comment:[{token:"comment",regex:".*?\\*\\/",next:"start"},{token:"comment",regex:".+"}],methods:[{token:"meta.function.objc",regex:"(?=\\{|#)|;",next:"start"}]};for(var u in r)this.$rules[u]?this.$rules[u].push&&this.$rules[u].push.apply(this.$rules[u],r[u]):this.$rules[u]=r[u];this.$rules.bracketed_content=this.$rules.bracketed_content.concat(this.$rules.start,t),this.embedRules(i,"doc-",[i.getEndRule("start")])};r.inherits(u,o),t.ObjectiveCHighlightRules=u}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/objectivec",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/objectivec_highlight_rules","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./objectivec_highlight_rules").ObjectiveCHighlightRules,o=e("./folding/cstyle").FoldMode,u=function(){this.HighlightRules=s,this.foldingRules=new o,this.$behaviour=this.$defaultBehaviour};r.inherits(u,i),function(){this.lineCommentStart="//",this.blockComment={start:"/*",end:"*/"},this.$id="ace/mode/objectivec"}.call(u.prototype),t.Mode=u}) diff --git a/public/themes/pterodactyl/vendor/ace/mode-perl.js b/public/themes/pterodactyl/vendor/ace/mode-perl.js new file mode 100644 index 0000000..726f03c --- /dev/null +++ b/public/themes/pterodactyl/vendor/ace/mode-perl.js @@ -0,0 +1 @@ +define("ace/mode/perl_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e="base|constant|continue|else|elsif|for|foreach|format|goto|if|last|local|my|next|no|package|parent|redo|require|scalar|sub|unless|until|while|use|vars",t="ARGV|ENV|INC|SIG",n="getprotobynumber|getprotobyname|getservbyname|gethostbyaddr|gethostbyname|getservbyport|getnetbyaddr|getnetbyname|getsockname|getpeername|setpriority|getprotoent|setprotoent|getpriority|endprotoent|getservent|setservent|endservent|sethostent|socketpair|getsockopt|gethostent|endhostent|setsockopt|setnetent|quotemeta|localtime|prototype|getnetent|endnetent|rewinddir|wantarray|getpwuid|closedir|getlogin|readlink|endgrent|getgrgid|getgrnam|shmwrite|shutdown|readline|endpwent|setgrent|readpipe|formline|truncate|dbmclose|syswrite|setpwent|getpwnam|getgrent|getpwent|ucfirst|sysread|setpgrp|shmread|sysseek|sysopen|telldir|defined|opendir|connect|lcfirst|getppid|binmode|syscall|sprintf|getpgrp|readdir|seekdir|waitpid|reverse|unshift|symlink|dbmopen|semget|msgrcv|rename|listen|chroot|msgsnd|shmctl|accept|unpack|exists|fileno|shmget|system|unlink|printf|gmtime|msgctl|semctl|values|rindex|substr|splice|length|msgget|select|socket|return|caller|delete|alarm|ioctl|index|undef|lstat|times|srand|chown|fcntl|close|write|umask|rmdir|study|sleep|chomp|untie|print|utime|mkdir|atan2|split|crypt|flock|chmod|BEGIN|bless|chdir|semop|shift|reset|link|stat|chop|grep|fork|dump|join|open|tell|pipe|exit|glob|warn|each|bind|sort|pack|eval|push|keys|getc|kill|seek|sqrt|send|wait|rand|tied|read|time|exec|recv|eof|chr|int|ord|exp|pos|pop|sin|log|abs|oct|hex|tie|cos|vec|END|ref|map|die|uc|lc|do",r=this.createKeywordMapper({keyword:e,"constant.language":t,"support.function":n},"identifier");this.$rules={start:[{token:"comment.doc",regex:"^=(?:begin|item)\\b",next:"block_comment"},{token:"string.regexp",regex:"[/](?:(?:\\[(?:\\\\]|[^\\]])+\\])|(?:\\\\/|[^\\]/]))*[/]\\w*\\s*(?=[).,;]|$)"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:'["].*\\\\$',next:"qqstring"},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"string",regex:"['].*\\\\$",next:"qstring"},{token:"constant.numeric",regex:"0x[0-9a-fA-F]+\\b"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:r,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"%#|\\$#|\\.\\.\\.|\\|\\|=|>>=|<<=|<=>|&&=|=>|!~|\\^=|&=|\\|=|\\.=|x=|%=|\\/=|\\*=|\\-=|\\+=|=~|\\*\\*|\\-\\-|\\.\\.|\\|\\||&&|\\+\\+|\\->|!=|==|>=|<=|>>|<<|,|=|\\?\\:|\\^|\\||x|%|\\/|\\*|<|&|\\\\|~|!|>|\\.|\\-|\\+|\\-C|\\-b|\\-S|\\-u|\\-t|\\-p|\\-l|\\-d|\\-f|\\-g|\\-s|\\-z|\\-k|\\-e|\\-O|\\-T|\\-B|\\-M|\\-A|\\-X|\\-W|\\-c|\\-R|\\-o|\\-x|\\-w|\\-r|\\b(?:and|cmp|eq|ge|gt|le|lt|ne|not|or|xor)"},{token:"comment",regex:"#.*$"},{token:"lparen",regex:"[[({]"},{token:"rparen",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],qqstring:[{token:"string",regex:'(?:(?:\\\\.)|(?:[^"\\\\]))*?"',next:"start"},{token:"string",regex:".+"}],qstring:[{token:"string",regex:"(?:(?:\\\\.)|(?:[^'\\\\]))*?'",next:"start"},{token:"string",regex:".+"}],block_comment:[{token:"comment.doc",regex:"^=cut\\b",next:"start"},{defaultToken:"comment.doc"}]}};r.inherits(s,i),t.PerlHighlightRules=s}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/perl",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/perl_highlight_rules","ace/mode/matching_brace_outdent","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./perl_highlight_rules").PerlHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("./folding/cstyle").FoldMode,a=function(){this.HighlightRules=s,this.$outdent=new o,this.foldingRules=new u({start:"^=(begin|item)\\b",end:"^=(cut)\\b"}),this.$behaviour=this.$defaultBehaviour};r.inherits(a,i),function(){this.lineCommentStart="#",this.blockComment=[{start:"=begin",end:"=cut",lineStartOnly:!0},{start:"=item",end:"=cut",lineStartOnly:!0}],this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().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},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.$id="ace/mode/perl"}.call(a.prototype),t.Mode=a}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-php.js b/public/themes/pterodactyl/vendor/ace/mode-php.js new file mode 100644 index 0000000..0fc768d --- /dev/null +++ b/public/themes/pterodactyl/vendor/ace/mode-php.js @@ -0,0 +1 @@ +define("ace/mode/doc_comment_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},s.getTagRule(),{defaultToken:"comment.doc",caseInsensitive:!0}]}};r.inherits(s,i),s.getTagRule=function(e){return{token:"comment.doc.tag.storage.type",regex:"\\b(?:TODO|FIXME|XXX|HACK)\\b"}},s.getStartRule=function(e){return{token:"comment.doc",regex:"\\/\\*(?=\\*)",next:e}},s.getEndRule=function(e){return{token:"comment.doc",regex:"\\*\\/",next:e}},t.DocCommentHighlightRules=s}),define("ace/mode/css_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text_highlight_rules").TextHighlightRules,o=t.supportType="align-content|align-items|align-self|all|animation|animation-delay|animation-direction|animation-duration|animation-fill-mode|animation-iteration-count|animation-name|animation-play-state|animation-timing-function|backface-visibility|background|background-attachment|background-blend-mode|background-clip|background-color|background-image|background-origin|background-position|background-repeat|background-size|border|border-bottom|border-bottom-color|border-bottom-left-radius|border-bottom-right-radius|border-bottom-style|border-bottom-width|border-collapse|border-color|border-image|border-image-outset|border-image-repeat|border-image-slice|border-image-source|border-image-width|border-left|border-left-color|border-left-style|border-left-width|border-radius|border-right|border-right-color|border-right-style|border-right-width|border-spacing|border-style|border-top|border-top-color|border-top-left-radius|border-top-right-radius|border-top-style|border-top-width|border-width|bottom|box-shadow|box-sizing|caption-side|clear|clip|color|column-count|column-fill|column-gap|column-rule|column-rule-color|column-rule-style|column-rule-width|column-span|column-width|columns|content|counter-increment|counter-reset|cursor|direction|display|empty-cells|filter|flex|flex-basis|flex-direction|flex-flow|flex-grow|flex-shrink|flex-wrap|float|font|font-family|font-size|font-size-adjust|font-stretch|font-style|font-variant|font-weight|hanging-punctuation|height|justify-content|left|letter-spacing|line-height|list-style|list-style-image|list-style-position|list-style-type|margin|margin-bottom|margin-left|margin-right|margin-top|max-height|max-width|min-height|min-width|nav-down|nav-index|nav-left|nav-right|nav-up|opacity|order|outline|outline-color|outline-offset|outline-style|outline-width|overflow|overflow-x|overflow-y|padding|padding-bottom|padding-left|padding-right|padding-top|page-break-after|page-break-before|page-break-inside|perspective|perspective-origin|position|quotes|resize|right|tab-size|table-layout|text-align|text-align-last|text-decoration|text-decoration-color|text-decoration-line|text-decoration-style|text-indent|text-justify|text-overflow|text-shadow|text-transform|top|transform|transform-origin|transform-style|transition|transition-delay|transition-duration|transition-property|transition-timing-function|unicode-bidi|vertical-align|visibility|white-space|width|word-break|word-spacing|word-wrap|z-index",u=t.supportFunction="rgb|rgba|url|attr|counter|counters",a=t.supportConstant="absolute|after-edge|after|all-scroll|all|alphabetic|always|antialiased|armenian|auto|avoid-column|avoid-page|avoid|balance|baseline|before-edge|before|below|bidi-override|block-line-height|block|bold|bolder|border-box|both|bottom|box|break-all|break-word|capitalize|caps-height|caption|center|central|char|circle|cjk-ideographic|clone|close-quote|col-resize|collapse|column|consider-shifts|contain|content-box|cover|crosshair|cubic-bezier|dashed|decimal-leading-zero|decimal|default|disabled|disc|disregard-shifts|distribute-all-lines|distribute-letter|distribute-space|distribute|dotted|double|e-resize|ease-in|ease-in-out|ease-out|ease|ellipsis|end|exclude-ruby|fill|fixed|georgian|glyphs|grid-height|groove|hand|hanging|hebrew|help|hidden|hiragana-iroha|hiragana|horizontal|icon|ideograph-alpha|ideograph-numeric|ideograph-parenthesis|ideograph-space|ideographic|inactive|include-ruby|inherit|initial|inline-block|inline-box|inline-line-height|inline-table|inline|inset|inside|inter-ideograph|inter-word|invert|italic|justify|katakana-iroha|katakana|keep-all|last|left|lighter|line-edge|line-through|line|linear|list-item|local|loose|lower-alpha|lower-greek|lower-latin|lower-roman|lowercase|lr-tb|ltr|mathematical|max-height|max-size|medium|menu|message-box|middle|move|n-resize|ne-resize|newspaper|no-change|no-close-quote|no-drop|no-open-quote|no-repeat|none|normal|not-allowed|nowrap|nw-resize|oblique|open-quote|outset|outside|overline|padding-box|page|pointer|pre-line|pre-wrap|pre|preserve-3d|progress|relative|repeat-x|repeat-y|repeat|replaced|reset-size|ridge|right|round|row-resize|rtl|s-resize|scroll|se-resize|separate|slice|small-caps|small-caption|solid|space|square|start|static|status-bar|step-end|step-start|steps|stretch|strict|sub|super|sw-resize|table-caption|table-cell|table-column-group|table-column|table-footer-group|table-header-group|table-row-group|table-row|table|tb-rl|text-after-edge|text-before-edge|text-bottom|text-size|text-top|text|thick|thin|transparent|underline|upper-alpha|upper-latin|upper-roman|uppercase|use-script|vertical-ideographic|vertical-text|visible|w-resize|wait|whitespace|z-index|zero",f=t.supportConstantColor="aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow",l=t.supportConstantFonts="arial|century|comic|courier|cursive|fantasy|garamond|georgia|helvetica|impact|lucida|symbol|system|tahoma|times|trebuchet|utopia|verdana|webdings|sans-serif|serif|monospace",c=t.numRe="\\-?(?:(?:[0-9]+)|(?:[0-9]*\\.[0-9]+))",h=t.pseudoElements="(\\:+)\\b(after|before|first-letter|first-line|moz-selection|selection)\\b",p=t.pseudoClasses="(:)\\b(active|checked|disabled|empty|enabled|first-child|first-of-type|focus|hover|indeterminate|invalid|last-child|last-of-type|link|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|only-child|only-of-type|required|root|target|valid|visited)\\b",d=function(){var e=this.createKeywordMapper({"support.function":u,"support.constant":a,"support.type":o,"support.constant.color":f,"support.constant.fonts":l},"text",!0);this.$rules={start:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"@.*?{",push:"media"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],media:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"\\}",next:"pop"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],comment:[{token:"comment",regex:"\\*\\/",next:"pop"},{defaultToken:"comment"}],ruleset:[{token:"paren.rparen",regex:"\\}",next:"pop"},{token:"comment",regex:"\\/\\*",push:"comment"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:["constant.numeric","keyword"],regex:"("+c+")(ch|cm|deg|em|ex|fr|gd|grad|Hz|in|kHz|mm|ms|pc|pt|px|rad|rem|s|turn|vh|vm|vw|%)"},{token:"constant.numeric",regex:c},{token:"constant.numeric",regex:"#[a-f0-9]{6}"},{token:"constant.numeric",regex:"#[a-f0-9]{3}"},{token:["punctuation","entity.other.attribute-name.pseudo-element.css"],regex:h},{token:["punctuation","entity.other.attribute-name.pseudo-class.css"],regex:p},{token:["support.function","string","support.function"],regex:"(url\\()(.*)(\\))"},{token:e,regex:"\\-?[a-zA-Z_][a-zA-Z0-9_\\-]*"},{caseInsensitive:!0}]},this.normalizeRules()};r.inherits(d,s),t.CssHighlightRules=d}),define("ace/mode/javascript_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";function a(){var e=o.replace("\\d","\\d\\-"),t={onMatch:function(e,t,n){var r=e.charAt(1)=="/"?2:1;if(r==1)t!=this.nextState?n.unshift(this.next,this.nextState,0):n.unshift(this.next),n[2]++;else if(r==2&&t==this.nextState){n[1]--;if(!n[1]||n[1]<0)n.shift(),n.shift()}return[{type:"meta.tag.punctuation."+(r==1?"":"end-")+"tag-open.xml",value:e.slice(0,r)},{type:"meta.tag.tag-name.xml",value:e.substr(r)}]},regex:"",onMatch:function(e,t,n){return t==n[0]&&n.shift(),e.length==2&&(n[0]==this.nextState&&n[1]--,(!n[1]||n[1]<0)&&n.splice(0,2)),this.next=n[0]||"start",[{type:this.token,value:e}]},nextState:"jsx"},n,f("jsxAttributes"),{token:"entity.other.attribute-name.xml",regex:e},{token:"keyword.operator.attribute-equals.xml",regex:"="},{token:"text.tag-whitespace.xml",regex:"\\s+"},{token:"string.attribute-value.xml",regex:"'",stateName:"jsx_attr_q",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',stateName:"jsx_attr_qq",push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},t],this.$rules.reference=[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}]}function f(e){return[{token:"comment",regex:/\/\*/,next:[i.getTagRule(),{token:"comment",regex:"\\*\\/",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]},{token:"comment",regex:"\\/\\/",next:[i.getTagRule(),{token:"comment",regex:"$|^",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]}]}var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./text_highlight_rules").TextHighlightRules,o="[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*",u=function(e){var t=this.createKeywordMapper({"variable.language":"Array|Boolean|Date|Function|Iterator|Number|Object|RegExp|String|Proxy|Namespace|QName|XML|XMLList|ArrayBuffer|Float32Array|Float64Array|Int16Array|Int32Array|Int8Array|Uint16Array|Uint32Array|Uint8Array|Uint8ClampedArray|Error|EvalError|InternalError|RangeError|ReferenceError|StopIteration|SyntaxError|TypeError|URIError|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|eval|isFinite|isNaN|parseFloat|parseInt|JSON|Math|this|arguments|prototype|window|document",keyword:"const|yield|import|get|set|async|await|break|case|catch|continue|default|delete|do|else|finally|for|function|if|in|of|instanceof|new|return|switch|throw|try|typeof|let|var|while|with|debugger|__parent__|__count__|escape|unescape|with|__proto__|class|enum|extends|super|export|implements|private|public|interface|package|protected|static","storage.type":"const|let|var|function","constant.language":"null|Infinity|NaN|undefined","support.function":"alert","constant.language.boolean":"true|false"},"identifier"),n="case|do|else|finally|in|instanceof|return|throw|try|typeof|yield|void",r="\\\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|u{[0-9a-fA-F]{1,6}}|[0-2][0-7]{0,2}|3[0-7][0-7]?|[4-7][0-7]?|.)";this.$rules={no_regex:[i.getStartRule("doc-start"),f("no_regex"),{token:"string",regex:"'(?=.)",next:"qstring"},{token:"string",regex:'"(?=.)',next:"qqstring"},{token:"constant.numeric",regex:/0(?:[xX][0-9a-fA-F]+|[bB][01]+)\b/},{token:"constant.numeric",regex:/[+-]?\d[\d_]*(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/},{token:["storage.type","punctuation.operator","support.function","punctuation.operator","entity.name.function","text","keyword.operator"],regex:"("+o+")(\\.)(prototype)(\\.)("+o+")(\\s*)(=)",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s+)(\\w+)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","text","entity.name.function","text","paren.lparen"],regex:"(function)(\\s+)("+o+")(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","punctuation.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["text","text","storage.type","text","paren.lparen"],regex:"(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:"keyword",regex:"(?:"+n+")\\b",next:"start"},{token:["support.constant"],regex:/that\b/},{token:["storage.type","punctuation.operator","support.function.firebug"],regex:/(console)(\.)(warn|info|log|error|time|trace|timeEnd|assert)\b/},{token:t,regex:o},{token:"punctuation.operator",regex:/[.](?![.])/,next:"property"},{token:"keyword.operator",regex:/--|\+\+|\.{3}|===|==|=|!=|!==|<+=?|>+=?|!|&&|\|\||\?:|[!$%&*+\-~\/^]=?/,next:"start"},{token:"punctuation.operator",regex:/[?:,;.]/,next:"start"},{token:"paren.lparen",regex:/[\[({]/,next:"start"},{token:"paren.rparen",regex:/[\])}]/},{token:"comment",regex:/^#!.*$/}],property:[{token:"text",regex:"\\s+"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(?:(\\s+)(\\w+))?(\\s*)(\\()",next:"function_arguments"},{token:"punctuation.operator",regex:/[.](?![.])/},{token:"support.function",regex:/(s(?:h(?:ift|ow(?:Mod(?:elessDialog|alDialog)|Help))|croll(?:X|By(?:Pages|Lines)?|Y|To)?|t(?:op|rike)|i(?:n|zeToContent|debar|gnText)|ort|u(?:p|b(?:str(?:ing)?)?)|pli(?:ce|t)|e(?:nd|t(?:Re(?:sizable|questHeader)|M(?:i(?:nutes|lliseconds)|onth)|Seconds|Ho(?:tKeys|urs)|Year|Cursor|Time(?:out)?|Interval|ZOptions|Date|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Date|FullYear)|FullYear|Active)|arch)|qrt|lice|avePreferences|mall)|h(?:ome|andleEvent)|navigate|c(?:har(?:CodeAt|At)|o(?:s|n(?:cat|textual|firm)|mpile)|eil|lear(?:Timeout|Interval)?|a(?:ptureEvents|ll)|reate(?:StyleSheet|Popup|EventObject))|t(?:o(?:GMTString|S(?:tring|ource)|U(?:TCString|pperCase)|Lo(?:caleString|werCase))|est|a(?:n|int(?:Enabled)?))|i(?:s(?:NaN|Finite)|ndexOf|talics)|d(?:isableExternalCapture|ump|etachEvent)|u(?:n(?:shift|taint|escape|watch)|pdateCommands)|j(?:oin|avaEnabled)|p(?:o(?:p|w)|ush|lugins.refresh|a(?:ddings|rse(?:Int|Float)?)|r(?:int|ompt|eference))|e(?:scape|nableExternalCapture|val|lementFromPoint|x(?:p|ec(?:Script|Command)?))|valueOf|UTC|queryCommand(?:State|Indeterm|Enabled|Value)|f(?:i(?:nd|le(?:ModifiedDate|Size|CreatedDate|UpdatedDate)|xed)|o(?:nt(?:size|color)|rward)|loor|romCharCode)|watch|l(?:ink|o(?:ad|g)|astIndexOf)|a(?:sin|nchor|cos|t(?:tachEvent|ob|an(?:2)?)|pply|lert|b(?:s|ort))|r(?:ou(?:nd|teEvents)|e(?:size(?:By|To)|calc|turnValue|place|verse|l(?:oad|ease(?:Capture|Events)))|andom)|g(?:o|et(?:ResponseHeader|M(?:i(?:nutes|lliseconds)|onth)|Se(?:conds|lection)|Hours|Year|Time(?:zoneOffset)?|Da(?:y|te)|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Da(?:y|te)|FullYear)|FullYear|A(?:ttention|llResponseHeaders)))|m(?:in|ove(?:B(?:y|elow)|To(?:Absolute)?|Above)|ergeAttributes|a(?:tch|rgins|x))|b(?:toa|ig|o(?:ld|rderWidths)|link|ack))\b(?=\()/},{token:"support.function.dom",regex:/(s(?:ub(?:stringData|mit)|plitText|e(?:t(?:NamedItem|Attribute(?:Node)?)|lect))|has(?:ChildNodes|Feature)|namedItem|c(?:l(?:ick|o(?:se|neNode))|reate(?:C(?:omment|DATASection|aption)|T(?:Head|extNode|Foot)|DocumentFragment|ProcessingInstruction|E(?:ntityReference|lement)|Attribute))|tabIndex|i(?:nsert(?:Row|Before|Cell|Data)|tem)|open|delete(?:Row|C(?:ell|aption)|T(?:Head|Foot)|Data)|focus|write(?:ln)?|a(?:dd|ppend(?:Child|Data))|re(?:set|place(?:Child|Data)|move(?:NamedItem|Child|Attribute(?:Node)?)?)|get(?:NamedItem|Element(?:sBy(?:Name|TagName|ClassName)|ById)|Attribute(?:Node)?)|blur)\b(?=\()/},{token:"support.constant",regex:/(s(?:ystemLanguage|cr(?:ipts|ollbars|een(?:X|Y|Top|Left))|t(?:yle(?:Sheets)?|atus(?:Text|bar)?)|ibling(?:Below|Above)|ource|uffixes|e(?:curity(?:Policy)?|l(?:ection|f)))|h(?:istory|ost(?:name)?|as(?:h|Focus))|y|X(?:MLDocument|SLDocument)|n(?:ext|ame(?:space(?:s|URI)|Prop))|M(?:IN_VALUE|AX_VALUE)|c(?:haracterSet|o(?:n(?:structor|trollers)|okieEnabled|lorDepth|mp(?:onents|lete))|urrent|puClass|l(?:i(?:p(?:boardData)?|entInformation)|osed|asses)|alle(?:e|r)|rypto)|t(?:o(?:olbar|p)|ext(?:Transform|Indent|Decoration|Align)|ags)|SQRT(?:1_2|2)|i(?:n(?:ner(?:Height|Width)|put)|ds|gnoreCase)|zIndex|o(?:scpu|n(?:readystatechange|Line)|uter(?:Height|Width)|p(?:sProfile|ener)|ffscreenBuffering)|NEGATIVE_INFINITY|d(?:i(?:splay|alog(?:Height|Top|Width|Left|Arguments)|rectories)|e(?:scription|fault(?:Status|Ch(?:ecked|arset)|View)))|u(?:ser(?:Profile|Language|Agent)|n(?:iqueID|defined)|pdateInterval)|_content|p(?:ixelDepth|ort|ersonalbar|kcs11|l(?:ugins|atform)|a(?:thname|dding(?:Right|Bottom|Top|Left)|rent(?:Window|Layer)?|ge(?:X(?:Offset)?|Y(?:Offset)?))|r(?:o(?:to(?:col|type)|duct(?:Sub)?|mpter)|e(?:vious|fix)))|e(?:n(?:coding|abledPlugin)|x(?:ternal|pando)|mbeds)|v(?:isibility|endor(?:Sub)?|Linkcolor)|URLUnencoded|P(?:I|OSITIVE_INFINITY)|f(?:ilename|o(?:nt(?:Size|Family|Weight)|rmName)|rame(?:s|Element)|gColor)|E|whiteSpace|l(?:i(?:stStyleType|n(?:eHeight|kColor))|o(?:ca(?:tion(?:bar)?|lName)|wsrc)|e(?:ngth|ft(?:Context)?)|a(?:st(?:M(?:odified|atch)|Index|Paren)|yer(?:s|X)|nguage))|a(?:pp(?:MinorVersion|Name|Co(?:deName|re)|Version)|vail(?:Height|Top|Width|Left)|ll|r(?:ity|guments)|Linkcolor|bove)|r(?:ight(?:Context)?|e(?:sponse(?:XML|Text)|adyState))|global|x|m(?:imeTypes|ultiline|enubar|argin(?:Right|Bottom|Top|Left))|L(?:N(?:10|2)|OG(?:10E|2E))|b(?:o(?:ttom|rder(?:Width|RightWidth|BottomWidth|Style|Color|TopWidth|LeftWidth))|ufferDepth|elow|ackground(?:Color|Image)))\b/},{token:"identifier",regex:o},{regex:"",token:"empty",next:"no_regex"}],start:[i.getStartRule("doc-start"),f("start"),{token:"string.regexp",regex:"\\/",next:"regex"},{token:"text",regex:"\\s+|^$",next:"start"},{token:"empty",regex:"",next:"no_regex"}],regex:[{token:"regexp.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"string.regexp",regex:"/[sxngimy]*",next:"no_regex"},{token:"invalid",regex:/\{\d+\b,?\d*\}[+*]|[+*$^?][+*]|[$^][?]|\?{3,}/},{token:"constant.language.escape",regex:/\(\?[:=!]|\)|\{\d+\b,?\d*\}|[+*]\?|[()$^+*?.]/},{token:"constant.language.delimiter",regex:/\|/},{token:"constant.language.escape",regex:/\[\^?/,next:"regex_character_class"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp"}],regex_character_class:[{token:"regexp.charclass.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"constant.language.escape",regex:"]",next:"regex"},{token:"constant.language.escape",regex:"-"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp.charachterclass"}],function_arguments:[{token:"variable.parameter",regex:o},{token:"punctuation.operator",regex:"[, ]+"},{token:"punctuation.operator",regex:"$"},{token:"empty",regex:"",next:"no_regex"}],qqstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qqstring"},{token:"string",regex:'"|$',next:"no_regex"},{defaultToken:"string"}],qstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qstring"},{token:"string",regex:"'|$",next:"no_regex"},{defaultToken:"string"}]};if(!e||!e.noES6)this.$rules.no_regex.unshift({regex:"[{}]",onMatch:function(e,t,n){this.next=e=="{"?this.nextState:"";if(e=="{"&&n.length)n.unshift("start",t);else if(e=="}"&&n.length){n.shift(),this.next=n.shift();if(this.next.indexOf("string")!=-1||this.next.indexOf("jsx")!=-1)return"paren.quasi.end"}return e=="{"?"paren.lparen":"paren.rparen"},nextState:"start"},{token:"string.quasi.start",regex:/`/,push:[{token:"constant.language.escape",regex:r},{token:"paren.quasi.start",regex:/\${/,push:"start"},{token:"string.quasi.end",regex:/`/,next:"pop"},{defaultToken:"string.quasi"}]}),(!e||e.jsx!=0)&&a.call(this);this.embedRules(i,"doc-",[i.getEndRule("no_regex")]),this.normalizeRules()};r.inherits(u,s),t.JavaScriptHighlightRules=u}),define("ace/mode/xml_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(e){var t="[_:a-zA-Z\u00c0-\uffff][-_:.a-zA-Z0-9\u00c0-\uffff]*";this.$rules={start:[{token:"string.cdata.xml",regex:"<\\!\\[CDATA\\[",next:"cdata"},{token:["punctuation.xml-decl.xml","keyword.xml-decl.xml"],regex:"(<\\?)(xml)(?=[\\s])",next:"xml_decl",caseInsensitive:!0},{token:["punctuation.instruction.xml","keyword.instruction.xml"],regex:"(<\\?)("+t+")",next:"processing_instruction"},{token:"comment.xml",regex:"<\\!--",next:"comment"},{token:["xml-pe.doctype.xml","xml-pe.doctype.xml"],regex:"(<\\!)(DOCTYPE)(?=[\\s])",next:"doctype",caseInsensitive:!0},{include:"tag"},{token:"text.end-tag-open.xml",regex:"",next:"start"}],processing_instruction:[{token:"punctuation.instruction.xml",regex:"\\?>",next:"start"},{defaultToken:"instruction.xml"}],doctype:[{include:"whitespace"},{include:"string"},{token:"xml-pe.doctype.xml",regex:">",next:"start"},{token:"xml-pe.xml",regex:"[-_a-zA-Z0-9:]+"},{token:"punctuation.int-subset",regex:"\\[",push:"int_subset"}],int_subset:[{token:"text.xml",regex:"\\s+"},{token:"punctuation.int-subset.xml",regex:"]",next:"pop"},{token:["punctuation.markup-decl.xml","keyword.markup-decl.xml"],regex:"(<\\!)("+t+")",push:[{token:"text",regex:"\\s+"},{token:"punctuation.markup-decl.xml",regex:">",next:"pop"},{include:"string"}]}],cdata:[{token:"string.cdata.xml",regex:"\\]\\]>",next:"start"},{token:"text.xml",regex:"\\s+"},{token:"text.xml",regex:"(?:[^\\]]|\\](?!\\]>))+"}],comment:[{token:"comment.xml",regex:"-->",next:"start"},{defaultToken:"comment.xml"}],reference:[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}],attr_reference:[{token:"constant.language.escape.reference.attribute-value.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}],tag:[{token:["meta.tag.punctuation.tag-open.xml","meta.tag.punctuation.end-tag-open.xml","meta.tag.tag-name.xml"],regex:"(?:(<)|(",next:"start"}]}],tag_whitespace:[{token:"text.tag-whitespace.xml",regex:"\\s+"}],whitespace:[{token:"text.whitespace.xml",regex:"\\s+"}],string:[{token:"string.xml",regex:"'",push:[{token:"string.xml",regex:"'",next:"pop"},{defaultToken:"string.xml"}]},{token:"string.xml",regex:'"',push:[{token:"string.xml",regex:'"',next:"pop"},{defaultToken:"string.xml"}]}],attributes:[{token:"entity.other.attribute-name.xml",regex:"(?:"+t+":)?"+t+""},{token:"keyword.operator.attribute-equals.xml",regex:"="},{include:"tag_whitespace"},{include:"attribute_value"}],attribute_value:[{token:"string.attribute-value.xml",regex:"'",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"attr_reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"attr_reference"},{defaultToken:"string.attribute-value.xml"}]}]},this.constructor===s&&this.normalizeRules()};(function(){this.embedTagRules=function(e,t,n){this.$rules.tag.unshift({token:["meta.tag.punctuation.tag-open.xml","meta.tag."+n+".tag-name.xml"],regex:"(<)("+n+"(?=\\s|>|$))",next:[{include:"attributes"},{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",next:t+"start"}]}),this.$rules[n+"-end"]=[{include:"attributes"},{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",next:"start",onMatch:function(e,t,n){return n.splice(0),this.token}}],this.embedRules(e,t,[{token:["meta.tag.punctuation.end-tag-open.xml","meta.tag."+n+".tag-name.xml"],regex:"(|$))",next:n+"-end"},{token:"string.cdata.xml",regex:"<\\!\\[CDATA\\["},{token:"string.cdata.xml",regex:"\\]\\]>"}])}}).call(i.prototype),r.inherits(s,i),t.XmlHighlightRules=s}),define("ace/mode/html_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/css_highlight_rules","ace/mode/javascript_highlight_rules","ace/mode/xml_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./css_highlight_rules").CssHighlightRules,o=e("./javascript_highlight_rules").JavaScriptHighlightRules,u=e("./xml_highlight_rules").XmlHighlightRules,a=i.createMap({a:"anchor",button:"form",form:"form",img:"image",input:"form",label:"form",option:"form",script:"script",select:"form",textarea:"form",style:"style",table:"table",tbody:"table",td:"table",tfoot:"table",th:"table",tr:"table"}),f=function(){u.call(this),this.addRules({attributes:[{include:"tag_whitespace"},{token:"entity.other.attribute-name.xml",regex:"[-_a-zA-Z0-9:.]+"},{token:"keyword.operator.attribute-equals.xml",regex:"=",push:[{include:"tag_whitespace"},{token:"string.unquoted.attribute-value.html",regex:"[^<>='\"`\\s]+",next:"pop"},{token:"empty",regex:"",next:"pop"}]},{include:"attribute_value"}],tag:[{token:function(e,t){var n=a[t];return["meta.tag.punctuation."+(e=="<"?"":"end-")+"tag-open.xml","meta.tag"+(n?"."+n:"")+".tag-name.xml"]},regex:"(",next:"start"}]}),this.embedTagRules(s,"css-","style"),this.embedTagRules((new o({jsx:!1})).getRules(),"js-","script"),this.constructor===f&&this.normalizeRules()};r.inherits(f,u),t.HtmlHighlightRules=f}),define("ace/mode/php_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules","ace/mode/html_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./doc_comment_highlight_rules").DocCommentHighlightRules,o=e("./text_highlight_rules").TextHighlightRules,u=e("./html_highlight_rules").HtmlHighlightRules,a=function(){var e=s,t=i.arrayToMap("abs|acos|acosh|addcslashes|addslashes|aggregate|aggregate_info|aggregate_methods|aggregate_methods_by_list|aggregate_methods_by_regexp|aggregate_properties|aggregate_properties_by_list|aggregate_properties_by_regexp|aggregation_info|amqpconnection|amqpexchange|amqpqueue|apache_child_terminate|apache_get_modules|apache_get_version|apache_getenv|apache_lookup_uri|apache_note|apache_request_headers|apache_reset_timeout|apache_response_headers|apache_setenv|apc_add|apc_bin_dump|apc_bin_dumpfile|apc_bin_load|apc_bin_loadfile|apc_cache_info|apc_cas|apc_clear_cache|apc_compile_file|apc_dec|apc_define_constants|apc_delete|apc_delete_file|apc_exists|apc_fetch|apc_inc|apc_load_constants|apc_sma_info|apc_store|apciterator|apd_breakpoint|apd_callstack|apd_clunk|apd_continue|apd_croak|apd_dump_function_table|apd_dump_persistent_resources|apd_dump_regular_resources|apd_echo|apd_get_active_symbols|apd_set_pprof_trace|apd_set_session|apd_set_session_trace|apd_set_session_trace_socket|appenditerator|array|array_change_key_case|array_chunk|array_combine|array_count_values|array_diff|array_diff_assoc|array_diff_key|array_diff_uassoc|array_diff_ukey|array_fill|array_fill_keys|array_filter|array_flip|array_intersect|array_intersect_assoc|array_intersect_key|array_intersect_uassoc|array_intersect_ukey|array_key_exists|array_keys|array_map|array_merge|array_merge_recursive|array_multisort|array_pad|array_pop|array_product|array_push|array_rand|array_reduce|array_replace|array_replace_recursive|array_reverse|array_search|array_shift|array_slice|array_splice|array_sum|array_udiff|array_udiff_assoc|array_udiff_uassoc|array_uintersect|array_uintersect_assoc|array_uintersect_uassoc|array_unique|array_unshift|array_values|array_walk|array_walk_recursive|arrayaccess|arrayiterator|arrayobject|arsort|asin|asinh|asort|assert|assert_options|atan|atan2|atanh|audioproperties|badfunctioncallexception|badmethodcallexception|base64_decode|base64_encode|base_convert|basename|bbcode_add_element|bbcode_add_smiley|bbcode_create|bbcode_destroy|bbcode_parse|bbcode_set_arg_parser|bbcode_set_flags|bcadd|bccomp|bcdiv|bcmod|bcmul|bcompiler_load|bcompiler_load_exe|bcompiler_parse_class|bcompiler_read|bcompiler_write_class|bcompiler_write_constant|bcompiler_write_exe_footer|bcompiler_write_file|bcompiler_write_footer|bcompiler_write_function|bcompiler_write_functions_from_file|bcompiler_write_header|bcompiler_write_included_filename|bcpow|bcpowmod|bcscale|bcsqrt|bcsub|bin2hex|bind_textdomain_codeset|bindec|bindtextdomain|bson_decode|bson_encode|bumpValue|bzclose|bzcompress|bzdecompress|bzerrno|bzerror|bzerrstr|bzflush|bzopen|bzread|bzwrite|cachingiterator|cairo|cairo_create|cairo_font_face_get_type|cairo_font_face_status|cairo_font_options_create|cairo_font_options_equal|cairo_font_options_get_antialias|cairo_font_options_get_hint_metrics|cairo_font_options_get_hint_style|cairo_font_options_get_subpixel_order|cairo_font_options_hash|cairo_font_options_merge|cairo_font_options_set_antialias|cairo_font_options_set_hint_metrics|cairo_font_options_set_hint_style|cairo_font_options_set_subpixel_order|cairo_font_options_status|cairo_format_stride_for_width|cairo_image_surface_create|cairo_image_surface_create_for_data|cairo_image_surface_create_from_png|cairo_image_surface_get_data|cairo_image_surface_get_format|cairo_image_surface_get_height|cairo_image_surface_get_stride|cairo_image_surface_get_width|cairo_matrix_create_scale|cairo_matrix_create_translate|cairo_matrix_invert|cairo_matrix_multiply|cairo_matrix_rotate|cairo_matrix_transform_distance|cairo_matrix_transform_point|cairo_matrix_translate|cairo_pattern_add_color_stop_rgb|cairo_pattern_add_color_stop_rgba|cairo_pattern_create_for_surface|cairo_pattern_create_linear|cairo_pattern_create_radial|cairo_pattern_create_rgb|cairo_pattern_create_rgba|cairo_pattern_get_color_stop_count|cairo_pattern_get_color_stop_rgba|cairo_pattern_get_extend|cairo_pattern_get_filter|cairo_pattern_get_linear_points|cairo_pattern_get_matrix|cairo_pattern_get_radial_circles|cairo_pattern_get_rgba|cairo_pattern_get_surface|cairo_pattern_get_type|cairo_pattern_set_extend|cairo_pattern_set_filter|cairo_pattern_set_matrix|cairo_pattern_status|cairo_pdf_surface_create|cairo_pdf_surface_set_size|cairo_ps_get_levels|cairo_ps_level_to_string|cairo_ps_surface_create|cairo_ps_surface_dsc_begin_page_setup|cairo_ps_surface_dsc_begin_setup|cairo_ps_surface_dsc_comment|cairo_ps_surface_get_eps|cairo_ps_surface_restrict_to_level|cairo_ps_surface_set_eps|cairo_ps_surface_set_size|cairo_scaled_font_create|cairo_scaled_font_extents|cairo_scaled_font_get_ctm|cairo_scaled_font_get_font_face|cairo_scaled_font_get_font_matrix|cairo_scaled_font_get_font_options|cairo_scaled_font_get_scale_matrix|cairo_scaled_font_get_type|cairo_scaled_font_glyph_extents|cairo_scaled_font_status|cairo_scaled_font_text_extents|cairo_surface_copy_page|cairo_surface_create_similar|cairo_surface_finish|cairo_surface_flush|cairo_surface_get_content|cairo_surface_get_device_offset|cairo_surface_get_font_options|cairo_surface_get_type|cairo_surface_mark_dirty|cairo_surface_mark_dirty_rectangle|cairo_surface_set_device_offset|cairo_surface_set_fallback_resolution|cairo_surface_show_page|cairo_surface_status|cairo_surface_write_to_png|cairo_svg_surface_create|cairo_svg_surface_restrict_to_version|cairo_svg_version_to_string|cairoantialias|cairocontent|cairocontext|cairoexception|cairoextend|cairofillrule|cairofilter|cairofontface|cairofontoptions|cairofontslant|cairofonttype|cairofontweight|cairoformat|cairogradientpattern|cairohintmetrics|cairohintstyle|cairoimagesurface|cairolineargradient|cairolinecap|cairolinejoin|cairomatrix|cairooperator|cairopath|cairopattern|cairopatterntype|cairopdfsurface|cairopslevel|cairopssurface|cairoradialgradient|cairoscaledfont|cairosolidpattern|cairostatus|cairosubpixelorder|cairosurface|cairosurfacepattern|cairosurfacetype|cairosvgsurface|cairosvgversion|cairotoyfontface|cal_days_in_month|cal_from_jd|cal_info|cal_to_jd|calcul_hmac|calculhmac|call_user_func|call_user_func_array|call_user_method|call_user_method_array|callbackfilteriterator|ceil|chdb|chdb_create|chdir|checkdate|checkdnsrr|chgrp|chmod|chop|chown|chr|chroot|chunk_split|class_alias|class_exists|class_implements|class_parents|class_uses|classkit_import|classkit_method_add|classkit_method_copy|classkit_method_redefine|classkit_method_remove|classkit_method_rename|clearstatcache|clone|closedir|closelog|collator|com|com_addref|com_create_guid|com_event_sink|com_get|com_get_active_object|com_invoke|com_isenum|com_load|com_load_typelib|com_message_pump|com_print_typeinfo|com_propget|com_propput|com_propset|com_release|com_set|compact|connection_aborted|connection_status|connection_timeout|constant|construct|construct|construct|convert_cyr_string|convert_uudecode|convert_uuencode|copy|cos|cosh|count|count_chars|countable|counter_bump|counter_bump_value|counter_create|counter_get|counter_get_meta|counter_get_named|counter_get_value|counter_reset|counter_reset_value|crack_check|crack_closedict|crack_getlastmessage|crack_opendict|crc32|create_function|crypt|ctype_alnum|ctype_alpha|ctype_cntrl|ctype_digit|ctype_graph|ctype_lower|ctype_print|ctype_punct|ctype_space|ctype_upper|ctype_xdigit|cubrid_affected_rows|cubrid_bind|cubrid_client_encoding|cubrid_close|cubrid_close_prepare|cubrid_close_request|cubrid_col_get|cubrid_col_size|cubrid_column_names|cubrid_column_types|cubrid_commit|cubrid_connect|cubrid_connect_with_url|cubrid_current_oid|cubrid_data_seek|cubrid_db_name|cubrid_disconnect|cubrid_drop|cubrid_errno|cubrid_error|cubrid_error_code|cubrid_error_code_facility|cubrid_error_msg|cubrid_execute|cubrid_fetch|cubrid_fetch_array|cubrid_fetch_assoc|cubrid_fetch_field|cubrid_fetch_lengths|cubrid_fetch_object|cubrid_fetch_row|cubrid_field_flags|cubrid_field_len|cubrid_field_name|cubrid_field_seek|cubrid_field_table|cubrid_field_type|cubrid_free_result|cubrid_get|cubrid_get_autocommit|cubrid_get_charset|cubrid_get_class_name|cubrid_get_client_info|cubrid_get_db_parameter|cubrid_get_server_info|cubrid_insert_id|cubrid_is_instance|cubrid_list_dbs|cubrid_load_from_glo|cubrid_lob_close|cubrid_lob_export|cubrid_lob_get|cubrid_lob_send|cubrid_lob_size|cubrid_lock_read|cubrid_lock_write|cubrid_move_cursor|cubrid_new_glo|cubrid_next_result|cubrid_num_cols|cubrid_num_fields|cubrid_num_rows|cubrid_ping|cubrid_prepare|cubrid_put|cubrid_query|cubrid_real_escape_string|cubrid_result|cubrid_rollback|cubrid_save_to_glo|cubrid_schema|cubrid_send_glo|cubrid_seq_drop|cubrid_seq_insert|cubrid_seq_put|cubrid_set_add|cubrid_set_autocommit|cubrid_set_db_parameter|cubrid_set_drop|cubrid_unbuffered_query|cubrid_version|curl_close|curl_copy_handle|curl_errno|curl_error|curl_exec|curl_getinfo|curl_init|curl_multi_add_handle|curl_multi_close|curl_multi_exec|curl_multi_getcontent|curl_multi_info_read|curl_multi_init|curl_multi_remove_handle|curl_multi_select|curl_setopt|curl_setopt_array|curl_version|current|cyrus_authenticate|cyrus_bind|cyrus_close|cyrus_connect|cyrus_query|cyrus_unbind|date|date_add|date_create|date_create_from_format|date_date_set|date_default_timezone_get|date_default_timezone_set|date_diff|date_format|date_get_last_errors|date_interval_create_from_date_string|date_interval_format|date_isodate_set|date_modify|date_offset_get|date_parse|date_parse_from_format|date_sub|date_sun_info|date_sunrise|date_sunset|date_time_set|date_timestamp_get|date_timestamp_set|date_timezone_get|date_timezone_set|dateinterval|dateperiod|datetime|datetimezone|db2_autocommit|db2_bind_param|db2_client_info|db2_close|db2_column_privileges|db2_columns|db2_commit|db2_conn_error|db2_conn_errormsg|db2_connect|db2_cursor_type|db2_escape_string|db2_exec|db2_execute|db2_fetch_array|db2_fetch_assoc|db2_fetch_both|db2_fetch_object|db2_fetch_row|db2_field_display_size|db2_field_name|db2_field_num|db2_field_precision|db2_field_scale|db2_field_type|db2_field_width|db2_foreign_keys|db2_free_result|db2_free_stmt|db2_get_option|db2_last_insert_id|db2_lob_read|db2_next_result|db2_num_fields|db2_num_rows|db2_pclose|db2_pconnect|db2_prepare|db2_primary_keys|db2_procedure_columns|db2_procedures|db2_result|db2_rollback|db2_server_info|db2_set_option|db2_special_columns|db2_statistics|db2_stmt_error|db2_stmt_errormsg|db2_table_privileges|db2_tables|dba_close|dba_delete|dba_exists|dba_fetch|dba_firstkey|dba_handlers|dba_insert|dba_key_split|dba_list|dba_nextkey|dba_open|dba_optimize|dba_popen|dba_replace|dba_sync|dbase_add_record|dbase_close|dbase_create|dbase_delete_record|dbase_get_header_info|dbase_get_record|dbase_get_record_with_names|dbase_numfields|dbase_numrecords|dbase_open|dbase_pack|dbase_replace_record|dbplus_add|dbplus_aql|dbplus_chdir|dbplus_close|dbplus_curr|dbplus_errcode|dbplus_errno|dbplus_find|dbplus_first|dbplus_flush|dbplus_freealllocks|dbplus_freelock|dbplus_freerlocks|dbplus_getlock|dbplus_getunique|dbplus_info|dbplus_last|dbplus_lockrel|dbplus_next|dbplus_open|dbplus_prev|dbplus_rchperm|dbplus_rcreate|dbplus_rcrtexact|dbplus_rcrtlike|dbplus_resolve|dbplus_restorepos|dbplus_rkeys|dbplus_ropen|dbplus_rquery|dbplus_rrename|dbplus_rsecindex|dbplus_runlink|dbplus_rzap|dbplus_savepos|dbplus_setindex|dbplus_setindexbynumber|dbplus_sql|dbplus_tcl|dbplus_tremove|dbplus_undo|dbplus_undoprepare|dbplus_unlockrel|dbplus_unselect|dbplus_update|dbplus_xlockrel|dbplus_xunlockrel|dbx_close|dbx_compare|dbx_connect|dbx_error|dbx_escape_string|dbx_fetch_row|dbx_query|dbx_sort|dcgettext|dcngettext|deaggregate|debug_backtrace|debug_print_backtrace|debug_zval_dump|decbin|dechex|decoct|define|define_syslog_variables|defined|deg2rad|delete|dgettext|die|dio_close|dio_fcntl|dio_open|dio_read|dio_seek|dio_stat|dio_tcsetattr|dio_truncate|dio_write|dir|directoryiterator|dirname|disk_free_space|disk_total_space|diskfreespace|dl|dngettext|dns_check_record|dns_get_mx|dns_get_record|dom_import_simplexml|domainexception|domattr|domattribute_name|domattribute_set_value|domattribute_specified|domattribute_value|domcharacterdata|domcomment|domdocument|domdocument_add_root|domdocument_create_attribute|domdocument_create_cdata_section|domdocument_create_comment|domdocument_create_element|domdocument_create_element_ns|domdocument_create_entity_reference|domdocument_create_processing_instruction|domdocument_create_text_node|domdocument_doctype|domdocument_document_element|domdocument_dump_file|domdocument_dump_mem|domdocument_get_element_by_id|domdocument_get_elements_by_tagname|domdocument_html_dump_mem|domdocument_xinclude|domdocumentfragment|domdocumenttype|domdocumenttype_entities|domdocumenttype_internal_subset|domdocumenttype_name|domdocumenttype_notations|domdocumenttype_public_id|domdocumenttype_system_id|domelement|domelement_get_attribute|domelement_get_attribute_node|domelement_get_elements_by_tagname|domelement_has_attribute|domelement_remove_attribute|domelement_set_attribute|domelement_set_attribute_node|domelement_tagname|domentity|domentityreference|domexception|domimplementation|domnamednodemap|domnode|domnode_add_namespace|domnode_append_child|domnode_append_sibling|domnode_attributes|domnode_child_nodes|domnode_clone_node|domnode_dump_node|domnode_first_child|domnode_get_content|domnode_has_attributes|domnode_has_child_nodes|domnode_insert_before|domnode_is_blank_node|domnode_last_child|domnode_next_sibling|domnode_node_name|domnode_node_type|domnode_node_value|domnode_owner_document|domnode_parent_node|domnode_prefix|domnode_previous_sibling|domnode_remove_child|domnode_replace_child|domnode_replace_node|domnode_set_content|domnode_set_name|domnode_set_namespace|domnode_unlink_node|domnodelist|domnotation|domprocessinginstruction|domprocessinginstruction_data|domprocessinginstruction_target|domtext|domxml_new_doc|domxml_open_file|domxml_open_mem|domxml_version|domxml_xmltree|domxml_xslt_stylesheet|domxml_xslt_stylesheet_doc|domxml_xslt_stylesheet_file|domxml_xslt_version|domxpath|domxsltstylesheet_process|domxsltstylesheet_result_dump_file|domxsltstylesheet_result_dump_mem|dotnet|dotnet_load|doubleval|each|easter_date|easter_days|echo|empty|emptyiterator|enchant_broker_describe|enchant_broker_dict_exists|enchant_broker_free|enchant_broker_free_dict|enchant_broker_get_error|enchant_broker_init|enchant_broker_list_dicts|enchant_broker_request_dict|enchant_broker_request_pwl_dict|enchant_broker_set_ordering|enchant_dict_add_to_personal|enchant_dict_add_to_session|enchant_dict_check|enchant_dict_describe|enchant_dict_get_error|enchant_dict_is_in_session|enchant_dict_quick_check|enchant_dict_store_replacement|enchant_dict_suggest|end|ereg|ereg_replace|eregi|eregi_replace|error_get_last|error_log|error_reporting|errorexception|escapeshellarg|escapeshellcmd|eval|event_add|event_base_free|event_base_loop|event_base_loopbreak|event_base_loopexit|event_base_new|event_base_priority_init|event_base_set|event_buffer_base_set|event_buffer_disable|event_buffer_enable|event_buffer_fd_set|event_buffer_free|event_buffer_new|event_buffer_priority_set|event_buffer_read|event_buffer_set_callback|event_buffer_timeout_set|event_buffer_watermark_set|event_buffer_write|event_del|event_free|event_new|event_set|exception|exec|exif_imagetype|exif_read_data|exif_tagname|exif_thumbnail|exit|exp|expect_expectl|expect_popen|explode|expm1|export|export|extension_loaded|extract|ezmlm_hash|fam_cancel_monitor|fam_close|fam_monitor_collection|fam_monitor_directory|fam_monitor_file|fam_next_event|fam_open|fam_pending|fam_resume_monitor|fam_suspend_monitor|fbsql_affected_rows|fbsql_autocommit|fbsql_blob_size|fbsql_change_user|fbsql_clob_size|fbsql_close|fbsql_commit|fbsql_connect|fbsql_create_blob|fbsql_create_clob|fbsql_create_db|fbsql_data_seek|fbsql_database|fbsql_database_password|fbsql_db_query|fbsql_db_status|fbsql_drop_db|fbsql_errno|fbsql_error|fbsql_fetch_array|fbsql_fetch_assoc|fbsql_fetch_field|fbsql_fetch_lengths|fbsql_fetch_object|fbsql_fetch_row|fbsql_field_flags|fbsql_field_len|fbsql_field_name|fbsql_field_seek|fbsql_field_table|fbsql_field_type|fbsql_free_result|fbsql_get_autostart_info|fbsql_hostname|fbsql_insert_id|fbsql_list_dbs|fbsql_list_fields|fbsql_list_tables|fbsql_next_result|fbsql_num_fields|fbsql_num_rows|fbsql_password|fbsql_pconnect|fbsql_query|fbsql_read_blob|fbsql_read_clob|fbsql_result|fbsql_rollback|fbsql_rows_fetched|fbsql_select_db|fbsql_set_characterset|fbsql_set_lob_mode|fbsql_set_password|fbsql_set_transaction|fbsql_start_db|fbsql_stop_db|fbsql_table_name|fbsql_tablename|fbsql_username|fbsql_warnings|fclose|fdf_add_doc_javascript|fdf_add_template|fdf_close|fdf_create|fdf_enum_values|fdf_errno|fdf_error|fdf_get_ap|fdf_get_attachment|fdf_get_encoding|fdf_get_file|fdf_get_flags|fdf_get_opt|fdf_get_status|fdf_get_value|fdf_get_version|fdf_header|fdf_next_field_name|fdf_open|fdf_open_string|fdf_remove_item|fdf_save|fdf_save_string|fdf_set_ap|fdf_set_encoding|fdf_set_file|fdf_set_flags|fdf_set_javascript_action|fdf_set_on_import_javascript|fdf_set_opt|fdf_set_status|fdf_set_submit_form_action|fdf_set_target_frame|fdf_set_value|fdf_set_version|feof|fflush|fgetc|fgetcsv|fgets|fgetss|file|file_exists|file_get_contents|file_put_contents|fileatime|filectime|filegroup|fileinode|filemtime|fileowner|fileperms|filepro|filepro_fieldcount|filepro_fieldname|filepro_fieldtype|filepro_fieldwidth|filepro_retrieve|filepro_rowcount|filesize|filesystemiterator|filetype|filter_has_var|filter_id|filter_input|filter_input_array|filter_list|filter_var|filter_var_array|filteriterator|finfo_buffer|finfo_close|finfo_file|finfo_open|finfo_set_flags|floatval|flock|floor|flush|fmod|fnmatch|fopen|forward_static_call|forward_static_call_array|fpassthru|fprintf|fputcsv|fputs|fread|frenchtojd|fribidi_log2vis|fscanf|fseek|fsockopen|fstat|ftell|ftok|ftp_alloc|ftp_cdup|ftp_chdir|ftp_chmod|ftp_close|ftp_connect|ftp_delete|ftp_exec|ftp_fget|ftp_fput|ftp_get|ftp_get_option|ftp_login|ftp_mdtm|ftp_mkdir|ftp_nb_continue|ftp_nb_fget|ftp_nb_fput|ftp_nb_get|ftp_nb_put|ftp_nlist|ftp_pasv|ftp_put|ftp_pwd|ftp_quit|ftp_raw|ftp_rawlist|ftp_rename|ftp_rmdir|ftp_set_option|ftp_site|ftp_size|ftp_ssl_connect|ftp_systype|ftruncate|func_get_arg|func_get_args|func_num_args|function_exists|fwrite|gc_collect_cycles|gc_disable|gc_enable|gc_enabled|gd_info|gearmanclient|gearmanjob|gearmantask|gearmanworker|geoip_continent_code_by_name|geoip_country_code3_by_name|geoip_country_code_by_name|geoip_country_name_by_name|geoip_database_info|geoip_db_avail|geoip_db_filename|geoip_db_get_all_info|geoip_id_by_name|geoip_isp_by_name|geoip_org_by_name|geoip_record_by_name|geoip_region_by_name|geoip_region_name_by_code|geoip_time_zone_by_country_and_region|getMeta|getNamed|getValue|get_browser|get_called_class|get_cfg_var|get_class|get_class_methods|get_class_vars|get_current_user|get_declared_classes|get_declared_interfaces|get_declared_traits|get_defined_constants|get_defined_functions|get_defined_vars|get_extension_funcs|get_headers|get_html_translation_table|get_include_path|get_included_files|get_loaded_extensions|get_magic_quotes_gpc|get_magic_quotes_runtime|get_meta_tags|get_object_vars|get_parent_class|get_required_files|get_resource_type|getallheaders|getconstant|getconstants|getconstructor|getcwd|getdate|getdefaultproperties|getdoccomment|getendline|getenv|getextension|getextensionname|getfilename|gethostbyaddr|gethostbyname|gethostbynamel|gethostname|getimagesize|getinterfacenames|getinterfaces|getlastmod|getmethod|getmethods|getmodifiers|getmxrr|getmygid|getmyinode|getmypid|getmyuid|getname|getnamespacename|getopt|getparentclass|getproperties|getproperty|getprotobyname|getprotobynumber|getrandmax|getrusage|getservbyname|getservbyport|getshortname|getstartline|getstaticproperties|getstaticpropertyvalue|gettext|gettimeofday|gettype|glob|globiterator|gmagick|gmagickdraw|gmagickpixel|gmdate|gmmktime|gmp_abs|gmp_add|gmp_and|gmp_clrbit|gmp_cmp|gmp_com|gmp_div|gmp_div_q|gmp_div_qr|gmp_div_r|gmp_divexact|gmp_fact|gmp_gcd|gmp_gcdext|gmp_hamdist|gmp_init|gmp_intval|gmp_invert|gmp_jacobi|gmp_legendre|gmp_mod|gmp_mul|gmp_neg|gmp_nextprime|gmp_or|gmp_perfect_square|gmp_popcount|gmp_pow|gmp_powm|gmp_prob_prime|gmp_random|gmp_scan0|gmp_scan1|gmp_setbit|gmp_sign|gmp_sqrt|gmp_sqrtrem|gmp_strval|gmp_sub|gmp_testbit|gmp_xor|gmstrftime|gnupg_adddecryptkey|gnupg_addencryptkey|gnupg_addsignkey|gnupg_cleardecryptkeys|gnupg_clearencryptkeys|gnupg_clearsignkeys|gnupg_decrypt|gnupg_decryptverify|gnupg_encrypt|gnupg_encryptsign|gnupg_export|gnupg_geterror|gnupg_getprotocol|gnupg_import|gnupg_init|gnupg_keyinfo|gnupg_setarmor|gnupg_seterrormode|gnupg_setsignmode|gnupg_sign|gnupg_verify|gopher_parsedir|grapheme_extract|grapheme_stripos|grapheme_stristr|grapheme_strlen|grapheme_strpos|grapheme_strripos|grapheme_strrpos|grapheme_strstr|grapheme_substr|gregoriantojd|gupnp_context_get_host_ip|gupnp_context_get_port|gupnp_context_get_subscription_timeout|gupnp_context_host_path|gupnp_context_new|gupnp_context_set_subscription_timeout|gupnp_context_timeout_add|gupnp_context_unhost_path|gupnp_control_point_browse_start|gupnp_control_point_browse_stop|gupnp_control_point_callback_set|gupnp_control_point_new|gupnp_device_action_callback_set|gupnp_device_info_get|gupnp_device_info_get_service|gupnp_root_device_get_available|gupnp_root_device_get_relative_location|gupnp_root_device_new|gupnp_root_device_set_available|gupnp_root_device_start|gupnp_root_device_stop|gupnp_service_action_get|gupnp_service_action_return|gupnp_service_action_return_error|gupnp_service_action_set|gupnp_service_freeze_notify|gupnp_service_info_get|gupnp_service_info_get_introspection|gupnp_service_introspection_get_state_variable|gupnp_service_notify|gupnp_service_proxy_action_get|gupnp_service_proxy_action_set|gupnp_service_proxy_add_notify|gupnp_service_proxy_callback_set|gupnp_service_proxy_get_subscribed|gupnp_service_proxy_remove_notify|gupnp_service_proxy_set_subscribed|gupnp_service_thaw_notify|gzclose|gzcompress|gzdecode|gzdeflate|gzencode|gzeof|gzfile|gzgetc|gzgets|gzgetss|gzinflate|gzopen|gzpassthru|gzputs|gzread|gzrewind|gzseek|gztell|gzuncompress|gzwrite|halt_compiler|haruannotation|haruannotation_setborderstyle|haruannotation_sethighlightmode|haruannotation_seticon|haruannotation_setopened|harudestination|harudestination_setfit|harudestination_setfitb|harudestination_setfitbh|harudestination_setfitbv|harudestination_setfith|harudestination_setfitr|harudestination_setfitv|harudestination_setxyz|harudoc|harudoc_addpage|harudoc_addpagelabel|harudoc_construct|harudoc_createoutline|harudoc_getcurrentencoder|harudoc_getcurrentpage|harudoc_getencoder|harudoc_getfont|harudoc_getinfoattr|harudoc_getpagelayout|harudoc_getpagemode|harudoc_getstreamsize|harudoc_insertpage|harudoc_loadjpeg|harudoc_loadpng|harudoc_loadraw|harudoc_loadttc|harudoc_loadttf|harudoc_loadtype1|harudoc_output|harudoc_readfromstream|harudoc_reseterror|harudoc_resetstream|harudoc_save|harudoc_savetostream|harudoc_setcompressionmode|harudoc_setcurrentencoder|harudoc_setencryptionmode|harudoc_setinfoattr|harudoc_setinfodateattr|harudoc_setopenaction|harudoc_setpagelayout|harudoc_setpagemode|harudoc_setpagesconfiguration|harudoc_setpassword|harudoc_setpermission|harudoc_usecnsencodings|harudoc_usecnsfonts|harudoc_usecntencodings|harudoc_usecntfonts|harudoc_usejpencodings|harudoc_usejpfonts|harudoc_usekrencodings|harudoc_usekrfonts|haruencoder|haruencoder_getbytetype|haruencoder_gettype|haruencoder_getunicode|haruencoder_getwritingmode|haruexception|harufont|harufont_getascent|harufont_getcapheight|harufont_getdescent|harufont_getencodingname|harufont_getfontname|harufont_gettextwidth|harufont_getunicodewidth|harufont_getxheight|harufont_measuretext|haruimage|haruimage_getbitspercomponent|haruimage_getcolorspace|haruimage_getheight|haruimage_getsize|haruimage_getwidth|haruimage_setcolormask|haruimage_setmaskimage|haruoutline|haruoutline_setdestination|haruoutline_setopened|harupage|harupage_arc|harupage_begintext|harupage_circle|harupage_closepath|harupage_concat|harupage_createdestination|harupage_createlinkannotation|harupage_createtextannotation|harupage_createurlannotation|harupage_curveto|harupage_curveto2|harupage_curveto3|harupage_drawimage|harupage_ellipse|harupage_endpath|harupage_endtext|harupage_eofill|harupage_eofillstroke|harupage_fill|harupage_fillstroke|harupage_getcharspace|harupage_getcmykfill|harupage_getcmykstroke|harupage_getcurrentfont|harupage_getcurrentfontsize|harupage_getcurrentpos|harupage_getcurrenttextpos|harupage_getdash|harupage_getfillingcolorspace|harupage_getflatness|harupage_getgmode|harupage_getgrayfill|harupage_getgraystroke|harupage_getheight|harupage_gethorizontalscaling|harupage_getlinecap|harupage_getlinejoin|harupage_getlinewidth|harupage_getmiterlimit|harupage_getrgbfill|harupage_getrgbstroke|harupage_getstrokingcolorspace|harupage_gettextleading|harupage_gettextmatrix|harupage_gettextrenderingmode|harupage_gettextrise|harupage_gettextwidth|harupage_gettransmatrix|harupage_getwidth|harupage_getwordspace|harupage_lineto|harupage_measuretext|harupage_movetextpos|harupage_moveto|harupage_movetonextline|harupage_rectangle|harupage_setcharspace|harupage_setcmykfill|harupage_setcmykstroke|harupage_setdash|harupage_setflatness|harupage_setfontandsize|harupage_setgrayfill|harupage_setgraystroke|harupage_setheight|harupage_sethorizontalscaling|harupage_setlinecap|harupage_setlinejoin|harupage_setlinewidth|harupage_setmiterlimit|harupage_setrgbfill|harupage_setrgbstroke|harupage_setrotate|harupage_setsize|harupage_setslideshow|harupage_settextleading|harupage_settextmatrix|harupage_settextrenderingmode|harupage_settextrise|harupage_setwidth|harupage_setwordspace|harupage_showtext|harupage_showtextnextline|harupage_stroke|harupage_textout|harupage_textrect|hasconstant|hash|hash_algos|hash_copy|hash_file|hash_final|hash_hmac|hash_hmac_file|hash_init|hash_update|hash_update_file|hash_update_stream|hasmethod|hasproperty|header|header_register_callback|header_remove|headers_list|headers_sent|hebrev|hebrevc|hex2bin|hexdec|highlight_file|highlight_string|html_entity_decode|htmlentities|htmlspecialchars|htmlspecialchars_decode|http_build_cookie|http_build_query|http_build_str|http_build_url|http_cache_etag|http_cache_last_modified|http_chunked_decode|http_date|http_deflate|http_get|http_get_request_body|http_get_request_body_stream|http_get_request_headers|http_head|http_inflate|http_match_etag|http_match_modified|http_match_request_header|http_negotiate_charset|http_negotiate_content_type|http_negotiate_language|http_parse_cookie|http_parse_headers|http_parse_message|http_parse_params|http_persistent_handles_clean|http_persistent_handles_count|http_persistent_handles_ident|http_post_data|http_post_fields|http_put_data|http_put_file|http_put_stream|http_redirect|http_request|http_request_body_encode|http_request_method_exists|http_request_method_name|http_request_method_register|http_request_method_unregister|http_response_code|http_send_content_disposition|http_send_content_type|http_send_data|http_send_file|http_send_last_modified|http_send_status|http_send_stream|http_support|http_throttle|httpdeflatestream|httpdeflatestream_construct|httpdeflatestream_factory|httpdeflatestream_finish|httpdeflatestream_flush|httpdeflatestream_update|httpinflatestream|httpinflatestream_construct|httpinflatestream_factory|httpinflatestream_finish|httpinflatestream_flush|httpinflatestream_update|httpmessage|httpmessage_addheaders|httpmessage_construct|httpmessage_detach|httpmessage_factory|httpmessage_fromenv|httpmessage_fromstring|httpmessage_getbody|httpmessage_getheader|httpmessage_getheaders|httpmessage_gethttpversion|httpmessage_getparentmessage|httpmessage_getrequestmethod|httpmessage_getrequesturl|httpmessage_getresponsecode|httpmessage_getresponsestatus|httpmessage_gettype|httpmessage_guesscontenttype|httpmessage_prepend|httpmessage_reverse|httpmessage_send|httpmessage_setbody|httpmessage_setheaders|httpmessage_sethttpversion|httpmessage_setrequestmethod|httpmessage_setrequesturl|httpmessage_setresponsecode|httpmessage_setresponsestatus|httpmessage_settype|httpmessage_tomessagetypeobject|httpmessage_tostring|httpquerystring|httpquerystring_construct|httpquerystring_get|httpquerystring_mod|httpquerystring_set|httpquerystring_singleton|httpquerystring_toarray|httpquerystring_tostring|httpquerystring_xlate|httprequest|httprequest_addcookies|httprequest_addheaders|httprequest_addpostfields|httprequest_addpostfile|httprequest_addputdata|httprequest_addquerydata|httprequest_addrawpostdata|httprequest_addssloptions|httprequest_clearhistory|httprequest_construct|httprequest_enablecookies|httprequest_getcontenttype|httprequest_getcookies|httprequest_getheaders|httprequest_gethistory|httprequest_getmethod|httprequest_getoptions|httprequest_getpostfields|httprequest_getpostfiles|httprequest_getputdata|httprequest_getputfile|httprequest_getquerydata|httprequest_getrawpostdata|httprequest_getrawrequestmessage|httprequest_getrawresponsemessage|httprequest_getrequestmessage|httprequest_getresponsebody|httprequest_getresponsecode|httprequest_getresponsecookies|httprequest_getresponsedata|httprequest_getresponseheader|httprequest_getresponseinfo|httprequest_getresponsemessage|httprequest_getresponsestatus|httprequest_getssloptions|httprequest_geturl|httprequest_resetcookies|httprequest_send|httprequest_setcontenttype|httprequest_setcookies|httprequest_setheaders|httprequest_setmethod|httprequest_setoptions|httprequest_setpostfields|httprequest_setpostfiles|httprequest_setputdata|httprequest_setputfile|httprequest_setquerydata|httprequest_setrawpostdata|httprequest_setssloptions|httprequest_seturl|httprequestpool|httprequestpool_attach|httprequestpool_construct|httprequestpool_destruct|httprequestpool_detach|httprequestpool_getattachedrequests|httprequestpool_getfinishedrequests|httprequestpool_reset|httprequestpool_send|httprequestpool_socketperform|httprequestpool_socketselect|httpresponse|httpresponse_capture|httpresponse_getbuffersize|httpresponse_getcache|httpresponse_getcachecontrol|httpresponse_getcontentdisposition|httpresponse_getcontenttype|httpresponse_getdata|httpresponse_getetag|httpresponse_getfile|httpresponse_getgzip|httpresponse_getheader|httpresponse_getlastmodified|httpresponse_getrequestbody|httpresponse_getrequestbodystream|httpresponse_getrequestheaders|httpresponse_getstream|httpresponse_getthrottledelay|httpresponse_guesscontenttype|httpresponse_redirect|httpresponse_send|httpresponse_setbuffersize|httpresponse_setcache|httpresponse_setcachecontrol|httpresponse_setcontentdisposition|httpresponse_setcontenttype|httpresponse_setdata|httpresponse_setetag|httpresponse_setfile|httpresponse_setgzip|httpresponse_setheader|httpresponse_setlastmodified|httpresponse_setstream|httpresponse_setthrottledelay|httpresponse_status|hw_array2objrec|hw_changeobject|hw_children|hw_childrenobj|hw_close|hw_connect|hw_connection_info|hw_cp|hw_deleteobject|hw_docbyanchor|hw_docbyanchorobj|hw_document_attributes|hw_document_bodytag|hw_document_content|hw_document_setcontent|hw_document_size|hw_dummy|hw_edittext|hw_error|hw_errormsg|hw_free_document|hw_getanchors|hw_getanchorsobj|hw_getandlock|hw_getchildcoll|hw_getchildcollobj|hw_getchilddoccoll|hw_getchilddoccollobj|hw_getobject|hw_getobjectbyquery|hw_getobjectbyquerycoll|hw_getobjectbyquerycollobj|hw_getobjectbyqueryobj|hw_getparents|hw_getparentsobj|hw_getrellink|hw_getremote|hw_getremotechildren|hw_getsrcbydestobj|hw_gettext|hw_getusername|hw_identify|hw_incollections|hw_info|hw_inscoll|hw_insdoc|hw_insertanchors|hw_insertdocument|hw_insertobject|hw_mapid|hw_modifyobject|hw_mv|hw_new_document|hw_objrec2array|hw_output_document|hw_pconnect|hw_pipedocument|hw_root|hw_setlinkroot|hw_stat|hw_unlock|hw_who|hwapi_attribute|hwapi_attribute_key|hwapi_attribute_langdepvalue|hwapi_attribute_value|hwapi_attribute_values|hwapi_checkin|hwapi_checkout|hwapi_children|hwapi_content|hwapi_content_mimetype|hwapi_content_read|hwapi_copy|hwapi_dbstat|hwapi_dcstat|hwapi_dstanchors|hwapi_dstofsrcanchor|hwapi_error_count|hwapi_error_reason|hwapi_find|hwapi_ftstat|hwapi_hgcsp|hwapi_hwstat|hwapi_identify|hwapi_info|hwapi_insert|hwapi_insertanchor|hwapi_insertcollection|hwapi_insertdocument|hwapi_link|hwapi_lock|hwapi_move|hwapi_new_content|hwapi_object|hwapi_object_assign|hwapi_object_attreditable|hwapi_object_count|hwapi_object_insert|hwapi_object_new|hwapi_object_remove|hwapi_object_title|hwapi_object_value|hwapi_objectbyanchor|hwapi_parents|hwapi_reason_description|hwapi_reason_type|hwapi_remove|hwapi_replace|hwapi_setcommittedversion|hwapi_srcanchors|hwapi_srcsofdst|hwapi_unlock|hwapi_user|hwapi_userlist|hypot|ibase_add_user|ibase_affected_rows|ibase_backup|ibase_blob_add|ibase_blob_cancel|ibase_blob_close|ibase_blob_create|ibase_blob_echo|ibase_blob_get|ibase_blob_import|ibase_blob_info|ibase_blob_open|ibase_close|ibase_commit|ibase_commit_ret|ibase_connect|ibase_db_info|ibase_delete_user|ibase_drop_db|ibase_errcode|ibase_errmsg|ibase_execute|ibase_fetch_assoc|ibase_fetch_object|ibase_fetch_row|ibase_field_info|ibase_free_event_handler|ibase_free_query|ibase_free_result|ibase_gen_id|ibase_maintain_db|ibase_modify_user|ibase_name_result|ibase_num_fields|ibase_num_params|ibase_param_info|ibase_pconnect|ibase_prepare|ibase_query|ibase_restore|ibase_rollback|ibase_rollback_ret|ibase_server_info|ibase_service_attach|ibase_service_detach|ibase_set_event_handler|ibase_timefmt|ibase_trans|ibase_wait_event|iconv|iconv_get_encoding|iconv_mime_decode|iconv_mime_decode_headers|iconv_mime_encode|iconv_set_encoding|iconv_strlen|iconv_strpos|iconv_strrpos|iconv_substr|id3_get_frame_long_name|id3_get_frame_short_name|id3_get_genre_id|id3_get_genre_list|id3_get_genre_name|id3_get_tag|id3_get_version|id3_remove_tag|id3_set_tag|id3v2attachedpictureframe|id3v2frame|id3v2tag|idate|idn_to_ascii|idn_to_unicode|idn_to_utf8|ifx_affected_rows|ifx_blobinfile_mode|ifx_byteasvarchar|ifx_close|ifx_connect|ifx_copy_blob|ifx_create_blob|ifx_create_char|ifx_do|ifx_error|ifx_errormsg|ifx_fetch_row|ifx_fieldproperties|ifx_fieldtypes|ifx_free_blob|ifx_free_char|ifx_free_result|ifx_get_blob|ifx_get_char|ifx_getsqlca|ifx_htmltbl_result|ifx_nullformat|ifx_num_fields|ifx_num_rows|ifx_pconnect|ifx_prepare|ifx_query|ifx_textasvarchar|ifx_update_blob|ifx_update_char|ifxus_close_slob|ifxus_create_slob|ifxus_free_slob|ifxus_open_slob|ifxus_read_slob|ifxus_seek_slob|ifxus_tell_slob|ifxus_write_slob|ignore_user_abort|iis_add_server|iis_get_dir_security|iis_get_script_map|iis_get_server_by_comment|iis_get_server_by_path|iis_get_server_rights|iis_get_service_state|iis_remove_server|iis_set_app_settings|iis_set_dir_security|iis_set_script_map|iis_set_server_rights|iis_start_server|iis_start_service|iis_stop_server|iis_stop_service|image2wbmp|image_type_to_extension|image_type_to_mime_type|imagealphablending|imageantialias|imagearc|imagechar|imagecharup|imagecolorallocate|imagecolorallocatealpha|imagecolorat|imagecolorclosest|imagecolorclosestalpha|imagecolorclosesthwb|imagecolordeallocate|imagecolorexact|imagecolorexactalpha|imagecolormatch|imagecolorresolve|imagecolorresolvealpha|imagecolorset|imagecolorsforindex|imagecolorstotal|imagecolortransparent|imageconvolution|imagecopy|imagecopymerge|imagecopymergegray|imagecopyresampled|imagecopyresized|imagecreate|imagecreatefromgd|imagecreatefromgd2|imagecreatefromgd2part|imagecreatefromgif|imagecreatefromjpeg|imagecreatefrompng|imagecreatefromstring|imagecreatefromwbmp|imagecreatefromxbm|imagecreatefromxpm|imagecreatetruecolor|imagedashedline|imagedestroy|imageellipse|imagefill|imagefilledarc|imagefilledellipse|imagefilledpolygon|imagefilledrectangle|imagefilltoborder|imagefilter|imagefontheight|imagefontwidth|imageftbbox|imagefttext|imagegammacorrect|imagegd|imagegd2|imagegif|imagegrabscreen|imagegrabwindow|imageinterlace|imageistruecolor|imagejpeg|imagelayereffect|imageline|imageloadfont|imagepalettecopy|imagepng|imagepolygon|imagepsbbox|imagepsencodefont|imagepsextendfont|imagepsfreefont|imagepsloadfont|imagepsslantfont|imagepstext|imagerectangle|imagerotate|imagesavealpha|imagesetbrush|imagesetpixel|imagesetstyle|imagesetthickness|imagesettile|imagestring|imagestringup|imagesx|imagesy|imagetruecolortopalette|imagettfbbox|imagettftext|imagetypes|imagewbmp|imagexbm|imagick|imagick_adaptiveblurimage|imagick_adaptiveresizeimage|imagick_adaptivesharpenimage|imagick_adaptivethresholdimage|imagick_addimage|imagick_addnoiseimage|imagick_affinetransformimage|imagick_animateimages|imagick_annotateimage|imagick_appendimages|imagick_averageimages|imagick_blackthresholdimage|imagick_blurimage|imagick_borderimage|imagick_charcoalimage|imagick_chopimage|imagick_clear|imagick_clipimage|imagick_clippathimage|imagick_clone|imagick_clutimage|imagick_coalesceimages|imagick_colorfloodfillimage|imagick_colorizeimage|imagick_combineimages|imagick_commentimage|imagick_compareimagechannels|imagick_compareimagelayers|imagick_compareimages|imagick_compositeimage|imagick_construct|imagick_contrastimage|imagick_contraststretchimage|imagick_convolveimage|imagick_cropimage|imagick_cropthumbnailimage|imagick_current|imagick_cyclecolormapimage|imagick_decipherimage|imagick_deconstructimages|imagick_deleteimageartifact|imagick_despeckleimage|imagick_destroy|imagick_displayimage|imagick_displayimages|imagick_distortimage|imagick_drawimage|imagick_edgeimage|imagick_embossimage|imagick_encipherimage|imagick_enhanceimage|imagick_equalizeimage|imagick_evaluateimage|imagick_extentimage|imagick_flattenimages|imagick_flipimage|imagick_floodfillpaintimage|imagick_flopimage|imagick_frameimage|imagick_fximage|imagick_gammaimage|imagick_gaussianblurimage|imagick_getcolorspace|imagick_getcompression|imagick_getcompressionquality|imagick_getcopyright|imagick_getfilename|imagick_getfont|imagick_getformat|imagick_getgravity|imagick_gethomeurl|imagick_getimage|imagick_getimagealphachannel|imagick_getimageartifact|imagick_getimagebackgroundcolor|imagick_getimageblob|imagick_getimageblueprimary|imagick_getimagebordercolor|imagick_getimagechanneldepth|imagick_getimagechanneldistortion|imagick_getimagechanneldistortions|imagick_getimagechannelextrema|imagick_getimagechannelmean|imagick_getimagechannelrange|imagick_getimagechannelstatistics|imagick_getimageclipmask|imagick_getimagecolormapcolor|imagick_getimagecolors|imagick_getimagecolorspace|imagick_getimagecompose|imagick_getimagecompression|imagick_getimagecompressionquality|imagick_getimagedelay|imagick_getimagedepth|imagick_getimagedispose|imagick_getimagedistortion|imagick_getimageextrema|imagick_getimagefilename|imagick_getimageformat|imagick_getimagegamma|imagick_getimagegeometry|imagick_getimagegravity|imagick_getimagegreenprimary|imagick_getimageheight|imagick_getimagehistogram|imagick_getimageindex|imagick_getimageinterlacescheme|imagick_getimageinterpolatemethod|imagick_getimageiterations|imagick_getimagelength|imagick_getimagemagicklicense|imagick_getimagematte|imagick_getimagemattecolor|imagick_getimageorientation|imagick_getimagepage|imagick_getimagepixelcolor|imagick_getimageprofile|imagick_getimageprofiles|imagick_getimageproperties|imagick_getimageproperty|imagick_getimageredprimary|imagick_getimageregion|imagick_getimagerenderingintent|imagick_getimageresolution|imagick_getimagesblob|imagick_getimagescene|imagick_getimagesignature|imagick_getimagesize|imagick_getimagetickspersecond|imagick_getimagetotalinkdensity|imagick_getimagetype|imagick_getimageunits|imagick_getimagevirtualpixelmethod|imagick_getimagewhitepoint|imagick_getimagewidth|imagick_getinterlacescheme|imagick_getiteratorindex|imagick_getnumberimages|imagick_getoption|imagick_getpackagename|imagick_getpage|imagick_getpixeliterator|imagick_getpixelregioniterator|imagick_getpointsize|imagick_getquantumdepth|imagick_getquantumrange|imagick_getreleasedate|imagick_getresource|imagick_getresourcelimit|imagick_getsamplingfactors|imagick_getsize|imagick_getsizeoffset|imagick_getversion|imagick_hasnextimage|imagick_haspreviousimage|imagick_identifyimage|imagick_implodeimage|imagick_labelimage|imagick_levelimage|imagick_linearstretchimage|imagick_liquidrescaleimage|imagick_magnifyimage|imagick_mapimage|imagick_mattefloodfillimage|imagick_medianfilterimage|imagick_mergeimagelayers|imagick_minifyimage|imagick_modulateimage|imagick_montageimage|imagick_morphimages|imagick_mosaicimages|imagick_motionblurimage|imagick_negateimage|imagick_newimage|imagick_newpseudoimage|imagick_nextimage|imagick_normalizeimage|imagick_oilpaintimage|imagick_opaquepaintimage|imagick_optimizeimagelayers|imagick_orderedposterizeimage|imagick_paintfloodfillimage|imagick_paintopaqueimage|imagick_painttransparentimage|imagick_pingimage|imagick_pingimageblob|imagick_pingimagefile|imagick_polaroidimage|imagick_posterizeimage|imagick_previewimages|imagick_previousimage|imagick_profileimage|imagick_quantizeimage|imagick_quantizeimages|imagick_queryfontmetrics|imagick_queryfonts|imagick_queryformats|imagick_radialblurimage|imagick_raiseimage|imagick_randomthresholdimage|imagick_readimage|imagick_readimageblob|imagick_readimagefile|imagick_recolorimage|imagick_reducenoiseimage|imagick_removeimage|imagick_removeimageprofile|imagick_render|imagick_resampleimage|imagick_resetimagepage|imagick_resizeimage|imagick_rollimage|imagick_rotateimage|imagick_roundcorners|imagick_sampleimage|imagick_scaleimage|imagick_separateimagechannel|imagick_sepiatoneimage|imagick_setbackgroundcolor|imagick_setcolorspace|imagick_setcompression|imagick_setcompressionquality|imagick_setfilename|imagick_setfirstiterator|imagick_setfont|imagick_setformat|imagick_setgravity|imagick_setimage|imagick_setimagealphachannel|imagick_setimageartifact|imagick_setimagebackgroundcolor|imagick_setimagebias|imagick_setimageblueprimary|imagick_setimagebordercolor|imagick_setimagechanneldepth|imagick_setimageclipmask|imagick_setimagecolormapcolor|imagick_setimagecolorspace|imagick_setimagecompose|imagick_setimagecompression|imagick_setimagecompressionquality|imagick_setimagedelay|imagick_setimagedepth|imagick_setimagedispose|imagick_setimageextent|imagick_setimagefilename|imagick_setimageformat|imagick_setimagegamma|imagick_setimagegravity|imagick_setimagegreenprimary|imagick_setimageindex|imagick_setimageinterlacescheme|imagick_setimageinterpolatemethod|imagick_setimageiterations|imagick_setimagematte|imagick_setimagemattecolor|imagick_setimageopacity|imagick_setimageorientation|imagick_setimagepage|imagick_setimageprofile|imagick_setimageproperty|imagick_setimageredprimary|imagick_setimagerenderingintent|imagick_setimageresolution|imagick_setimagescene|imagick_setimagetickspersecond|imagick_setimagetype|imagick_setimageunits|imagick_setimagevirtualpixelmethod|imagick_setimagewhitepoint|imagick_setinterlacescheme|imagick_setiteratorindex|imagick_setlastiterator|imagick_setoption|imagick_setpage|imagick_setpointsize|imagick_setresolution|imagick_setresourcelimit|imagick_setsamplingfactors|imagick_setsize|imagick_setsizeoffset|imagick_settype|imagick_shadeimage|imagick_shadowimage|imagick_sharpenimage|imagick_shaveimage|imagick_shearimage|imagick_sigmoidalcontrastimage|imagick_sketchimage|imagick_solarizeimage|imagick_spliceimage|imagick_spreadimage|imagick_steganoimage|imagick_stereoimage|imagick_stripimage|imagick_swirlimage|imagick_textureimage|imagick_thresholdimage|imagick_thumbnailimage|imagick_tintimage|imagick_transformimage|imagick_transparentpaintimage|imagick_transposeimage|imagick_transverseimage|imagick_trimimage|imagick_uniqueimagecolors|imagick_unsharpmaskimage|imagick_valid|imagick_vignetteimage|imagick_waveimage|imagick_whitethresholdimage|imagick_writeimage|imagick_writeimagefile|imagick_writeimages|imagick_writeimagesfile|imagickdraw|imagickdraw_affine|imagickdraw_annotation|imagickdraw_arc|imagickdraw_bezier|imagickdraw_circle|imagickdraw_clear|imagickdraw_clone|imagickdraw_color|imagickdraw_comment|imagickdraw_composite|imagickdraw_construct|imagickdraw_destroy|imagickdraw_ellipse|imagickdraw_getclippath|imagickdraw_getcliprule|imagickdraw_getclipunits|imagickdraw_getfillcolor|imagickdraw_getfillopacity|imagickdraw_getfillrule|imagickdraw_getfont|imagickdraw_getfontfamily|imagickdraw_getfontsize|imagickdraw_getfontstyle|imagickdraw_getfontweight|imagickdraw_getgravity|imagickdraw_getstrokeantialias|imagickdraw_getstrokecolor|imagickdraw_getstrokedasharray|imagickdraw_getstrokedashoffset|imagickdraw_getstrokelinecap|imagickdraw_getstrokelinejoin|imagickdraw_getstrokemiterlimit|imagickdraw_getstrokeopacity|imagickdraw_getstrokewidth|imagickdraw_gettextalignment|imagickdraw_gettextantialias|imagickdraw_gettextdecoration|imagickdraw_gettextencoding|imagickdraw_gettextundercolor|imagickdraw_getvectorgraphics|imagickdraw_line|imagickdraw_matte|imagickdraw_pathclose|imagickdraw_pathcurvetoabsolute|imagickdraw_pathcurvetoquadraticbezierabsolute|imagickdraw_pathcurvetoquadraticbezierrelative|imagickdraw_pathcurvetoquadraticbeziersmoothabsolute|imagickdraw_pathcurvetoquadraticbeziersmoothrelative|imagickdraw_pathcurvetorelative|imagickdraw_pathcurvetosmoothabsolute|imagickdraw_pathcurvetosmoothrelative|imagickdraw_pathellipticarcabsolute|imagickdraw_pathellipticarcrelative|imagickdraw_pathfinish|imagickdraw_pathlinetoabsolute|imagickdraw_pathlinetohorizontalabsolute|imagickdraw_pathlinetohorizontalrelative|imagickdraw_pathlinetorelative|imagickdraw_pathlinetoverticalabsolute|imagickdraw_pathlinetoverticalrelative|imagickdraw_pathmovetoabsolute|imagickdraw_pathmovetorelative|imagickdraw_pathstart|imagickdraw_point|imagickdraw_polygon|imagickdraw_polyline|imagickdraw_pop|imagickdraw_popclippath|imagickdraw_popdefs|imagickdraw_poppattern|imagickdraw_push|imagickdraw_pushclippath|imagickdraw_pushdefs|imagickdraw_pushpattern|imagickdraw_rectangle|imagickdraw_render|imagickdraw_rotate|imagickdraw_roundrectangle|imagickdraw_scale|imagickdraw_setclippath|imagickdraw_setcliprule|imagickdraw_setclipunits|imagickdraw_setfillalpha|imagickdraw_setfillcolor|imagickdraw_setfillopacity|imagickdraw_setfillpatternurl|imagickdraw_setfillrule|imagickdraw_setfont|imagickdraw_setfontfamily|imagickdraw_setfontsize|imagickdraw_setfontstretch|imagickdraw_setfontstyle|imagickdraw_setfontweight|imagickdraw_setgravity|imagickdraw_setstrokealpha|imagickdraw_setstrokeantialias|imagickdraw_setstrokecolor|imagickdraw_setstrokedasharray|imagickdraw_setstrokedashoffset|imagickdraw_setstrokelinecap|imagickdraw_setstrokelinejoin|imagickdraw_setstrokemiterlimit|imagickdraw_setstrokeopacity|imagickdraw_setstrokepatternurl|imagickdraw_setstrokewidth|imagickdraw_settextalignment|imagickdraw_settextantialias|imagickdraw_settextdecoration|imagickdraw_settextencoding|imagickdraw_settextundercolor|imagickdraw_setvectorgraphics|imagickdraw_setviewbox|imagickdraw_skewx|imagickdraw_skewy|imagickdraw_translate|imagickpixel|imagickpixel_clear|imagickpixel_construct|imagickpixel_destroy|imagickpixel_getcolor|imagickpixel_getcolorasstring|imagickpixel_getcolorcount|imagickpixel_getcolorvalue|imagickpixel_gethsl|imagickpixel_issimilar|imagickpixel_setcolor|imagickpixel_setcolorvalue|imagickpixel_sethsl|imagickpixeliterator|imagickpixeliterator_clear|imagickpixeliterator_construct|imagickpixeliterator_destroy|imagickpixeliterator_getcurrentiteratorrow|imagickpixeliterator_getiteratorrow|imagickpixeliterator_getnextiteratorrow|imagickpixeliterator_getpreviousiteratorrow|imagickpixeliterator_newpixeliterator|imagickpixeliterator_newpixelregioniterator|imagickpixeliterator_resetiterator|imagickpixeliterator_setiteratorfirstrow|imagickpixeliterator_setiteratorlastrow|imagickpixeliterator_setiteratorrow|imagickpixeliterator_synciterator|imap_8bit|imap_alerts|imap_append|imap_base64|imap_binary|imap_body|imap_bodystruct|imap_check|imap_clearflag_full|imap_close|imap_create|imap_createmailbox|imap_delete|imap_deletemailbox|imap_errors|imap_expunge|imap_fetch_overview|imap_fetchbody|imap_fetchheader|imap_fetchmime|imap_fetchstructure|imap_fetchtext|imap_gc|imap_get_quota|imap_get_quotaroot|imap_getacl|imap_getmailboxes|imap_getsubscribed|imap_header|imap_headerinfo|imap_headers|imap_last_error|imap_list|imap_listmailbox|imap_listscan|imap_listsubscribed|imap_lsub|imap_mail|imap_mail_compose|imap_mail_copy|imap_mail_move|imap_mailboxmsginfo|imap_mime_header_decode|imap_msgno|imap_num_msg|imap_num_recent|imap_open|imap_ping|imap_qprint|imap_rename|imap_renamemailbox|imap_reopen|imap_rfc822_parse_adrlist|imap_rfc822_parse_headers|imap_rfc822_write_address|imap_savebody|imap_scan|imap_scanmailbox|imap_search|imap_set_quota|imap_setacl|imap_setflag_full|imap_sort|imap_status|imap_subscribe|imap_thread|imap_timeout|imap_uid|imap_undelete|imap_unsubscribe|imap_utf7_decode|imap_utf7_encode|imap_utf8|implementsinterface|implode|import_request_variables|in_array|include|include_once|inclued_get_data|inet_ntop|inet_pton|infiniteiterator|ingres_autocommit|ingres_autocommit_state|ingres_charset|ingres_close|ingres_commit|ingres_connect|ingres_cursor|ingres_errno|ingres_error|ingres_errsqlstate|ingres_escape_string|ingres_execute|ingres_fetch_array|ingres_fetch_assoc|ingres_fetch_object|ingres_fetch_proc_return|ingres_fetch_row|ingres_field_length|ingres_field_name|ingres_field_nullable|ingres_field_precision|ingres_field_scale|ingres_field_type|ingres_free_result|ingres_next_error|ingres_num_fields|ingres_num_rows|ingres_pconnect|ingres_prepare|ingres_query|ingres_result_seek|ingres_rollback|ingres_set_environment|ingres_unbuffered_query|ini_alter|ini_get|ini_get_all|ini_restore|ini_set|innamespace|inotify_add_watch|inotify_init|inotify_queue_len|inotify_read|inotify_rm_watch|interface_exists|intl_error_name|intl_get_error_code|intl_get_error_message|intl_is_failure|intldateformatter|intval|invalidargumentexception|invoke|invokeargs|ip2long|iptcembed|iptcparse|is_a|is_array|is_bool|is_callable|is_dir|is_double|is_executable|is_file|is_finite|is_float|is_infinite|is_int|is_integer|is_link|is_long|is_nan|is_null|is_numeric|is_object|is_readable|is_real|is_resource|is_scalar|is_soap_fault|is_string|is_subclass_of|is_uploaded_file|is_writable|is_writeable|isabstract|iscloneable|isdisabled|isfinal|isinstance|isinstantiable|isinterface|isinternal|isiterateable|isset|issubclassof|isuserdefined|iterator|iterator_apply|iterator_count|iterator_to_array|iteratoraggregate|iteratoriterator|java_last_exception_clear|java_last_exception_get|jddayofweek|jdmonthname|jdtofrench|jdtogregorian|jdtojewish|jdtojulian|jdtounix|jewishtojd|join|jpeg2wbmp|json_decode|json_encode|json_last_error|jsonserializable|judy|judy_type|judy_version|juliantojd|kadm5_chpass_principal|kadm5_create_principal|kadm5_delete_principal|kadm5_destroy|kadm5_flush|kadm5_get_policies|kadm5_get_principal|kadm5_get_principals|kadm5_init_with_password|kadm5_modify_principal|key|krsort|ksort|lcfirst|lcg_value|lchgrp|lchown|ldap_8859_to_t61|ldap_add|ldap_bind|ldap_close|ldap_compare|ldap_connect|ldap_count_entries|ldap_delete|ldap_dn2ufn|ldap_err2str|ldap_errno|ldap_error|ldap_explode_dn|ldap_first_attribute|ldap_first_entry|ldap_first_reference|ldap_free_result|ldap_get_attributes|ldap_get_dn|ldap_get_entries|ldap_get_option|ldap_get_values|ldap_get_values_len|ldap_list|ldap_mod_add|ldap_mod_del|ldap_mod_replace|ldap_modify|ldap_next_attribute|ldap_next_entry|ldap_next_reference|ldap_parse_reference|ldap_parse_result|ldap_read|ldap_rename|ldap_sasl_bind|ldap_search|ldap_set_option|ldap_set_rebind_proc|ldap_sort|ldap_start_tls|ldap_t61_to_8859|ldap_unbind|lengthexception|levenshtein|libxml_clear_errors|libxml_disable_entity_loader|libxml_get_errors|libxml_get_last_error|libxml_set_streams_context|libxml_use_internal_errors|libxmlerror|limititerator|link|linkinfo|list|locale|localeconv|localtime|log|log10|log1p|logicexception|long2ip|lstat|ltrim|lzf_compress|lzf_decompress|lzf_optimized_for|m_checkstatus|m_completeauthorizations|m_connect|m_connectionerror|m_deletetrans|m_destroyconn|m_destroyengine|m_getcell|m_getcellbynum|m_getcommadelimited|m_getheader|m_initconn|m_initengine|m_iscommadelimited|m_maxconntimeout|m_monitor|m_numcolumns|m_numrows|m_parsecommadelimited|m_responsekeys|m_responseparam|m_returnstatus|m_setblocking|m_setdropfile|m_setip|m_setssl|m_setssl_cafile|m_setssl_files|m_settimeout|m_sslcert_gen_hash|m_transactionssent|m_transinqueue|m_transkeyval|m_transnew|m_transsend|m_uwait|m_validateidentifier|m_verifyconnection|m_verifysslcert|magic_quotes_runtime|mail|mailparse_determine_best_xfer_encoding|mailparse_msg_create|mailparse_msg_extract_part|mailparse_msg_extract_part_file|mailparse_msg_extract_whole_part_file|mailparse_msg_free|mailparse_msg_get_part|mailparse_msg_get_part_data|mailparse_msg_get_structure|mailparse_msg_parse|mailparse_msg_parse_file|mailparse_rfc822_parse_addresses|mailparse_stream_encode|mailparse_uudecode_all|main|max|maxdb_affected_rows|maxdb_autocommit|maxdb_bind_param|maxdb_bind_result|maxdb_change_user|maxdb_character_set_name|maxdb_client_encoding|maxdb_close|maxdb_close_long_data|maxdb_commit|maxdb_connect|maxdb_connect_errno|maxdb_connect_error|maxdb_data_seek|maxdb_debug|maxdb_disable_reads_from_master|maxdb_disable_rpl_parse|maxdb_dump_debug_info|maxdb_embedded_connect|maxdb_enable_reads_from_master|maxdb_enable_rpl_parse|maxdb_errno|maxdb_error|maxdb_escape_string|maxdb_execute|maxdb_fetch|maxdb_fetch_array|maxdb_fetch_assoc|maxdb_fetch_field|maxdb_fetch_field_direct|maxdb_fetch_fields|maxdb_fetch_lengths|maxdb_fetch_object|maxdb_fetch_row|maxdb_field_count|maxdb_field_seek|maxdb_field_tell|maxdb_free_result|maxdb_get_client_info|maxdb_get_client_version|maxdb_get_host_info|maxdb_get_metadata|maxdb_get_proto_info|maxdb_get_server_info|maxdb_get_server_version|maxdb_info|maxdb_init|maxdb_insert_id|maxdb_kill|maxdb_master_query|maxdb_more_results|maxdb_multi_query|maxdb_next_result|maxdb_num_fields|maxdb_num_rows|maxdb_options|maxdb_param_count|maxdb_ping|maxdb_prepare|maxdb_query|maxdb_real_connect|maxdb_real_escape_string|maxdb_real_query|maxdb_report|maxdb_rollback|maxdb_rpl_parse_enabled|maxdb_rpl_probe|maxdb_rpl_query_type|maxdb_select_db|maxdb_send_long_data|maxdb_send_query|maxdb_server_end|maxdb_server_init|maxdb_set_opt|maxdb_sqlstate|maxdb_ssl_set|maxdb_stat|maxdb_stmt_affected_rows|maxdb_stmt_bind_param|maxdb_stmt_bind_result|maxdb_stmt_close|maxdb_stmt_close_long_data|maxdb_stmt_data_seek|maxdb_stmt_errno|maxdb_stmt_error|maxdb_stmt_execute|maxdb_stmt_fetch|maxdb_stmt_free_result|maxdb_stmt_init|maxdb_stmt_num_rows|maxdb_stmt_param_count|maxdb_stmt_prepare|maxdb_stmt_reset|maxdb_stmt_result_metadata|maxdb_stmt_send_long_data|maxdb_stmt_sqlstate|maxdb_stmt_store_result|maxdb_store_result|maxdb_thread_id|maxdb_thread_safe|maxdb_use_result|maxdb_warning_count|mb_check_encoding|mb_convert_case|mb_convert_encoding|mb_convert_kana|mb_convert_variables|mb_decode_mimeheader|mb_decode_numericentity|mb_detect_encoding|mb_detect_order|mb_encode_mimeheader|mb_encode_numericentity|mb_encoding_aliases|mb_ereg|mb_ereg_match|mb_ereg_replace|mb_ereg_search|mb_ereg_search_getpos|mb_ereg_search_getregs|mb_ereg_search_init|mb_ereg_search_pos|mb_ereg_search_regs|mb_ereg_search_setpos|mb_eregi|mb_eregi_replace|mb_get_info|mb_http_input|mb_http_output|mb_internal_encoding|mb_language|mb_list_encodings|mb_output_handler|mb_parse_str|mb_preferred_mime_name|mb_regex_encoding|mb_regex_set_options|mb_send_mail|mb_split|mb_strcut|mb_strimwidth|mb_stripos|mb_stristr|mb_strlen|mb_strpos|mb_strrchr|mb_strrichr|mb_strripos|mb_strrpos|mb_strstr|mb_strtolower|mb_strtoupper|mb_strwidth|mb_substitute_character|mb_substr|mb_substr_count|mcrypt_cbc|mcrypt_cfb|mcrypt_create_iv|mcrypt_decrypt|mcrypt_ecb|mcrypt_enc_get_algorithms_name|mcrypt_enc_get_block_size|mcrypt_enc_get_iv_size|mcrypt_enc_get_key_size|mcrypt_enc_get_modes_name|mcrypt_enc_get_supported_key_sizes|mcrypt_enc_is_block_algorithm|mcrypt_enc_is_block_algorithm_mode|mcrypt_enc_is_block_mode|mcrypt_enc_self_test|mcrypt_encrypt|mcrypt_generic|mcrypt_generic_deinit|mcrypt_generic_end|mcrypt_generic_init|mcrypt_get_block_size|mcrypt_get_cipher_name|mcrypt_get_iv_size|mcrypt_get_key_size|mcrypt_list_algorithms|mcrypt_list_modes|mcrypt_module_close|mcrypt_module_get_algo_block_size|mcrypt_module_get_algo_key_size|mcrypt_module_get_supported_key_sizes|mcrypt_module_is_block_algorithm|mcrypt_module_is_block_algorithm_mode|mcrypt_module_is_block_mode|mcrypt_module_open|mcrypt_module_self_test|mcrypt_ofb|md5|md5_file|mdecrypt_generic|memcache|memcache_debug|memcached|memory_get_peak_usage|memory_get_usage|messageformatter|metaphone|method_exists|mhash|mhash_count|mhash_get_block_size|mhash_get_hash_name|mhash_keygen_s2k|microtime|mime_content_type|min|ming_keypress|ming_setcubicthreshold|ming_setscale|ming_setswfcompression|ming_useconstants|ming_useswfversion|mkdir|mktime|money_format|mongo|mongobindata|mongocode|mongocollection|mongoconnectionexception|mongocursor|mongocursorexception|mongocursortimeoutexception|mongodate|mongodb|mongodbref|mongoexception|mongogridfs|mongogridfscursor|mongogridfsexception|mongogridfsfile|mongoid|mongoint32|mongoint64|mongomaxkey|mongominkey|mongoregex|mongotimestamp|move_uploaded_file|mpegfile|mqseries_back|mqseries_begin|mqseries_close|mqseries_cmit|mqseries_conn|mqseries_connx|mqseries_disc|mqseries_get|mqseries_inq|mqseries_open|mqseries_put|mqseries_put1|mqseries_set|mqseries_strerror|msession_connect|msession_count|msession_create|msession_destroy|msession_disconnect|msession_find|msession_get|msession_get_array|msession_get_data|msession_inc|msession_list|msession_listvar|msession_lock|msession_plugin|msession_randstr|msession_set|msession_set_array|msession_set_data|msession_timeout|msession_uniq|msession_unlock|msg_get_queue|msg_queue_exists|msg_receive|msg_remove_queue|msg_send|msg_set_queue|msg_stat_queue|msql|msql_affected_rows|msql_close|msql_connect|msql_create_db|msql_createdb|msql_data_seek|msql_db_query|msql_dbname|msql_drop_db|msql_error|msql_fetch_array|msql_fetch_field|msql_fetch_object|msql_fetch_row|msql_field_flags|msql_field_len|msql_field_name|msql_field_seek|msql_field_table|msql_field_type|msql_fieldflags|msql_fieldlen|msql_fieldname|msql_fieldtable|msql_fieldtype|msql_free_result|msql_list_dbs|msql_list_fields|msql_list_tables|msql_num_fields|msql_num_rows|msql_numfields|msql_numrows|msql_pconnect|msql_query|msql_regcase|msql_result|msql_select_db|msql_tablename|mssql_bind|mssql_close|mssql_connect|mssql_data_seek|mssql_execute|mssql_fetch_array|mssql_fetch_assoc|mssql_fetch_batch|mssql_fetch_field|mssql_fetch_object|mssql_fetch_row|mssql_field_length|mssql_field_name|mssql_field_seek|mssql_field_type|mssql_free_result|mssql_free_statement|mssql_get_last_message|mssql_guid_string|mssql_init|mssql_min_error_severity|mssql_min_message_severity|mssql_next_result|mssql_num_fields|mssql_num_rows|mssql_pconnect|mssql_query|mssql_result|mssql_rows_affected|mssql_select_db|mt_getrandmax|mt_rand|mt_srand|multipleiterator|mysql_affected_rows|mysql_client_encoding|mysql_close|mysql_connect|mysql_create_db|mysql_data_seek|mysql_db_name|mysql_db_query|mysql_drop_db|mysql_errno|mysql_error|mysql_escape_string|mysql_fetch_array|mysql_fetch_assoc|mysql_fetch_field|mysql_fetch_lengths|mysql_fetch_object|mysql_fetch_row|mysql_field_flags|mysql_field_len|mysql_field_name|mysql_field_seek|mysql_field_table|mysql_field_type|mysql_free_result|mysql_get_client_info|mysql_get_host_info|mysql_get_proto_info|mysql_get_server_info|mysql_info|mysql_insert_id|mysql_list_dbs|mysql_list_fields|mysql_list_processes|mysql_list_tables|mysql_num_fields|mysql_num_rows|mysql_pconnect|mysql_ping|mysql_query|mysql_real_escape_string|mysql_result|mysql_select_db|mysql_set_charset|mysql_stat|mysql_tablename|mysql_thread_id|mysql_unbuffered_query|mysqli|mysqli_affected_rows|mysqli_autocommit|mysqli_bind_param|mysqli_bind_result|mysqli_cache_stats|mysqli_change_user|mysqli_character_set_name|mysqli_client_encoding|mysqli_close|mysqli_commit|mysqli_connect|mysqli_connect_errno|mysqli_connect_error|mysqli_data_seek|mysqli_debug|mysqli_disable_reads_from_master|mysqli_disable_rpl_parse|mysqli_driver|mysqli_dump_debug_info|mysqli_embedded_server_end|mysqli_embedded_server_start|mysqli_enable_reads_from_master|mysqli_enable_rpl_parse|mysqli_errno|mysqli_error|mysqli_escape_string|mysqli_execute|mysqli_fetch|mysqli_fetch_all|mysqli_fetch_array|mysqli_fetch_assoc|mysqli_fetch_field|mysqli_fetch_field_direct|mysqli_fetch_fields|mysqli_fetch_lengths|mysqli_fetch_object|mysqli_fetch_row|mysqli_field_count|mysqli_field_seek|mysqli_field_tell|mysqli_free_result|mysqli_get_charset|mysqli_get_client_info|mysqli_get_client_stats|mysqli_get_client_version|mysqli_get_connection_stats|mysqli_get_host_info|mysqli_get_metadata|mysqli_get_proto_info|mysqli_get_server_info|mysqli_get_server_version|mysqli_get_warnings|mysqli_info|mysqli_init|mysqli_insert_id|mysqli_kill|mysqli_link_construct|mysqli_master_query|mysqli_more_results|mysqli_multi_query|mysqli_next_result|mysqli_num_fields|mysqli_num_rows|mysqli_options|mysqli_param_count|mysqli_ping|mysqli_poll|mysqli_prepare|mysqli_query|mysqli_real_connect|mysqli_real_escape_string|mysqli_real_query|mysqli_reap_async_query|mysqli_refresh|mysqli_report|mysqli_result|mysqli_rollback|mysqli_rpl_parse_enabled|mysqli_rpl_probe|mysqli_rpl_query_type|mysqli_select_db|mysqli_send_long_data|mysqli_send_query|mysqli_set_charset|mysqli_set_local_infile_default|mysqli_set_local_infile_handler|mysqli_set_opt|mysqli_slave_query|mysqli_sqlstate|mysqli_ssl_set|mysqli_stat|mysqli_stmt|mysqli_stmt_affected_rows|mysqli_stmt_attr_get|mysqli_stmt_attr_set|mysqli_stmt_bind_param|mysqli_stmt_bind_result|mysqli_stmt_close|mysqli_stmt_data_seek|mysqli_stmt_errno|mysqli_stmt_error|mysqli_stmt_execute|mysqli_stmt_fetch|mysqli_stmt_field_count|mysqli_stmt_free_result|mysqli_stmt_get_result|mysqli_stmt_get_warnings|mysqli_stmt_init|mysqli_stmt_insert_id|mysqli_stmt_next_result|mysqli_stmt_num_rows|mysqli_stmt_param_count|mysqli_stmt_prepare|mysqli_stmt_reset|mysqli_stmt_result_metadata|mysqli_stmt_send_long_data|mysqli_stmt_sqlstate|mysqli_stmt_store_result|mysqli_store_result|mysqli_thread_id|mysqli_thread_safe|mysqli_use_result|mysqli_warning|mysqli_warning_count|mysqlnd_ms_get_stats|mysqlnd_ms_query_is_select|mysqlnd_ms_set_user_pick_server|mysqlnd_qc_change_handler|mysqlnd_qc_clear_cache|mysqlnd_qc_get_cache_info|mysqlnd_qc_get_core_stats|mysqlnd_qc_get_handler|mysqlnd_qc_get_query_trace_log|mysqlnd_qc_set_user_handlers|natcasesort|natsort|ncurses_addch|ncurses_addchnstr|ncurses_addchstr|ncurses_addnstr|ncurses_addstr|ncurses_assume_default_colors|ncurses_attroff|ncurses_attron|ncurses_attrset|ncurses_baudrate|ncurses_beep|ncurses_bkgd|ncurses_bkgdset|ncurses_border|ncurses_bottom_panel|ncurses_can_change_color|ncurses_cbreak|ncurses_clear|ncurses_clrtobot|ncurses_clrtoeol|ncurses_color_content|ncurses_color_set|ncurses_curs_set|ncurses_def_prog_mode|ncurses_def_shell_mode|ncurses_define_key|ncurses_del_panel|ncurses_delay_output|ncurses_delch|ncurses_deleteln|ncurses_delwin|ncurses_doupdate|ncurses_echo|ncurses_echochar|ncurses_end|ncurses_erase|ncurses_erasechar|ncurses_filter|ncurses_flash|ncurses_flushinp|ncurses_getch|ncurses_getmaxyx|ncurses_getmouse|ncurses_getyx|ncurses_halfdelay|ncurses_has_colors|ncurses_has_ic|ncurses_has_il|ncurses_has_key|ncurses_hide_panel|ncurses_hline|ncurses_inch|ncurses_init|ncurses_init_color|ncurses_init_pair|ncurses_insch|ncurses_insdelln|ncurses_insertln|ncurses_insstr|ncurses_instr|ncurses_isendwin|ncurses_keyok|ncurses_keypad|ncurses_killchar|ncurses_longname|ncurses_meta|ncurses_mouse_trafo|ncurses_mouseinterval|ncurses_mousemask|ncurses_move|ncurses_move_panel|ncurses_mvaddch|ncurses_mvaddchnstr|ncurses_mvaddchstr|ncurses_mvaddnstr|ncurses_mvaddstr|ncurses_mvcur|ncurses_mvdelch|ncurses_mvgetch|ncurses_mvhline|ncurses_mvinch|ncurses_mvvline|ncurses_mvwaddstr|ncurses_napms|ncurses_new_panel|ncurses_newpad|ncurses_newwin|ncurses_nl|ncurses_nocbreak|ncurses_noecho|ncurses_nonl|ncurses_noqiflush|ncurses_noraw|ncurses_pair_content|ncurses_panel_above|ncurses_panel_below|ncurses_panel_window|ncurses_pnoutrefresh|ncurses_prefresh|ncurses_putp|ncurses_qiflush|ncurses_raw|ncurses_refresh|ncurses_replace_panel|ncurses_reset_prog_mode|ncurses_reset_shell_mode|ncurses_resetty|ncurses_savetty|ncurses_scr_dump|ncurses_scr_init|ncurses_scr_restore|ncurses_scr_set|ncurses_scrl|ncurses_show_panel|ncurses_slk_attr|ncurses_slk_attroff|ncurses_slk_attron|ncurses_slk_attrset|ncurses_slk_clear|ncurses_slk_color|ncurses_slk_init|ncurses_slk_noutrefresh|ncurses_slk_refresh|ncurses_slk_restore|ncurses_slk_set|ncurses_slk_touch|ncurses_standend|ncurses_standout|ncurses_start_color|ncurses_termattrs|ncurses_termname|ncurses_timeout|ncurses_top_panel|ncurses_typeahead|ncurses_ungetch|ncurses_ungetmouse|ncurses_update_panels|ncurses_use_default_colors|ncurses_use_env|ncurses_use_extended_names|ncurses_vidattr|ncurses_vline|ncurses_waddch|ncurses_waddstr|ncurses_wattroff|ncurses_wattron|ncurses_wattrset|ncurses_wborder|ncurses_wclear|ncurses_wcolor_set|ncurses_werase|ncurses_wgetch|ncurses_whline|ncurses_wmouse_trafo|ncurses_wmove|ncurses_wnoutrefresh|ncurses_wrefresh|ncurses_wstandend|ncurses_wstandout|ncurses_wvline|newinstance|newinstanceargs|newt_bell|newt_button|newt_button_bar|newt_centered_window|newt_checkbox|newt_checkbox_get_value|newt_checkbox_set_flags|newt_checkbox_set_value|newt_checkbox_tree|newt_checkbox_tree_add_item|newt_checkbox_tree_find_item|newt_checkbox_tree_get_current|newt_checkbox_tree_get_entry_value|newt_checkbox_tree_get_multi_selection|newt_checkbox_tree_get_selection|newt_checkbox_tree_multi|newt_checkbox_tree_set_current|newt_checkbox_tree_set_entry|newt_checkbox_tree_set_entry_value|newt_checkbox_tree_set_width|newt_clear_key_buffer|newt_cls|newt_compact_button|newt_component_add_callback|newt_component_takes_focus|newt_create_grid|newt_cursor_off|newt_cursor_on|newt_delay|newt_draw_form|newt_draw_root_text|newt_entry|newt_entry_get_value|newt_entry_set|newt_entry_set_filter|newt_entry_set_flags|newt_finished|newt_form|newt_form_add_component|newt_form_add_components|newt_form_add_hot_key|newt_form_destroy|newt_form_get_current|newt_form_run|newt_form_set_background|newt_form_set_height|newt_form_set_size|newt_form_set_timer|newt_form_set_width|newt_form_watch_fd|newt_get_screen_size|newt_grid_add_components_to_form|newt_grid_basic_window|newt_grid_free|newt_grid_get_size|newt_grid_h_close_stacked|newt_grid_h_stacked|newt_grid_place|newt_grid_set_field|newt_grid_simple_window|newt_grid_v_close_stacked|newt_grid_v_stacked|newt_grid_wrapped_window|newt_grid_wrapped_window_at|newt_init|newt_label|newt_label_set_text|newt_listbox|newt_listbox_append_entry|newt_listbox_clear|newt_listbox_clear_selection|newt_listbox_delete_entry|newt_listbox_get_current|newt_listbox_get_selection|newt_listbox_insert_entry|newt_listbox_item_count|newt_listbox_select_item|newt_listbox_set_current|newt_listbox_set_current_by_key|newt_listbox_set_data|newt_listbox_set_entry|newt_listbox_set_width|newt_listitem|newt_listitem_get_data|newt_listitem_set|newt_open_window|newt_pop_help_line|newt_pop_window|newt_push_help_line|newt_radio_get_current|newt_radiobutton|newt_redraw_help_line|newt_reflow_text|newt_refresh|newt_resize_screen|newt_resume|newt_run_form|newt_scale|newt_scale_set|newt_scrollbar_set|newt_set_help_callback|newt_set_suspend_callback|newt_suspend|newt_textbox|newt_textbox_get_num_lines|newt_textbox_reflowed|newt_textbox_set_height|newt_textbox_set_text|newt_vertical_scrollbar|newt_wait_for_key|newt_win_choice|newt_win_entries|newt_win_menu|newt_win_message|newt_win_messagev|newt_win_ternary|next|ngettext|nl2br|nl_langinfo|norewinditerator|normalizer|notes_body|notes_copy_db|notes_create_db|notes_create_note|notes_drop_db|notes_find_note|notes_header_info|notes_list_msgs|notes_mark_read|notes_mark_unread|notes_nav_create|notes_search|notes_unread|notes_version|nsapi_request_headers|nsapi_response_headers|nsapi_virtual|nthmac|number_format|numberformatter|oauth|oauth_get_sbs|oauth_urlencode|oauthexception|oauthprovider|ob_clean|ob_deflatehandler|ob_end_clean|ob_end_flush|ob_etaghandler|ob_flush|ob_get_clean|ob_get_contents|ob_get_flush|ob_get_length|ob_get_level|ob_get_status|ob_gzhandler|ob_iconv_handler|ob_implicit_flush|ob_inflatehandler|ob_list_handlers|ob_start|ob_tidyhandler|oci_bind_array_by_name|oci_bind_by_name|oci_cancel|oci_client_version|oci_close|oci_collection_append|oci_collection_assign|oci_collection_element_assign|oci_collection_element_get|oci_collection_free|oci_collection_max|oci_collection_size|oci_collection_trim|oci_commit|oci_connect|oci_define_by_name|oci_error|oci_execute|oci_fetch|oci_fetch_all|oci_fetch_array|oci_fetch_assoc|oci_fetch_object|oci_fetch_row|oci_field_is_null|oci_field_name|oci_field_precision|oci_field_scale|oci_field_size|oci_field_type|oci_field_type_raw|oci_free_statement|oci_internal_debug|oci_lob_append|oci_lob_close|oci_lob_copy|oci_lob_eof|oci_lob_erase|oci_lob_export|oci_lob_flush|oci_lob_free|oci_lob_getbuffering|oci_lob_import|oci_lob_is_equal|oci_lob_load|oci_lob_read|oci_lob_rewind|oci_lob_save|oci_lob_savefile|oci_lob_seek|oci_lob_setbuffering|oci_lob_size|oci_lob_tell|oci_lob_truncate|oci_lob_write|oci_lob_writetemporary|oci_lob_writetofile|oci_new_collection|oci_new_connect|oci_new_cursor|oci_new_descriptor|oci_num_fields|oci_num_rows|oci_parse|oci_password_change|oci_pconnect|oci_result|oci_rollback|oci_server_version|oci_set_action|oci_set_client_identifier|oci_set_client_info|oci_set_edition|oci_set_module_name|oci_set_prefetch|oci_statement_type|ocibindbyname|ocicancel|ocicloselob|ocicollappend|ocicollassign|ocicollassignelem|ocicollgetelem|ocicollmax|ocicollsize|ocicolltrim|ocicolumnisnull|ocicolumnname|ocicolumnprecision|ocicolumnscale|ocicolumnsize|ocicolumntype|ocicolumntyperaw|ocicommit|ocidefinebyname|ocierror|ociexecute|ocifetch|ocifetchinto|ocifetchstatement|ocifreecollection|ocifreecursor|ocifreedesc|ocifreestatement|ociinternaldebug|ociloadlob|ocilogoff|ocilogon|ocinewcollection|ocinewcursor|ocinewdescriptor|ocinlogon|ocinumcols|ociparse|ociplogon|ociresult|ocirollback|ocirowcount|ocisavelob|ocisavelobfile|ociserverversion|ocisetprefetch|ocistatementtype|ociwritelobtofile|ociwritetemporarylob|octdec|odbc_autocommit|odbc_binmode|odbc_close|odbc_close_all|odbc_columnprivileges|odbc_columns|odbc_commit|odbc_connect|odbc_cursor|odbc_data_source|odbc_do|odbc_error|odbc_errormsg|odbc_exec|odbc_execute|odbc_fetch_array|odbc_fetch_into|odbc_fetch_object|odbc_fetch_row|odbc_field_len|odbc_field_name|odbc_field_num|odbc_field_precision|odbc_field_scale|odbc_field_type|odbc_foreignkeys|odbc_free_result|odbc_gettypeinfo|odbc_longreadlen|odbc_next_result|odbc_num_fields|odbc_num_rows|odbc_pconnect|odbc_prepare|odbc_primarykeys|odbc_procedurecolumns|odbc_procedures|odbc_result|odbc_result_all|odbc_rollback|odbc_setoption|odbc_specialcolumns|odbc_statistics|odbc_tableprivileges|odbc_tables|openal_buffer_create|openal_buffer_data|openal_buffer_destroy|openal_buffer_get|openal_buffer_loadwav|openal_context_create|openal_context_current|openal_context_destroy|openal_context_process|openal_context_suspend|openal_device_close|openal_device_open|openal_listener_get|openal_listener_set|openal_source_create|openal_source_destroy|openal_source_get|openal_source_pause|openal_source_play|openal_source_rewind|openal_source_set|openal_source_stop|openal_stream|opendir|openlog|openssl_cipher_iv_length|openssl_csr_export|openssl_csr_export_to_file|openssl_csr_get_public_key|openssl_csr_get_subject|openssl_csr_new|openssl_csr_sign|openssl_decrypt|openssl_dh_compute_key|openssl_digest|openssl_encrypt|openssl_error_string|openssl_free_key|openssl_get_cipher_methods|openssl_get_md_methods|openssl_get_privatekey|openssl_get_publickey|openssl_open|openssl_pkcs12_export|openssl_pkcs12_export_to_file|openssl_pkcs12_read|openssl_pkcs7_decrypt|openssl_pkcs7_encrypt|openssl_pkcs7_sign|openssl_pkcs7_verify|openssl_pkey_export|openssl_pkey_export_to_file|openssl_pkey_free|openssl_pkey_get_details|openssl_pkey_get_private|openssl_pkey_get_public|openssl_pkey_new|openssl_private_decrypt|openssl_private_encrypt|openssl_public_decrypt|openssl_public_encrypt|openssl_random_pseudo_bytes|openssl_seal|openssl_sign|openssl_verify|openssl_x509_check_private_key|openssl_x509_checkpurpose|openssl_x509_export|openssl_x509_export_to_file|openssl_x509_free|openssl_x509_parse|openssl_x509_read|ord|outeriterator|outofboundsexception|outofrangeexception|output_add_rewrite_var|output_reset_rewrite_vars|overflowexception|overload|override_function|ovrimos_close|ovrimos_commit|ovrimos_connect|ovrimos_cursor|ovrimos_exec|ovrimos_execute|ovrimos_fetch_into|ovrimos_fetch_row|ovrimos_field_len|ovrimos_field_name|ovrimos_field_num|ovrimos_field_type|ovrimos_free_result|ovrimos_longreadlen|ovrimos_num_fields|ovrimos_num_rows|ovrimos_prepare|ovrimos_result|ovrimos_result_all|ovrimos_rollback|pack|parentiterator|parse_ini_file|parse_ini_string|parse_str|parse_url|parsekit_compile_file|parsekit_compile_string|parsekit_func_arginfo|passthru|pathinfo|pclose|pcntl_alarm|pcntl_exec|pcntl_fork|pcntl_getpriority|pcntl_setpriority|pcntl_signal|pcntl_signal_dispatch|pcntl_sigprocmask|pcntl_sigtimedwait|pcntl_sigwaitinfo|pcntl_wait|pcntl_waitpid|pcntl_wexitstatus|pcntl_wifexited|pcntl_wifsignaled|pcntl_wifstopped|pcntl_wstopsig|pcntl_wtermsig|pdf_activate_item|pdf_add_annotation|pdf_add_bookmark|pdf_add_launchlink|pdf_add_locallink|pdf_add_nameddest|pdf_add_note|pdf_add_outline|pdf_add_pdflink|pdf_add_table_cell|pdf_add_textflow|pdf_add_thumbnail|pdf_add_weblink|pdf_arc|pdf_arcn|pdf_attach_file|pdf_begin_document|pdf_begin_font|pdf_begin_glyph|pdf_begin_item|pdf_begin_layer|pdf_begin_page|pdf_begin_page_ext|pdf_begin_pattern|pdf_begin_template|pdf_begin_template_ext|pdf_circle|pdf_clip|pdf_close|pdf_close_image|pdf_close_pdi|pdf_close_pdi_page|pdf_closepath|pdf_closepath_fill_stroke|pdf_closepath_stroke|pdf_concat|pdf_continue_text|pdf_create_3dview|pdf_create_action|pdf_create_annotation|pdf_create_bookmark|pdf_create_field|pdf_create_fieldgroup|pdf_create_gstate|pdf_create_pvf|pdf_create_textflow|pdf_curveto|pdf_define_layer|pdf_delete|pdf_delete_pvf|pdf_delete_table|pdf_delete_textflow|pdf_encoding_set_char|pdf_end_document|pdf_end_font|pdf_end_glyph|pdf_end_item|pdf_end_layer|pdf_end_page|pdf_end_page_ext|pdf_end_pattern|pdf_end_template|pdf_endpath|pdf_fill|pdf_fill_imageblock|pdf_fill_pdfblock|pdf_fill_stroke|pdf_fill_textblock|pdf_findfont|pdf_fit_image|pdf_fit_pdi_page|pdf_fit_table|pdf_fit_textflow|pdf_fit_textline|pdf_get_apiname|pdf_get_buffer|pdf_get_errmsg|pdf_get_errnum|pdf_get_font|pdf_get_fontname|pdf_get_fontsize|pdf_get_image_height|pdf_get_image_width|pdf_get_majorversion|pdf_get_minorversion|pdf_get_parameter|pdf_get_pdi_parameter|pdf_get_pdi_value|pdf_get_value|pdf_info_font|pdf_info_matchbox|pdf_info_table|pdf_info_textflow|pdf_info_textline|pdf_initgraphics|pdf_lineto|pdf_load_3ddata|pdf_load_font|pdf_load_iccprofile|pdf_load_image|pdf_makespotcolor|pdf_moveto|pdf_new|pdf_open_ccitt|pdf_open_file|pdf_open_gif|pdf_open_image|pdf_open_image_file|pdf_open_jpeg|pdf_open_memory_image|pdf_open_pdi|pdf_open_pdi_document|pdf_open_pdi_page|pdf_open_tiff|pdf_pcos_get_number|pdf_pcos_get_stream|pdf_pcos_get_string|pdf_place_image|pdf_place_pdi_page|pdf_process_pdi|pdf_rect|pdf_restore|pdf_resume_page|pdf_rotate|pdf_save|pdf_scale|pdf_set_border_color|pdf_set_border_dash|pdf_set_border_style|pdf_set_char_spacing|pdf_set_duration|pdf_set_gstate|pdf_set_horiz_scaling|pdf_set_info|pdf_set_info_author|pdf_set_info_creator|pdf_set_info_keywords|pdf_set_info_subject|pdf_set_info_title|pdf_set_layer_dependency|pdf_set_leading|pdf_set_parameter|pdf_set_text_matrix|pdf_set_text_pos|pdf_set_text_rendering|pdf_set_text_rise|pdf_set_value|pdf_set_word_spacing|pdf_setcolor|pdf_setdash|pdf_setdashpattern|pdf_setflat|pdf_setfont|pdf_setgray|pdf_setgray_fill|pdf_setgray_stroke|pdf_setlinecap|pdf_setlinejoin|pdf_setlinewidth|pdf_setmatrix|pdf_setmiterlimit|pdf_setpolydash|pdf_setrgbcolor|pdf_setrgbcolor_fill|pdf_setrgbcolor_stroke|pdf_shading|pdf_shading_pattern|pdf_shfill|pdf_show|pdf_show_boxed|pdf_show_xy|pdf_skew|pdf_stringwidth|pdf_stroke|pdf_suspend_page|pdf_translate|pdf_utf16_to_utf8|pdf_utf32_to_utf16|pdf_utf8_to_utf16|pdo|pdo_cubrid_schema|pdo_pgsqllobcreate|pdo_pgsqllobopen|pdo_pgsqllobunlink|pdo_sqlitecreateaggregate|pdo_sqlitecreatefunction|pdoexception|pdostatement|pfsockopen|pg_affected_rows|pg_cancel_query|pg_client_encoding|pg_close|pg_connect|pg_connection_busy|pg_connection_reset|pg_connection_status|pg_convert|pg_copy_from|pg_copy_to|pg_dbname|pg_delete|pg_end_copy|pg_escape_bytea|pg_escape_string|pg_execute|pg_fetch_all|pg_fetch_all_columns|pg_fetch_array|pg_fetch_assoc|pg_fetch_object|pg_fetch_result|pg_fetch_row|pg_field_is_null|pg_field_name|pg_field_num|pg_field_prtlen|pg_field_size|pg_field_table|pg_field_type|pg_field_type_oid|pg_free_result|pg_get_notify|pg_get_pid|pg_get_result|pg_host|pg_insert|pg_last_error|pg_last_notice|pg_last_oid|pg_lo_close|pg_lo_create|pg_lo_export|pg_lo_import|pg_lo_open|pg_lo_read|pg_lo_read_all|pg_lo_seek|pg_lo_tell|pg_lo_unlink|pg_lo_write|pg_meta_data|pg_num_fields|pg_num_rows|pg_options|pg_parameter_status|pg_pconnect|pg_ping|pg_port|pg_prepare|pg_put_line|pg_query|pg_query_params|pg_result_error|pg_result_error_field|pg_result_seek|pg_result_status|pg_select|pg_send_execute|pg_send_prepare|pg_send_query|pg_send_query_params|pg_set_client_encoding|pg_set_error_verbosity|pg_trace|pg_transaction_status|pg_tty|pg_unescape_bytea|pg_untrace|pg_update|pg_version|php_check_syntax|php_ini_loaded_file|php_ini_scanned_files|php_logo_guid|php_sapi_name|php_strip_whitespace|php_uname|phpcredits|phpinfo|phpversion|pi|png2wbmp|popen|pos|posix_access|posix_ctermid|posix_errno|posix_get_last_error|posix_getcwd|posix_getegid|posix_geteuid|posix_getgid|posix_getgrgid|posix_getgrnam|posix_getgroups|posix_getlogin|posix_getpgid|posix_getpgrp|posix_getpid|posix_getppid|posix_getpwnam|posix_getpwuid|posix_getrlimit|posix_getsid|posix_getuid|posix_initgroups|posix_isatty|posix_kill|posix_mkfifo|posix_mknod|posix_setegid|posix_seteuid|posix_setgid|posix_setpgid|posix_setsid|posix_setuid|posix_strerror|posix_times|posix_ttyname|posix_uname|pow|preg_filter|preg_grep|preg_last_error|preg_match|preg_match_all|preg_quote|preg_replace|preg_replace_callback|preg_split|prev|print|print_r|printer_abort|printer_close|printer_create_brush|printer_create_dc|printer_create_font|printer_create_pen|printer_delete_brush|printer_delete_dc|printer_delete_font|printer_delete_pen|printer_draw_bmp|printer_draw_chord|printer_draw_elipse|printer_draw_line|printer_draw_pie|printer_draw_rectangle|printer_draw_roundrect|printer_draw_text|printer_end_doc|printer_end_page|printer_get_option|printer_list|printer_logical_fontheight|printer_open|printer_select_brush|printer_select_font|printer_select_pen|printer_set_option|printer_start_doc|printer_start_page|printer_write|printf|proc_close|proc_get_status|proc_nice|proc_open|proc_terminate|property_exists|ps_add_bookmark|ps_add_launchlink|ps_add_locallink|ps_add_note|ps_add_pdflink|ps_add_weblink|ps_arc|ps_arcn|ps_begin_page|ps_begin_pattern|ps_begin_template|ps_circle|ps_clip|ps_close|ps_close_image|ps_closepath|ps_closepath_stroke|ps_continue_text|ps_curveto|ps_delete|ps_end_page|ps_end_pattern|ps_end_template|ps_fill|ps_fill_stroke|ps_findfont|ps_get_buffer|ps_get_parameter|ps_get_value|ps_hyphenate|ps_include_file|ps_lineto|ps_makespotcolor|ps_moveto|ps_new|ps_open_file|ps_open_image|ps_open_image_file|ps_open_memory_image|ps_place_image|ps_rect|ps_restore|ps_rotate|ps_save|ps_scale|ps_set_border_color|ps_set_border_dash|ps_set_border_style|ps_set_info|ps_set_parameter|ps_set_text_pos|ps_set_value|ps_setcolor|ps_setdash|ps_setflat|ps_setfont|ps_setgray|ps_setlinecap|ps_setlinejoin|ps_setlinewidth|ps_setmiterlimit|ps_setoverprintmode|ps_setpolydash|ps_shading|ps_shading_pattern|ps_shfill|ps_show|ps_show2|ps_show_boxed|ps_show_xy|ps_show_xy2|ps_string_geometry|ps_stringwidth|ps_stroke|ps_symbol|ps_symbol_name|ps_symbol_width|ps_translate|pspell_add_to_personal|pspell_add_to_session|pspell_check|pspell_clear_session|pspell_config_create|pspell_config_data_dir|pspell_config_dict_dir|pspell_config_ignore|pspell_config_mode|pspell_config_personal|pspell_config_repl|pspell_config_runtogether|pspell_config_save_repl|pspell_new|pspell_new_config|pspell_new_personal|pspell_save_wordlist|pspell_store_replacement|pspell_suggest|putenv|px_close|px_create_fp|px_date2string|px_delete|px_delete_record|px_get_field|px_get_info|px_get_parameter|px_get_record|px_get_schema|px_get_value|px_insert_record|px_new|px_numfields|px_numrecords|px_open_fp|px_put_record|px_retrieve_record|px_set_blob_file|px_set_parameter|px_set_tablename|px_set_targetencoding|px_set_value|px_timestamp2string|px_update_record|qdom_error|qdom_tree|quoted_printable_decode|quoted_printable_encode|quotemeta|rad2deg|radius_acct_open|radius_add_server|radius_auth_open|radius_close|radius_config|radius_create_request|radius_cvt_addr|radius_cvt_int|radius_cvt_string|radius_demangle|radius_demangle_mppe_key|radius_get_attr|radius_get_vendor_attr|radius_put_addr|radius_put_attr|radius_put_int|radius_put_string|radius_put_vendor_addr|radius_put_vendor_attr|radius_put_vendor_int|radius_put_vendor_string|radius_request_authenticator|radius_send_request|radius_server_secret|radius_strerror|rand|range|rangeexception|rar_wrapper_cache_stats|rararchive|rarentry|rarexception|rawurldecode|rawurlencode|read_exif_data|readdir|readfile|readgzfile|readline|readline_add_history|readline_callback_handler_install|readline_callback_handler_remove|readline_callback_read_char|readline_clear_history|readline_completion_function|readline_info|readline_list_history|readline_on_new_line|readline_read_history|readline_redisplay|readline_write_history|readlink|realpath|realpath_cache_get|realpath_cache_size|recode|recode_file|recode_string|recursivearrayiterator|recursivecachingiterator|recursivecallbackfilteriterator|recursivedirectoryiterator|recursivefilteriterator|recursiveiterator|recursiveiteratoriterator|recursiveregexiterator|recursivetreeiterator|reflection|reflectionclass|reflectionexception|reflectionextension|reflectionfunction|reflectionfunctionabstract|reflectionmethod|reflectionobject|reflectionparameter|reflectionproperty|reflector|regexiterator|register_shutdown_function|register_tick_function|rename|rename_function|require|require_once|reset|resetValue|resourcebundle|restore_error_handler|restore_exception_handler|restore_include_path|return|rewind|rewinddir|rmdir|round|rpm_close|rpm_get_tag|rpm_is_valid|rpm_open|rpm_version|rrd_create|rrd_error|rrd_fetch|rrd_first|rrd_graph|rrd_info|rrd_last|rrd_lastupdate|rrd_restore|rrd_tune|rrd_update|rrd_xport|rrdcreator|rrdgraph|rrdupdater|rsort|rtrim|runkit_class_adopt|runkit_class_emancipate|runkit_constant_add|runkit_constant_redefine|runkit_constant_remove|runkit_function_add|runkit_function_copy|runkit_function_redefine|runkit_function_remove|runkit_function_rename|runkit_import|runkit_lint|runkit_lint_file|runkit_method_add|runkit_method_copy|runkit_method_redefine|runkit_method_remove|runkit_method_rename|runkit_return_value_used|runkit_sandbox_output_handler|runkit_superglobals|runtimeexception|samconnection_commit|samconnection_connect|samconnection_constructor|samconnection_disconnect|samconnection_errno|samconnection_error|samconnection_isconnected|samconnection_peek|samconnection_peekall|samconnection_receive|samconnection_remove|samconnection_rollback|samconnection_send|samconnection_setDebug|samconnection_subscribe|samconnection_unsubscribe|sammessage_body|sammessage_constructor|sammessage_header|sca_createdataobject|sca_getservice|sca_localproxy_createdataobject|sca_soapproxy_createdataobject|scandir|sdo_das_changesummary_beginlogging|sdo_das_changesummary_endlogging|sdo_das_changesummary_getchangeddataobjects|sdo_das_changesummary_getchangetype|sdo_das_changesummary_getoldcontainer|sdo_das_changesummary_getoldvalues|sdo_das_changesummary_islogging|sdo_das_datafactory_addpropertytotype|sdo_das_datafactory_addtype|sdo_das_datafactory_getdatafactory|sdo_das_dataobject_getchangesummary|sdo_das_relational_applychanges|sdo_das_relational_construct|sdo_das_relational_createrootdataobject|sdo_das_relational_executepreparedquery|sdo_das_relational_executequery|sdo_das_setting_getlistindex|sdo_das_setting_getpropertyindex|sdo_das_setting_getpropertyname|sdo_das_setting_getvalue|sdo_das_setting_isset|sdo_das_xml_addtypes|sdo_das_xml_create|sdo_das_xml_createdataobject|sdo_das_xml_createdocument|sdo_das_xml_document_getrootdataobject|sdo_das_xml_document_getrootelementname|sdo_das_xml_document_getrootelementuri|sdo_das_xml_document_setencoding|sdo_das_xml_document_setxmldeclaration|sdo_das_xml_document_setxmlversion|sdo_das_xml_loadfile|sdo_das_xml_loadstring|sdo_das_xml_savefile|sdo_das_xml_savestring|sdo_datafactory_create|sdo_dataobject_clear|sdo_dataobject_createdataobject|sdo_dataobject_getcontainer|sdo_dataobject_getsequence|sdo_dataobject_gettypename|sdo_dataobject_gettypenamespaceuri|sdo_exception_getcause|sdo_list_insert|sdo_model_property_getcontainingtype|sdo_model_property_getdefault|sdo_model_property_getname|sdo_model_property_gettype|sdo_model_property_iscontainment|sdo_model_property_ismany|sdo_model_reflectiondataobject_construct|sdo_model_reflectiondataobject_export|sdo_model_reflectiondataobject_getcontainmentproperty|sdo_model_reflectiondataobject_getinstanceproperties|sdo_model_reflectiondataobject_gettype|sdo_model_type_getbasetype|sdo_model_type_getname|sdo_model_type_getnamespaceuri|sdo_model_type_getproperties|sdo_model_type_getproperty|sdo_model_type_isabstracttype|sdo_model_type_isdatatype|sdo_model_type_isinstance|sdo_model_type_isopentype|sdo_model_type_issequencedtype|sdo_sequence_getproperty|sdo_sequence_insert|sdo_sequence_move|seekableiterator|sem_acquire|sem_get|sem_release|sem_remove|serializable|serialize|session_cache_expire|session_cache_limiter|session_commit|session_decode|session_destroy|session_encode|session_get_cookie_params|session_id|session_is_registered|session_module_name|session_name|session_pgsql_add_error|session_pgsql_get_error|session_pgsql_get_field|session_pgsql_reset|session_pgsql_set_field|session_pgsql_status|session_regenerate_id|session_register|session_save_path|session_set_cookie_params|session_set_save_handler|session_start|session_unregister|session_unset|session_write_close|setCounterClass|set_error_handler|set_exception_handler|set_file_buffer|set_include_path|set_magic_quotes_runtime|set_socket_blocking|set_time_limit|setcookie|setlocale|setproctitle|setrawcookie|setstaticpropertyvalue|setthreadtitle|settype|sha1|sha1_file|shell_exec|shm_attach|shm_detach|shm_get_var|shm_has_var|shm_put_var|shm_remove|shm_remove_var|shmop_close|shmop_delete|shmop_open|shmop_read|shmop_size|shmop_write|show_source|shuffle|signeurlpaiement|similar_text|simplexml_import_dom|simplexml_load_file|simplexml_load_string|simplexmlelement|simplexmliterator|sin|sinh|sizeof|sleep|snmp|snmp2_get|snmp2_getnext|snmp2_real_walk|snmp2_set|snmp2_walk|snmp3_get|snmp3_getnext|snmp3_real_walk|snmp3_set|snmp3_walk|snmp_get_quick_print|snmp_get_valueretrieval|snmp_read_mib|snmp_set_enum_print|snmp_set_oid_numeric_print|snmp_set_oid_output_format|snmp_set_quick_print|snmp_set_valueretrieval|snmpget|snmpgetnext|snmprealwalk|snmpset|snmpwalk|snmpwalkoid|soapclient|soapfault|soapheader|soapparam|soapserver|soapvar|socket_accept|socket_bind|socket_clear_error|socket_close|socket_connect|socket_create|socket_create_listen|socket_create_pair|socket_get_option|socket_get_status|socket_getpeername|socket_getsockname|socket_last_error|socket_listen|socket_read|socket_recv|socket_recvfrom|socket_select|socket_send|socket_sendto|socket_set_block|socket_set_blocking|socket_set_nonblock|socket_set_option|socket_set_timeout|socket_shutdown|socket_strerror|socket_write|solr_get_version|solrclient|solrclientexception|solrdocument|solrdocumentfield|solrexception|solrgenericresponse|solrillegalargumentexception|solrillegaloperationexception|solrinputdocument|solrmodifiableparams|solrobject|solrparams|solrpingresponse|solrquery|solrqueryresponse|solrresponse|solrupdateresponse|solrutils|sort|soundex|sphinxclient|spl_autoload|spl_autoload_call|spl_autoload_extensions|spl_autoload_functions|spl_autoload_register|spl_autoload_unregister|spl_classes|spl_object_hash|splbool|spldoublylinkedlist|splenum|splfileinfo|splfileobject|splfixedarray|splfloat|splheap|splint|split|spliti|splmaxheap|splminheap|splobjectstorage|splobserver|splpriorityqueue|splqueue|splstack|splstring|splsubject|spltempfileobject|spoofchecker|sprintf|sql_regcase|sqlite3|sqlite3result|sqlite3stmt|sqlite_array_query|sqlite_busy_timeout|sqlite_changes|sqlite_close|sqlite_column|sqlite_create_aggregate|sqlite_create_function|sqlite_current|sqlite_error_string|sqlite_escape_string|sqlite_exec|sqlite_factory|sqlite_fetch_all|sqlite_fetch_array|sqlite_fetch_column_types|sqlite_fetch_object|sqlite_fetch_single|sqlite_fetch_string|sqlite_field_name|sqlite_has_more|sqlite_has_prev|sqlite_key|sqlite_last_error|sqlite_last_insert_rowid|sqlite_libencoding|sqlite_libversion|sqlite_next|sqlite_num_fields|sqlite_num_rows|sqlite_open|sqlite_popen|sqlite_prev|sqlite_query|sqlite_rewind|sqlite_seek|sqlite_single_query|sqlite_udf_decode_binary|sqlite_udf_encode_binary|sqlite_unbuffered_query|sqlite_valid|sqrt|srand|sscanf|ssdeep_fuzzy_compare|ssdeep_fuzzy_hash|ssdeep_fuzzy_hash_filename|ssh2_auth_hostbased_file|ssh2_auth_none|ssh2_auth_password|ssh2_auth_pubkey_file|ssh2_connect|ssh2_exec|ssh2_fetch_stream|ssh2_fingerprint|ssh2_methods_negotiated|ssh2_publickey_add|ssh2_publickey_init|ssh2_publickey_list|ssh2_publickey_remove|ssh2_scp_recv|ssh2_scp_send|ssh2_sftp|ssh2_sftp_lstat|ssh2_sftp_mkdir|ssh2_sftp_readlink|ssh2_sftp_realpath|ssh2_sftp_rename|ssh2_sftp_rmdir|ssh2_sftp_stat|ssh2_sftp_symlink|ssh2_sftp_unlink|ssh2_shell|ssh2_tunnel|stat|stats_absolute_deviation|stats_cdf_beta|stats_cdf_binomial|stats_cdf_cauchy|stats_cdf_chisquare|stats_cdf_exponential|stats_cdf_f|stats_cdf_gamma|stats_cdf_laplace|stats_cdf_logistic|stats_cdf_negative_binomial|stats_cdf_noncentral_chisquare|stats_cdf_noncentral_f|stats_cdf_poisson|stats_cdf_t|stats_cdf_uniform|stats_cdf_weibull|stats_covariance|stats_den_uniform|stats_dens_beta|stats_dens_cauchy|stats_dens_chisquare|stats_dens_exponential|stats_dens_f|stats_dens_gamma|stats_dens_laplace|stats_dens_logistic|stats_dens_negative_binomial|stats_dens_normal|stats_dens_pmf_binomial|stats_dens_pmf_hypergeometric|stats_dens_pmf_poisson|stats_dens_t|stats_dens_weibull|stats_harmonic_mean|stats_kurtosis|stats_rand_gen_beta|stats_rand_gen_chisquare|stats_rand_gen_exponential|stats_rand_gen_f|stats_rand_gen_funiform|stats_rand_gen_gamma|stats_rand_gen_ibinomial|stats_rand_gen_ibinomial_negative|stats_rand_gen_int|stats_rand_gen_ipoisson|stats_rand_gen_iuniform|stats_rand_gen_noncenral_chisquare|stats_rand_gen_noncentral_f|stats_rand_gen_noncentral_t|stats_rand_gen_normal|stats_rand_gen_t|stats_rand_get_seeds|stats_rand_phrase_to_seeds|stats_rand_ranf|stats_rand_setall|stats_skew|stats_standard_deviation|stats_stat_binomial_coef|stats_stat_correlation|stats_stat_gennch|stats_stat_independent_t|stats_stat_innerproduct|stats_stat_noncentral_t|stats_stat_paired_t|stats_stat_percentile|stats_stat_powersum|stats_variance|stomp|stomp_connect_error|stomp_version|stompexception|stompframe|str_getcsv|str_ireplace|str_pad|str_repeat|str_replace|str_rot13|str_shuffle|str_split|str_word_count|strcasecmp|strchr|strcmp|strcoll|strcspn|stream_bucket_append|stream_bucket_make_writeable|stream_bucket_new|stream_bucket_prepend|stream_context_create|stream_context_get_default|stream_context_get_options|stream_context_get_params|stream_context_set_default|stream_context_set_option|stream_context_set_params|stream_copy_to_stream|stream_encoding|stream_filter_append|stream_filter_prepend|stream_filter_register|stream_filter_remove|stream_get_contents|stream_get_filters|stream_get_line|stream_get_meta_data|stream_get_transports|stream_get_wrappers|stream_is_local|stream_notification_callback|stream_register_wrapper|stream_resolve_include_path|stream_select|stream_set_blocking|stream_set_read_buffer|stream_set_timeout|stream_set_write_buffer|stream_socket_accept|stream_socket_client|stream_socket_enable_crypto|stream_socket_get_name|stream_socket_pair|stream_socket_recvfrom|stream_socket_sendto|stream_socket_server|stream_socket_shutdown|stream_supports_lock|stream_wrapper_register|stream_wrapper_restore|stream_wrapper_unregister|streamwrapper|strftime|strip_tags|stripcslashes|stripos|stripslashes|stristr|strlen|strnatcasecmp|strnatcmp|strncasecmp|strncmp|strpbrk|strpos|strptime|strrchr|strrev|strripos|strrpos|strspn|strstr|strtok|strtolower|strtotime|strtoupper|strtr|strval|substr|substr_compare|substr_count|substr_replace|svm|svmmodel|svn_add|svn_auth_get_parameter|svn_auth_set_parameter|svn_blame|svn_cat|svn_checkout|svn_cleanup|svn_client_version|svn_commit|svn_delete|svn_diff|svn_export|svn_fs_abort_txn|svn_fs_apply_text|svn_fs_begin_txn2|svn_fs_change_node_prop|svn_fs_check_path|svn_fs_contents_changed|svn_fs_copy|svn_fs_delete|svn_fs_dir_entries|svn_fs_file_contents|svn_fs_file_length|svn_fs_is_dir|svn_fs_is_file|svn_fs_make_dir|svn_fs_make_file|svn_fs_node_created_rev|svn_fs_node_prop|svn_fs_props_changed|svn_fs_revision_prop|svn_fs_revision_root|svn_fs_txn_root|svn_fs_youngest_rev|svn_import|svn_log|svn_ls|svn_mkdir|svn_repos_create|svn_repos_fs|svn_repos_fs_begin_txn_for_commit|svn_repos_fs_commit_txn|svn_repos_hotcopy|svn_repos_open|svn_repos_recover|svn_revert|svn_status|svn_update|swf_actiongeturl|swf_actiongotoframe|swf_actiongotolabel|swf_actionnextframe|swf_actionplay|swf_actionprevframe|swf_actionsettarget|swf_actionstop|swf_actiontogglequality|swf_actionwaitforframe|swf_addbuttonrecord|swf_addcolor|swf_closefile|swf_definebitmap|swf_definefont|swf_defineline|swf_definepoly|swf_definerect|swf_definetext|swf_endbutton|swf_enddoaction|swf_endshape|swf_endsymbol|swf_fontsize|swf_fontslant|swf_fonttracking|swf_getbitmapinfo|swf_getfontinfo|swf_getframe|swf_labelframe|swf_lookat|swf_modifyobject|swf_mulcolor|swf_nextid|swf_oncondition|swf_openfile|swf_ortho|swf_ortho2|swf_perspective|swf_placeobject|swf_polarview|swf_popmatrix|swf_posround|swf_pushmatrix|swf_removeobject|swf_rotate|swf_scale|swf_setfont|swf_setframe|swf_shapearc|swf_shapecurveto|swf_shapecurveto3|swf_shapefillbitmapclip|swf_shapefillbitmaptile|swf_shapefilloff|swf_shapefillsolid|swf_shapelinesolid|swf_shapelineto|swf_shapemoveto|swf_showframe|swf_startbutton|swf_startdoaction|swf_startshape|swf_startsymbol|swf_textwidth|swf_translate|swf_viewport|swfaction|swfbitmap|swfbutton|swfdisplayitem|swffill|swffont|swffontchar|swfgradient|swfmorph|swfmovie|swfprebuiltclip|swfshape|swfsound|swfsoundinstance|swfsprite|swftext|swftextfield|swfvideostream|swish_construct|swish_getmetalist|swish_getpropertylist|swish_prepare|swish_query|swishresult_getmetalist|swishresult_stem|swishresults_getparsedwords|swishresults_getremovedstopwords|swishresults_nextresult|swishresults_seekresult|swishsearch_execute|swishsearch_resetlimit|swishsearch_setlimit|swishsearch_setphrasedelimiter|swishsearch_setsort|swishsearch_setstructure|sybase_affected_rows|sybase_close|sybase_connect|sybase_data_seek|sybase_deadlock_retry_count|sybase_fetch_array|sybase_fetch_assoc|sybase_fetch_field|sybase_fetch_object|sybase_fetch_row|sybase_field_seek|sybase_free_result|sybase_get_last_message|sybase_min_client_severity|sybase_min_error_severity|sybase_min_message_severity|sybase_min_server_severity|sybase_num_fields|sybase_num_rows|sybase_pconnect|sybase_query|sybase_result|sybase_select_db|sybase_set_message_handler|sybase_unbuffered_query|symlink|sys_get_temp_dir|sys_getloadavg|syslog|system|tag|tan|tanh|tcpwrap_check|tempnam|textdomain|tidy|tidy_access_count|tidy_config_count|tidy_diagnose|tidy_error_count|tidy_get_error_buffer|tidy_get_output|tidy_load_config|tidy_reset_config|tidy_save_config|tidy_set_encoding|tidy_setopt|tidy_warning_count|tidynode|time|time_nanosleep|time_sleep_until|timezone_abbreviations_list|timezone_identifiers_list|timezone_location_get|timezone_name_from_abbr|timezone_name_get|timezone_offset_get|timezone_open|timezone_transitions_get|timezone_version_get|tmpfile|token_get_all|token_name|tokyotyrant|tokyotyrantquery|tokyotyranttable|tostring|tostring|touch|trait_exists|transliterator|traversable|trigger_error|trim|uasort|ucfirst|ucwords|udm_add_search_limit|udm_alloc_agent|udm_alloc_agent_array|udm_api_version|udm_cat_list|udm_cat_path|udm_check_charset|udm_check_stored|udm_clear_search_limits|udm_close_stored|udm_crc32|udm_errno|udm_error|udm_find|udm_free_agent|udm_free_ispell_data|udm_free_res|udm_get_doc_count|udm_get_res_field|udm_get_res_param|udm_hash32|udm_load_ispell_data|udm_open_stored|udm_set_agent_param|uksort|umask|underflowexception|unexpectedvalueexception|uniqid|unixtojd|unlink|unpack|unregister_tick_function|unserialize|unset|urldecode|urlencode|use_soap_error_handler|user_error|usleep|usort|utf8_decode|utf8_encode|v8js|v8jsexception|var_dump|var_export|variant|variant_abs|variant_add|variant_and|variant_cast|variant_cat|variant_cmp|variant_date_from_timestamp|variant_date_to_timestamp|variant_div|variant_eqv|variant_fix|variant_get_type|variant_idiv|variant_imp|variant_int|variant_mod|variant_mul|variant_neg|variant_not|variant_or|variant_pow|variant_round|variant_set|variant_set_type|variant_sub|variant_xor|version_compare|vfprintf|virtual|vpopmail_add_alias_domain|vpopmail_add_alias_domain_ex|vpopmail_add_domain|vpopmail_add_domain_ex|vpopmail_add_user|vpopmail_alias_add|vpopmail_alias_del|vpopmail_alias_del_domain|vpopmail_alias_get|vpopmail_alias_get_all|vpopmail_auth_user|vpopmail_del_domain|vpopmail_del_domain_ex|vpopmail_del_user|vpopmail_error|vpopmail_passwd|vpopmail_set_user_quota|vprintf|vsprintf|w32api_deftype|w32api_init_dtype|w32api_invoke_function|w32api_register_function|w32api_set_call_method|wddx_add_vars|wddx_deserialize|wddx_packet_end|wddx_packet_start|wddx_serialize_value|wddx_serialize_vars|win32_continue_service|win32_create_service|win32_delete_service|win32_get_last_control_message|win32_pause_service|win32_ps_list_procs|win32_ps_stat_mem|win32_ps_stat_proc|win32_query_service_status|win32_set_service_status|win32_start_service|win32_start_service_ctrl_dispatcher|win32_stop_service|wincache_fcache_fileinfo|wincache_fcache_meminfo|wincache_lock|wincache_ocache_fileinfo|wincache_ocache_meminfo|wincache_refresh_if_changed|wincache_rplist_fileinfo|wincache_rplist_meminfo|wincache_scache_info|wincache_scache_meminfo|wincache_ucache_add|wincache_ucache_cas|wincache_ucache_clear|wincache_ucache_dec|wincache_ucache_delete|wincache_ucache_exists|wincache_ucache_get|wincache_ucache_inc|wincache_ucache_info|wincache_ucache_meminfo|wincache_ucache_set|wincache_unlock|wordwrap|xattr_get|xattr_list|xattr_remove|xattr_set|xattr_supported|xdiff_file_bdiff|xdiff_file_bdiff_size|xdiff_file_bpatch|xdiff_file_diff|xdiff_file_diff_binary|xdiff_file_merge3|xdiff_file_patch|xdiff_file_patch_binary|xdiff_file_rabdiff|xdiff_string_bdiff|xdiff_string_bdiff_size|xdiff_string_bpatch|xdiff_string_diff|xdiff_string_diff_binary|xdiff_string_merge3|xdiff_string_patch|xdiff_string_patch_binary|xdiff_string_rabdiff|xhprof_disable|xhprof_enable|xhprof_sample_disable|xhprof_sample_enable|xml_error_string|xml_get_current_byte_index|xml_get_current_column_number|xml_get_current_line_number|xml_get_error_code|xml_parse|xml_parse_into_struct|xml_parser_create|xml_parser_create_ns|xml_parser_free|xml_parser_get_option|xml_parser_set_option|xml_set_character_data_handler|xml_set_default_handler|xml_set_element_handler|xml_set_end_namespace_decl_handler|xml_set_external_entity_ref_handler|xml_set_notation_decl_handler|xml_set_object|xml_set_processing_instruction_handler|xml_set_start_namespace_decl_handler|xml_set_unparsed_entity_decl_handler|xmlreader|xmlrpc_decode|xmlrpc_decode_request|xmlrpc_encode|xmlrpc_encode_request|xmlrpc_get_type|xmlrpc_is_fault|xmlrpc_parse_method_descriptions|xmlrpc_server_add_introspection_data|xmlrpc_server_call_method|xmlrpc_server_create|xmlrpc_server_destroy|xmlrpc_server_register_introspection_callback|xmlrpc_server_register_method|xmlrpc_set_type|xmlwriter_end_attribute|xmlwriter_end_cdata|xmlwriter_end_comment|xmlwriter_end_document|xmlwriter_end_dtd|xmlwriter_end_dtd_attlist|xmlwriter_end_dtd_element|xmlwriter_end_dtd_entity|xmlwriter_end_element|xmlwriter_end_pi|xmlwriter_flush|xmlwriter_full_end_element|xmlwriter_open_memory|xmlwriter_open_uri|xmlwriter_output_memory|xmlwriter_set_indent|xmlwriter_set_indent_string|xmlwriter_start_attribute|xmlwriter_start_attribute_ns|xmlwriter_start_cdata|xmlwriter_start_comment|xmlwriter_start_document|xmlwriter_start_dtd|xmlwriter_start_dtd_attlist|xmlwriter_start_dtd_element|xmlwriter_start_dtd_entity|xmlwriter_start_element|xmlwriter_start_element_ns|xmlwriter_start_pi|xmlwriter_text|xmlwriter_write_attribute|xmlwriter_write_attribute_ns|xmlwriter_write_cdata|xmlwriter_write_comment|xmlwriter_write_dtd|xmlwriter_write_dtd_attlist|xmlwriter_write_dtd_element|xmlwriter_write_dtd_entity|xmlwriter_write_element|xmlwriter_write_element_ns|xmlwriter_write_pi|xmlwriter_write_raw|xpath_eval|xpath_eval_expression|xpath_new_context|xpath_register_ns|xpath_register_ns_auto|xptr_eval|xptr_new_context|xslt_backend_info|xslt_backend_name|xslt_backend_version|xslt_create|xslt_errno|xslt_error|xslt_free|xslt_getopt|xslt_process|xslt_set_base|xslt_set_encoding|xslt_set_error_handler|xslt_set_log|xslt_set_object|xslt_set_sax_handler|xslt_set_sax_handlers|xslt_set_scheme_handler|xslt_set_scheme_handlers|xslt_setopt|xsltprocessor|yaml_emit|yaml_emit_file|yaml_parse|yaml_parse_file|yaml_parse_url|yaz_addinfo|yaz_ccl_conf|yaz_ccl_parse|yaz_close|yaz_connect|yaz_database|yaz_element|yaz_errno|yaz_error|yaz_es|yaz_es_result|yaz_get_option|yaz_hits|yaz_itemorder|yaz_present|yaz_range|yaz_record|yaz_scan|yaz_scan_result|yaz_schema|yaz_search|yaz_set_option|yaz_sort|yaz_syntax|yaz_wait|yp_all|yp_cat|yp_err_string|yp_errno|yp_first|yp_get_default_domain|yp_master|yp_match|yp_next|yp_order|zend_logo_guid|zend_thread_id|zend_version|zip_close|zip_entry_close|zip_entry_compressedsize|zip_entry_compressionmethod|zip_entry_filesize|zip_entry_name|zip_entry_open|zip_entry_read|zip_open|zip_read|ziparchive|ziparchive_addemptydir|ziparchive_addfile|ziparchive_addfromstring|ziparchive_close|ziparchive_deleteindex|ziparchive_deletename|ziparchive_extractto|ziparchive_getarchivecomment|ziparchive_getcommentindex|ziparchive_getcommentname|ziparchive_getfromindex|ziparchive_getfromname|ziparchive_getnameindex|ziparchive_getstatusstring|ziparchive_getstream|ziparchive_locatename|ziparchive_open|ziparchive_renameindex|ziparchive_renamename|ziparchive_setCommentName|ziparchive_setarchivecomment|ziparchive_setcommentindex|ziparchive_statindex|ziparchive_statname|ziparchive_unchangeall|ziparchive_unchangearchive|ziparchive_unchangeindex|ziparchive_unchangename|zlib_get_coding_type".split("|")),n=i.arrayToMap("abstract|and|array|as|break|case|catch|class|clone|const|continue|declare|default|do|else|elseif|enddeclare|endfor|endforeach|endif|endswitch|endwhile|extends|final|for|foreach|function|global|goto|if|implements|interface|instanceof|namespace|new|or|private|protected|public|static|switch|throw|trait|try|use|var|while|xor".split("|")),r=i.arrayToMap("die|echo|empty|exit|eval|include|include_once|isset|list|require|require_once|return|print|unset".split("|")),o=i.arrayToMap("true|TRUE|false|FALSE|null|NULL|__CLASS__|__DIR__|__FILE__|__LINE__|__METHOD__|__FUNCTION__|__NAMESPACE__".split("|")),u=i.arrayToMap("$GLOBALS|$_SERVER|$_GET|$_POST|$_FILES|$_REQUEST|$_SESSION|$_ENV|$_COOKIE|$php_errormsg|$HTTP_RAW_POST_DATA|$http_response_header|$argc|$argv".split("|")),a=i.arrayToMap("key_exists|cairo_matrix_create_scale|cairo_matrix_create_translate|call_user_method|call_user_method_array|com_addref|com_get|com_invoke|com_isenum|com_load|com_release|com_set|connection_timeout|cubrid_load_from_glo|cubrid_new_glo|cubrid_save_to_glo|cubrid_send_glo|define_syslog_variables|dl|ereg|ereg_replace|eregi|eregi_replace|hw_documentattributes|hw_documentbodytag|hw_documentsize|hw_outputdocument|imagedashedline|maxdb_bind_param|maxdb_bind_result|maxdb_client_encoding|maxdb_close_long_data|maxdb_execute|maxdb_fetch|maxdb_get_metadata|maxdb_param_count|maxdb_send_long_data|mcrypt_ecb|mcrypt_generic_end|mime_content_type|mysql_createdb|mysql_dbname|mysql_db_query|mysql_drop_db|mysql_dropdb|mysql_escape_string|mysql_fieldflags|mysql_fieldflags|mysql_fieldname|mysql_fieldtable|mysql_fieldtype|mysql_freeresult|mysql_listdbs|mysql_list_fields|mysql_listfields|mysql_list_tables|mysql_listtables|mysql_numfields|mysql_numrows|mysql_selectdb|mysql_tablename|mysqli_bind_param|mysqli_bind_result|mysqli_disable_reads_from_master|mysqli_disable_rpl_parse|mysqli_enable_reads_from_master|mysqli_enable_rpl_parse|mysqli_execute|mysqli_fetch|mysqli_get_metadata|mysqli_master_query|mysqli_param_count|mysqli_rpl_parse_enabled|mysqli_rpl_probe|mysqli_rpl_query_type|mysqli_send_long_data|mysqli_send_query|mysqli_slave_query|ocibindbyname|ocicancel|ocicloselob|ocicollappend|ocicollassign|ocicollassignelem|ocicollgetelem|ocicollmax|ocicollsize|ocicolltrim|ocicolumnisnull|ocicolumnname|ocicolumnprecision|ocicolumnscale|ocicolumnsize|ocicolumntype|ocicolumntyperaw|ocicommit|ocidefinebyname|ocierror|ociexecute|ocifetch|ocifetchinto|ocifetchstatement|ocifreecollection|ocifreecursor|ocifreedesc|ocifreestatement|ociinternaldebug|ociloadlob|ocilogoff|ocilogon|ocinewcollection|ocinewcursor|ocinewdescriptor|ocinlogon|ocinumcols|ociparse|ociplogon|ociresult|ocirollback|ocirowcount|ocisavelob|ocisavelobfile|ociserverversion|ocisetprefetch|ocistatementtype|ociwritelobtofile|ociwritetemporarylob|PDF_add_annotation|PDF_add_bookmark|PDF_add_launchlink|PDF_add_locallink|PDF_add_note|PDF_add_outline|PDF_add_pdflink|PDF_add_weblink|PDF_attach_file|PDF_begin_page|PDF_begin_template|PDF_close_pdi|PDF_close|PDF_findfont|PDF_get_font|PDF_get_fontname|PDF_get_fontsize|PDF_get_image_height|PDF_get_image_width|PDF_get_majorversion|PDF_get_minorversion|PDF_get_pdi_parameter|PDF_get_pdi_value|PDF_open_ccitt|PDF_open_file|PDF_open_gif|PDF_open_image_file|PDF_open_image|PDF_open_jpeg|PDF_open_pdi|PDF_open_tiff|PDF_place_image|PDF_place_pdi_page|PDF_set_border_color|PDF_set_border_dash|PDF_set_border_style|PDF_set_char_spacing|PDF_set_duration|PDF_set_horiz_scaling|PDF_set_info_author|PDF_set_info_creator|PDF_set_info_keywords|PDF_set_info_subject|PDF_set_info_title|PDF_set_leading|PDF_set_text_matrix|PDF_set_text_rendering|PDF_set_text_rise|PDF_set_word_spacing|PDF_setgray_fill|PDF_setgray_stroke|PDF_setgray|PDF_setpolydash|PDF_setrgbcolor_fill|PDF_setrgbcolor_stroke|PDF_setrgbcolor|PDF_show_boxed|php_check_syntax|px_set_tablename|px_set_targetencoding|runkit_sandbox_output_handler|session_is_registered|session_register|session_unregisterset_magic_quotes_runtime|magic_quotes_runtime|set_socket_blocking|socket_set_blocking|set_socket_timeout|socket_set_timeout|split|spliti|sql_regcase".split("|")),f=i.arrayToMap("cfunction|old_function".split("|")),l=i.arrayToMap([]);this.$rules={start:[{token:"comment",regex:/(?:#|\/\/)(?:[^?]|\?[^>])*/},e.getStartRule("doc-start"),{token:"comment",regex:"\\/\\*",next:"comment"},{token:"string.regexp",regex:"[/](?:(?:\\[(?:\\\\]|[^\\]])+\\])|(?:\\\\/|[^\\]/]))*[/][gimy]*\\s*(?=[).,;]|$)"},{token:"string",regex:'"',next:"qqstring"},{token:"string",regex:"'",next:"qstring"},{token:"constant.numeric",regex:"0[xX][0-9a-fA-F]+\\b"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:"constant.language",regex:"\\b(?:DEFAULT_INCLUDE_PATH|E_(?:ALL|CO(?:MPILE_(?:ERROR|WARNING)|RE_(?:ERROR|WARNING))|ERROR|NOTICE|PARSE|STRICT|USER_(?:ERROR|NOTICE|WARNING)|WARNING)|P(?:EAR_(?:EXTENSION_DIR|INSTALL_DIR)|HP_(?:BINDIR|CONFIG_FILE_(?:PATH|SCAN_DIR)|DATADIR|E(?:OL|XTENSION_DIR)|INT_(?:MAX|SIZE)|L(?:IBDIR|OCALSTATEDIR)|O(?:S|UTPUT_HANDLER_(?:CONT|END|START))|PREFIX|S(?:API|HLIB_SUFFIX|YSCONFDIR)|VERSION))|__COMPILER_HALT_OFFSET__)\\b"},{token:["keyword","text","support.class"],regex:"\\b(new)(\\s+)(\\w+)"},{token:["support.class","keyword.operator"],regex:"\\b(\\w+)(::)"},{token:"constant.language",regex:"\\b(?:A(?:B(?:DAY_(?:1|2|3|4|5|6|7)|MON_(?:1(?:0|1|2|)|2|3|4|5|6|7|8|9))|LT_DIGITS|M_STR|SSERT_(?:ACTIVE|BAIL|CALLBACK|QUIET_EVAL|WARNING))|C(?:ASE_(?:LOWER|UPPER)|HAR_MAX|O(?:DESET|NNECTION_(?:ABORTED|NORMAL|TIMEOUT)|UNT_(?:NORMAL|RECURSIVE))|R(?:EDITS_(?:ALL|DOCS|FULLPAGE|G(?:ENERAL|ROUP)|MODULES|QA|SAPI)|NCYSTR|YPT_(?:BLOWFISH|EXT_DES|MD5|S(?:ALT_LENGTH|TD_DES)))|URRENCY_SYMBOL)|D(?:AY_(?:1|2|3|4|5|6|7)|ECIMAL_POINT|IRECTORY_SEPARATOR|_(?:FMT|T_FMT))|E(?:NT_(?:COMPAT|NOQUOTES|QUOTES)|RA(?:_(?:D_(?:FMT|T_FMT)|T_FMT|YEAR)|)|XTR_(?:IF_EXISTS|OVERWRITE|PREFIX_(?:ALL|I(?:F_EXISTS|NVALID)|SAME)|SKIP))|FRAC_DIGITS|GROUPING|HTML_(?:ENTITIES|SPECIALCHARS)|IN(?:FO_(?:ALL|C(?:ONFIGURATION|REDITS)|ENVIRONMENT|GENERAL|LICENSE|MODULES|VARIABLES)|I_(?:ALL|PERDIR|SYSTEM|USER)|T_(?:CURR_SYMBOL|FRAC_DIGITS))|L(?:C_(?:ALL|C(?:OLLATE|TYPE)|M(?:ESSAGES|ONETARY)|NUMERIC|TIME)|O(?:CK_(?:EX|NB|SH|UN)|G_(?:A(?:LERT|UTH(?:PRIV|))|C(?:ONS|R(?:IT|ON))|D(?:AEMON|EBUG)|E(?:MERG|RR)|INFO|KERN|L(?:OCAL(?:0|1|2|3|4|5|6|7)|PR)|MAIL|N(?:DELAY|EWS|O(?:TICE|WAIT))|ODELAY|P(?:ERROR|ID)|SYSLOG|U(?:SER|UCP)|WARNING)))|M(?:ON_(?:1(?:0|1|2|)|2|3|4|5|6|7|8|9|DECIMAL_POINT|GROUPING|THOUSANDS_SEP)|_(?:1_PI|2_(?:PI|SQRTPI)|E|L(?:N(?:10|2)|OG(?:10E|2E))|PI(?:_(?:2|4)|)|SQRT(?:1_2|2)))|N(?:EGATIVE_SIGN|O(?:EXPR|STR)|_(?:CS_PRECEDES|S(?:EP_BY_SPACE|IGN_POSN)))|P(?:ATH(?:INFO_(?:BASENAME|DIRNAME|EXTENSION)|_SEPARATOR)|M_STR|OSITIVE_SIGN|_(?:CS_PRECEDES|S(?:EP_BY_SPACE|IGN_POSN)))|RADIXCHAR|S(?:EEK_(?:CUR|END|SET)|ORT_(?:ASC|DESC|NUMERIC|REGULAR|STRING)|TR_PAD_(?:BOTH|LEFT|RIGHT))|T(?:HOUS(?:ANDS_SEP|EP)|_FMT(?:_AMPM|))|YES(?:EXPR|STR)|STD(?:IN|OUT|ERR))\\b"},{token:function(e){return n.hasOwnProperty(e)?"keyword":o.hasOwnProperty(e)?"constant.language":u.hasOwnProperty(e)?"variable.language":l.hasOwnProperty(e)?"invalid.illegal":t.hasOwnProperty(e)?"support.function":e=="debugger"?"invalid.deprecated":e.match(/^(\$[a-zA-Z_\x7f-\uffff][a-zA-Z0-9_\x7f-\uffff]*|self|parent)$/)?"variable":"identifier"},regex:/[a-zA-Z_$\x7f-\uffff][a-zA-Z0-9_\x7f-\uffff]*/},{onMatch:function(e,t,n){e=e.substr(3);if(e[0]=="'"||e[0]=='"')e=e.slice(1,-1);return n.unshift(this.next,e),"markup.list"},regex:/<<<(?:\w+|'\w+'|"\w+")$/,next:"heredoc"},{token:"keyword.operator",regex:"::|!|\\$|%|&|\\*|\\-\\-|\\-|\\+\\+|\\+|~|===|==|!=|!==|<=|>=|=>|<<=|>>=|>>>=|<>|<|>|=|!|&&|\\|\\||\\?\\:|\\*=|%=|\\+=|\\-=|&=|\\^=|\\b(?:in|instanceof|new|delete|typeof|void)"},{token:"paren.lparen",regex:"[[({]"},{token:"paren.rparen",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],heredoc:[{onMatch:function(e,t,n){return n[1]!=e?"string":(n.shift(),n.shift(),"markup.list")},regex:"^\\w+(?=;?$)",next:"start"},{token:"string",regex:".*"}],comment:[{token:"comment",regex:"\\*\\/",next:"start"},{defaultToken:"comment"}],qqstring:[{token:"constant.language.escape",regex:'\\\\(?:[nrtvef\\\\"$]|[0-7]{1,3}|x[0-9A-Fa-f]{1,2})'},{token:"variable",regex:/\$[\w]+(?:\[[\w\]+]|[=\-]>\w+)?/},{token:"variable",regex:/\$\{[^"\}]+\}?/},{token:"string",regex:'"',next:"start"},{defaultToken:"string"}],qstring:[{token:"constant.language.escape",regex:/\\['\\]/},{token:"string",regex:"'",next:"start"},{defaultToken:"string"}]},this.embedRules(s,"doc-",[s.getEndRule("start")])};r.inherits(a,o);var f=function(){u.call(this);var e=[{token:"support.php_tag",regex:"<\\?(?:php|=)?",push:"php-start"}],t=[{token:"support.php_tag",regex:"\\?>",next:"pop"}];for(var n in this.$rules)this.$rules[n].unshift.apply(this.$rules[n],e);this.embedRules(a,"php-",t,["start"]),this.normalizeRules()};r.inherits(f,u),t.PhpHighlightRules=f,t.PhpLangHighlightRules=a}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define("ace/mode/php_completions",["require","exports","module"],function(e,t,n){"use strict";function s(e,t){return e.type.lastIndexOf(t)>-1}var r={abs:["int abs(int number)","Return the absolute value of the number"],acos:["float acos(float number)","Return the arc cosine of the number in radians"],acosh:["float acosh(float number)","Returns the inverse hyperbolic cosine of the number, i.e. the value whose hyperbolic cosine is number"],addGlob:["bool addGlob(string pattern[,int flags [, array options]])","Add files matching the glob pattern. See php's glob for the pattern syntax."],addPattern:["bool addPattern(string pattern[, string path [, array options]])","Add files matching the pcre pattern. See php's pcre for the pattern syntax."],addcslashes:["string addcslashes(string str, string charlist)","Escapes all chars mentioned in charlist with backslash. It creates octal representations if asked to backslash characters with 8th bit set or with ASCII<32 (except '\\n', '\\r', '\\t' etc...)"],addslashes:["string addslashes(string str)","Escapes single quote, double quotes and backslash characters in a string with backslashes"],apache_child_terminate:["bool apache_child_terminate(void)","Terminate apache process after this request"],apache_get_modules:["array apache_get_modules(void)","Get a list of loaded Apache modules"],apache_get_version:["string apache_get_version(void)","Fetch Apache version"],apache_getenv:["bool apache_getenv(string variable [, bool walk_to_top])","Get an Apache subprocess_env variable"],apache_lookup_uri:["object apache_lookup_uri(string URI)","Perform a partial request of the given URI to obtain information about it"],apache_note:["string apache_note(string note_name [, string note_value])","Get and set Apache request notes"],apache_request_auth_name:["string apache_request_auth_name()",""],apache_request_auth_type:["string apache_request_auth_type()",""],apache_request_discard_request_body:["long apache_request_discard_request_body()",""],apache_request_err_headers_out:["array apache_request_err_headers_out([{string name|array list} [, string value [, bool replace = false]]])","* fetch all headers that go out in case of an error or a subrequest"],apache_request_headers:["array apache_request_headers(void)","Fetch all HTTP request headers"],apache_request_headers_in:["array apache_request_headers_in()","* fetch all incoming request headers"],apache_request_headers_out:["array apache_request_headers_out([{string name|array list} [, string value [, bool replace = false]]])","* fetch all outgoing request headers"],apache_request_is_initial_req:["bool apache_request_is_initial_req()",""],apache_request_log_error:["boolean apache_request_log_error(string message, [long facility])",""],apache_request_meets_conditions:["long apache_request_meets_conditions()",""],apache_request_remote_host:["int apache_request_remote_host([int type])",""],apache_request_run:["long apache_request_run()","This is a wrapper for ap_sub_run_req and ap_destory_sub_req. It takes sub_request, runs it, destroys it, and returns it's status."],apache_request_satisfies:["long apache_request_satisfies()",""],apache_request_server_port:["int apache_request_server_port()",""],apache_request_set_etag:["void apache_request_set_etag()",""],apache_request_set_last_modified:["void apache_request_set_last_modified()",""],apache_request_some_auth_required:["bool apache_request_some_auth_required()",""],apache_request_sub_req_lookup_file:["object apache_request_sub_req_lookup_file(string file)","Returns sub-request for the specified file. You would need to run it yourself with run()."],apache_request_sub_req_lookup_uri:["object apache_request_sub_req_lookup_uri(string uri)","Returns sub-request for the specified uri. You would need to run it yourself with run()"],apache_request_sub_req_method_uri:["object apache_request_sub_req_method_uri(string method, string uri)","Returns sub-request for the specified file. You would need to run it yourself with run()."],apache_request_update_mtime:["long apache_request_update_mtime([int dependency_mtime])",""],apache_reset_timeout:["bool apache_reset_timeout(void)","Reset the Apache write timer"],apache_response_headers:["array apache_response_headers(void)","Fetch all HTTP response headers"],apache_setenv:["bool apache_setenv(string variable, string value [, bool walk_to_top])","Set an Apache subprocess_env variable"],array_change_key_case:["array array_change_key_case(array input [, int case=CASE_LOWER])","Retuns an array with all string keys lowercased [or uppercased]"],array_chunk:["array array_chunk(array input, int size [, bool preserve_keys])","Split array into chunks"],array_combine:["array array_combine(array keys, array values)","Creates an array by using the elements of the first parameter as keys and the elements of the second as the corresponding values"],array_count_values:["array array_count_values(array input)","Return the value as key and the frequency of that value in input as value"],array_diff:["array array_diff(array arr1, array arr2 [, array ...])","Returns the entries of arr1 that have values which are not present in any of the others arguments."],array_diff_assoc:["array array_diff_assoc(array arr1, array arr2 [, array ...])","Returns the entries of arr1 that have values which are not present in any of the others arguments but do additional checks whether the keys are equal"],array_diff_key:["array array_diff_key(array arr1, array arr2 [, array ...])","Returns the entries of arr1 that have keys which are not present in any of the others arguments. This function is like array_diff() but works on the keys instead of the values. The associativity is preserved."],array_diff_uassoc:["array array_diff_uassoc(array arr1, array arr2 [, array ...], callback data_comp_func)","Returns the entries of arr1 that have values which are not present in any of the others arguments but do additional checks whether the keys are equal. Elements are compared by user supplied function."],array_diff_ukey:["array array_diff_ukey(array arr1, array arr2 [, array ...], callback key_comp_func)","Returns the entries of arr1 that have keys which are not present in any of the others arguments. User supplied function is used for comparing the keys. This function is like array_udiff() but works on the keys instead of the values. The associativity is preserved."],array_fill:["array array_fill(int start_key, int num, mixed val)","Create an array containing num elements starting with index start_key each initialized to val"],array_fill_keys:["array array_fill_keys(array keys, mixed val)","Create an array using the elements of the first parameter as keys each initialized to val"],array_filter:["array array_filter(array input [, mixed callback])","Filters elements from the array via the callback."],array_flip:["array array_flip(array input)","Return array with key <-> value flipped"],array_intersect:["array array_intersect(array arr1, array arr2 [, array ...])","Returns the entries of arr1 that have values which are present in all the other arguments"],array_intersect_assoc:["array array_intersect_assoc(array arr1, array arr2 [, array ...])","Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check"],array_intersect_key:["array array_intersect_key(array arr1, array arr2 [, array ...])","Returns the entries of arr1 that have keys which are present in all the other arguments. Kind of equivalent to array_diff(array_keys($arr1), array_keys($arr2)[,array_keys(...)]). Equivalent of array_intersect_assoc() but does not do compare of the data."],array_intersect_uassoc:["array array_intersect_uassoc(array arr1, array arr2 [, array ...], callback key_compare_func)","Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check and they are compared by using an user-supplied callback."],array_intersect_ukey:["array array_intersect_ukey(array arr1, array arr2 [, array ...], callback key_compare_func)","Returns the entries of arr1 that have keys which are present in all the other arguments. Kind of equivalent to array_diff(array_keys($arr1), array_keys($arr2)[,array_keys(...)]). The comparison of the keys is performed by a user supplied function. Equivalent of array_intersect_uassoc() but does not do compare of the data."],array_key_exists:["bool array_key_exists(mixed key, array search)","Checks if the given key or index exists in the array"],array_keys:["array array_keys(array input [, mixed search_value[, bool strict]])","Return just the keys from the input array, optionally only for the specified search_value"],array_map:["array array_map(mixed callback, array input1 [, array input2 ,...])","Applies the callback to the elements in given arrays."],array_merge:["array array_merge(array arr1, array arr2 [, array ...])","Merges elements from passed arrays into one array"],array_merge_recursive:["array array_merge_recursive(array arr1, array arr2 [, array ...])","Recursively merges elements from passed arrays into one array"],array_multisort:["bool array_multisort(array ar1 [, SORT_ASC|SORT_DESC [, SORT_REGULAR|SORT_NUMERIC|SORT_STRING]] [, array ar2 [, SORT_ASC|SORT_DESC [, SORT_REGULAR|SORT_NUMERIC|SORT_STRING]], ...])","Sort multiple arrays at once similar to how ORDER BY clause works in SQL"],array_pad:["array array_pad(array input, int pad_size, mixed pad_value)","Returns a copy of input array padded with pad_value to size pad_size"],array_pop:["mixed array_pop(array stack)","Pops an element off the end of the array"],array_product:["mixed array_product(array input)","Returns the product of the array entries"],array_push:["int array_push(array stack, mixed var [, mixed ...])","Pushes elements onto the end of the array"],array_rand:["mixed array_rand(array input [, int num_req])","Return key/keys for random entry/entries in the array"],array_reduce:["mixed array_reduce(array input, mixed callback [, mixed initial])","Iteratively reduce the array to a single value via the callback."],array_replace:["array array_replace(array arr1, array arr2 [, array ...])","Replaces elements from passed arrays into one array"],array_replace_recursive:["array array_replace_recursive(array arr1, array arr2 [, array ...])","Recursively replaces elements from passed arrays into one array"],array_reverse:["array array_reverse(array input [, bool preserve keys])","Return input as a new array with the order of the entries reversed"],array_search:["mixed array_search(mixed needle, array haystack [, bool strict])","Searches the array for a given value and returns the corresponding key if successful"],array_shift:["mixed array_shift(array stack)","Pops an element off the beginning of the array"],array_slice:["array array_slice(array input, int offset [, int length [, bool preserve_keys]])","Returns elements specified by offset and length"],array_splice:["array array_splice(array input, int offset [, int length [, array replacement]])","Removes the elements designated by offset and length and replace them with supplied array"],array_sum:["mixed array_sum(array input)","Returns the sum of the array entries"],array_udiff:["array array_udiff(array arr1, array arr2 [, array ...], callback data_comp_func)","Returns the entries of arr1 that have values which are not present in any of the others arguments. Elements are compared by user supplied function."],array_udiff_assoc:["array array_udiff_assoc(array arr1, array arr2 [, array ...], callback key_comp_func)","Returns the entries of arr1 that have values which are not present in any of the others arguments but do additional checks whether the keys are equal. Keys are compared by user supplied function."],array_udiff_uassoc:["array array_udiff_uassoc(array arr1, array arr2 [, array ...], callback data_comp_func, callback key_comp_func)","Returns the entries of arr1 that have values which are not present in any of the others arguments but do additional checks whether the keys are equal. Keys and elements are compared by user supplied functions."],array_uintersect:["array array_uintersect(array arr1, array arr2 [, array ...], callback data_compare_func)","Returns the entries of arr1 that have values which are present in all the other arguments. Data is compared by using an user-supplied callback."],array_uintersect_assoc:["array array_uintersect_assoc(array arr1, array arr2 [, array ...], callback data_compare_func)","Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check. Data is compared by using an user-supplied callback."],array_uintersect_uassoc:["array array_uintersect_uassoc(array arr1, array arr2 [, array ...], callback data_compare_func, callback key_compare_func)","Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check. Both data and keys are compared by using user-supplied callbacks."],array_unique:["array array_unique(array input [, int sort_flags])","Removes duplicate values from array"],array_unshift:["int array_unshift(array stack, mixed var [, mixed ...])","Pushes elements onto the beginning of the array"],array_values:["array array_values(array input)","Return just the values from the input array"],array_walk:["bool array_walk(array input, string funcname [, mixed userdata])","Apply a user function to every member of an array"],array_walk_recursive:["bool array_walk_recursive(array input, string funcname [, mixed userdata])","Apply a user function recursively to every member of an array"],arsort:["bool arsort(array &array_arg [, int sort_flags])","Sort an array in reverse order and maintain index association"],asin:["float asin(float number)","Returns the arc sine of the number in radians"],asinh:["float asinh(float number)","Returns the inverse hyperbolic sine of the number, i.e. the value whose hyperbolic sine is number"],asort:["bool asort(array &array_arg [, int sort_flags])","Sort an array and maintain index association"],assert:["int assert(string|bool assertion)","Checks if assertion is false"],assert_options:["mixed assert_options(int what [, mixed value])","Set/get the various assert flags"],atan:["float atan(float number)","Returns the arc tangent of the number in radians"],atan2:["float atan2(float y, float x)","Returns the arc tangent of y/x, with the resulting quadrant determined by the signs of y and x"],atanh:["float atanh(float number)","Returns the inverse hyperbolic tangent of the number, i.e. the value whose hyperbolic tangent is number"],attachIterator:["void attachIterator(Iterator iterator[, mixed info])","Attach a new iterator"],base64_decode:["string base64_decode(string str[, bool strict])","Decodes string using MIME base64 algorithm"],base64_encode:["string base64_encode(string str)","Encodes string using MIME base64 algorithm"],base_convert:["string base_convert(string number, int frombase, int tobase)","Converts a number in a string from any base <= 36 to any base <= 36"],basename:["string basename(string path [, string suffix])","Returns the filename component of the path"],bcadd:["string bcadd(string left_operand, string right_operand [, int scale])","Returns the sum of two arbitrary precision numbers"],bccomp:["int bccomp(string left_operand, string right_operand [, int scale])","Compares two arbitrary precision numbers"],bcdiv:["string bcdiv(string left_operand, string right_operand [, int scale])","Returns the quotient of two arbitrary precision numbers (division)"],bcmod:["string bcmod(string left_operand, string right_operand)","Returns the modulus of the two arbitrary precision operands"],bcmul:["string bcmul(string left_operand, string right_operand [, int scale])","Returns the multiplication of two arbitrary precision numbers"],bcpow:["string bcpow(string x, string y [, int scale])","Returns the value of an arbitrary precision number raised to the power of another"],bcpowmod:["string bcpowmod(string x, string y, string mod [, int scale])","Returns the value of an arbitrary precision number raised to the power of another reduced by a modulous"],bcscale:["bool bcscale(int scale)","Sets default scale parameter for all bc math functions"],bcsqrt:["string bcsqrt(string operand [, int scale])","Returns the square root of an arbitray precision number"],bcsub:["string bcsub(string left_operand, string right_operand [, int scale])","Returns the difference between two arbitrary precision numbers"],bin2hex:["string bin2hex(string data)","Converts the binary representation of data to hex"],bind_textdomain_codeset:["string bind_textdomain_codeset (string domain, string codeset)","Specify the character encoding in which the messages from the DOMAIN message catalog will be returned."],bindec:["int bindec(string binary_number)","Returns the decimal equivalent of the binary number"],bindtextdomain:["string bindtextdomain(string domain_name, string dir)","Bind to the text domain domain_name, looking for translations in dir. Returns the current domain"],birdstep_autocommit:["bool birdstep_autocommit(int index)",""],birdstep_close:["bool birdstep_close(int id)",""],birdstep_commit:["bool birdstep_commit(int index)",""],birdstep_connect:["int birdstep_connect(string server, string user, string pass)",""],birdstep_exec:["int birdstep_exec(int index, string exec_str)",""],birdstep_fetch:["bool birdstep_fetch(int index)",""],birdstep_fieldname:["string birdstep_fieldname(int index, int col)",""],birdstep_fieldnum:["int birdstep_fieldnum(int index)",""],birdstep_freeresult:["bool birdstep_freeresult(int index)",""],birdstep_off_autocommit:["bool birdstep_off_autocommit(int index)",""],birdstep_result:["mixed birdstep_result(int index, mixed col)",""],birdstep_rollback:["bool birdstep_rollback(int index)",""],bzcompress:["string bzcompress(string source [, int blocksize100k [, int workfactor]])","Compresses a string into BZip2 encoded data"],bzdecompress:["string bzdecompress(string source [, int small])","Decompresses BZip2 compressed data"],bzerrno:["int bzerrno(resource bz)","Returns the error number"],bzerror:["array bzerror(resource bz)","Returns the error number and error string in an associative array"],bzerrstr:["string bzerrstr(resource bz)","Returns the error string"],bzopen:["resource bzopen(string|int file|fp, string mode)","Opens a new BZip2 stream"],bzread:["string bzread(resource bz[, int length])","Reads up to length bytes from a BZip2 stream, or 1024 bytes if length is not specified"],cal_days_in_month:["int cal_days_in_month(int calendar, int month, int year)","Returns the number of days in a month for a given year and calendar"],cal_from_jd:["array cal_from_jd(int jd, int calendar)","Converts from Julian Day Count to a supported calendar and return extended information"],cal_info:["array cal_info([int calendar])","Returns information about a particular calendar"],cal_to_jd:["int cal_to_jd(int calendar, int month, int day, int year)","Converts from a supported calendar to Julian Day Count"],call_user_func:["mixed call_user_func(mixed function_name [, mixed parmeter] [, mixed ...])","Call a user function which is the first parameter"],call_user_func_array:["mixed call_user_func_array(string function_name, array parameters)","Call a user function which is the first parameter with the arguments contained in array"],call_user_method:["mixed call_user_method(string method_name, mixed object [, mixed parameter] [, mixed ...])","Call a user method on a specific object or class"],call_user_method_array:["mixed call_user_method_array(string method_name, mixed object, array params)","Call a user method on a specific object or class using a parameter array"],ceil:["float ceil(float number)","Returns the next highest integer value of the number"],chdir:["bool chdir(string directory)","Change the current directory"],checkdate:["bool checkdate(int month, int day, int year)","Returns true(1) if it is a valid date in gregorian calendar"],chgrp:["bool chgrp(string filename, mixed group)","Change file group"],chmod:["bool chmod(string filename, int mode)","Change file mode"],chown:["bool chown (string filename, mixed user)","Change file owner"],chr:["string chr(int ascii)","Converts ASCII code to a character"],chroot:["bool chroot(string directory)","Change root directory"],chunk_split:["string chunk_split(string str [, int chunklen [, string ending]])","Returns split line"],class_alias:["bool class_alias(string user_class_name , string alias_name [, bool autoload])","Creates an alias for user defined class"],class_exists:["bool class_exists(string classname [, bool autoload])","Checks if the class exists"],class_implements:["array class_implements(mixed what [, bool autoload ])","Return all classes and interfaces implemented by SPL"],class_parents:["array class_parents(object instance [, boolean autoload = true])","Return an array containing the names of all parent classes"],clearstatcache:["void clearstatcache([bool clear_realpath_cache[, string filename]])","Clear file stat cache"],closedir:["void closedir([resource dir_handle])","Close directory connection identified by the dir_handle"],closelog:["bool closelog(void)","Close connection to system logger"],collator_asort:["bool collator_asort( Collator $coll, array(string) $arr )","* Sort array using specified collator, maintaining index association."],collator_compare:["int collator_compare( Collator $coll, string $str1, string $str2 )","* Compare two strings."],collator_create:["Collator collator_create( string $locale )","* Create collator."],collator_get_attribute:["int collator_get_attribute( Collator $coll, int $attr )","* Get collation attribute value."],collator_get_error_code:["int collator_get_error_code( Collator $coll )","* Get collator's last error code."],collator_get_error_message:["string collator_get_error_message( Collator $coll )","* Get text description for collator's last error code."],collator_get_locale:["string collator_get_locale( Collator $coll, int $type )","* Gets the locale name of the collator."],collator_get_sort_key:["bool collator_get_sort_key( Collator $coll, string $str )","* Get a sort key for a string from a Collator. }}}"],collator_get_strength:["int collator_get_strength(Collator coll)","* Returns the current collation strength."],collator_set_attribute:["bool collator_set_attribute( Collator $coll, int $attr, int $val )","* Set collation attribute."],collator_set_strength:["bool collator_set_strength(Collator coll, int strength)","* Set the collation strength."],collator_sort:["bool collator_sort( Collator $coll, array(string) $arr [, int $sort_flags] )","* Sort array using specified collator."],collator_sort_with_sort_keys:["bool collator_sort_with_sort_keys( Collator $coll, array(string) $arr )","* Equivalent to standard PHP sort using Collator. * Uses ICU ucol_getSortKey for performance."],com_create_guid:["string com_create_guid()","Generate a globally unique identifier (GUID)"],com_event_sink:["bool com_event_sink(object comobject, object sinkobject [, mixed sinkinterface])","Connect events from a COM object to a PHP object"],com_get_active_object:["object com_get_active_object(string progid [, int code_page ])","Returns a handle to an already running instance of a COM object"],com_load_typelib:["bool com_load_typelib(string typelib_name [, int case_insensitive])","Loads a Typelibrary and registers its constants"],com_message_pump:["bool com_message_pump([int timeoutms])","Process COM messages, sleeping for up to timeoutms milliseconds"],com_print_typeinfo:["bool com_print_typeinfo(object comobject | string typelib, string dispinterface, bool wantsink)","Print out a PHP class definition for a dispatchable interface"],compact:["array compact(mixed var_names [, mixed ...])","Creates a hash containing variables and their values"],compose_locale:["static string compose_locale($array)","* Creates a locale by combining the parts of locale-ID passed * }}}"],confirm_extname_compiled:["string confirm_extname_compiled(string arg)","Return a string to confirm that the module is compiled in"],connection_aborted:["int connection_aborted(void)","Returns true if client disconnected"],connection_status:["int connection_status(void)","Returns the connection status bitfield"],constant:["mixed constant(string const_name)","Given the name of a constant this function will return the constant's associated value"],convert_cyr_string:["string convert_cyr_string(string str, string from, string to)","Convert from one Cyrillic character set to another"],convert_uudecode:["string convert_uudecode(string data)","decode a uuencoded string"],convert_uuencode:["string convert_uuencode(string data)","uuencode a string"],copy:["bool copy(string source_file, string destination_file [, resource context])","Copy a file"],cos:["float cos(float number)","Returns the cosine of the number in radians"],cosh:["float cosh(float number)","Returns the hyperbolic cosine of the number, defined as (exp(number) + exp(-number))/2"],count:["int count(mixed var [, int mode])","Count the number of elements in a variable (usually an array)"],count_chars:["mixed count_chars(string input [, int mode])","Returns info about what characters are used in input"],crc32:["string crc32(string str)","Calculate the crc32 polynomial of a string"],create_function:["string create_function(string args, string code)","Creates an anonymous function, and returns its name (funny, eh?)"],crypt:["string crypt(string str [, string salt])","Hash a string"],ctype_alnum:["bool ctype_alnum(mixed c)","Checks for alphanumeric character(s)"],ctype_alpha:["bool ctype_alpha(mixed c)","Checks for alphabetic character(s)"],ctype_cntrl:["bool ctype_cntrl(mixed c)","Checks for control character(s)"],ctype_digit:["bool ctype_digit(mixed c)","Checks for numeric character(s)"],ctype_graph:["bool ctype_graph(mixed c)","Checks for any printable character(s) except space"],ctype_lower:["bool ctype_lower(mixed c)","Checks for lowercase character(s)"],ctype_print:["bool ctype_print(mixed c)","Checks for printable character(s)"],ctype_punct:["bool ctype_punct(mixed c)","Checks for any printable character which is not whitespace or an alphanumeric character"],ctype_space:["bool ctype_space(mixed c)","Checks for whitespace character(s)"],ctype_upper:["bool ctype_upper(mixed c)","Checks for uppercase character(s)"],ctype_xdigit:["bool ctype_xdigit(mixed c)","Checks for character(s) representing a hexadecimal digit"],curl_close:["void curl_close(resource ch)","Close a cURL session"],curl_copy_handle:["resource curl_copy_handle(resource ch)","Copy a cURL handle along with all of it's preferences"],curl_errno:["int curl_errno(resource ch)","Return an integer containing the last error number"],curl_error:["string curl_error(resource ch)","Return a string contain the last error for the current session"],curl_exec:["bool curl_exec(resource ch)","Perform a cURL session"],curl_getinfo:["mixed curl_getinfo(resource ch [, int option])","Get information regarding a specific transfer"],curl_init:["resource curl_init([string url])","Initialize a cURL session"],curl_multi_add_handle:["int curl_multi_add_handle(resource mh, resource ch)","Add a normal cURL handle to a cURL multi handle"],curl_multi_close:["void curl_multi_close(resource mh)","Close a set of cURL handles"],curl_multi_exec:["int curl_multi_exec(resource mh, int &still_running)","Run the sub-connections of the current cURL handle"],curl_multi_getcontent:["string curl_multi_getcontent(resource ch)","Return the content of a cURL handle if CURLOPT_RETURNTRANSFER is set"],curl_multi_info_read:["array curl_multi_info_read(resource mh [, long msgs_in_queue])","Get information about the current transfers"],curl_multi_init:["resource curl_multi_init(void)","Returns a new cURL multi handle"],curl_multi_remove_handle:["int curl_multi_remove_handle(resource mh, resource ch)","Remove a multi handle from a set of cURL handles"],curl_multi_select:["int curl_multi_select(resource mh[, double timeout])",'Get all the sockets associated with the cURL extension, which can then be "selected"'],curl_setopt:["bool curl_setopt(resource ch, int option, mixed value)","Set an option for a cURL transfer"],curl_setopt_array:["bool curl_setopt_array(resource ch, array options)","Set an array of option for a cURL transfer"],curl_version:["array curl_version([int version])","Return cURL version information."],current:["mixed current(array array_arg)","Return the element currently pointed to by the internal array pointer"],date:["string date(string format [, long timestamp])","Format a local date/time"],date_add:["DateTime date_add(DateTime object, DateInterval interval)","Adds an interval to the current date in object."],date_create:["DateTime date_create([string time[, DateTimeZone object]])","Returns new DateTime object"],date_create_from_format:["DateTime date_create_from_format(string format, string time[, DateTimeZone object])","Returns new DateTime object formatted according to the specified format"],date_date_set:["DateTime date_date_set(DateTime object, long year, long month, long day)","Sets the date."],date_default_timezone_get:["string date_default_timezone_get()","Gets the default timezone used by all date/time functions in a script"],date_default_timezone_set:["bool date_default_timezone_set(string timezone_identifier)","Sets the default timezone used by all date/time functions in a script"],date_diff:["DateInterval date_diff(DateTime object [, bool absolute])","Returns the difference between two DateTime objects."],date_format:["string date_format(DateTime object, string format)","Returns date formatted according to given format"],date_get_last_errors:["array date_get_last_errors()","Returns the warnings and errors found while parsing a date/time string."],date_interval_create_from_date_string:["DateInterval date_interval_create_from_date_string(string time)","Uses the normal date parsers and sets up a DateInterval from the relative parts of the parsed string"],date_interval_format:["string date_interval_format(DateInterval object, string format)","Formats the interval."],date_isodate_set:["DateTime date_isodate_set(DateTime object, long year, long week[, long day])","Sets the ISO date."],date_modify:["DateTime date_modify(DateTime object, string modify)","Alters the timestamp."],date_offset_get:["long date_offset_get(DateTime object)","Returns the DST offset."],date_parse:["array date_parse(string date)","Returns associative array with detailed info about given date"],date_parse_from_format:["array date_parse_from_format(string format, string date)","Returns associative array with detailed info about given date"],date_sub:["DateTime date_sub(DateTime object, DateInterval interval)","Subtracts an interval to the current date in object."],date_sun_info:["array date_sun_info(long time, float latitude, float longitude)","Returns an array with information about sun set/rise and twilight begin/end"],date_sunrise:["mixed date_sunrise(mixed time [, int format [, float latitude [, float longitude [, float zenith [, float gmt_offset]]]]])","Returns time of sunrise for a given day and location"],date_sunset:["mixed date_sunset(mixed time [, int format [, float latitude [, float longitude [, float zenith [, float gmt_offset]]]]])","Returns time of sunset for a given day and location"],date_time_set:["DateTime date_time_set(DateTime object, long hour, long minute[, long second])","Sets the time."],date_timestamp_get:["long date_timestamp_get(DateTime object)","Gets the Unix timestamp."],date_timestamp_set:["DateTime date_timestamp_set(DateTime object, long unixTimestamp)","Sets the date and time based on an Unix timestamp."],date_timezone_get:["DateTimeZone date_timezone_get(DateTime object)","Return new DateTimeZone object relative to give DateTime"],date_timezone_set:["DateTime date_timezone_set(DateTime object, DateTimeZone object)","Sets the timezone for the DateTime object."],datefmt_create:["IntlDateFormatter datefmt_create(string $locale, long date_type, long time_type[, string $timezone_str, long $calendar, string $pattern] )","* Create formatter."],datefmt_format:["string datefmt_format( [mixed]int $args or array $args )","* Format the time value as a string. }}}"],datefmt_get_calendar:["string datefmt_get_calendar( IntlDateFormatter $mf )","* Get formatter calendar."],datefmt_get_datetype:["string datefmt_get_datetype( IntlDateFormatter $mf )","* Get formatter datetype."],datefmt_get_error_code:["int datefmt_get_error_code( IntlDateFormatter $nf )","* Get formatter's last error code."],datefmt_get_error_message:["string datefmt_get_error_message( IntlDateFormatter $coll )","* Get text description for formatter's last error code."],datefmt_get_locale:["string datefmt_get_locale(IntlDateFormatter $mf)","* Get formatter locale."],datefmt_get_pattern:["string datefmt_get_pattern( IntlDateFormatter $mf )","* Get formatter pattern."],datefmt_get_timetype:["string datefmt_get_timetype( IntlDateFormatter $mf )","* Get formatter timetype."],datefmt_get_timezone_id:["string datefmt_get_timezone_id( IntlDateFormatter $mf )","* Get formatter timezone_id."],datefmt_isLenient:["string datefmt_isLenient(IntlDateFormatter $mf)","* Get formatter locale."],datefmt_localtime:["integer datefmt_localtime( IntlDateFormatter $fmt, string $text_to_parse[, int $parse_pos ])","* Parse the string $value to a localtime array }}}"],datefmt_parse:["integer datefmt_parse( IntlDateFormatter $fmt, string $text_to_parse [, int $parse_pos] )","* Parse the string $value starting at parse_pos to a Unix timestamp -int }}}"],datefmt_setLenient:["string datefmt_setLenient(IntlDateFormatter $mf)","* Set formatter lenient."],datefmt_set_calendar:["bool datefmt_set_calendar( IntlDateFormatter $mf, int $calendar )","* Set formatter calendar."],datefmt_set_pattern:["bool datefmt_set_pattern( IntlDateFormatter $mf, string $pattern )","* Set formatter pattern."],datefmt_set_timezone_id:["boolean datefmt_set_timezone_id( IntlDateFormatter $mf,$timezone_id)","* Set formatter timezone_id."],dba_close:["void dba_close(resource handle)","Closes database"],dba_delete:["bool dba_delete(string key, resource handle)","Deletes the entry associated with key If inifile: remove all other key lines"],dba_exists:["bool dba_exists(string key, resource handle)","Checks, if the specified key exists"],dba_fetch:["string dba_fetch(string key, [int skip ,] resource handle)","Fetches the data associated with key"],dba_firstkey:["string dba_firstkey(resource handle)","Resets the internal key pointer and returns the first key"],dba_handlers:["array dba_handlers([bool full_info])","List configured database handlers"],dba_insert:["bool dba_insert(string key, string value, resource handle)","If not inifile: Insert value as key, return false, if key exists already If inifile: Add vakue as key (next instance of key)"],dba_key_split:["array|false dba_key_split(string key)","Splits an inifile key into an array of the form array(0=>group,1=>value_name) but returns false if input is false or null"],dba_list:["array dba_list()","List opened databases"],dba_nextkey:["string dba_nextkey(resource handle)","Returns the next key"],dba_open:["resource dba_open(string path, string mode [, string handlername, string ...])","Opens path using the specified handler in mode"],dba_optimize:["bool dba_optimize(resource handle)","Optimizes (e.g. clean up, vacuum) database"],dba_popen:["resource dba_popen(string path, string mode [, string handlername, string ...])","Opens path using the specified handler in mode persistently"],dba_replace:["bool dba_replace(string key, string value, resource handle)","Inserts value as key, replaces key, if key exists already If inifile: remove all other key lines"],dba_sync:["bool dba_sync(resource handle)","Synchronizes database"],dcgettext:["string dcgettext(string domain_name, string msgid, long category)","Return the translation of msgid for domain_name and category, or msgid unaltered if a translation does not exist"],dcngettext:["string dcngettext (string domain, string msgid1, string msgid2, int n, int category)","Plural version of dcgettext()"],debug_backtrace:["array debug_backtrace([bool provide_object])","Return backtrace as array"],debug_print_backtrace:["void debug_print_backtrace(void) */","ZEND_FUNCTION(debug_print_backtrace) { zend_execute_data *ptr, *skip; int lineno; char *function_name; char *filename; char *class_name = NULL; char *call_type; char *include_filename = NULL; zval *arg_array = NULL; int indent = 0; if (zend_parse_parameters_none() == FAILURE) { return; } ptr = EG(current_execute_data);","PHP_FUNCTION(dom_document_relaxNG_validate_file) { _dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE); } /* }}} end dom_document_relaxNG_validate_file"],dom_document_relaxNG_validate_xml:["boolean dom_document_relaxNG_validate_xml(string source); */","PHP_FUNCTION(dom_document_relaxNG_validate_xml) { _dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING); } /* }}} end dom_document_relaxNG_validate_xml"],dom_document_rename_node:["DOMNode dom_document_rename_node(node n, string namespaceURI, string qualifiedName);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-renameNode Since: DOM Level 3"],dom_document_save:["int dom_document_save(string file);","Convenience method to save to file"],dom_document_save_html:["string dom_document_save_html();","Convenience method to output as html"],dom_document_save_html_file:["int dom_document_save_html_file(string file);","Convenience method to save to file as html"],dom_document_savexml:["string dom_document_savexml([node n]);","URL: http://www.w3.org/TR/DOM-Level-3-LS/load-save.html#LS-DocumentLS-saveXML Since: DOM Level 3"],dom_document_schema_validate:["boolean dom_document_schema_validate(string source); */","PHP_FUNCTION(dom_document_schema_validate_xml) { _dom_document_schema_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING); } /* }}} end dom_document_schema_validate"],dom_document_schema_validate_file:["boolean dom_document_schema_validate_file(string filename); */","PHP_FUNCTION(dom_document_schema_validate_file) { _dom_document_schema_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE); } /* }}} end dom_document_schema_validate_file"],dom_document_validate:["boolean dom_document_validate();","Since: DOM extended"],dom_document_xinclude:["int dom_document_xinclude([int options])","Substitutues xincludes in a DomDocument"],dom_domconfiguration_can_set_parameter:["boolean dom_domconfiguration_can_set_parameter(string name, domuserdata value);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#DOMConfiguration-canSetParameter Since:"],dom_domconfiguration_get_parameter:["domdomuserdata dom_domconfiguration_get_parameter(string name);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#DOMConfiguration-getParameter Since:"],dom_domconfiguration_set_parameter:["dom_void dom_domconfiguration_set_parameter(string name, domuserdata value);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#DOMConfiguration-property Since:"],dom_domerrorhandler_handle_error:["dom_boolean dom_domerrorhandler_handle_error(domerror error);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-ERRORS-DOMErrorHandler-handleError Since:"],dom_domimplementation_create_document:["DOMDocument dom_domimplementation_create_document(string namespaceURI, string qualifiedName, DOMDocumentType doctype);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Level-2-Core-DOM-createDocument Since: DOM Level 2"],dom_domimplementation_create_document_type:["DOMDocumentType dom_domimplementation_create_document_type(string qualifiedName, string publicId, string systemId);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Level-2-Core-DOM-createDocType Since: DOM Level 2"],dom_domimplementation_get_feature:["DOMNode dom_domimplementation_get_feature(string feature, string version);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#DOMImplementation3-getFeature Since: DOM Level 3"],dom_domimplementation_has_feature:["boolean dom_domimplementation_has_feature(string feature, string version);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-5CED94D7 Since:"],dom_domimplementationlist_item:["domdomimplementation dom_domimplementationlist_item(int index);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#DOMImplementationList-item Since:"],dom_domimplementationsource_get_domimplementation:["domdomimplementation dom_domimplementationsource_get_domimplementation(string features);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-getDOMImpl Since:"],dom_domimplementationsource_get_domimplementations:["domimplementationlist dom_domimplementationsource_get_domimplementations(string features);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-getDOMImpls Since:"],dom_domstringlist_item:["domstring dom_domstringlist_item(int index);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#DOMStringList-item Since:"],dom_element_get_attribute:["string dom_element_get_attribute(string name);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-666EE0F9 Since:"],dom_element_get_attribute_node:["DOMAttr dom_element_get_attribute_node(string name);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-217A91B8 Since:"],dom_element_get_attribute_node_ns:["DOMAttr dom_element_get_attribute_node_ns(string namespaceURI, string localName);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElGetAtNodeNS Since: DOM Level 2"],dom_element_get_attribute_ns:["string dom_element_get_attribute_ns(string namespaceURI, string localName);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElGetAttrNS Since: DOM Level 2"],dom_element_get_elements_by_tag_name:["DOMNodeList dom_element_get_elements_by_tag_name(string name);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1938918D Since:"],dom_element_get_elements_by_tag_name_ns:["DOMNodeList dom_element_get_elements_by_tag_name_ns(string namespaceURI, string localName);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-A6C90942 Since: DOM Level 2"],dom_element_has_attribute:["boolean dom_element_has_attribute(string name);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElHasAttr Since: DOM Level 2"],dom_element_has_attribute_ns:["boolean dom_element_has_attribute_ns(string namespaceURI, string localName);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElHasAttrNS Since: DOM Level 2"],dom_element_remove_attribute:["void dom_element_remove_attribute(string name);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-6D6AC0F9 Since:"],dom_element_remove_attribute_node:["DOMAttr dom_element_remove_attribute_node(DOMAttr oldAttr);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-D589198 Since:"],dom_element_remove_attribute_ns:["void dom_element_remove_attribute_ns(string namespaceURI, string localName);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElRemAtNS Since: DOM Level 2"],dom_element_set_attribute:["void dom_element_set_attribute(string name, string value);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-F68F082 Since:"],dom_element_set_attribute_node:["DOMAttr dom_element_set_attribute_node(DOMAttr newAttr);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-887236154 Since:"],dom_element_set_attribute_node_ns:["DOMAttr dom_element_set_attribute_node_ns(DOMAttr newAttr);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetAtNodeNS Since: DOM Level 2"],dom_element_set_attribute_ns:["void dom_element_set_attribute_ns(string namespaceURI, string qualifiedName, string value);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetAttrNS Since: DOM Level 2"],dom_element_set_id_attribute:["void dom_element_set_id_attribute(string name, boolean isId);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetIdAttr Since: DOM Level 3"],dom_element_set_id_attribute_node:["void dom_element_set_id_attribute_node(attr idAttr, boolean isId);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetIdAttrNode Since: DOM Level 3"],dom_element_set_id_attribute_ns:["void dom_element_set_id_attribute_ns(string namespaceURI, string localName, boolean isId);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetIdAttrNS Since: DOM Level 3"],dom_import_simplexml:["somNode dom_import_simplexml(sxeobject node)","Get a simplexml_element object from dom to allow for processing"],dom_namednodemap_get_named_item:["DOMNode dom_namednodemap_get_named_item(string name);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1074577549 Since:"],dom_namednodemap_get_named_item_ns:["DOMNode dom_namednodemap_get_named_item_ns(string namespaceURI, string localName);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-getNamedItemNS Since: DOM Level 2"],dom_namednodemap_item:["DOMNode dom_namednodemap_item(int index);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-349467F9 Since:"],dom_namednodemap_remove_named_item:["DOMNode dom_namednodemap_remove_named_item(string name);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-D58B193 Since:"],dom_namednodemap_remove_named_item_ns:["DOMNode dom_namednodemap_remove_named_item_ns(string namespaceURI, string localName);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-removeNamedItemNS Since: DOM Level 2"],dom_namednodemap_set_named_item:["DOMNode dom_namednodemap_set_named_item(DOMNode arg);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1025163788 Since:"],dom_namednodemap_set_named_item_ns:["DOMNode dom_namednodemap_set_named_item_ns(DOMNode arg);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-setNamedItemNS Since: DOM Level 2"],dom_namelist_get_name:["string dom_namelist_get_name(int index);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#NameList-getName Since:"],dom_namelist_get_namespace_uri:["string dom_namelist_get_namespace_uri(int index);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#NameList-getNamespaceURI Since:"],dom_node_append_child:["DomNode dom_node_append_child(DomNode newChild);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-184E7107 Since:"],dom_node_clone_node:["DomNode dom_node_clone_node(boolean deep);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-3A0ED0A4 Since:"],dom_node_compare_document_position:["short dom_node_compare_document_position(DomNode other);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Node3-compareDocumentPosition Since: DOM Level 3"],dom_node_get_feature:["DomNode dom_node_get_feature(string feature, string version);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Node3-getFeature Since: DOM Level 3"],dom_node_get_user_data:["mixed dom_node_get_user_data(string key);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Node3-getUserData Since: DOM Level 3"],dom_node_has_attributes:["boolean dom_node_has_attributes();","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-NodeHasAttrs Since: DOM Level 2"],dom_node_has_child_nodes:["boolean dom_node_has_child_nodes();","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-810594187 Since:"],dom_node_insert_before:["domnode dom_node_insert_before(DomNode newChild, DomNode refChild);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-952280727 Since:"],dom_node_is_default_namespace:["boolean dom_node_is_default_namespace(string namespaceURI);","URL: http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-isDefaultNamespace Since: DOM Level 3"],dom_node_is_equal_node:["boolean dom_node_is_equal_node(DomNode arg);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Node3-isEqualNode Since: DOM Level 3"],dom_node_is_same_node:["boolean dom_node_is_same_node(DomNode other);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Node3-isSameNode Since: DOM Level 3"],dom_node_is_supported:["boolean dom_node_is_supported(string feature, string version);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Level-2-Core-Node-supports Since: DOM Level 2"],dom_node_lookup_namespace_uri:["string dom_node_lookup_namespace_uri(string prefix);","URL: http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI Since: DOM Level 3"],dom_node_lookup_prefix:["string dom_node_lookup_prefix(string namespaceURI);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Node3-lookupNamespacePrefix Since: DOM Level 3"],dom_node_normalize:["void dom_node_normalize();","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-normalize Since:"],dom_node_remove_child:["DomNode dom_node_remove_child(DomNode oldChild);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1734834066 Since:"],dom_node_replace_child:["DomNode dom_node_replace_child(DomNode newChild, DomNode oldChild);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-785887307 Since:"],dom_node_set_user_data:["mixed dom_node_set_user_data(string key, mixed data, userdatahandler handler);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Node3-setUserData Since: DOM Level 3"],dom_nodelist_item:["DOMNode dom_nodelist_item(int index);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-844377136 Since:"],dom_string_extend_find_offset16:["int dom_string_extend_find_offset16(int offset32);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#i18n-methods-StringExtend-findOffset16 Since:"],dom_string_extend_find_offset32:["int dom_string_extend_find_offset32(int offset16);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#i18n-methods-StringExtend-findOffset32 Since:"],dom_text_is_whitespace_in_element_content:["boolean dom_text_is_whitespace_in_element_content();","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Text3-isWhitespaceInElementContent Since: DOM Level 3"],dom_text_replace_whole_text:["DOMText dom_text_replace_whole_text(string content);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Text3-replaceWholeText Since: DOM Level 3"],dom_text_split_text:["DOMText dom_text_split_text(int offset);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-38853C1D Since:"],dom_userdatahandler_handle:["dom_void dom_userdatahandler_handle(short operation, string key, domobject data, node src, node dst);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-handleUserDataEvent Since:"],dom_xpath_evaluate:["mixed dom_xpath_evaluate(string expr [,DOMNode context]); */","PHP_FUNCTION(dom_xpath_evaluate) { php_xpath_eval(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_DOM_XPATH_EVALUATE); } /* }}} end dom_xpath_evaluate"],dom_xpath_query:["DOMNodeList dom_xpath_query(string expr [,DOMNode context]); */","PHP_FUNCTION(dom_xpath_query) { php_xpath_eval(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_DOM_XPATH_QUERY); } /* }}} end dom_xpath_query"],dom_xpath_register_ns:["boolean dom_xpath_register_ns(string prefix, string uri); */",'PHP_FUNCTION(dom_xpath_register_ns) { zval *id; xmlXPathContextPtr ctxp; int prefix_len, ns_uri_len; dom_xpath_object *intern; unsigned char *prefix, *ns_uri; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oss", &id, dom_xpath_class_entry, &prefix, &prefix_len, &ns_uri, &ns_uri_len) == FAILURE) { return; } intern = (dom_xpath_object *)zend_object_store_get_object(id TSRMLS_CC); ctxp = (xmlXPathContextPtr) intern->ptr; if (ctxp == NULL) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid XPath Context"); RETURN_FALSE; } if (xmlXPathRegisterNs(ctxp, prefix, ns_uri) != 0) { RETURN_FALSE } RETURN_TRUE; } /* }}}'],dom_xpath_register_php_functions:["void dom_xpath_register_php_functions() */",'PHP_FUNCTION(dom_xpath_register_php_functions) { zval *id; dom_xpath_object *intern; zval *array_value, **entry, *new_string; int name_len = 0; char *name; DOM_GET_THIS(id); if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "a", &array_value) == SUCCESS) { intern = (dom_xpath_object *)zend_object_store_get_object(id TSRMLS_CC); zend_hash_internal_pointer_reset(Z_ARRVAL_P(array_value)); while (zend_hash_get_current_data(Z_ARRVAL_P(array_value), (void **)&entry) == SUCCESS) { SEPARATE_ZVAL(entry); convert_to_string_ex(entry); MAKE_STD_ZVAL(new_string); ZVAL_LONG(new_string,1); zend_hash_update(intern->registered_phpfunctions, Z_STRVAL_PP(entry), Z_STRLEN_PP(entry) + 1, &new_string, sizeof(zval*), NULL); zend_hash_move_forward(Z_ARRVAL_P(array_value)); } intern->registerPhpFunctions = 2; RETURN_TRUE; } else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == SUCCESS) { intern = (dom_xpath_object *)zend_object_store_get_object(id TSRMLS_CC); MAKE_STD_ZVAL(new_string); ZVAL_LONG(new_string,1); zend_hash_update(intern->registered_phpfunctions, name, name_len + 1, &new_string, sizeof(zval*), NULL); intern->registerPhpFunctions = 2; } else { intern = (dom_xpath_object *)zend_object_store_get_object(id TSRMLS_CC); intern->registerPhpFunctions = 1; } } /* }}} end dom_xpath_register_php_functions'],each:["array each(array arr)","Return the currently pointed key..value pair in the passed array, and advance the pointer to the next element"],easter_date:["int easter_date([int year])","Return the timestamp of midnight on Easter of a given year (defaults to current year)"],easter_days:["int easter_days([int year, [int method]])","Return the number of days after March 21 that Easter falls on for a given year (defaults to current year)"],echo:["void echo(string arg1 [, string ...])","Output one or more strings"],empty:["bool empty( mixed var )","Determine whether a variable is empty"],enchant_broker_describe:["array enchant_broker_describe(resource broker)","Enumerates the Enchant providers and tells you some rudimentary information about them. The same info is provided through phpinfo()"],enchant_broker_dict_exists:["bool enchant_broker_dict_exists(resource broker, string tag)","Wether a dictionary exists or not. Using non-empty tag"],enchant_broker_free:["boolean enchant_broker_free(resource broker)","Destroys the broker object and its dictionnaries"],enchant_broker_free_dict:["resource enchant_broker_free_dict(resource dict)","Free the dictionary resource"],enchant_broker_get_dict_path:["string enchant_broker_get_dict_path(resource broker, int dict_type)","Get the directory path for a given backend, works with ispell and myspell"],enchant_broker_get_error:["string enchant_broker_get_error(resource broker)","Returns the last error of the broker"],enchant_broker_init:["resource enchant_broker_init()","create a new broker object capable of requesting"],enchant_broker_list_dicts:["string enchant_broker_list_dicts(resource broker)","Lists the dictionaries available for the given broker"],enchant_broker_request_dict:["resource enchant_broker_request_dict(resource broker, string tag)",'create a new dictionary using tag, the non-empty language tag you wish to request a dictionary for ("en_US", "de_DE", ...)'],enchant_broker_request_pwl_dict:["resource enchant_broker_request_pwl_dict(resource broker, string filename)","creates a dictionary using a PWL file. A PWL file is personal word file one word per line. It must exist before the call."],enchant_broker_set_dict_path:["bool enchant_broker_set_dict_path(resource broker, int dict_type, string value)","Set the directory path for a given backend, works with ispell and myspell"],enchant_broker_set_ordering:["bool enchant_broker_set_ordering(resource broker, string tag, string ordering)","Declares a preference of dictionaries to use for the language described/referred to by 'tag'. The ordering is a comma delimited list of provider names. As a special exception, the \"*\" tag can be used as a language tag to declare a default ordering for any language that does not explictly declare an ordering."],enchant_dict_add_to_personal:["void enchant_dict_add_to_personal(resource dict, string word)","add 'word' to personal word list"],enchant_dict_add_to_session:["void enchant_dict_add_to_session(resource dict, string word)","add 'word' to this spell-checking session"],enchant_dict_check:["bool enchant_dict_check(resource dict, string word)","If the word is correctly spelled return true, otherwise return false"],enchant_dict_describe:["array enchant_dict_describe(resource dict)","Describes an individual dictionary 'dict'"],enchant_dict_get_error:["string enchant_dict_get_error(resource dict)","Returns the last error of the current spelling-session"],enchant_dict_is_in_session:["bool enchant_dict_is_in_session(resource dict, string word)","whether or not 'word' exists in this spelling-session"],enchant_dict_quick_check:["bool enchant_dict_quick_check(resource dict, string word [, array &suggestions])","If the word is correctly spelled return true, otherwise return false, if suggestions variable is provided, fill it with spelling alternatives."],enchant_dict_store_replacement:["void enchant_dict_store_replacement(resource dict, string mis, string cor)","add a correction for 'mis' using 'cor'. Notes that you replaced @mis with @cor, so it's possibly more likely that future occurrences of @mis will be replaced with @cor. So it might bump @cor up in the suggestion list."],enchant_dict_suggest:["array enchant_dict_suggest(resource dict, string word)","Will return a list of values if any of those pre-conditions are not met."],end:["mixed end(array array_arg)","Advances array argument's internal pointer to the last element and return it"],ereg:["int ereg(string pattern, string string [, array registers])","Regular expression match"],ereg_replace:["string ereg_replace(string pattern, string replacement, string string)","Replace regular expression"],eregi:["int eregi(string pattern, string string [, array registers])","Case-insensitive regular expression match"],eregi_replace:["string eregi_replace(string pattern, string replacement, string string)","Case insensitive replace regular expression"],error_get_last:["array error_get_last()","Get the last occurred error as associative array. Returns NULL if there hasn't been an error yet."],error_log:["bool error_log(string message [, int message_type [, string destination [, string extra_headers]]])","Send an error message somewhere"],error_reporting:["int error_reporting([int new_error_level])","Return the current error_reporting level, and if an argument was passed - change to the new level"],escapeshellarg:["string escapeshellarg(string arg)","Quote and escape an argument for use in a shell command"],escapeshellcmd:["string escapeshellcmd(string command)","Escape shell metacharacters"],exec:["string exec(string command [, array &output [, int &return_value]])","Execute an external program"],exif_imagetype:["int exif_imagetype(string imagefile)","Get the type of an image"],exif_read_data:["array exif_read_data(string filename [, sections_needed [, sub_arrays[, read_thumbnail]]])","Reads header data from the JPEG/TIFF image filename and optionally reads the internal thumbnails"],exif_tagname:["string exif_tagname(index)","Get headername for index or false if not defined"],exif_thumbnail:["string exif_thumbnail(string filename [, &width, &height [, &imagetype]])","Reads the embedded thumbnail"],exit:["void exit([mixed status])","Output a message and terminate the current script"],exp:["float exp(float number)","Returns e raised to the power of the number"],explode:["array explode(string separator, string str [, int limit])","Splits a string on string separator and return array of components. If limit is positive only limit number of components is returned. If limit is negative all components except the last abs(limit) are returned."],expm1:["float expm1(float number)","Returns exp(number) - 1, computed in a way that accurate even when the value of number is close to zero"],extension_loaded:["bool extension_loaded(string extension_name)","Returns true if the named extension is loaded"],extract:["int extract(array var_array [, int extract_type [, string prefix]])","Imports variables into symbol table from an array"],ezmlm_hash:["int ezmlm_hash(string addr)","Calculate EZMLM list hash value."],fclose:["bool fclose(resource fp)","Close an open file pointer"],feof:["bool feof(resource fp)","Test for end-of-file on a file pointer"],fflush:["bool fflush(resource fp)","Flushes output"],fgetc:["string fgetc(resource fp)","Get a character from file pointer"],fgetcsv:["array fgetcsv(resource fp [,int length [, string delimiter [, string enclosure [, string escape]]]])","Get line from file pointer and parse for CSV fields"],fgets:["string fgets(resource fp[, int length])","Get a line from file pointer"],fgetss:["string fgetss(resource fp [, int length [, string allowable_tags]])","Get a line from file pointer and strip HTML tags"],file:["array file(string filename [, int flags[, resource context]])","Read entire file into an array"],file_exists:["bool file_exists(string filename)","Returns true if filename exists"],file_get_contents:["string file_get_contents(string filename [, bool use_include_path [, resource context [, long offset [, long maxlen]]]])","Read the entire file into a string"],file_put_contents:["int file_put_contents(string file, mixed data [, int flags [, resource context]])","Write/Create a file with contents data and return the number of bytes written"],fileatime:["int fileatime(string filename)","Get last access time of file"],filectime:["int filectime(string filename)","Get inode modification time of file"],filegroup:["int filegroup(string filename)","Get file group"],fileinode:["int fileinode(string filename)","Get file inode"],filemtime:["int filemtime(string filename)","Get last modification time of file"],fileowner:["int fileowner(string filename)","Get file owner"],fileperms:["int fileperms(string filename)","Get file permissions"],filesize:["int filesize(string filename)","Get file size"],filetype:["string filetype(string filename)","Get file type"],filter_has_var:["mixed filter_has_var(constant type, string variable_name)","* Returns true if the variable with the name 'name' exists in source."],filter_input:["mixed filter_input(constant type, string variable_name [, long filter [, mixed options]])","* Returns the filtered variable 'name'* from source `type`."],filter_input_array:["mixed filter_input_array(constant type, [, mixed options]])","* Returns an array with all arguments defined in 'definition'."],filter_var:["mixed filter_var(mixed variable [, long filter [, mixed options]])","* Returns the filtered version of the vriable."],filter_var_array:["mixed filter_var_array(array data, [, mixed options]])","* Returns an array with all arguments defined in 'definition'."],finfo_buffer:["string finfo_buffer(resource finfo, char *string [, int options [, resource context]])","Return infromation about a string buffer."],finfo_close:["resource finfo_close(resource finfo)","Close fileinfo resource."],finfo_file:["string finfo_file(resource finfo, char *file_name [, int options [, resource context]])","Return information about a file."],finfo_open:["resource finfo_open([int options [, string arg]])","Create a new fileinfo resource."],finfo_set_flags:["bool finfo_set_flags(resource finfo, int options)","Set libmagic configuration options."],floatval:["float floatval(mixed var)","Get the float value of a variable"],flock:["bool flock(resource fp, int operation [, int &wouldblock])","Portable file locking"],floor:["float floor(float number)","Returns the next lowest integer value from the number"],flush:["void flush(void)","Flush the output buffer"],fmod:["float fmod(float x, float y)","Returns the remainder of dividing x by y as a float"],fnmatch:["bool fnmatch(string pattern, string filename [, int flags])","Match filename against pattern"],fopen:["resource fopen(string filename, string mode [, bool use_include_path [, resource context]])","Open a file or a URL and return a file pointer"],forward_static_call:["mixed forward_static_call(mixed function_name [, mixed parmeter] [, mixed ...])","Call a user function which is the first parameter"],fpassthru:["int fpassthru(resource fp)","Output all remaining data from a file pointer"],fprintf:["int fprintf(resource stream, string format [, mixed arg1 [, mixed ...]])","Output a formatted string into a stream"],fputcsv:["int fputcsv(resource fp, array fields [, string delimiter [, string enclosure]])","Format line as CSV and write to file pointer"],fread:["string fread(resource fp, int length)","Binary-safe file read"],frenchtojd:["int frenchtojd(int month, int day, int year)","Converts a french republic calendar date to julian day count"],fscanf:["mixed fscanf(resource stream, string format [, string ...])","Implements a mostly ANSI compatible fscanf()"],fseek:["int fseek(resource fp, int offset [, int whence])","Seek on a file pointer"],fsockopen:["resource fsockopen(string hostname, int port [, int errno [, string errstr [, float timeout]]])","Open Internet or Unix domain socket connection"],fstat:["array fstat(resource fp)","Stat() on a filehandle"],ftell:["int ftell(resource fp)","Get file pointer's read/write position"],ftok:["int ftok(string pathname, string proj)","Convert a pathname and a project identifier to a System V IPC key"],ftp_alloc:["bool ftp_alloc(resource stream, int size[, &response])","Attempt to allocate space on the remote FTP server"],ftp_cdup:["bool ftp_cdup(resource stream)","Changes to the parent directory"],ftp_chdir:["bool ftp_chdir(resource stream, string directory)","Changes directories"],ftp_chmod:["int ftp_chmod(resource stream, int mode, string filename)","Sets permissions on a file"],ftp_close:["bool ftp_close(resource stream)","Closes the FTP stream"],ftp_connect:["resource ftp_connect(string host [, int port [, int timeout]])","Opens a FTP stream"],ftp_delete:["bool ftp_delete(resource stream, string file)","Deletes a file"],ftp_exec:["bool ftp_exec(resource stream, string command)","Requests execution of a program on the FTP server"],ftp_fget:["bool ftp_fget(resource stream, resource fp, string remote_file, int mode[, int resumepos])","Retrieves a file from the FTP server and writes it to an open file"],ftp_fput:["bool ftp_fput(resource stream, string remote_file, resource fp, int mode[, int startpos])","Stores a file from an open file to the FTP server"],ftp_get:["bool ftp_get(resource stream, string local_file, string remote_file, int mode[, int resume_pos])","Retrieves a file from the FTP server and writes it to a local file"],ftp_get_option:["mixed ftp_get_option(resource stream, int option)","Gets an FTP option"],ftp_login:["bool ftp_login(resource stream, string username, string password)","Logs into the FTP server"],ftp_mdtm:["int ftp_mdtm(resource stream, string filename)","Returns the last modification time of the file, or -1 on error"],ftp_mkdir:["string ftp_mkdir(resource stream, string directory)","Creates a directory and returns the absolute path for the new directory or false on error"],ftp_nb_continue:["int ftp_nb_continue(resource stream)","Continues retrieving/sending a file nbronously"],ftp_nb_fget:["int ftp_nb_fget(resource stream, resource fp, string remote_file, int mode[, int resumepos])","Retrieves a file from the FTP server asynchronly and writes it to an open file"],ftp_nb_fput:["int ftp_nb_fput(resource stream, string remote_file, resource fp, int mode[, int startpos])","Stores a file from an open file to the FTP server nbronly"],ftp_nb_get:["int ftp_nb_get(resource stream, string local_file, string remote_file, int mode[, int resume_pos])","Retrieves a file from the FTP server nbhronly and writes it to a local file"],ftp_nb_put:["int ftp_nb_put(resource stream, string remote_file, string local_file, int mode[, int startpos])","Stores a file on the FTP server"],ftp_nlist:["array ftp_nlist(resource stream, string directory)","Returns an array of filenames in the given directory"],ftp_pasv:["bool ftp_pasv(resource stream, bool pasv)","Turns passive mode on or off"],ftp_put:["bool ftp_put(resource stream, string remote_file, string local_file, int mode[, int startpos])","Stores a file on the FTP server"],ftp_pwd:["string ftp_pwd(resource stream)","Returns the present working directory"],ftp_raw:["array ftp_raw(resource stream, string command)","Sends a literal command to the FTP server"],ftp_rawlist:["array ftp_rawlist(resource stream, string directory [, bool recursive])","Returns a detailed listing of a directory as an array of output lines"],ftp_rename:["bool ftp_rename(resource stream, string src, string dest)","Renames the given file to a new path"],ftp_rmdir:["bool ftp_rmdir(resource stream, string directory)","Removes a directory"],ftp_set_option:["bool ftp_set_option(resource stream, int option, mixed value)","Sets an FTP option"],ftp_site:["bool ftp_site(resource stream, string cmd)","Sends a SITE command to the server"],ftp_size:["int ftp_size(resource stream, string filename)","Returns the size of the file, or -1 on error"],ftp_ssl_connect:["resource ftp_ssl_connect(string host [, int port [, int timeout]])","Opens a FTP-SSL stream"],ftp_systype:["string ftp_systype(resource stream)","Returns the system type identifier"],ftruncate:["bool ftruncate(resource fp, int size)","Truncate file to 'size' length"],func_get_arg:["mixed func_get_arg(int arg_num)","Get the $arg_num'th argument that was passed to the function"],func_get_args:["array func_get_args()","Get an array of the arguments that were passed to the function"],func_num_args:["int func_num_args(void)","Get the number of arguments that were passed to the function"],function_exists:["bool function_exists(string function_name)","Checks if the function exists"],fwrite:["int fwrite(resource fp, string str [, int length])","Binary-safe file write"],gc_collect_cycles:["int gc_collect_cycles(void)","Forces collection of any existing garbage cycles. Returns number of freed zvals"],gc_disable:["void gc_disable(void)","Deactivates the circular reference collector"],gc_enable:["void gc_enable(void)","Activates the circular reference collector"],gc_enabled:["void gc_enabled(void)","Returns status of the circular reference collector"],gd_info:["array gd_info()",""],getKeywords:["static array getKeywords(string $locale) {","* return an associative array containing keyword-value * pairs for this locale. The keys are keys to the array (doh!) * }}}"],get_browser:["mixed get_browser([string browser_name [, bool return_array]])","Get information about the capabilities of a browser. If browser_name is omitted or null, HTTP_USER_AGENT is used. Returns an object by default; if return_array is true, returns an array."],get_called_class:["string get_called_class()",'Retrieves the "Late Static Binding" class name'],get_cfg_var:["mixed get_cfg_var(string option_name)","Get the value of a PHP configuration option"],get_class:["string get_class([object object])","Retrieves the class name"],get_class_methods:["array get_class_methods(mixed class)","Returns an array of method names for class or class instance."],get_class_vars:["array get_class_vars(string class_name)","Returns an array of default properties of the class."],get_current_user:["string get_current_user(void)","Get the name of the owner of the current PHP script"],get_declared_classes:["array get_declared_classes()","Returns an array of all declared classes."],get_declared_interfaces:["array get_declared_interfaces()","Returns an array of all declared interfaces."],get_defined_constants:["array get_defined_constants([bool categorize])","Return an array containing the names and values of all defined constants"],get_defined_functions:["array get_defined_functions(void)","Returns an array of all defined functions"],get_defined_vars:["array get_defined_vars(void)","Returns an associative array of names and values of all currently defined variable names (variables in the current scope)"],get_display_language:["static string get_display_language($locale[, $in_locale = null])","* gets the language for the $locale in $in_locale or default_locale"],get_display_name:["static string get_display_name($locale[, $in_locale = null])","* gets the name for the $locale in $in_locale or default_locale"],get_display_region:["static string get_display_region($locale, $in_locale = null)","* gets the region for the $locale in $in_locale or default_locale"],get_display_script:["static string get_display_script($locale, $in_locale = null)","* gets the script for the $locale in $in_locale or default_locale"],get_extension_funcs:["array get_extension_funcs(string extension_name)","Returns an array with the names of functions belonging to the named extension"],get_headers:["array get_headers(string url[, int format])","fetches all the headers sent by the server in response to a HTTP request"],get_html_translation_table:["array get_html_translation_table([int table [, int quote_style]])","Returns the internal translation table used by htmlspecialchars and htmlentities"],get_include_path:["string get_include_path()","Get the current include_path configuration option"],get_included_files:["array get_included_files(void)","Returns an array with the file names that were include_once()'d"],get_loaded_extensions:["array get_loaded_extensions([bool zend_extensions])","Return an array containing names of loaded extensions"],get_magic_quotes_gpc:["int get_magic_quotes_gpc(void)","Get the current active configuration setting of magic_quotes_gpc"],get_magic_quotes_runtime:["int get_magic_quotes_runtime(void)","Get the current active configuration setting of magic_quotes_runtime"],get_meta_tags:["array get_meta_tags(string filename [, bool use_include_path])","Extracts all meta tag content attributes from a file and returns an array"],get_object_vars:["array get_object_vars(object obj)","Returns an array of object properties"],get_parent_class:["string get_parent_class([mixed object])","Retrieves the parent class name for object or class or current scope."],get_resource_type:["string get_resource_type(resource res)","Get the resource type name for a given resource"],getallheaders:["array getallheaders(void)",""],getcwd:["mixed getcwd(void)","Gets the current directory"],getdate:["array getdate([int timestamp])","Get date/time information"],getenv:["string getenv(string varname)","Get the value of an environment variable"],gethostbyaddr:["string gethostbyaddr(string ip_address)","Get the Internet host name corresponding to a given IP address"],gethostbyname:["string gethostbyname(string hostname)","Get the IP address corresponding to a given Internet host name"],gethostbynamel:["array gethostbynamel(string hostname)","Return a list of IP addresses that a given hostname resolves to."],gethostname:["string gethostname()","Get the host name of the current machine"],getimagesize:["array getimagesize(string imagefile [, array info])","Get the size of an image as 4-element array"],getlastmod:["int getlastmod(void)","Get time of last page modification"],getmygid:["int getmygid(void)","Get PHP script owner's GID"],getmyinode:["int getmyinode(void)","Get the inode of the current script being parsed"],getmypid:["int getmypid(void)","Get current process ID"],getmyuid:["int getmyuid(void)","Get PHP script owner's UID"],getopt:["array getopt(string options [, array longopts])","Get options from the command line argument list"],getprotobyname:["int getprotobyname(string name)","Returns protocol number associated with name as per /etc/protocols"],getprotobynumber:["string getprotobynumber(int proto)","Returns protocol name associated with protocol number proto"],getrandmax:["int getrandmax(void)","Returns the maximum value a random number can have"],getrusage:["array getrusage([int who])","Returns an array of usage statistics"],getservbyname:["int getservbyname(string service, string protocol)",'Returns port associated with service. Protocol must be "tcp" or "udp"'],getservbyport:["string getservbyport(int port, string protocol)",'Returns service name associated with port. Protocol must be "tcp" or "udp"'],gettext:["string gettext(string msgid)","Return the translation of msgid for the current domain, or msgid unaltered if a translation does not exist"],gettimeofday:["array gettimeofday([bool get_as_float])","Returns the current time as array"],gettype:["string gettype(mixed var)","Returns the type of the variable"],glob:["array glob(string pattern [, int flags])","Find pathnames matching a pattern"],gmdate:["string gmdate(string format [, long timestamp])","Format a GMT date/time"],gmmktime:["int gmmktime([int hour [, int min [, int sec [, int mon [, int day [, int year]]]]]])","Get UNIX timestamp for a GMT date"],gmp_abs:["resource gmp_abs(resource a)","Calculates absolute value"],gmp_add:["resource gmp_add(resource a, resource b)","Add a and b"],gmp_and:["resource gmp_and(resource a, resource b)","Calculates logical AND of a and b"],gmp_clrbit:["void gmp_clrbit(resource &a, int index)","Clears bit in a"],gmp_cmp:["int gmp_cmp(resource a, resource b)","Compares two numbers"],gmp_com:["resource gmp_com(resource a)","Calculates one's complement of a"],gmp_div_q:["resource gmp_div_q(resource a, resource b [, int round])","Divide a by b, returns quotient only"],gmp_div_qr:["array gmp_div_qr(resource a, resource b [, int round])","Divide a by b, returns quotient and reminder"],gmp_div_r:["resource gmp_div_r(resource a, resource b [, int round])","Divide a by b, returns reminder only"],gmp_divexact:["resource gmp_divexact(resource a, resource b)","Divide a by b using exact division algorithm"],gmp_fact:["resource gmp_fact(int a)","Calculates factorial function"],gmp_gcd:["resource gmp_gcd(resource a, resource b)","Computes greatest common denominator (gcd) of a and b"],gmp_gcdext:["array gmp_gcdext(resource a, resource b)","Computes G, S, and T, such that AS + BT = G = `gcd' (A, B)"],gmp_hamdist:["int gmp_hamdist(resource a, resource b)","Calculates hamming distance between a and b"],gmp_init:["resource gmp_init(mixed number [, int base])","Initializes GMP number"],gmp_intval:["int gmp_intval(resource gmpnumber)","Gets signed long value of GMP number"],gmp_invert:["resource gmp_invert(resource a, resource b)","Computes the inverse of a modulo b"],gmp_jacobi:["int gmp_jacobi(resource a, resource b)","Computes Jacobi symbol"],gmp_legendre:["int gmp_legendre(resource a, resource b)","Computes Legendre symbol"],gmp_mod:["resource gmp_mod(resource a, resource b)","Computes a modulo b"],gmp_mul:["resource gmp_mul(resource a, resource b)","Multiply a and b"],gmp_neg:["resource gmp_neg(resource a)","Negates a number"],gmp_nextprime:["resource gmp_nextprime(resource a)","Finds next prime of a"],gmp_or:["resource gmp_or(resource a, resource b)","Calculates logical OR of a and b"],gmp_perfect_square:["bool gmp_perfect_square(resource a)","Checks if a is an exact square"],gmp_popcount:["int gmp_popcount(resource a)","Calculates the population count of a"],gmp_pow:["resource gmp_pow(resource base, int exp)","Raise base to power exp"],gmp_powm:["resource gmp_powm(resource base, resource exp, resource mod)","Raise base to power exp and take result modulo mod"],gmp_prob_prime:["int gmp_prob_prime(resource a[, int reps])",'Checks if a is "probably prime"'],gmp_random:["resource gmp_random([int limiter])","Gets random number"],gmp_scan0:["int gmp_scan0(resource a, int start)","Finds first zero bit"],gmp_scan1:["int gmp_scan1(resource a, int start)","Finds first non-zero bit"],gmp_setbit:["void gmp_setbit(resource &a, int index[, bool set_clear])","Sets or clear bit in a"],gmp_sign:["int gmp_sign(resource a)","Gets the sign of the number"],gmp_sqrt:["resource gmp_sqrt(resource a)","Takes integer part of square root of a"],gmp_sqrtrem:["array gmp_sqrtrem(resource a)","Square root with remainder"],gmp_strval:["string gmp_strval(resource gmpnumber [, int base])","Gets string representation of GMP number"],gmp_sub:["resource gmp_sub(resource a, resource b)","Subtract b from a"],gmp_testbit:["bool gmp_testbit(resource a, int index)","Tests if bit is set in a"],gmp_xor:["resource gmp_xor(resource a, resource b)","Calculates logical exclusive OR of a and b"],gmstrftime:["string gmstrftime(string format [, int timestamp])","Format a GMT/UCT time/date according to locale settings"],grapheme_extract:["string grapheme_extract(string str, int size[, int extract_type[, int start[, int next]]])","Function to extract a sequence of default grapheme clusters"],grapheme_stripos:["int grapheme_stripos(string haystack, string needle [, int offset ])","Find position of first occurrence of a string within another, ignoring case differences"],grapheme_stristr:["string grapheme_stristr(string haystack, string needle[, bool part])","Finds first occurrence of a string within another"],grapheme_strlen:["int grapheme_strlen(string str)","Get number of graphemes in a string"],grapheme_strpos:["int grapheme_strpos(string haystack, string needle [, int offset ])","Find position of first occurrence of a string within another"],grapheme_strripos:["int grapheme_strripos(string haystack, string needle [, int offset])","Find position of last occurrence of a string within another, ignoring case"],grapheme_strrpos:["int grapheme_strrpos(string haystack, string needle [, int offset])","Find position of last occurrence of a string within another"],grapheme_strstr:["string grapheme_strstr(string haystack, string needle[, bool part])","Finds first occurrence of a string within another"],grapheme_substr:["string grapheme_substr(string str, int start [, int length])","Returns part of a string"],gregoriantojd:["int gregoriantojd(int month, int day, int year)","Converts a gregorian calendar date to julian day count"],gzcompress:["string gzcompress(string data [, int level])","Gzip-compress a string"],gzdeflate:["string gzdeflate(string data [, int level])","Gzip-compress a string"],gzencode:["string gzencode(string data [, int level [, int encoding_mode]])","GZ encode a string"],gzfile:["array gzfile(string filename [, int use_include_path])","Read und uncompress entire .gz-file into an array"],gzinflate:["string gzinflate(string data [, int length])","Unzip a gzip-compressed string"],gzopen:["resource gzopen(string filename, string mode [, int use_include_path])","Open a .gz-file and return a .gz-file pointer"],gzuncompress:["string gzuncompress(string data [, int length])","Unzip a gzip-compressed string"],hash:["string hash(string algo, string data[, bool raw_output = false])","Generate a hash of a given input string Returns lowercase hexits by default"],hash_algos:["array hash_algos(void)","Return a list of registered hashing algorithms"],hash_copy:["resource hash_copy(resource context)","Copy hash resource"],hash_file:["string hash_file(string algo, string filename[, bool raw_output = false])","Generate a hash of a given file Returns lowercase hexits by default"],hash_final:["string hash_final(resource context[, bool raw_output=false])","Output resulting digest"],hash_hmac:["string hash_hmac(string algo, string data, string key[, bool raw_output = false])","Generate a hash of a given input string with a key using HMAC Returns lowercase hexits by default"],hash_hmac_file:["string hash_hmac_file(string algo, string filename, string key[, bool raw_output = false])","Generate a hash of a given file with a key using HMAC Returns lowercase hexits by default"],hash_init:["resource hash_init(string algo[, int options, string key])","Initialize a hashing context"],hash_update:["bool hash_update(resource context, string data)","Pump data into the hashing algorithm"],hash_update_file:["bool hash_update_file(resource context, string filename[, resource context])","Pump data into the hashing algorithm from a file"],hash_update_stream:["int hash_update_stream(resource context, resource handle[, integer length])","Pump data into the hashing algorithm from an open stream"],header:["void header(string header [, bool replace, [int http_response_code]])","Sends a raw HTTP header"],header_remove:["void header_remove([string name])","Removes an HTTP header previously set using header()"],headers_list:["array headers_list(void)","Return list of headers to be sent / already sent"],headers_sent:["bool headers_sent([string &$file [, int &$line]])","Returns true if headers have already been sent, false otherwise"],hebrev:["string hebrev(string str [, int max_chars_per_line])","Converts logical Hebrew text to visual text"],hebrevc:["string hebrevc(string str [, int max_chars_per_line])","Converts logical Hebrew text to visual text with newline conversion"],hexdec:["int hexdec(string hexadecimal_number)","Returns the decimal equivalent of the hexadecimal number"],highlight_file:["bool highlight_file(string file_name [, bool return] )","Syntax highlight a source file"],highlight_string:["bool highlight_string(string string [, bool return] )","Syntax highlight a string or optionally return it"],html_entity_decode:["string html_entity_decode(string string [, int quote_style][, string charset])","Convert all HTML entities to their applicable characters"],htmlentities:["string htmlentities(string string [, int quote_style[, string charset[, bool double_encode]]])","Convert all applicable characters to HTML entities"],htmlspecialchars:["string htmlspecialchars(string string [, int quote_style[, string charset[, bool double_encode]]])","Convert special characters to HTML entities"],htmlspecialchars_decode:["string htmlspecialchars_decode(string string [, int quote_style])","Convert special HTML entities back to characters"],http_build_query:["string http_build_query(mixed formdata [, string prefix [, string arg_separator]])","Generates a form-encoded query string from an associative array or object."],hypot:["float hypot(float num1, float num2)","Returns sqrt(num1*num1 + num2*num2)"],ibase_add_user:["bool ibase_add_user(resource service_handle, string user_name, string password [, string first_name [, string middle_name [, string last_name]]])","Add a user to security database"],ibase_affected_rows:["int ibase_affected_rows( [ resource link_identifier ] )","Returns the number of rows affected by the previous INSERT, UPDATE or DELETE statement"],ibase_backup:["mixed ibase_backup(resource service_handle, string source_db, string dest_file [, int options [, bool verbose]])","Initiates a backup task in the service manager and returns immediately"],ibase_blob_add:["bool ibase_blob_add(resource blob_handle, string data)","Add data into created blob"],ibase_blob_cancel:["bool ibase_blob_cancel(resource blob_handle)","Cancel creating blob"],ibase_blob_close:["string ibase_blob_close(resource blob_handle)","Close blob"],ibase_blob_create:["resource ibase_blob_create([resource link_identifier])","Create blob for adding data"],ibase_blob_echo:["bool ibase_blob_echo([ resource link_identifier, ] string blob_id)","Output blob contents to browser"],ibase_blob_get:["string ibase_blob_get(resource blob_handle, int len)","Get len bytes data from open blob"],ibase_blob_import:["string ibase_blob_import([ resource link_identifier, ] resource file)","Create blob, copy file in it, and close it"],ibase_blob_info:["array ibase_blob_info([ resource link_identifier, ] string blob_id)","Return blob length and other useful info"],ibase_blob_open:["resource ibase_blob_open([ resource link_identifier, ] string blob_id)","Open blob for retrieving data parts"],ibase_close:["bool ibase_close([resource link_identifier])","Close an InterBase connection"],ibase_commit:["bool ibase_commit( resource link_identifier )","Commit transaction"],ibase_commit_ret:["bool ibase_commit_ret( resource link_identifier )","Commit transaction and retain the transaction context"],ibase_connect:["resource ibase_connect(string database [, string username [, string password [, string charset [, int buffers [, int dialect [, string role]]]]]])","Open a connection to an InterBase database"],ibase_db_info:["string ibase_db_info(resource service_handle, string db, int action [, int argument])","Request statistics about a database"],ibase_delete_user:["bool ibase_delete_user(resource service_handle, string user_name, string password [, string first_name [, string middle_name [, string last_name]]])","Delete a user from security database"],ibase_drop_db:["bool ibase_drop_db([resource link_identifier])","Drop an InterBase database"],ibase_errcode:["int ibase_errcode(void)","Return error code"],ibase_errmsg:["string ibase_errmsg(void)","Return error message"],ibase_execute:["mixed ibase_execute(resource query [, mixed bind_arg [, mixed bind_arg [, ...]]])","Execute a previously prepared query"],ibase_fetch_assoc:["array ibase_fetch_assoc(resource result [, int fetch_flags])","Fetch a row from the results of a query"],ibase_fetch_object:["object ibase_fetch_object(resource result [, int fetch_flags])","Fetch a object from the results of a query"],ibase_fetch_row:["array ibase_fetch_row(resource result [, int fetch_flags])","Fetch a row from the results of a query"],ibase_field_info:["array ibase_field_info(resource query_result, int field_number)","Get information about a field"],ibase_free_event_handler:["bool ibase_free_event_handler(resource event)","Frees the event handler set by ibase_set_event_handler()"],ibase_free_query:["bool ibase_free_query(resource query)","Free memory used by a query"],ibase_free_result:["bool ibase_free_result(resource result)","Free the memory used by a result"],ibase_gen_id:["int ibase_gen_id(string generator [, int increment [, resource link_identifier ]])","Increments the named generator and returns its new value"],ibase_maintain_db:["bool ibase_maintain_db(resource service_handle, string db, int action [, int argument])","Execute a maintenance command on the database server"],ibase_modify_user:["bool ibase_modify_user(resource service_handle, string user_name, string password [, string first_name [, string middle_name [, string last_name]]])","Modify a user in security database"],ibase_name_result:["bool ibase_name_result(resource result, string name)","Assign a name to a result for use with ... WHERE CURRENT OF statements"],ibase_num_fields:["int ibase_num_fields(resource query_result)","Get the number of fields in result"],ibase_num_params:["int ibase_num_params(resource query)","Get the number of params in a prepared query"],ibase_num_rows:["int ibase_num_rows( resource result_identifier )","Return the number of rows that are available in a result"],ibase_param_info:["array ibase_param_info(resource query, int field_number)","Get information about a parameter"],ibase_pconnect:["resource ibase_pconnect(string database [, string username [, string password [, string charset [, int buffers [, int dialect [, string role]]]]]])","Open a persistent connection to an InterBase database"],ibase_prepare:["resource ibase_prepare(resource link_identifier[, string query [, resource trans_identifier ]])","Prepare a query for later execution"],ibase_query:["mixed ibase_query([resource link_identifier, [ resource link_identifier, ]] string query [, mixed bind_arg [, mixed bind_arg [, ...]]])","Execute a query"],ibase_restore:["mixed ibase_restore(resource service_handle, string source_file, string dest_db [, int options [, bool verbose]])","Initiates a restore task in the service manager and returns immediately"],ibase_rollback:["bool ibase_rollback( resource link_identifier )","Rollback transaction"],ibase_rollback_ret:["bool ibase_rollback_ret( resource link_identifier )","Rollback transaction and retain the transaction context"],ibase_server_info:["string ibase_server_info(resource service_handle, int action)","Request information about a database server"],ibase_service_attach:["resource ibase_service_attach(string host, string dba_username, string dba_password)","Connect to the service manager"],ibase_service_detach:["bool ibase_service_detach(resource service_handle)","Disconnect from the service manager"],ibase_set_event_handler:["resource ibase_set_event_handler([resource link_identifier,] callback handler, string event [, string event [, ...]])","Register the callback for handling each of the named events"],ibase_trans:["resource ibase_trans([int trans_args [, resource link_identifier [, ... ], int trans_args [, resource link_identifier [, ... ]] [, ...]]])","Start a transaction over one or several databases"],ibase_wait_event:["string ibase_wait_event([resource link_identifier,] string event [, string event [, ...]])","Waits for any one of the passed Interbase events to be posted by the database, and returns its name"],iconv:["string iconv(string in_charset, string out_charset, string str)","Returns str converted to the out_charset character set"],iconv_get_encoding:["mixed iconv_get_encoding([string type])","Get internal encoding and output encoding for ob_iconv_handler()"],iconv_mime_decode:["string iconv_mime_decode(string encoded_string [, int mode, string charset])","Decodes a mime header field"],iconv_mime_decode_headers:["array iconv_mime_decode_headers(string headers [, int mode, string charset])","Decodes multiple mime header fields"],iconv_mime_encode:["string iconv_mime_encode(string field_name, string field_value [, array preference])","Composes a mime header field with field_name and field_value in a specified scheme"],iconv_set_encoding:["bool iconv_set_encoding(string type, string charset)","Sets internal encoding and output encoding for ob_iconv_handler()"],iconv_strlen:["int iconv_strlen(string str [, string charset])","Returns the character count of str"],iconv_strpos:["int iconv_strpos(string haystack, string needle [, int offset [, string charset]])","Finds position of first occurrence of needle within part of haystack beginning with offset"],iconv_strrpos:["int iconv_strrpos(string haystack, string needle [, string charset])","Finds position of last occurrence of needle within part of haystack beginning with offset"],iconv_substr:["string iconv_substr(string str, int offset, [int length, string charset])","Returns specified part of a string"],idate:["int idate(string format [, int timestamp])","Format a local time/date as integer"],idn_to_ascii:["int idn_to_ascii(string domain[, int options])","Converts an Unicode domain to ASCII representation, as defined in the IDNA RFC"],idn_to_utf8:["int idn_to_utf8(string domain[, int options])","Converts an ASCII representation of the domain to Unicode (UTF-8), as defined in the IDNA RFC"],ignore_user_abort:["int ignore_user_abort([string value])","Set whether we want to ignore a user abort event or not"],image2wbmp:["bool image2wbmp(resource im [, string filename [, int threshold]])","Output WBMP image to browser or file"],image_type_to_extension:["string image_type_to_extension(int imagetype [, bool include_dot])","Get file extension for image-type returned by getimagesize, exif_read_data, exif_thumbnail, exif_imagetype"],image_type_to_mime_type:["string image_type_to_mime_type(int imagetype)","Get Mime-Type for image-type returned by getimagesize, exif_read_data, exif_thumbnail, exif_imagetype"],imagealphablending:["bool imagealphablending(resource im, bool on)","Turn alpha blending mode on or off for the given image"],imageantialias:["bool imageantialias(resource im, bool on)","Should antialiased functions used or not"],imagearc:["bool imagearc(resource im, int cx, int cy, int w, int h, int s, int e, int col)","Draw a partial ellipse"],imagechar:["bool imagechar(resource im, int font, int x, int y, string c, int col)","Draw a character"],imagecharup:["bool imagecharup(resource im, int font, int x, int y, string c, int col)","Draw a character rotated 90 degrees counter-clockwise"],imagecolorallocate:["int imagecolorallocate(resource im, int red, int green, int blue)","Allocate a color for an image"],imagecolorallocatealpha:["int imagecolorallocatealpha(resource im, int red, int green, int blue, int alpha)","Allocate a color with an alpha level. Works for true color and palette based images"],imagecolorat:["int imagecolorat(resource im, int x, int y)","Get the index of the color of a pixel"],imagecolorclosest:["int imagecolorclosest(resource im, int red, int green, int blue)","Get the index of the closest color to the specified color"],imagecolorclosestalpha:["int imagecolorclosestalpha(resource im, int red, int green, int blue, int alpha)","Find the closest matching colour with alpha transparency"],imagecolorclosesthwb:["int imagecolorclosesthwb(resource im, int red, int green, int blue)","Get the index of the color which has the hue, white and blackness nearest to the given color"],imagecolordeallocate:["bool imagecolordeallocate(resource im, int index)","De-allocate a color for an image"],imagecolorexact:["int imagecolorexact(resource im, int red, int green, int blue)","Get the index of the specified color"],imagecolorexactalpha:["int imagecolorexactalpha(resource im, int red, int green, int blue, int alpha)","Find exact match for colour with transparency"],imagecolormatch:["bool imagecolormatch(resource im1, resource im2)","Makes the colors of the palette version of an image more closely match the true color version"],imagecolorresolve:["int imagecolorresolve(resource im, int red, int green, int blue)","Get the index of the specified color or its closest possible alternative"],imagecolorresolvealpha:["int imagecolorresolvealpha(resource im, int red, int green, int blue, int alpha)","Resolve/Allocate a colour with an alpha level. Works for true colour and palette based images"],imagecolorset:["void imagecolorset(resource im, int col, int red, int green, int blue)","Set the color for the specified palette index"],imagecolorsforindex:["array imagecolorsforindex(resource im, int col)","Get the colors for an index"],imagecolorstotal:["int imagecolorstotal(resource im)","Find out the number of colors in an image's palette"],imagecolortransparent:["int imagecolortransparent(resource im [, int col])","Define a color as transparent"],imageconvolution:["resource imageconvolution(resource src_im, array matrix3x3, double div, double offset)","Apply a 3x3 convolution matrix, using coefficient div and offset"],imagecopy:["bool imagecopy(resource dst_im, resource src_im, int dst_x, int dst_y, int src_x, int src_y, int src_w, int src_h)","Copy part of an image"],imagecopymerge:["bool imagecopymerge(resource src_im, resource dst_im, int dst_x, int dst_y, int src_x, int src_y, int src_w, int src_h, int pct)","Merge one part of an image with another"],imagecopymergegray:["bool imagecopymergegray(resource src_im, resource dst_im, int dst_x, int dst_y, int src_x, int src_y, int src_w, int src_h, int pct)","Merge one part of an image with another"],imagecopyresampled:["bool imagecopyresampled(resource dst_im, resource src_im, int dst_x, int dst_y, int src_x, int src_y, int dst_w, int dst_h, int src_w, int src_h)","Copy and resize part of an image using resampling to help ensure clarity"],imagecopyresized:["bool imagecopyresized(resource dst_im, resource src_im, int dst_x, int dst_y, int src_x, int src_y, int dst_w, int dst_h, int src_w, int src_h)","Copy and resize part of an image"],imagecreate:["resource imagecreate(int x_size, int y_size)","Create a new image"],imagecreatefromgd:["resource imagecreatefromgd(string filename)","Create a new image from GD file or URL"],imagecreatefromgd2:["resource imagecreatefromgd2(string filename)","Create a new image from GD2 file or URL"],imagecreatefromgd2part:["resource imagecreatefromgd2part(string filename, int srcX, int srcY, int width, int height)","Create a new image from a given part of GD2 file or URL"],imagecreatefromgif:["resource imagecreatefromgif(string filename)","Create a new image from GIF file or URL"],imagecreatefromjpeg:["resource imagecreatefromjpeg(string filename)","Create a new image from JPEG file or URL"],imagecreatefrompng:["resource imagecreatefrompng(string filename)","Create a new image from PNG file or URL"],imagecreatefromstring:["resource imagecreatefromstring(string image)","Create a new image from the image stream in the string"],imagecreatefromwbmp:["resource imagecreatefromwbmp(string filename)","Create a new image from WBMP file or URL"],imagecreatefromxbm:["resource imagecreatefromxbm(string filename)","Create a new image from XBM file or URL"],imagecreatefromxpm:["resource imagecreatefromxpm(string filename)","Create a new image from XPM file or URL"],imagecreatetruecolor:["resource imagecreatetruecolor(int x_size, int y_size)","Create a new true color image"],imagedashedline:["bool imagedashedline(resource im, int x1, int y1, int x2, int y2, int col)","Draw a dashed line"],imagedestroy:["bool imagedestroy(resource im)","Destroy an image"],imageellipse:["bool imageellipse(resource im, int cx, int cy, int w, int h, int color)","Draw an ellipse"],imagefill:["bool imagefill(resource im, int x, int y, int col)","Flood fill"],imagefilledarc:["bool imagefilledarc(resource im, int cx, int cy, int w, int h, int s, int e, int col, int style)","Draw a filled partial ellipse"],imagefilledellipse:["bool imagefilledellipse(resource im, int cx, int cy, int w, int h, int color)","Draw an ellipse"],imagefilledpolygon:["bool imagefilledpolygon(resource im, array point, int num_points, int col)","Draw a filled polygon"],imagefilledrectangle:["bool imagefilledrectangle(resource im, int x1, int y1, int x2, int y2, int col)","Draw a filled rectangle"],imagefilltoborder:["bool imagefilltoborder(resource im, int x, int y, int border, int col)","Flood fill to specific color"],imagefilter:["bool imagefilter(resource src_im, int filtertype, [args] )","Applies Filter an image using a custom angle"],imagefontheight:["int imagefontheight(int font)","Get font height"],imagefontwidth:["int imagefontwidth(int font)","Get font width"],imageftbbox:["array imageftbbox(float size, float angle, string font_file, string text [, array extrainfo])","Give the bounding box of a text using fonts via freetype2"],imagefttext:["array imagefttext(resource im, float size, float angle, int x, int y, int col, string font_file, string text [, array extrainfo])","Write text to the image using fonts via freetype2"],imagegammacorrect:["bool imagegammacorrect(resource im, float inputgamma, float outputgamma)","Apply a gamma correction to a GD image"],imagegd:["bool imagegd(resource im [, string filename])","Output GD image to browser or file"],imagegd2:["bool imagegd2(resource im [, string filename, [, int chunk_size, [, int type]]])","Output GD2 image to browser or file"],imagegif:["bool imagegif(resource im [, string filename])","Output GIF image to browser or file"],imagegrabscreen:["resource imagegrabscreen()","Grab a screenshot"],imagegrabwindow:["resource imagegrabwindow(int window_handle [, int client_area])","Grab a window or its client area using a windows handle (HWND property in COM instance)"],imageinterlace:["int imageinterlace(resource im [, int interlace])","Enable or disable interlace"],imageistruecolor:["bool imageistruecolor(resource im)","return true if the image uses truecolor"],imagejpeg:["bool imagejpeg(resource im [, string filename [, int quality]])","Output JPEG image to browser or file"],imagelayereffect:["bool imagelayereffect(resource im, int effect)","Set the alpha blending flag to use the bundled libgd layering effects"],imageline:["bool imageline(resource im, int x1, int y1, int x2, int y2, int col)","Draw a line"],imageloadfont:["int imageloadfont(string filename)","Load a new font"],imagepalettecopy:["void imagepalettecopy(resource dst, resource src)","Copy the palette from the src image onto the dst image"],imagepng:["bool imagepng(resource im [, string filename])","Output PNG image to browser or file"],imagepolygon:["bool imagepolygon(resource im, array point, int num_points, int col)","Draw a polygon"],imagepsbbox:["array imagepsbbox(string text, resource font, int size [, int space, int tightness, float angle])","Return the bounding box needed by a string if rasterized"],imagepscopyfont:["int imagepscopyfont(int font_index)","Make a copy of a font for purposes like extending or reenconding"],imagepsencodefont:["bool imagepsencodefont(resource font_index, string filename)","To change a fonts character encoding vector"],imagepsextendfont:["bool imagepsextendfont(resource font_index, float extend)","Extend or or condense (if extend < 1) a font"],imagepsfreefont:["bool imagepsfreefont(resource font_index)","Free memory used by a font"],imagepsloadfont:["resource imagepsloadfont(string pathname)","Load a new font from specified file"],imagepsslantfont:["bool imagepsslantfont(resource font_index, float slant)","Slant a font"],imagepstext:["array imagepstext(resource image, string text, resource font, int size, int foreground, int background, int xcoord, int ycoord [, int space [, int tightness [, float angle [, int antialias])","Rasterize a string over an image"],imagerectangle:["bool imagerectangle(resource im, int x1, int y1, int x2, int y2, int col)","Draw a rectangle"],imagerotate:["resource imagerotate(resource src_im, float angle, int bgdcolor [, int ignoretransparent])","Rotate an image using a custom angle"],imagesavealpha:["bool imagesavealpha(resource im, bool on)","Include alpha channel to a saved image"],imagesetbrush:["bool imagesetbrush(resource image, resource brush)",'Set the brush image to $brush when filling $image with the "IMG_COLOR_BRUSHED" color'],imagesetpixel:["bool imagesetpixel(resource im, int x, int y, int col)","Set a single pixel"],imagesetstyle:["bool imagesetstyle(resource im, array styles)","Set the line drawing styles for use with imageline and IMG_COLOR_STYLED."],imagesetthickness:["bool imagesetthickness(resource im, int thickness)","Set line thickness for drawing lines, ellipses, rectangles, polygons etc."],imagesettile:["bool imagesettile(resource image, resource tile)",'Set the tile image to $tile when filling $image with the "IMG_COLOR_TILED" color'],imagestring:["bool imagestring(resource im, int font, int x, int y, string str, int col)","Draw a string horizontally"],imagestringup:["bool imagestringup(resource im, int font, int x, int y, string str, int col)","Draw a string vertically - rotated 90 degrees counter-clockwise"],imagesx:["int imagesx(resource im)","Get image width"],imagesy:["int imagesy(resource im)","Get image height"],imagetruecolortopalette:["void imagetruecolortopalette(resource im, bool ditherFlag, int colorsWanted)","Convert a true colour image to a palette based image with a number of colours, optionally using dithering."],imagettfbbox:["array imagettfbbox(float size, float angle, string font_file, string text)","Give the bounding box of a text using TrueType fonts"],imagettftext:["array imagettftext(resource im, float size, float angle, int x, int y, int col, string font_file, string text)","Write text to the image using a TrueType font"],imagetypes:["int imagetypes(void)","Return the types of images supported in a bitfield - 1=GIF, 2=JPEG, 4=PNG, 8=WBMP, 16=XPM"],imagewbmp:["bool imagewbmp(resource im [, string filename, [, int foreground]])","Output WBMP image to browser or file"],imagexbm:["int imagexbm(int im, string filename [, int foreground])","Output XBM image to browser or file"],imap_8bit:["string imap_8bit(string text)","Convert an 8-bit string to a quoted-printable string"],imap_alerts:["array imap_alerts(void)","Returns an array of all IMAP alerts that have been generated since the last page load or since the last imap_alerts() call, whichever came last. The alert stack is cleared after imap_alerts() is called."],imap_append:["bool imap_append(resource stream_id, string folder, string message [, string options [, string internal_date]])","Append a new message to a specified mailbox"],imap_base64:["string imap_base64(string text)","Decode BASE64 encoded text"],imap_binary:["string imap_binary(string text)","Convert an 8bit string to a base64 string"],imap_body:["string imap_body(resource stream_id, int msg_no [, int options])","Read the message body"],imap_bodystruct:["object imap_bodystruct(resource stream_id, int msg_no, string section)","Read the structure of a specified body section of a specific message"],imap_check:["object imap_check(resource stream_id)","Get mailbox properties"],imap_clearflag_full:["bool imap_clearflag_full(resource stream_id, string sequence, string flag [, int options])","Clears flags on messages"],imap_close:["bool imap_close(resource stream_id [, int options])","Close an IMAP stream"],imap_createmailbox:["bool imap_createmailbox(resource stream_id, string mailbox)","Create a new mailbox"],imap_delete:["bool imap_delete(resource stream_id, int msg_no [, int options])","Mark a message for deletion"],imap_deletemailbox:["bool imap_deletemailbox(resource stream_id, string mailbox)","Delete a mailbox"],imap_errors:["array imap_errors(void)","Returns an array of all IMAP errors generated since the last page load, or since the last imap_errors() call, whichever came last. The error stack is cleared after imap_errors() is called."],imap_expunge:["bool imap_expunge(resource stream_id)","Permanently delete all messages marked for deletion"],imap_fetch_overview:["array imap_fetch_overview(resource stream_id, string sequence [, int options])","Read an overview of the information in the headers of the given message sequence"],imap_fetchbody:["string imap_fetchbody(resource stream_id, int msg_no, string section [, int options])","Get a specific body section"],imap_fetchheader:["string imap_fetchheader(resource stream_id, int msg_no [, int options])","Get the full unfiltered header for a message"],imap_fetchstructure:["object imap_fetchstructure(resource stream_id, int msg_no [, int options])","Read the full structure of a message"],imap_gc:["bool imap_gc(resource stream_id, int flags)","This function garbage collects (purges) the cache of entries of a specific type."],imap_get_quota:["array imap_get_quota(resource stream_id, string qroot)","Returns the quota set to the mailbox account qroot"],imap_get_quotaroot:["array imap_get_quotaroot(resource stream_id, string mbox)","Returns the quota set to the mailbox account mbox"],imap_getacl:["array imap_getacl(resource stream_id, string mailbox)","Gets the ACL for a given mailbox"],imap_getmailboxes:["array imap_getmailboxes(resource stream_id, string ref, string pattern)","Reads the list of mailboxes and returns a full array of objects containing name, attributes, and delimiter"],imap_getsubscribed:["array imap_getsubscribed(resource stream_id, string ref, string pattern)","Return a list of subscribed mailboxes, in the same format as imap_getmailboxes()"],imap_headerinfo:["object imap_headerinfo(resource stream_id, int msg_no [, int from_length [, int subject_length [, string default_host]]])","Read the headers of the message"],imap_headers:["array imap_headers(resource stream_id)","Returns headers for all messages in a mailbox"],imap_last_error:["string imap_last_error(void)","Returns the last error that was generated by an IMAP function. The error stack is NOT cleared after this call."],imap_list:["array imap_list(resource stream_id, string ref, string pattern)","Read the list of mailboxes"],imap_listscan:["array imap_listscan(resource stream_id, string ref, string pattern, string content)","Read list of mailboxes containing a certain string"],imap_lsub:["array imap_lsub(resource stream_id, string ref, string pattern)","Return a list of subscribed mailboxes"],imap_mail:["bool imap_mail(string to, string subject, string message [, string additional_headers [, string cc [, string bcc [, string rpath]]]])","Send an email message"],imap_mail_compose:["string imap_mail_compose(array envelope, array body)","Create a MIME message based on given envelope and body sections"],imap_mail_copy:["bool imap_mail_copy(resource stream_id, string msglist, string mailbox [, int options])","Copy specified message to a mailbox"],imap_mail_move:["bool imap_mail_move(resource stream_id, string sequence, string mailbox [, int options])","Move specified message to a mailbox"],imap_mailboxmsginfo:["object imap_mailboxmsginfo(resource stream_id)","Returns info about the current mailbox"],imap_mime_header_decode:["array imap_mime_header_decode(string str)","Decode mime header element in accordance with RFC 2047 and return array of objects containing 'charset' encoding and decoded 'text'"],imap_msgno:["int imap_msgno(resource stream_id, int unique_msg_id)","Get the sequence number associated with a UID"],imap_mutf7_to_utf8:["string imap_mutf7_to_utf8(string in)","Decode a modified UTF-7 string to UTF-8"],imap_num_msg:["int imap_num_msg(resource stream_id)","Gives the number of messages in the current mailbox"],imap_num_recent:["int imap_num_recent(resource stream_id)","Gives the number of recent messages in current mailbox"],imap_open:["resource imap_open(string mailbox, string user, string password [, int options [, int n_retries]])","Open an IMAP stream to a mailbox"],imap_ping:["bool imap_ping(resource stream_id)","Check if the IMAP stream is still active"],imap_qprint:["string imap_qprint(string text)","Convert a quoted-printable string to an 8-bit string"],imap_renamemailbox:["bool imap_renamemailbox(resource stream_id, string old_name, string new_name)","Rename a mailbox"],imap_reopen:["bool imap_reopen(resource stream_id, string mailbox [, int options [, int n_retries]])","Reopen an IMAP stream to a new mailbox"],imap_rfc822_parse_adrlist:["array imap_rfc822_parse_adrlist(string address_string, string default_host)","Parses an address string"],imap_rfc822_parse_headers:["object imap_rfc822_parse_headers(string headers [, string default_host])","Parse a set of mail headers contained in a string, and return an object similar to imap_headerinfo()"],imap_rfc822_write_address:["string imap_rfc822_write_address(string mailbox, string host, string personal)","Returns a properly formatted email address given the mailbox, host, and personal info"],imap_savebody:['bool imap_savebody(resource stream_id, string|resource file, int msg_no[, string section = ""[, int options = 0]])',"Save a specific body section to a file"],imap_search:["array imap_search(resource stream_id, string criteria [, int options [, string charset]])","Return a list of messages matching the given criteria"],imap_set_quota:["bool imap_set_quota(resource stream_id, string qroot, int mailbox_size)","Will set the quota for qroot mailbox"],imap_setacl:["bool imap_setacl(resource stream_id, string mailbox, string id, string rights)","Sets the ACL for a given mailbox"],imap_setflag_full:["bool imap_setflag_full(resource stream_id, string sequence, string flag [, int options])","Sets flags on messages"],imap_sort:["array imap_sort(resource stream_id, int criteria, int reverse [, int options [, string search_criteria [, string charset]]])","Sort an array of message headers, optionally including only messages that meet specified criteria."],imap_status:["object imap_status(resource stream_id, string mailbox, int options)","Get status info from a mailbox"],imap_subscribe:["bool imap_subscribe(resource stream_id, string mailbox)","Subscribe to a mailbox"],imap_thread:["array imap_thread(resource stream_id [, int options])","Return threaded by REFERENCES tree"],imap_timeout:["mixed imap_timeout(int timeout_type [, int timeout])","Set or fetch imap timeout"],imap_uid:["int imap_uid(resource stream_id, int msg_no)","Get the unique message id associated with a standard sequential message number"],imap_undelete:["bool imap_undelete(resource stream_id, int msg_no [, int flags])","Remove the delete flag from a message"],imap_unsubscribe:["bool imap_unsubscribe(resource stream_id, string mailbox)","Unsubscribe from a mailbox"],imap_utf7_decode:["string imap_utf7_decode(string buf)","Decode a modified UTF-7 string"],imap_utf7_encode:["string imap_utf7_encode(string buf)","Encode a string in modified UTF-7"],imap_utf8:["string imap_utf8(string mime_encoded_text)","Convert a mime-encoded text to UTF-8"],imap_utf8_to_mutf7:["string imap_utf8_to_mutf7(string in)","Encode a UTF-8 string to modified UTF-7"],implode:["string implode([string glue,] array pieces)","Joins array elements placing glue string between items and return one string"],import_request_variables:["bool import_request_variables(string types [, string prefix])","Import GET/POST/Cookie variables into the global scope"],in_array:["bool in_array(mixed needle, array haystack [, bool strict])","Checks if the given value exists in the array"],include:["bool include(string path)","Includes and evaluates the specified file"],include_once:["bool include_once(string path)","Includes and evaluates the specified file"],inet_ntop:["string inet_ntop(string in_addr)","Converts a packed inet address to a human readable IP address string"],inet_pton:["string inet_pton(string ip_address)","Converts a human readable IP address to a packed binary string"],ini_get:["string ini_get(string varname)","Get a configuration option"],ini_get_all:["array ini_get_all([string extension[, bool details = true]])","Get all configuration options"],ini_restore:["void ini_restore(string varname)","Restore the value of a configuration option specified by varname"],ini_set:["string ini_set(string varname, string newvalue)","Set a configuration option, returns false on error and the old value of the configuration option on success"],interface_exists:["bool interface_exists(string classname [, bool autoload])","Checks if the class exists"],intl_error_name:["string intl_error_name()","* Return a string for a given error code. * The string will be the same as the name of the error code constant."],intl_get_error_code:["int intl_get_error_code()","* Get code of the last occured error."],intl_get_error_message:["string intl_get_error_message()","* Get text description of the last occured error."],intl_is_failure:["bool intl_is_failure()","* Check whether the given error code indicates a failure. * Returns true if it does, and false if the code * indicates success or a warning."],intval:["int intval(mixed var [, int base])","Get the integer value of a variable using the optional base for the conversion"],ip2long:["int ip2long(string ip_address)","Converts a string containing an (IPv4) Internet Protocol dotted address into a proper address"],iptcembed:["array iptcembed(string iptcdata, string jpeg_file_name [, int spool])","Embed binary IPTC data into a JPEG image."],iptcparse:["array iptcparse(string iptcdata)","Parse binary IPTC-data into associative array"],is_a:["bool is_a(object object, string class_name)","Returns true if the object is of this class or has this class as one of its parents"],is_array:["bool is_array(mixed var)","Returns true if variable is an array"],is_bool:["bool is_bool(mixed var)","Returns true if variable is a boolean"],is_callable:["bool is_callable(mixed var [, bool syntax_only [, string callable_name]])","Returns true if var is callable."],is_dir:["bool is_dir(string filename)","Returns true if file is directory"],is_executable:["bool is_executable(string filename)","Returns true if file is executable"],is_file:["bool is_file(string filename)","Returns true if file is a regular file"],is_finite:["bool is_finite(float val)","Returns whether argument is finite"],is_float:["bool is_float(mixed var)","Returns true if variable is float point"],is_infinite:["bool is_infinite(float val)","Returns whether argument is infinite"],is_link:["bool is_link(string filename)","Returns true if file is symbolic link"],is_long:["bool is_long(mixed var)","Returns true if variable is a long (integer)"],is_nan:["bool is_nan(float val)","Returns whether argument is not a number"],is_null:["bool is_null(mixed var)","Returns true if variable is null"],is_numeric:["bool is_numeric(mixed value)","Returns true if value is a number or a numeric string"],is_object:["bool is_object(mixed var)","Returns true if variable is an object"],is_readable:["bool is_readable(string filename)","Returns true if file can be read"],is_resource:["bool is_resource(mixed var)","Returns true if variable is a resource"],is_scalar:["bool is_scalar(mixed value)","Returns true if value is a scalar"],is_string:["bool is_string(mixed var)","Returns true if variable is a string"],is_subclass_of:["bool is_subclass_of(object object, string class_name)","Returns true if the object has this class as one of its parents"],is_uploaded_file:["bool is_uploaded_file(string path)","Check if file was created by rfc1867 upload"],is_writable:["bool is_writable(string filename)","Returns true if file can be written"],isset:["bool isset(mixed var [, mixed var])","Determine whether a variable is set"],iterator_apply:["int iterator_apply(Traversable it, mixed function [, mixed params])","Calls a function for every element in an iterator"],iterator_count:["int iterator_count(Traversable it)","Count the elements in an iterator"],iterator_to_array:["array iterator_to_array(Traversable it [, bool use_keys = true])","Copy the iterator into an array"],jddayofweek:["mixed jddayofweek(int juliandaycount [, int mode])","Returns name or number of day of week from julian day count"],jdmonthname:["string jdmonthname(int juliandaycount, int mode)","Returns name of month for julian day count"],jdtofrench:["string jdtofrench(int juliandaycount)","Converts a julian day count to a french republic calendar date"],jdtogregorian:["string jdtogregorian(int juliandaycount)","Converts a julian day count to a gregorian calendar date"],jdtojewish:["string jdtojewish(int juliandaycount [, bool hebrew [, int fl]])","Converts a julian day count to a jewish calendar date"],jdtojulian:["string jdtojulian(int juliandaycount)","Convert a julian day count to a julian calendar date"],jdtounix:["int jdtounix(int jday)","Convert Julian Day to UNIX timestamp"],jewishtojd:["int jewishtojd(int month, int day, int year)","Converts a jewish calendar date to a julian day count"],join:["string join(array src, string glue)","An alias for implode"],jpeg2wbmp:["bool jpeg2wbmp (string f_org, string f_dest, int d_height, int d_width, int threshold)","Convert JPEG image to WBMP image"],json_decode:["mixed json_decode(string json [, bool assoc [, long depth]])","Decodes the JSON representation into a PHP value"],json_encode:["string json_encode(mixed data [, int options])","Returns the JSON representation of a value"],json_last_error:["int json_last_error()","Returns the error code of the last json_decode()."],juliantojd:["int juliantojd(int month, int day, int year)","Converts a julian calendar date to julian day count"],key:["mixed key(array array_arg)","Return the key of the element currently pointed to by the internal array pointer"],krsort:["bool krsort(array &array_arg [, int sort_flags])","Sort an array by key value in reverse order"],ksort:["bool ksort(array &array_arg [, int sort_flags])","Sort an array by key"],lcfirst:["string lcfirst(string str)","Make a string's first character lowercase"],lcg_value:["float lcg_value()","Returns a value from the combined linear congruential generator"],lchgrp:["bool lchgrp(string filename, mixed group)","Change symlink group"],ldap_8859_to_t61:["string ldap_8859_to_t61(string value)","Translate 8859 characters to t61 characters"],ldap_add:["bool ldap_add(resource link, string dn, array entry)","Add entries to LDAP directory"],ldap_bind:["bool ldap_bind(resource link [, string dn [, string password]])","Bind to LDAP directory"],ldap_compare:["bool ldap_compare(resource link, string dn, string attr, string value)","Determine if an entry has a specific value for one of its attributes"],ldap_connect:["resource ldap_connect([string host [, int port [, string wallet [, string wallet_passwd [, int authmode]]]]])","Connect to an LDAP server"],ldap_count_entries:["int ldap_count_entries(resource link, resource result)","Count the number of entries in a search result"],ldap_delete:["bool ldap_delete(resource link, string dn)","Delete an entry from a directory"],ldap_dn2ufn:["string ldap_dn2ufn(string dn)","Convert DN to User Friendly Naming format"],ldap_err2str:["string ldap_err2str(int errno)","Convert error number to error string"],ldap_errno:["int ldap_errno(resource link)","Get the current ldap error number"],ldap_error:["string ldap_error(resource link)","Get the current ldap error string"],ldap_explode_dn:["array ldap_explode_dn(string dn, int with_attrib)","Splits DN into its component parts"],ldap_first_attribute:["string ldap_first_attribute(resource link, resource result_entry)","Return first attribute"],ldap_first_entry:["resource ldap_first_entry(resource link, resource result)","Return first result id"],ldap_first_reference:["resource ldap_first_reference(resource link, resource result)","Return first reference"],ldap_free_result:["bool ldap_free_result(resource result)","Free result memory"],ldap_get_attributes:["array ldap_get_attributes(resource link, resource result_entry)","Get attributes from a search result entry"],ldap_get_dn:["string ldap_get_dn(resource link, resource result_entry)","Get the DN of a result entry"],ldap_get_entries:["array ldap_get_entries(resource link, resource result)","Get all result entries"],ldap_get_option:["bool ldap_get_option(resource link, int option, mixed retval)","Get the current value of various session-wide parameters"],ldap_get_values_len:["array ldap_get_values_len(resource link, resource result_entry, string attribute)","Get all values with lengths from a result entry"],ldap_list:["resource ldap_list(resource|array link, string base_dn, string filter [, array attrs [, int attrsonly [, int sizelimit [, int timelimit [, int deref]]]]])","Single-level search"],ldap_mod_add:["bool ldap_mod_add(resource link, string dn, array entry)","Add attribute values to current"],ldap_mod_del:["bool ldap_mod_del(resource link, string dn, array entry)","Delete attribute values"],ldap_mod_replace:["bool ldap_mod_replace(resource link, string dn, array entry)","Replace attribute values with new ones"],ldap_next_attribute:["string ldap_next_attribute(resource link, resource result_entry)","Get the next attribute in result"],ldap_next_entry:["resource ldap_next_entry(resource link, resource result_entry)","Get next result entry"],ldap_next_reference:["resource ldap_next_reference(resource link, resource reference_entry)","Get next reference"],ldap_parse_reference:["bool ldap_parse_reference(resource link, resource reference_entry, array referrals)","Extract information from reference entry"],ldap_parse_result:["bool ldap_parse_result(resource link, resource result, int errcode, string matcheddn, string errmsg, array referrals)","Extract information from result"],ldap_read:["resource ldap_read(resource|array link, string base_dn, string filter [, array attrs [, int attrsonly [, int sizelimit [, int timelimit [, int deref]]]]])","Read an entry"],ldap_rename:["bool ldap_rename(resource link, string dn, string newrdn, string newparent, bool deleteoldrdn);","Modify the name of an entry"],ldap_sasl_bind:["bool ldap_sasl_bind(resource link [, string binddn [, string password [, string sasl_mech [, string sasl_realm [, string sasl_authc_id [, string sasl_authz_id [, string props]]]]]]])","Bind to LDAP directory using SASL"],ldap_search:["resource ldap_search(resource|array link, string base_dn, string filter [, array attrs [, int attrsonly [, int sizelimit [, int timelimit [, int deref]]]]])","Search LDAP tree under base_dn"],ldap_set_option:["bool ldap_set_option(resource link, int option, mixed newval)","Set the value of various session-wide parameters"],ldap_set_rebind_proc:["bool ldap_set_rebind_proc(resource link, string callback)","Set a callback function to do re-binds on referral chasing."],ldap_sort:["bool ldap_sort(resource link, resource result, string sortfilter)","Sort LDAP result entries"],ldap_start_tls:["bool ldap_start_tls(resource link)","Start TLS"],ldap_t61_to_8859:["string ldap_t61_to_8859(string value)","Translate t61 characters to 8859 characters"],ldap_unbind:["bool ldap_unbind(resource link)","Unbind from LDAP directory"],leak:["void leak(int num_bytes=3)","Cause an intentional memory leak, for testing/debugging purposes"],levenshtein:["int levenshtein(string str1, string str2[, int cost_ins, int cost_rep, int cost_del])","Calculate Levenshtein distance between two strings"],libxml_clear_errors:["void libxml_clear_errors()","Clear last error from libxml"],libxml_disable_entity_loader:["bool libxml_disable_entity_loader([boolean disable])","Disable/Enable ability to load external entities"],libxml_get_errors:["object libxml_get_errors()","Retrieve array of errors"],libxml_get_last_error:["object libxml_get_last_error()","Retrieve last error from libxml"],libxml_set_streams_context:["void libxml_set_streams_context(resource streams_context)","Set the streams context for the next libxml document load or write"],libxml_use_internal_errors:["bool libxml_use_internal_errors([boolean use_errors])","Disable libxml errors and allow user to fetch error information as needed"],link:["int link(string target, string link)","Create a hard link"],linkinfo:["int linkinfo(string filename)","Returns the st_dev field of the UNIX C stat structure describing the link"],litespeed_request_headers:["array litespeed_request_headers(void)","Fetch all HTTP request headers"],litespeed_response_headers:["array litespeed_response_headers(void)","Fetch all HTTP response headers"],locale_accept_from_http:["string locale_accept_from_http(string $http_accept)",null],locale_canonicalize:["static string locale_canonicalize(Locale $loc, string $locale)","* @param string $locale The locale string to canonicalize"],locale_filter_matches:["boolean locale_filter_matches(string $langtag, string $locale[, bool $canonicalize])","* Checks if a $langtag filter matches with $locale according to RFC 4647's basic filtering algorithm"],locale_get_all_variants:["static array locale_get_all_variants($locale)","* gets an array containing the list of variants, or null"],locale_get_default:["static string locale_get_default( )","Get default locale"],locale_get_keywords:["static array locale_get_keywords(string $locale) {","* return an associative array containing keyword-value * pairs for this locale. The keys are keys to the array (doh!)"],locale_get_primary_language:["static string locale_get_primary_language($locale)","* gets the primary language for the $locale"],locale_get_region:["static string locale_get_region($locale)","* gets the region for the $locale"],locale_get_script:["static string locale_get_script($locale)","* gets the script for the $locale"],locale_lookup:["string locale_lookup(array $langtag, string $locale[, bool $canonicalize[, string $default = null]])","* Searchs the items in $langtag for the best match to the language * range"],locale_set_default:["static string locale_set_default( string $locale )","Set default locale"],localeconv:["array localeconv(void)","Returns numeric formatting information based on the current locale"],localtime:["array localtime([int timestamp [, bool associative_array]])","Returns the results of the C system call localtime as an associative array if the associative_array argument is set to 1 other wise it is a regular array"],log:["float log(float number, [float base])","Returns the natural logarithm of the number, or the base log if base is specified"],log10:["float log10(float number)","Returns the base-10 logarithm of the number"],log1p:["float log1p(float number)","Returns log(1 + number), computed in a way that accurate even when the value of number is close to zero"],long2ip:["string long2ip(int proper_address)","Converts an (IPv4) Internet network address into a string in Internet standard dotted format"],lstat:["array lstat(string filename)","Give information about a file or symbolic link"],ltrim:["string ltrim(string str [, string character_mask])","Strips whitespace from the beginning of a string"],mail:["int mail(string to, string subject, string message [, string additional_headers [, string additional_parameters]])","Send an email message"],max:["mixed max(mixed arg1 [, mixed arg2 [, mixed ...]])","Return the highest value in an array or a series of arguments"],mb_check_encoding:["bool mb_check_encoding([string var[, string encoding]])","Check if the string is valid for the specified encoding"],mb_convert_case:["string mb_convert_case(string sourcestring, int mode [, string encoding])","Returns a case-folded version of sourcestring"],mb_convert_encoding:["string mb_convert_encoding(string str, string to-encoding [, mixed from-encoding])","Returns converted string in desired encoding"],mb_convert_kana:["string mb_convert_kana(string str [, string option] [, string encoding])","Conversion between full-width character and half-width character (Japanese)"],mb_convert_variables:["string mb_convert_variables(string to-encoding, mixed from-encoding, mixed vars [, ...])","Converts the string resource in variables to desired encoding"],mb_decode_mimeheader:["string mb_decode_mimeheader(string string)",'Decodes the MIME "encoded-word" in the string'],mb_decode_numericentity:["string mb_decode_numericentity(string string, array convmap [, string encoding])","Converts HTML numeric entities to character code"],mb_detect_encoding:["string mb_detect_encoding(string str [, mixed encoding_list [, bool strict]])","Encodings of the given string is returned (as a string)"],mb_detect_order:["bool|array mb_detect_order([mixed encoding-list])","Sets the current detect_order or Return the current detect_order as a array"],mb_encode_mimeheader:["string mb_encode_mimeheader(string str [, string charset [, string transfer-encoding [, string linefeed [, int indent]]]])",'Converts the string to MIME "encoded-word" in the format of =?charset?(B|Q)?encoded_string?='],mb_encode_numericentity:["string mb_encode_numericentity(string string, array convmap [, string encoding])","Converts specified characters to HTML numeric entities"],mb_encoding_aliases:["array mb_encoding_aliases(string encoding)","Returns an array of the aliases of a given encoding name"],mb_ereg:["int mb_ereg(string pattern, string string [, array registers])","Regular expression match for multibyte string"],mb_ereg_match:["bool mb_ereg_match(string pattern, string string [,string option])","Regular expression match for multibyte string"],mb_ereg_replace:["string mb_ereg_replace(string pattern, string replacement, string string [, string option])","Replace regular expression for multibyte string"],mb_ereg_search:["bool mb_ereg_search([string pattern[, string option]])","Regular expression search for multibyte string"],mb_ereg_search_getpos:["int mb_ereg_search_getpos(void)","Get search start position"],mb_ereg_search_getregs:["array mb_ereg_search_getregs(void)","Get matched substring of the last time"],mb_ereg_search_init:["bool mb_ereg_search_init(string string [, string pattern[, string option]])","Initialize string and regular expression for search."],mb_ereg_search_pos:["array mb_ereg_search_pos([string pattern[, string option]])","Regular expression search for multibyte string"],mb_ereg_search_regs:["array mb_ereg_search_regs([string pattern[, string option]])","Regular expression search for multibyte string"],mb_ereg_search_setpos:["bool mb_ereg_search_setpos(int position)","Set search start position"],mb_eregi:["int mb_eregi(string pattern, string string [, array registers])","Case-insensitive regular expression match for multibyte string"],mb_eregi_replace:["string mb_eregi_replace(string pattern, string replacement, string string)","Case insensitive replace regular expression for multibyte string"],mb_get_info:["mixed mb_get_info([string type])","Returns the current settings of mbstring"],mb_http_input:["mixed mb_http_input([string type])","Returns the input encoding"],mb_http_output:["string mb_http_output([string encoding])","Sets the current output_encoding or returns the current output_encoding as a string"],mb_internal_encoding:["string mb_internal_encoding([string encoding])","Sets the current internal encoding or Returns the current internal encoding as a string"],mb_language:["string mb_language([string language])","Sets the current language or Returns the current language as a string"],mb_list_encodings:["mixed mb_list_encodings()","Returns an array of all supported entity encodings"],mb_output_handler:["string mb_output_handler(string contents, int status)","Returns string in output buffer converted to the http_output encoding"],mb_parse_str:["bool mb_parse_str(string encoded_string [, array result])","Parses GET/POST/COOKIE data and sets global variables"],mb_preferred_mime_name:["string mb_preferred_mime_name(string encoding)","Return the preferred MIME name (charset) as a string"],mb_regex_encoding:["string mb_regex_encoding([string encoding])","Returns the current encoding for regex as a string."],mb_regex_set_options:["string mb_regex_set_options([string options])","Set or get the default options for mbregex functions"],mb_send_mail:["int mb_send_mail(string to, string subject, string message [, string additional_headers [, string additional_parameters]])","* Sends an email message with MIME scheme"],mb_split:["array mb_split(string pattern, string string [, int limit])","split multibyte string into array by regular expression"],mb_strcut:["string mb_strcut(string str, int start [, int length [, string encoding]])","Returns part of a string"],mb_strimwidth:["string mb_strimwidth(string str, int start, int width [, string trimmarker [, string encoding]])","Trim the string in terminal width"],mb_stripos:["int mb_stripos(string haystack, string needle [, int offset [, string encoding]])","Finds position of first occurrence of a string within another, case insensitive"],mb_stristr:["string mb_stristr(string haystack, string needle[, bool part[, string encoding]])","Finds first occurrence of a string within another, case insensitive"],mb_strlen:["int mb_strlen(string str [, string encoding])","Get character numbers of a string"],mb_strpos:["int mb_strpos(string haystack, string needle [, int offset [, string encoding]])","Find position of first occurrence of a string within another"],mb_strrchr:["string mb_strrchr(string haystack, string needle[, bool part[, string encoding]])","Finds the last occurrence of a character in a string within another"],mb_strrichr:["string mb_strrichr(string haystack, string needle[, bool part[, string encoding]])","Finds the last occurrence of a character in a string within another, case insensitive"],mb_strripos:["int mb_strripos(string haystack, string needle [, int offset [, string encoding]])","Finds position of last occurrence of a string within another, case insensitive"],mb_strrpos:["int mb_strrpos(string haystack, string needle [, int offset [, string encoding]])","Find position of last occurrence of a string within another"],mb_strstr:["string mb_strstr(string haystack, string needle[, bool part[, string encoding]])","Finds first occurrence of a string within another"],mb_strtolower:["string mb_strtolower(string sourcestring [, string encoding])","* Returns a lowercased version of sourcestring"],mb_strtoupper:["string mb_strtoupper(string sourcestring [, string encoding])","* Returns a uppercased version of sourcestring"],mb_strwidth:["int mb_strwidth(string str [, string encoding])","Gets terminal width of a string"],mb_substitute_character:["mixed mb_substitute_character([mixed substchar])","Sets the current substitute_character or returns the current substitute_character"],mb_substr:["string mb_substr(string str, int start [, int length [, string encoding]])","Returns part of a string"],mb_substr_count:["int mb_substr_count(string haystack, string needle [, string encoding])","Count the number of substring occurrences"],mcrypt_cbc:["string mcrypt_cbc(int cipher, string key, string data, int mode, string iv)","CBC crypt/decrypt data using key key with cipher cipher starting with iv"],mcrypt_cfb:["string mcrypt_cfb(int cipher, string key, string data, int mode, string iv)","CFB crypt/decrypt data using key key with cipher cipher starting with iv"],mcrypt_create_iv:["string mcrypt_create_iv(int size, int source)","Create an initialization vector (IV)"],mcrypt_decrypt:["string mcrypt_decrypt(string cipher, string key, string data, string mode, string iv)","OFB crypt/decrypt data using key key with cipher cipher starting with iv"],mcrypt_ecb:["string mcrypt_ecb(int cipher, string key, string data, int mode, string iv)","ECB crypt/decrypt data using key key with cipher cipher starting with iv"],mcrypt_enc_get_algorithms_name:["string mcrypt_enc_get_algorithms_name(resource td)","Returns the name of the algorithm specified by the descriptor td"],mcrypt_enc_get_block_size:["int mcrypt_enc_get_block_size(resource td)","Returns the block size of the cipher specified by the descriptor td"],mcrypt_enc_get_iv_size:["int mcrypt_enc_get_iv_size(resource td)","Returns the size of the IV in bytes of the algorithm specified by the descriptor td"],mcrypt_enc_get_key_size:["int mcrypt_enc_get_key_size(resource td)","Returns the maximum supported key size in bytes of the algorithm specified by the descriptor td"],mcrypt_enc_get_modes_name:["string mcrypt_enc_get_modes_name(resource td)","Returns the name of the mode specified by the descriptor td"],mcrypt_enc_get_supported_key_sizes:["array mcrypt_enc_get_supported_key_sizes(resource td)","This function decrypts the crypttext"],mcrypt_enc_is_block_algorithm:["bool mcrypt_enc_is_block_algorithm(resource td)","Returns TRUE if the alrogithm is a block algorithms"],mcrypt_enc_is_block_algorithm_mode:["bool mcrypt_enc_is_block_algorithm_mode(resource td)","Returns TRUE if the mode is for use with block algorithms"],mcrypt_enc_is_block_mode:["bool mcrypt_enc_is_block_mode(resource td)","Returns TRUE if the mode outputs blocks"],mcrypt_enc_self_test:["int mcrypt_enc_self_test(resource td)","This function runs the self test on the algorithm specified by the descriptor td"],mcrypt_encrypt:["string mcrypt_encrypt(string cipher, string key, string data, string mode, string iv)","OFB crypt/decrypt data using key key with cipher cipher starting with iv"],mcrypt_generic:["string mcrypt_generic(resource td, string data)","This function encrypts the plaintext"],mcrypt_generic_deinit:["bool mcrypt_generic_deinit(resource td)","This function terminates encrypt specified by the descriptor td"],mcrypt_generic_init:["int mcrypt_generic_init(resource td, string key, string iv)","This function initializes all buffers for the specific module"],mcrypt_get_block_size:["int mcrypt_get_block_size(string cipher, string module)","Get the key size of cipher"],mcrypt_get_cipher_name:["string mcrypt_get_cipher_name(string cipher)","Get the key size of cipher"],mcrypt_get_iv_size:["int mcrypt_get_iv_size(string cipher, string module)","Get the IV size of cipher (Usually the same as the blocksize)"],mcrypt_get_key_size:["int mcrypt_get_key_size(string cipher, string module)","Get the key size of cipher"],mcrypt_list_algorithms:["array mcrypt_list_algorithms([string lib_dir])",'List all algorithms in "module_dir"'],mcrypt_list_modes:["array mcrypt_list_modes([string lib_dir])",'List all modes "module_dir"'],mcrypt_module_close:["bool mcrypt_module_close(resource td)","Free the descriptor td"],mcrypt_module_get_algo_block_size:["int mcrypt_module_get_algo_block_size(string algorithm [, string lib_dir])","Returns the block size of the algorithm"],mcrypt_module_get_algo_key_size:["int mcrypt_module_get_algo_key_size(string algorithm [, string lib_dir])","Returns the maximum supported key size of the algorithm"],mcrypt_module_get_supported_key_sizes:["array mcrypt_module_get_supported_key_sizes(string algorithm [, string lib_dir])","This function decrypts the crypttext"],mcrypt_module_is_block_algorithm:["bool mcrypt_module_is_block_algorithm(string algorithm [, string lib_dir])","Returns TRUE if the algorithm is a block algorithm"],mcrypt_module_is_block_algorithm_mode:["bool mcrypt_module_is_block_algorithm_mode(string mode [, string lib_dir])","Returns TRUE if the mode is for use with block algorithms"],mcrypt_module_is_block_mode:["bool mcrypt_module_is_block_mode(string mode [, string lib_dir])","Returns TRUE if the mode outputs blocks of bytes"],mcrypt_module_open:["resource mcrypt_module_open(string cipher, string cipher_directory, string mode, string mode_directory)","Opens the module of the algorithm and the mode to be used"],mcrypt_module_self_test:["bool mcrypt_module_self_test(string algorithm [, string lib_dir])",'Does a self test of the module "module"'],mcrypt_ofb:["string mcrypt_ofb(int cipher, string key, string data, int mode, string iv)","OFB crypt/decrypt data using key key with cipher cipher starting with iv"],md5:["string md5(string str, [ bool raw_output])","Calculate the md5 hash of a string"],md5_file:["string md5_file(string filename [, bool raw_output])","Calculate the md5 hash of given filename"],mdecrypt_generic:["string mdecrypt_generic(resource td, string data)","This function decrypts the plaintext"],memory_get_peak_usage:["int memory_get_peak_usage([real_usage])","Returns the peak allocated by PHP memory"],memory_get_usage:["int memory_get_usage([real_usage])","Returns the allocated by PHP memory"],metaphone:["string metaphone(string text[, int phones])","Break english phrases down into their phonemes"],method_exists:["bool method_exists(object object, string method)","Checks if the class method exists"],mhash:["string mhash(int hash, string data [, string key])","Hash data with hash"],mhash_count:["int mhash_count(void)","Gets the number of available hashes"],mhash_get_block_size:["int mhash_get_block_size(int hash)","Gets the block size of hash"],mhash_get_hash_name:["string mhash_get_hash_name(int hash)","Gets the name of hash"],mhash_keygen_s2k:["string mhash_keygen_s2k(int hash, string input_password, string salt, int bytes)","Generates a key using hash functions"],microtime:["mixed microtime([bool get_as_float])","Returns either a string or a float containing the current time in seconds and microseconds"],mime_content_type:["string mime_content_type(string filename|resource stream)","Return content-type for file"],min:["mixed min(mixed arg1 [, mixed arg2 [, mixed ...]])","Return the lowest value in an array or a series of arguments"],mkdir:["bool mkdir(string pathname [, int mode [, bool recursive [, resource context]]])","Create a directory"],mktime:["int mktime([int hour [, int min [, int sec [, int mon [, int day [, int year]]]]]])","Get UNIX timestamp for a date"],money_format:["string money_format(string format , float value)","Convert monetary value(s) to string"],move_uploaded_file:["bool move_uploaded_file(string path, string new_path)","Move a file if and only if it was created by an upload"],msg_get_queue:["resource msg_get_queue(int key [, int perms])","Attach to a message queue"],msg_queue_exists:["bool msg_queue_exists(int key)","Check wether a message queue exists"],msg_receive:["mixed msg_receive(resource queue, int desiredmsgtype, int &msgtype, int maxsize, mixed message [, bool unserialize=true [, int flags=0 [, int errorcode]]])","Send a message of type msgtype (must be > 0) to a message queue"],msg_remove_queue:["bool msg_remove_queue(resource queue)","Destroy the queue"],msg_send:["bool msg_send(resource queue, int msgtype, mixed message [, bool serialize=true [, bool blocking=true [, int errorcode]]])","Send a message of type msgtype (must be > 0) to a message queue"],msg_set_queue:["bool msg_set_queue(resource queue, array data)","Set information for a message queue"],msg_stat_queue:["array msg_stat_queue(resource queue)","Returns information about a message queue"],msgfmt_create:["MessageFormatter msgfmt_create( string $locale, string $pattern )","* Create formatter."],msgfmt_format:["mixed msgfmt_format( MessageFormatter $nf, array $args )","* Format a message."],msgfmt_format_message:["mixed msgfmt_format_message( string $locale, string $pattern, array $args )","* Format a message."],msgfmt_get_error_code:["int msgfmt_get_error_code( MessageFormatter $nf )","* Get formatter's last error code."],msgfmt_get_error_message:["string msgfmt_get_error_message( MessageFormatter $coll )","* Get text description for formatter's last error code."],msgfmt_get_locale:["string msgfmt_get_locale(MessageFormatter $mf)","* Get formatter locale."],msgfmt_get_pattern:["string msgfmt_get_pattern( MessageFormatter $mf )","* Get formatter pattern."],msgfmt_parse:["array msgfmt_parse( MessageFormatter $nf, string $source )","* Parse a message."],msgfmt_set_pattern:["bool msgfmt_set_pattern( MessageFormatter $mf, string $pattern )","* Set formatter pattern."],mssql_bind:["bool mssql_bind(resource stmt, string param_name, mixed var, int type [, bool is_output [, bool is_null [, int maxlen]]])","Adds a parameter to a stored procedure or a remote stored procedure"],mssql_close:["bool mssql_close([resource conn_id])","Closes a connection to a MS-SQL server"],mssql_connect:["int mssql_connect([string servername [, string username [, string password [, bool new_link]]]])","Establishes a connection to a MS-SQL server"],mssql_data_seek:["bool mssql_data_seek(resource result_id, int offset)","Moves the internal row pointer of the MS-SQL result associated with the specified result identifier to pointer to the specified row number"],mssql_execute:["mixed mssql_execute(resource stmt [, bool skip_results = false])","Executes a stored procedure on a MS-SQL server database"],mssql_fetch_array:["array mssql_fetch_array(resource result_id [, int result_type])","Returns an associative array of the current row in the result set specified by result_id"],mssql_fetch_assoc:["array mssql_fetch_assoc(resource result_id)","Returns an associative array of the current row in the result set specified by result_id"],mssql_fetch_batch:["int mssql_fetch_batch(resource result_index)","Returns the next batch of records"],mssql_fetch_field:["object mssql_fetch_field(resource result_id [, int offset])","Gets information about certain fields in a query result"],mssql_fetch_object:["object mssql_fetch_object(resource result_id)","Returns a pseudo-object of the current row in the result set specified by result_id"],mssql_fetch_row:["array mssql_fetch_row(resource result_id)","Returns an array of the current row in the result set specified by result_id"],mssql_field_length:["int mssql_field_length(resource result_id [, int offset])","Get the length of a MS-SQL field"],mssql_field_name:["string mssql_field_name(resource result_id [, int offset])","Returns the name of the field given by offset in the result set given by result_id"],mssql_field_seek:["bool mssql_field_seek(resource result_id, int offset)","Seeks to the specified field offset"],mssql_field_type:["string mssql_field_type(resource result_id [, int offset])","Returns the type of a field"],mssql_free_result:["bool mssql_free_result(resource result_index)","Free a MS-SQL result index"],mssql_free_statement:["bool mssql_free_statement(resource result_index)","Free a MS-SQL statement index"],mssql_get_last_message:["string mssql_get_last_message(void)","Gets the last message from the MS-SQL server"],mssql_guid_string:["string mssql_guid_string(string binary [,bool short_format])","Converts a 16 byte binary GUID to a string"],mssql_init:["int mssql_init(string sp_name [, resource conn_id])","Initializes a stored procedure or a remote stored procedure"],mssql_min_error_severity:["void mssql_min_error_severity(int severity)","Sets the lower error severity"],mssql_min_message_severity:["void mssql_min_message_severity(int severity)","Sets the lower message severity"],mssql_next_result:["bool mssql_next_result(resource result_id)","Move the internal result pointer to the next result"],mssql_num_fields:["int mssql_num_fields(resource mssql_result_index)","Returns the number of fields fetched in from the result id specified"],mssql_num_rows:["int mssql_num_rows(resource mssql_result_index)","Returns the number of rows fetched in from the result id specified"],mssql_pconnect:["int mssql_pconnect([string servername [, string username [, string password [, bool new_link]]]])","Establishes a persistent connection to a MS-SQL server"],mssql_query:["resource mssql_query(string query [, resource conn_id [, int batch_size]])","Perform an SQL query on a MS-SQL server database"],mssql_result:["string mssql_result(resource result_id, int row, mixed field)","Returns the contents of one cell from a MS-SQL result set"],mssql_rows_affected:["int mssql_rows_affected(resource conn_id)","Returns the number of records affected by the query"],mssql_select_db:["bool mssql_select_db(string database_name [, resource conn_id])","Select a MS-SQL database"],mt_getrandmax:["int mt_getrandmax(void)","Returns the maximum value a random number from Mersenne Twister can have"],mt_rand:["int mt_rand([int min, int max])","Returns a random number from Mersenne Twister"],mt_srand:["void mt_srand([int seed])","Seeds Mersenne Twister random number generator"],mysql_affected_rows:["int mysql_affected_rows([int link_identifier])","Gets number of affected rows in previous MySQL operation"],mysql_client_encoding:["string mysql_client_encoding([int link_identifier])","Returns the default character set for the current connection"],mysql_close:["bool mysql_close([int link_identifier])","Close a MySQL connection"],mysql_connect:["resource mysql_connect([string hostname[:port][:/path/to/socket] [, string username [, string password [, bool new [, int flags]]]]])","Opens a connection to a MySQL Server"],mysql_create_db:["bool mysql_create_db(string database_name [, int link_identifier])","Create a MySQL database"],mysql_data_seek:["bool mysql_data_seek(resource result, int row_number)","Move internal result pointer"],mysql_db_query:["resource mysql_db_query(string database_name, string query [, int link_identifier])","Sends an SQL query to MySQL"],mysql_drop_db:["bool mysql_drop_db(string database_name [, int link_identifier])","Drops (delete) a MySQL database"],mysql_errno:["int mysql_errno([int link_identifier])","Returns the number of the error message from previous MySQL operation"],mysql_error:["string mysql_error([int link_identifier])","Returns the text of the error message from previous MySQL operation"],mysql_escape_string:["string mysql_escape_string(string to_be_escaped)","Escape string for mysql query"],mysql_fetch_array:["array mysql_fetch_array(resource result [, int result_type])","Fetch a result row as an array (associative, numeric or both)"],mysql_fetch_assoc:["array mysql_fetch_assoc(resource result)","Fetch a result row as an associative array"],mysql_fetch_field:["object mysql_fetch_field(resource result [, int field_offset])","Gets column information from a result and return as an object"],mysql_fetch_lengths:["array mysql_fetch_lengths(resource result)","Gets max data size of each column in a result"],mysql_fetch_object:["object mysql_fetch_object(resource result [, string class_name [, NULL|array ctor_params]])","Fetch a result row as an object"],mysql_fetch_row:["array mysql_fetch_row(resource result)","Gets a result row as an enumerated array"],mysql_field_flags:["string mysql_field_flags(resource result, int field_offset)","Gets the flags associated with the specified field in a result"],mysql_field_len:["int mysql_field_len(resource result, int field_offset)","Returns the length of the specified field"],mysql_field_name:["string mysql_field_name(resource result, int field_index)","Gets the name of the specified field in a result"],mysql_field_seek:["bool mysql_field_seek(resource result, int field_offset)","Sets result pointer to a specific field offset"],mysql_field_table:["string mysql_field_table(resource result, int field_offset)","Gets name of the table the specified field is in"],mysql_field_type:["string mysql_field_type(resource result, int field_offset)","Gets the type of the specified field in a result"],mysql_free_result:["bool mysql_free_result(resource result)","Free result memory"],mysql_get_client_info:["string mysql_get_client_info(void)","Returns a string that represents the client library version"],mysql_get_host_info:["string mysql_get_host_info([int link_identifier])","Returns a string describing the type of connection in use, including the server host name"],mysql_get_proto_info:["int mysql_get_proto_info([int link_identifier])","Returns the protocol version used by current connection"],mysql_get_server_info:["string mysql_get_server_info([int link_identifier])","Returns a string that represents the server version number"],mysql_info:["string mysql_info([int link_identifier])","Returns a string containing information about the most recent query"],mysql_insert_id:["int mysql_insert_id([int link_identifier])","Gets the ID generated from the previous INSERT operation"],mysql_list_dbs:["resource mysql_list_dbs([int link_identifier])","List databases available on a MySQL server"],mysql_list_fields:["resource mysql_list_fields(string database_name, string table_name [, int link_identifier])","List MySQL result fields"],mysql_list_processes:["resource mysql_list_processes([int link_identifier])","Returns a result set describing the current server threads"],mysql_list_tables:["resource mysql_list_tables(string database_name [, int link_identifier])","List tables in a MySQL database"],mysql_num_fields:["int mysql_num_fields(resource result)","Gets number of fields in a result"],mysql_num_rows:["int mysql_num_rows(resource result)","Gets number of rows in a result"],mysql_pconnect:["resource mysql_pconnect([string hostname[:port][:/path/to/socket] [, string username [, string password [, int flags]]]])","Opens a persistent connection to a MySQL Server"],mysql_ping:["bool mysql_ping([int link_identifier])","Ping a server connection. If no connection then reconnect."],mysql_query:["resource mysql_query(string query [, int link_identifier])","Sends an SQL query to MySQL"],mysql_real_escape_string:["string mysql_real_escape_string(string to_be_escaped [, int link_identifier])","Escape special characters in a string for use in a SQL statement, taking into account the current charset of the connection"],mysql_result:["mixed mysql_result(resource result, int row [, mixed field])","Gets result data"],mysql_select_db:["bool mysql_select_db(string database_name [, int link_identifier])","Selects a MySQL database"],mysql_set_charset:["bool mysql_set_charset(string csname [, int link_identifier])","sets client character set"],mysql_stat:["string mysql_stat([int link_identifier])","Returns a string containing status information"],mysql_thread_id:["int mysql_thread_id([int link_identifier])","Returns the thread id of current connection"],mysql_unbuffered_query:["resource mysql_unbuffered_query(string query [, int link_identifier])","Sends an SQL query to MySQL, without fetching and buffering the result rows"],mysqli_affected_rows:["mixed mysqli_affected_rows(object link)","Get number of affected rows in previous MySQL operation"],mysqli_autocommit:["bool mysqli_autocommit(object link, bool mode)","Turn auto commit on or of"],mysqli_cache_stats:["array mysqli_cache_stats(void)","Returns statistics about the zval cache"],mysqli_change_user:["bool mysqli_change_user(object link, string user, string password, string database)","Change logged-in user of the active connection"],mysqli_character_set_name:["string mysqli_character_set_name(object link)","Returns the name of the character set used for this connection"],mysqli_close:["bool mysqli_close(object link)","Close connection"],mysqli_commit:["bool mysqli_commit(object link)","Commit outstanding actions and close transaction"],mysqli_connect:["object mysqli_connect([string hostname [,string username [,string passwd [,string dbname [,int port [,string socket]]]]]])","Open a connection to a mysql server"],mysqli_connect_errno:["int mysqli_connect_errno(void)","Returns the numerical value of the error message from last connect command"],mysqli_connect_error:["string mysqli_connect_error(void)","Returns the text of the error message from previous MySQL operation"],mysqli_data_seek:["bool mysqli_data_seek(object result, int offset)","Move internal result pointer"],mysqli_debug:["void mysqli_debug(string debug)",""],mysqli_dump_debug_info:["bool mysqli_dump_debug_info(object link)",""],mysqli_embedded_server_end:["void mysqli_embedded_server_end(void)",""],mysqli_embedded_server_start:["bool mysqli_embedded_server_start(bool start, array arguments, array groups)","initialize and start embedded server"],mysqli_errno:["int mysqli_errno(object link)","Returns the numerical value of the error message from previous MySQL operation"],mysqli_error:["string mysqli_error(object link)","Returns the text of the error message from previous MySQL operation"],mysqli_fetch_all:["mixed mysqli_fetch_all (object result [,int resulttype])","Fetches all result rows as an associative array, a numeric array, or both"],mysqli_fetch_array:["mixed mysqli_fetch_array (object result [,int resulttype])","Fetch a result row as an associative array, a numeric array, or both"],mysqli_fetch_assoc:["mixed mysqli_fetch_assoc (object result)","Fetch a result row as an associative array"],mysqli_fetch_field:["mixed mysqli_fetch_field (object result)","Get column information from a result and return as an object"],mysqli_fetch_field_direct:["mixed mysqli_fetch_field_direct (object result, int offset)","Fetch meta-data for a single field"],mysqli_fetch_fields:["mixed mysqli_fetch_fields (object result)","Return array of objects containing field meta-data"],mysqli_fetch_lengths:["mixed mysqli_fetch_lengths (object result)","Get the length of each output in a result"],mysqli_fetch_object:["mixed mysqli_fetch_object (object result [, string class_name [, NULL|array ctor_params]])","Fetch a result row as an object"],mysqli_fetch_row:["array mysqli_fetch_row (object result)","Get a result row as an enumerated array"],mysqli_field_count:["int mysqli_field_count(object link)","Fetch the number of fields returned by the last query for the given link"],mysqli_field_seek:["int mysqli_field_seek(object result, int fieldnr)","Set result pointer to a specified field offset"],mysqli_field_tell:["int mysqli_field_tell(object result)","Get current field offset of result pointer"],mysqli_free_result:["void mysqli_free_result(object result)","Free query result memory for the given result handle"],mysqli_get_charset:["object mysqli_get_charset(object link)","returns a character set object"],mysqli_get_client_info:["string mysqli_get_client_info(void)","Get MySQL client info"],mysqli_get_client_stats:["array mysqli_get_client_stats(void)","Returns statistics about the zval cache"],mysqli_get_client_version:["int mysqli_get_client_version(void)","Get MySQL client info"],mysqli_get_connection_stats:["array mysqli_get_connection_stats(void)","Returns statistics about the zval cache"],mysqli_get_host_info:["string mysqli_get_host_info (object link)","Get MySQL host info"],mysqli_get_proto_info:["int mysqli_get_proto_info(object link)","Get MySQL protocol information"],mysqli_get_server_info:["string mysqli_get_server_info(object link)","Get MySQL server info"],mysqli_get_server_version:["int mysqli_get_server_version(object link)","Return the MySQL version for the server referenced by the given link"],mysqli_get_warnings:["object mysqli_get_warnings(object link) */",'PHP_FUNCTION(mysqli_get_warnings) { MY_MYSQL *mysql; zval *mysql_link; MYSQLI_RESOURCE *mysqli_resource; MYSQLI_WARNING *w; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) { return; } MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL*, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID); if (mysql_warning_count(mysql->mysql)) { w = php_get_warnings(mysql->mysql TSRMLS_CC); } else { RETURN_FALSE; } mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE)); mysqli_resource->ptr = mysqli_resource->info = (void *)w; mysqli_resource->status = MYSQLI_STATUS_VALID; MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_warning_class_entry); } /* }}}'],mysqli_info:["string mysqli_info(object link)","Get information about the most recent query"],mysqli_init:["resource mysqli_init(void)","Initialize mysqli and return a resource for use with mysql_real_connect"],mysqli_insert_id:["mixed mysqli_insert_id(object link)","Get the ID generated from the previous INSERT operation"],mysqli_kill:["bool mysqli_kill(object link, int processid)","Kill a mysql process on the server"],mysqli_link_construct:["object mysqli_link_construct()",""],mysqli_more_results:["bool mysqli_more_results(object link)","check if there any more query results from a multi query"],mysqli_multi_query:["bool mysqli_multi_query(object link, string query)","allows to execute multiple queries"],mysqli_next_result:["bool mysqli_next_result(object link)","read next result from multi_query"],mysqli_num_fields:["int mysqli_num_fields(object result)","Get number of fields in result"],mysqli_num_rows:["mixed mysqli_num_rows(object result)","Get number of rows in result"],mysqli_options:["bool mysqli_options(object link, int flags, mixed values)","Set options"],mysqli_ping:["bool mysqli_ping(object link)","Ping a server connection or reconnect if there is no connection"],mysqli_poll:["int mysqli_poll(array read, array write, array error, long sec [, long usec])","Poll connections"],mysqli_prepare:["mixed mysqli_prepare(object link, string query)","Prepare a SQL statement for execution"],mysqli_query:["mixed mysqli_query(object link, string query [,int resultmode]) */",'PHP_FUNCTION(mysqli_query) { MY_MYSQL *mysql; zval *mysql_link; MYSQLI_RESOURCE *mysqli_resource; MYSQL_RES *result; char *query = NULL; unsigned int query_len; unsigned long resultmode = MYSQLI_STORE_RESULT; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|l", &mysql_link, mysqli_link_class_entry, &query, &query_len, &resultmode) == FAILURE) { return; } if (!query_len) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty query"); RETURN_FALSE; } if ((resultmode & ~MYSQLI_ASYNC) != MYSQLI_USE_RESULT && (resultmode & ~MYSQLI_ASYNC) != MYSQLI_STORE_RESULT) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid value for resultmode"); RETURN_FALSE; } MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL*, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID); MYSQLI_DISABLE_MQ; #ifdef MYSQLI_USE_MYSQLND if (resultmode & MYSQLI_ASYNC) { if (mysqli_async_query(mysql->mysql, query, query_len)) { MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql); RETURN_FALSE; } mysql->async_result_fetch_type = resultmode & ~MYSQLI_ASYNC; RETURN_TRUE; } #endif if (mysql_real_query(mysql->mysql, query, query_len)) { MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql); RETURN_FALSE; } if (!mysql_field_count(mysql->mysql)) { /* no result set - not a SELECT'],mysqli_real_connect:["bool mysqli_real_connect(object link [,string hostname [,string username [,string passwd [,string dbname [,int port [,string socket [,int flags]]]]]]])","Open a connection to a mysql server"],mysqli_real_escape_string:["string mysqli_real_escape_string(object link, string escapestr)","Escapes special characters in a string for use in a SQL statement, taking into account the current charset of the connection"],mysqli_real_query:["bool mysqli_real_query(object link, string query)","Binary-safe version of mysql_query()"],mysqli_reap_async_query:["int mysqli_reap_async_query(object link)","Poll connections"],mysqli_refresh:["bool mysqli_refresh(object link, long options)","Flush tables or caches, or reset replication server information"],mysqli_report:["bool mysqli_report(int flags)","sets report level"],mysqli_rollback:["bool mysqli_rollback(object link)","Undo actions from current transaction"],mysqli_select_db:["bool mysqli_select_db(object link, string dbname)","Select a MySQL database"],mysqli_set_charset:["bool mysqli_set_charset(object link, string csname)","sets client character set"],mysqli_set_local_infile_default:["void mysqli_set_local_infile_default(object link)","unsets user defined handler for load local infile command"],mysqli_set_local_infile_handler:["bool mysqli_set_local_infile_handler(object link, callback read_func)","Set callback functions for LOAD DATA LOCAL INFILE"],mysqli_sqlstate:["string mysqli_sqlstate(object link)","Returns the SQLSTATE error from previous MySQL operation"],mysqli_ssl_set:["bool mysqli_ssl_set(object link ,string key ,string cert ,string ca ,string capath ,string cipher])",""],mysqli_stat:["mixed mysqli_stat(object link)","Get current system status"],mysqli_stmt_affected_rows:["mixed mysqli_stmt_affected_rows(object stmt)","Return the number of rows affected in the last query for the given link"],mysqli_stmt_attr_get:["int mysqli_stmt_attr_get(object stmt, long attr)",""],mysqli_stmt_attr_set:["int mysqli_stmt_attr_set(object stmt, long attr, long mode)",""],mysqli_stmt_bind_param:["bool mysqli_stmt_bind_param(object stmt, string types, mixed variable [,mixed,....])","Bind variables to a prepared statement as parameters"],mysqli_stmt_bind_result:["bool mysqli_stmt_bind_result(object stmt, mixed var, [,mixed, ...])","Bind variables to a prepared statement for result storage"],mysqli_stmt_close:["bool mysqli_stmt_close(object stmt)","Close statement"],mysqli_stmt_data_seek:["void mysqli_stmt_data_seek(object stmt, int offset)","Move internal result pointer"],mysqli_stmt_errno:["int mysqli_stmt_errno(object stmt)",""],mysqli_stmt_error:["string mysqli_stmt_error(object stmt)",""],mysqli_stmt_execute:["bool mysqli_stmt_execute(object stmt)","Execute a prepared statement"],mysqli_stmt_fetch:["mixed mysqli_stmt_fetch(object stmt)","Fetch results from a prepared statement into the bound variables"],mysqli_stmt_field_count:["int mysqli_stmt_field_count(object stmt) {","Return the number of result columns for the given statement"],mysqli_stmt_free_result:["void mysqli_stmt_free_result(object stmt)","Free stored result memory for the given statement handle"],mysqli_stmt_get_result:["object mysqli_stmt_get_result(object link)","Buffer result set on client"],mysqli_stmt_get_warnings:["object mysqli_stmt_get_warnings(object link) */",'PHP_FUNCTION(mysqli_stmt_get_warnings) { MY_STMT *stmt; zval *stmt_link; MYSQLI_RESOURCE *mysqli_resource; MYSQLI_WARNING *w; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &stmt_link, mysqli_stmt_class_entry) == FAILURE) { return; } MYSQLI_FETCH_RESOURCE(stmt, MY_STMT*, &stmt_link, "mysqli_stmt", MYSQLI_STATUS_VALID); if (mysqli_stmt_warning_count(stmt->stmt)) { w = php_get_warnings(mysqli_stmt_get_connection(stmt->stmt) TSRMLS_CC); } else { RETURN_FALSE; } mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE)); mysqli_resource->ptr = mysqli_resource->info = (void *)w; mysqli_resource->status = MYSQLI_STATUS_VALID; MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_warning_class_entry); } /* }}}'],mysqli_stmt_init:["mixed mysqli_stmt_init(object link)","Initialize statement object"],mysqli_stmt_insert_id:["mixed mysqli_stmt_insert_id(object stmt)","Get the ID generated from the previous INSERT operation"],mysqli_stmt_next_result:["bool mysqli_stmt_next_result(object link)","read next result from multi_query"],mysqli_stmt_num_rows:["mixed mysqli_stmt_num_rows(object stmt)","Return the number of rows in statements result set"],mysqli_stmt_param_count:["int mysqli_stmt_param_count(object stmt)","Return the number of parameter for the given statement"],mysqli_stmt_prepare:["bool mysqli_stmt_prepare(object stmt, string query)","prepare server side statement with query"],mysqli_stmt_reset:["bool mysqli_stmt_reset(object stmt)","reset a prepared statement"],mysqli_stmt_result_metadata:["mixed mysqli_stmt_result_metadata(object stmt)","return result set from statement"],mysqli_stmt_send_long_data:["bool mysqli_stmt_send_long_data(object stmt, int param_nr, string data)",""],mysqli_stmt_sqlstate:["string mysqli_stmt_sqlstate(object stmt)",""],mysqli_stmt_store_result:["bool mysqli_stmt_store_result(stmt)",""],mysqli_store_result:["object mysqli_store_result(object link)","Buffer result set on client"],mysqli_thread_id:["int mysqli_thread_id(object link)","Return the current thread ID"],mysqli_thread_safe:["bool mysqli_thread_safe(void)","Return whether thread safety is given or not"],mysqli_use_result:["mixed mysqli_use_result(object link)","Directly retrieve query results - do not buffer results on client side"],mysqli_warning_count:["int mysqli_warning_count (object link)","Return number of warnings from the last query for the given link"],natcasesort:["void natcasesort(array &array_arg)","Sort an array using case-insensitive natural sort"],natsort:["void natsort(array &array_arg)","Sort an array using natural sort"],next:["mixed next(array array_arg)","Move array argument's internal pointer to the next element and return it"],ngettext:["string ngettext(string MSGID1, string MSGID2, int N)","Plural version of gettext()"],nl2br:["string nl2br(string str [, bool is_xhtml])","Converts newlines to HTML line breaks"],nl_langinfo:["string nl_langinfo(int item)","Query language and locale information"],normalizer_is_normalize:["bool normalizer_is_normalize( string $input [, string $form = FORM_C] )","* Test if a string is in a given normalization form."],normalizer_normalize:["string normalizer_normalize( string $input [, string $form = FORM_C] )","* Normalize a string."],nsapi_request_headers:["array nsapi_request_headers(void)","Get all headers from the request"],nsapi_response_headers:["array nsapi_response_headers(void)","Get all headers from the response"],nsapi_virtual:["bool nsapi_virtual(string uri)","Perform an NSAPI sub-request"],number_format:["string number_format(float number [, int num_decimal_places [, string dec_seperator, string thousands_seperator]])","Formats a number with grouped thousands"],numfmt_create:["NumberFormatter numfmt_create( string $locale, int style[, string $pattern ] )","* Create number formatter."],numfmt_format:["mixed numfmt_format( NumberFormatter $nf, mixed $num[, int type] )","* Format a number."],numfmt_format_currency:["mixed numfmt_format_currency( NumberFormatter $nf, double $num, string $currency )","* Format a number as currency."],numfmt_get_attribute:["mixed numfmt_get_attribute( NumberFormatter $nf, int $attr )","* Get formatter attribute value."],numfmt_get_error_code:["int numfmt_get_error_code( NumberFormatter $nf )","* Get formatter's last error code."],numfmt_get_error_message:["string numfmt_get_error_message( NumberFormatter $nf )","* Get text description for formatter's last error code."],numfmt_get_locale:["string numfmt_get_locale( NumberFormatter $nf[, int type] )","* Get formatter locale."],numfmt_get_pattern:["string numfmt_get_pattern( NumberFormatter $nf )","* Get formatter pattern."],numfmt_get_symbol:["string numfmt_get_symbol( NumberFormatter $nf, int $attr )","* Get formatter symbol value."],numfmt_get_text_attribute:["string numfmt_get_text_attribute( NumberFormatter $nf, int $attr )","* Get formatter attribute value."],numfmt_parse:["mixed numfmt_parse( NumberFormatter $nf, string $str[, int $type, int &$position ])","* Parse a number."],numfmt_parse_currency:["double numfmt_parse_currency( NumberFormatter $nf, string $str, string $¤cy[, int $&position] )","* Parse a number as currency."],numfmt_parse_message:["array numfmt_parse_message( string $locale, string $pattern, string $source )","* Parse a message."],numfmt_set_attribute:["bool numfmt_set_attribute( NumberFormatter $nf, int $attr, mixed $value )","* Get formatter attribute value."],numfmt_set_pattern:["bool numfmt_set_pattern( NumberFormatter $nf, string $pattern )","* Set formatter pattern."],numfmt_set_symbol:["bool numfmt_set_symbol( NumberFormatter $nf, int $attr, string $symbol )","* Set formatter symbol value."],numfmt_set_text_attribute:["bool numfmt_set_text_attribute( NumberFormatter $nf, int $attr, string $value )","* Get formatter attribute value."],ob_clean:["bool ob_clean(void)","Clean (delete) the current output buffer"],ob_end_clean:["bool ob_end_clean(void)","Clean the output buffer, and delete current output buffer"],ob_end_flush:["bool ob_end_flush(void)","Flush (send) the output buffer, and delete current output buffer"],ob_flush:["bool ob_flush(void)","Flush (send) contents of the output buffer. The last buffer content is sent to next buffer"],ob_get_clean:["bool ob_get_clean(void)","Get current buffer contents and delete current output buffer"],ob_get_contents:["string ob_get_contents(void)","Return the contents of the output buffer"],ob_get_flush:["bool ob_get_flush(void)","Get current buffer contents, flush (send) the output buffer, and delete current output buffer"],ob_get_length:["int ob_get_length(void)","Return the length of the output buffer"],ob_get_level:["int ob_get_level(void)","Return the nesting level of the output buffer"],ob_get_status:["false|array ob_get_status([bool full_status])","Return the status of the active or all output buffers"],ob_gzhandler:["string ob_gzhandler(string str, int mode)","Encode str based on accept-encoding setting - designed to be called from ob_start()"],ob_iconv_handler:["string ob_iconv_handler(string contents, int status)","Returns str in output buffer converted to the iconv.output_encoding character set"],ob_implicit_flush:["void ob_implicit_flush([int flag])","Turn implicit flush on/off and is equivalent to calling flush() after every output call"],ob_list_handlers:["false|array ob_list_handlers()","* List all output_buffers in an array"],ob_start:["bool ob_start([ string|array user_function [, int chunk_size [, bool erase]]])","Turn on Output Buffering (specifying an optional output handler)."],oci_bind_array_by_name:["bool oci_bind_array_by_name(resource stmt, string name, array &var, int max_table_length [, int max_item_length [, int type ]])","Bind a PHP array to an Oracle PL/SQL type by name"],oci_bind_by_name:["bool oci_bind_by_name(resource stmt, string name, mixed &var, [, int maxlength [, int type]])","Bind a PHP variable to an Oracle placeholder by name"],oci_cancel:["bool oci_cancel(resource stmt)","Cancel reading from a cursor"],oci_close:["bool oci_close(resource connection)","Disconnect from database"],oci_collection_append:["bool oci_collection_append(string value)","Append an object to the collection"],oci_collection_assign:["bool oci_collection_assign(object from)","Assign a collection from another existing collection"],oci_collection_element_assign:["bool oci_collection_element_assign(int index, string val)","Assign element val to collection at index ndx"],oci_collection_element_get:["string oci_collection_element_get(int ndx)","Retrieve the value at collection index ndx"],oci_collection_max:["int oci_collection_max()","Return the max value of a collection. For a varray this is the maximum length of the array"],oci_collection_size:["int oci_collection_size()","Return the size of a collection"],oci_collection_trim:["bool oci_collection_trim(int num)","Trim num elements from the end of a collection"],oci_commit:["bool oci_commit(resource connection)","Commit the current context"],oci_connect:["resource oci_connect(string user, string pass [, string db [, string charset [, int session_mode ]])","Connect to an Oracle database and log on. Returns a new session."],oci_define_by_name:["bool oci_define_by_name(resource stmt, string name, mixed &var [, int type])","Define a PHP variable to an Oracle column by name"],oci_error:["array oci_error([resource stmt|connection|global])","Return the last error of stmt|connection|global. If no error happened returns false."],oci_execute:["bool oci_execute(resource stmt [, int mode])","Execute a parsed statement"],oci_fetch:["bool oci_fetch(resource stmt)","Prepare a new row of data for reading"],oci_fetch_all:["int oci_fetch_all(resource stmt, array &output[, int skip[, int maxrows[, int flags]]])","Fetch all rows of result data into an array"],oci_fetch_array:["array oci_fetch_array( resource stmt [, int mode ])","Fetch a result row as an array"],oci_fetch_assoc:["array oci_fetch_assoc( resource stmt )","Fetch a result row as an associative array"],oci_fetch_object:["object oci_fetch_object( resource stmt )","Fetch a result row as an object"],oci_fetch_row:["array oci_fetch_row( resource stmt )","Fetch a result row as an enumerated array"],oci_field_is_null:["bool oci_field_is_null(resource stmt, int col)","Tell whether a column is NULL"],oci_field_name:["string oci_field_name(resource stmt, int col)","Tell the name of a column"],oci_field_precision:["int oci_field_precision(resource stmt, int col)","Tell the precision of a column"],oci_field_scale:["int oci_field_scale(resource stmt, int col)","Tell the scale of a column"],oci_field_size:["int oci_field_size(resource stmt, int col)","Tell the maximum data size of a column"],oci_field_type:["mixed oci_field_type(resource stmt, int col)","Tell the data type of a column"],oci_field_type_raw:["int oci_field_type_raw(resource stmt, int col)","Tell the raw oracle data type of a column"],oci_free_collection:["bool oci_free_collection()","Deletes collection object"],oci_free_descriptor:["bool oci_free_descriptor()","Deletes large object description"],oci_free_statement:["bool oci_free_statement(resource stmt)","Free all resources associated with a statement"],oci_internal_debug:["void oci_internal_debug(int onoff)","Toggle internal debugging output for the OCI extension"],oci_lob_append:["bool oci_lob_append( object lob )","Appends data from a LOB to another LOB"],oci_lob_close:["bool oci_lob_close()","Closes lob descriptor"],oci_lob_copy:["bool oci_lob_copy( object lob_to, object lob_from [, int length ] )","Copies data from a LOB to another LOB"],oci_lob_eof:["bool oci_lob_eof()","Checks if EOF is reached"],oci_lob_erase:["int oci_lob_erase( [ int offset [, int length ] ] )","Erases a specified portion of the internal LOB, starting at a specified offset"],oci_lob_export:["bool oci_lob_export([string filename [, int start [, int length]]])","Writes a large object into a file"],oci_lob_flush:["bool oci_lob_flush( [ int flag ] )","Flushes the LOB buffer"],oci_lob_import:["bool oci_lob_import( string filename )","Loads file into a LOB"],oci_lob_is_equal:["bool oci_lob_is_equal( object lob1, object lob2 )","Tests to see if two LOB/FILE locators are equal"],oci_lob_load:["string oci_lob_load()","Loads a large object"],oci_lob_read:["string oci_lob_read( int length )","Reads particular part of a large object"],oci_lob_rewind:["bool oci_lob_rewind()","Rewind pointer of a LOB"],oci_lob_save:["bool oci_lob_save( string data [, int offset ])","Saves a large object"],oci_lob_seek:["bool oci_lob_seek( int offset [, int whence ])","Moves the pointer of a LOB"],oci_lob_size:["int oci_lob_size()","Returns size of a large object"],oci_lob_tell:["int oci_lob_tell()","Tells LOB pointer position"],oci_lob_truncate:["bool oci_lob_truncate( [ int length ])","Truncates a LOB"],oci_lob_write:["int oci_lob_write( string string [, int length ])","Writes data to current position of a LOB"],oci_lob_write_temporary:["bool oci_lob_write_temporary(string var [, int lob_type])","Writes temporary blob"],oci_new_collection:["object oci_new_collection(resource connection, string tdo [, string schema])","Initialize a new collection"],oci_new_connect:["resource oci_new_connect(string user, string pass [, string db])","Connect to an Oracle database and log on. Returns a new session."],oci_new_cursor:["resource oci_new_cursor(resource connection)","Return a new cursor (Statement-Handle) - use this to bind ref-cursors!"],oci_new_descriptor:["object oci_new_descriptor(resource connection [, int type])","Initialize a new empty descriptor LOB/FILE (LOB is default)"],oci_num_fields:["int oci_num_fields(resource stmt)","Return the number of result columns in a statement"],oci_num_rows:["int oci_num_rows(resource stmt)","Return the row count of an OCI statement"],oci_parse:["resource oci_parse(resource connection, string query)","Parse a query and return a statement"],oci_password_change:["bool oci_password_change(resource connection, string username, string old_password, string new_password)","Changes the password of an account"],oci_pconnect:["resource oci_pconnect(string user, string pass [, string db [, string charset ]])","Connect to an Oracle database using a persistent connection and log on. Returns a new session."],oci_result:["string oci_result(resource stmt, mixed column)","Return a single column of result data"],oci_rollback:["bool oci_rollback(resource connection)","Rollback the current context"],oci_server_version:["string oci_server_version(resource connection)","Return a string containing server version information"],oci_set_action:["bool oci_set_action(resource connection, string value)","Sets the action attribute on the connection"],oci_set_client_identifier:["bool oci_set_client_identifier(resource connection, string value)","Sets the client identifier attribute on the connection"],oci_set_client_info:["bool oci_set_client_info(resource connection, string value)","Sets the client info attribute on the connection"],oci_set_edition:["bool oci_set_edition(string value)","Sets the edition attribute for all subsequent connections created"],oci_set_module_name:["bool oci_set_module_name(resource connection, string value)","Sets the module attribute on the connection"],oci_set_prefetch:["bool oci_set_prefetch(resource stmt, int prefetch_rows)","Sets the number of rows to be prefetched on execute to prefetch_rows for stmt"],oci_statement_type:["string oci_statement_type(resource stmt)","Return the query type of an OCI statement"],ocifetchinto:["int ocifetchinto(resource stmt, array &output [, int mode])","Fetch a row of result data into an array"],ocigetbufferinglob:["bool ocigetbufferinglob()","Returns current state of buffering for a LOB"],ocisetbufferinglob:["bool ocisetbufferinglob( boolean flag )","Enables/disables buffering for a LOB"],octdec:["int octdec(string octal_number)","Returns the decimal equivalent of an octal string"],odbc_autocommit:["mixed odbc_autocommit(resource connection_id [, int OnOff])","Toggle autocommit mode or get status"],odbc_binmode:["bool odbc_binmode(int result_id, int mode)","Handle binary column data"],odbc_close:["void odbc_close(resource connection_id)","Close an ODBC connection"],odbc_close_all:["void odbc_close_all(void)","Close all ODBC connections"],odbc_columnprivileges:["resource odbc_columnprivileges(resource connection_id, string catalog, string schema, string table, string column)","Returns a result identifier that can be used to fetch a list of columns and associated privileges for the specified table"],odbc_columns:["resource odbc_columns(resource connection_id [, string qualifier [, string owner [, string table_name [, string column_name]]]])","Returns a result identifier that can be used to fetch a list of column names in specified tables"],odbc_commit:["bool odbc_commit(resource connection_id)","Commit an ODBC transaction"],odbc_connect:["resource odbc_connect(string DSN, string user, string password [, int cursor_option])","Connect to a datasource"],odbc_cursor:["string odbc_cursor(resource result_id)","Get cursor name"],odbc_data_source:["array odbc_data_source(resource connection_id, int fetch_type)","Return information about the currently connected data source"],odbc_error:["string odbc_error([resource connection_id])","Get the last error code"],odbc_errormsg:["string odbc_errormsg([resource connection_id])","Get the last error message"],odbc_exec:["resource odbc_exec(resource connection_id, string query [, int flags])","Prepare and execute an SQL statement"],odbc_execute:["bool odbc_execute(resource result_id [, array parameters_array])","Execute a prepared statement"],odbc_fetch_array:["array odbc_fetch_array(int result [, int rownumber])","Fetch a result row as an associative array"],odbc_fetch_into:["int odbc_fetch_into(resource result_id, array &result_array, [, int rownumber])","Fetch one result row into an array"],odbc_fetch_object:["object odbc_fetch_object(int result [, int rownumber])","Fetch a result row as an object"],odbc_fetch_row:["bool odbc_fetch_row(resource result_id [, int row_number])","Fetch a row"],odbc_field_len:["int odbc_field_len(resource result_id, int field_number)","Get the length (precision) of a column"],odbc_field_name:["string odbc_field_name(resource result_id, int field_number)","Get a column name"],odbc_field_num:["int odbc_field_num(resource result_id, string field_name)","Return column number"],odbc_field_scale:["int odbc_field_scale(resource result_id, int field_number)","Get the scale of a column"],odbc_field_type:["string odbc_field_type(resource result_id, int field_number)","Get the datatype of a column"],odbc_foreignkeys:["resource odbc_foreignkeys(resource connection_id, string pk_qualifier, string pk_owner, string pk_table, string fk_qualifier, string fk_owner, string fk_table)","Returns a result identifier to either a list of foreign keys in the specified table or a list of foreign keys in other tables that refer to the primary key in the specified table"],odbc_free_result:["bool odbc_free_result(resource result_id)","Free resources associated with a result"],odbc_gettypeinfo:["resource odbc_gettypeinfo(resource connection_id [, int data_type])","Returns a result identifier containing information about data types supported by the data source"],odbc_longreadlen:["bool odbc_longreadlen(int result_id, int length)","Handle LONG columns"],odbc_next_result:["bool odbc_next_result(resource result_id)","Checks if multiple results are avaiable"],odbc_num_fields:["int odbc_num_fields(resource result_id)","Get number of columns in a result"],odbc_num_rows:["int odbc_num_rows(resource result_id)","Get number of rows in a result"],odbc_pconnect:["resource odbc_pconnect(string DSN, string user, string password [, int cursor_option])","Establish a persistent connection to a datasource"],odbc_prepare:["resource odbc_prepare(resource connection_id, string query)","Prepares a statement for execution"],odbc_primarykeys:["resource odbc_primarykeys(resource connection_id, string qualifier, string owner, string table)","Returns a result identifier listing the column names that comprise the primary key for a table"],odbc_procedurecolumns:["resource odbc_procedurecolumns(resource connection_id [, string qualifier, string owner, string proc, string column])","Returns a result identifier containing the list of input and output parameters, as well as the columns that make up the result set for the specified procedures"],odbc_procedures:["resource odbc_procedures(resource connection_id [, string qualifier, string owner, string name])","Returns a result identifier containg the list of procedure names in a datasource"],odbc_result:["mixed odbc_result(resource result_id, mixed field)","Get result data"],odbc_result_all:["int odbc_result_all(resource result_id [, string format])","Print result as HTML table"],odbc_rollback:["bool odbc_rollback(resource connection_id)","Rollback a transaction"],odbc_setoption:["bool odbc_setoption(resource conn_id|result_id, int which, int option, int value)","Sets connection or statement options"],odbc_specialcolumns:["resource odbc_specialcolumns(resource connection_id, int type, string qualifier, string owner, string table, int scope, int nullable)","Returns a result identifier containing either the optimal set of columns that uniquely identifies a row in the table or columns that are automatically updated when any value in the row is updated by a transaction"],odbc_statistics:["resource odbc_statistics(resource connection_id, string qualifier, string owner, string name, int unique, int accuracy)","Returns a result identifier that contains statistics about a single table and the indexes associated with the table"],odbc_tableprivileges:["resource odbc_tableprivileges(resource connection_id, string qualifier, string owner, string name)","Returns a result identifier containing a list of tables and the privileges associated with each table"],odbc_tables:["resource odbc_tables(resource connection_id [, string qualifier [, string owner [, string name [, string table_types]]]])","Call the SQLTables function"],opendir:["mixed opendir(string path[, resource context])","Open a directory and return a dir_handle"],openlog:["bool openlog(string ident, int option, int facility)","Open connection to system logger"],openssl_csr_export:["bool openssl_csr_export(resource csr, string &out [, bool notext=true])","Exports a CSR to file or a var"],openssl_csr_export_to_file:["bool openssl_csr_export_to_file(resource csr, string outfilename [, bool notext=true])","Exports a CSR to file"],openssl_csr_get_public_key:["mixed openssl_csr_get_public_key(mixed csr)","Returns the subject of a CERT or FALSE on error"],openssl_csr_get_subject:["mixed openssl_csr_get_subject(mixed csr)","Returns the subject of a CERT or FALSE on error"],openssl_csr_new:["bool openssl_csr_new(array dn, resource &privkey [, array configargs [, array extraattribs]])","Generates a privkey and CSR"],openssl_csr_sign:["resource openssl_csr_sign(mixed csr, mixed x509, mixed priv_key, long days [, array config_args [, long serial]])","Signs a cert with another CERT"],openssl_decrypt:["string openssl_decrypt(string data, string method, string password [, bool raw_input=false])","Takes raw or base64 encoded string and dectupt it using given method and key"],openssl_dh_compute_key:["string openssl_dh_compute_key(string pub_key, resource dh_key)","Computes shared sicret for public value of remote DH key and local DH key"],openssl_digest:["string openssl_digest(string data, string method [, bool raw_output=false])","Computes digest hash value for given data using given method, returns raw or binhex encoded string"],openssl_encrypt:["string openssl_encrypt(string data, string method, string password [, bool raw_output=false])","Encrypts given data with given method and key, returns raw or base64 encoded string"],openssl_error_string:["mixed openssl_error_string(void)","Returns a description of the last error, and alters the index of the error messages. Returns false when the are no more messages"],openssl_get_cipher_methods:["array openssl_get_cipher_methods([bool aliases = false])","Return array of available cipher methods"],openssl_get_md_methods:["array openssl_get_md_methods([bool aliases = false])","Return array of available digest methods"],openssl_open:["bool openssl_open(string data, &string opendata, string ekey, mixed privkey)","Opens data"],openssl_pkcs12_export:["bool openssl_pkcs12_export(mixed x509, string &out, mixed priv_key, string pass[, array args])","Creates and exports a PKCS12 to a var"],openssl_pkcs12_export_to_file:["bool openssl_pkcs12_export_to_file(mixed x509, string filename, mixed priv_key, string pass[, array args])","Creates and exports a PKCS to file"],openssl_pkcs12_read:["bool openssl_pkcs12_read(string PKCS12, array &certs, string pass)","Parses a PKCS12 to an array"],openssl_pkcs7_decrypt:["bool openssl_pkcs7_decrypt(string infilename, string outfilename, mixed recipcert [, mixed recipkey])","Decrypts the S/MIME message in the file name infilename and output the results to the file name outfilename. recipcert is a CERT for one of the recipients. recipkey specifies the private key matching recipcert, if recipcert does not include the key"],openssl_pkcs7_encrypt:["bool openssl_pkcs7_encrypt(string infile, string outfile, mixed recipcerts, array headers [, long flags [, long cipher]])","Encrypts the message in the file named infile with the certificates in recipcerts and output the result to the file named outfile"],openssl_pkcs7_sign:["bool openssl_pkcs7_sign(string infile, string outfile, mixed signcert, mixed signkey, array headers [, long flags [, string extracertsfilename]])","Signs the MIME message in the file named infile with signcert/signkey and output the result to file name outfile. headers lists plain text headers to exclude from the signed portion of the message, and should include to, from and subject as a minimum"],openssl_pkcs7_verify:["bool openssl_pkcs7_verify(string filename, long flags [, string signerscerts [, array cainfo [, string extracerts [, string content]]]])","Verifys that the data block is intact, the signer is who they say they are, and returns the CERTs of the signers"],openssl_pkey_export:["bool openssl_pkey_export(mixed key, &mixed out [, string passphrase [, array config_args]])","Gets an exportable representation of a key into a string or file"],openssl_pkey_export_to_file:["bool openssl_pkey_export_to_file(mixed key, string outfilename [, string passphrase, array config_args)","Gets an exportable representation of a key into a file"],openssl_pkey_free:["void openssl_pkey_free(int key)","Frees a key"],openssl_pkey_get_details:["resource openssl_pkey_get_details(resource key)","returns an array with the key details (bits, pkey, type)"],openssl_pkey_get_private:["int openssl_pkey_get_private(string key [, string passphrase])","Gets private keys"],openssl_pkey_get_public:["int openssl_pkey_get_public(mixed cert)","Gets public key from X.509 certificate"],openssl_pkey_new:["resource openssl_pkey_new([array configargs])","Generates a new private key"],openssl_private_decrypt:["bool openssl_private_decrypt(string data, string &decrypted, mixed key [, int padding])","Decrypts data with private key"],openssl_private_encrypt:["bool openssl_private_encrypt(string data, string &crypted, mixed key [, int padding])","Encrypts data with private key"],openssl_public_decrypt:["bool openssl_public_decrypt(string data, string &crypted, resource key [, int padding])","Decrypts data with public key"],openssl_public_encrypt:["bool openssl_public_encrypt(string data, string &crypted, mixed key [, int padding])","Encrypts data with public key"],openssl_random_pseudo_bytes:["string openssl_random_pseudo_bytes(integer length [, &bool returned_strong_result])","Returns a string of the length specified filled with random pseudo bytes"],openssl_seal:["int openssl_seal(string data, &string sealdata, &array ekeys, array pubkeys)","Seals data"],openssl_sign:["bool openssl_sign(string data, &string signature, mixed key[, mixed method])","Signs data"],openssl_verify:["int openssl_verify(string data, string signature, mixed key[, mixed method])","Verifys data"],openssl_x509_check_private_key:["bool openssl_x509_check_private_key(mixed cert, mixed key)","Checks if a private key corresponds to a CERT"],openssl_x509_checkpurpose:["int openssl_x509_checkpurpose(mixed x509cert, int purpose, array cainfo [, string untrustedfile])","Checks the CERT to see if it can be used for the purpose in purpose. cainfo holds information about trusted CAs"],openssl_x509_export:["bool openssl_x509_export(mixed x509, string &out [, bool notext = true])","Exports a CERT to file or a var"],openssl_x509_export_to_file:["bool openssl_x509_export_to_file(mixed x509, string outfilename [, bool notext = true])","Exports a CERT to file or a var"],openssl_x509_free:["void openssl_x509_free(resource x509)","Frees X.509 certificates"],openssl_x509_parse:["array openssl_x509_parse(mixed x509 [, bool shortnames=true])","Returns an array of the fields/values of the CERT"],openssl_x509_read:["resource openssl_x509_read(mixed cert)","Reads X.509 certificates"],ord:["int ord(string character)","Returns ASCII value of character"],output_add_rewrite_var:["bool output_add_rewrite_var(string name, string value)","Add URL rewriter values"],output_reset_rewrite_vars:["bool output_reset_rewrite_vars(void)","Reset(clear) URL rewriter values"],pack:["string pack(string format, mixed arg1 [, mixed arg2 [, mixed ...]])","Takes one or more arguments and packs them into a binary string according to the format argument"],parse_ini_file:["array parse_ini_file(string filename [, bool process_sections [, int scanner_mode]])","Parse configuration file"],parse_ini_string:["array parse_ini_string(string ini_string [, bool process_sections [, int scanner_mode]])","Parse configuration string"],parse_locale:["static array parse_locale($locale)","* parses a locale-id into an array the different parts of it"],parse_str:["void parse_str(string encoded_string [, array result])","Parses GET/POST/COOKIE data and sets global variables"],parse_url:["mixed parse_url(string url, [int url_component])","Parse a URL and return its components"],passthru:["void passthru(string command [, int &return_value])","Execute an external program and display raw output"],pathinfo:["array pathinfo(string path[, int options])","Returns information about a certain string"],pclose:["int pclose(resource fp)","Close a file pointer opened by popen()"],pcnlt_sigwaitinfo:["int pcnlt_sigwaitinfo(array set[, array &siginfo])","Synchronously wait for queued signals"],pcntl_alarm:["int pcntl_alarm(int seconds)","Set an alarm clock for delivery of a signal"],pcntl_exec:["bool pcntl_exec(string path [, array args [, array envs]])","Executes specified program in current process space as defined by exec(2)"],pcntl_fork:["int pcntl_fork(void)","Forks the currently running process following the same behavior as the UNIX fork() system call"],pcntl_getpriority:["int pcntl_getpriority([int pid [, int process_identifier]])","Get the priority of any process"],pcntl_setpriority:["bool pcntl_setpriority(int priority [, int pid [, int process_identifier]])","Change the priority of any process"],pcntl_signal:["bool pcntl_signal(int signo, callback handle [, bool restart_syscalls])","Assigns a system signal handler to a PHP function"],pcntl_signal_dispatch:["bool pcntl_signal_dispatch()","Dispatch signals to signal handlers"],pcntl_sigprocmask:["bool pcntl_sigprocmask(int how, array set[, array &oldset])","Examine and change blocked signals"],pcntl_sigtimedwait:["int pcntl_sigtimedwait(array set[, array &siginfo[, int seconds[, int nanoseconds]]])","Wait for queued signals"],pcntl_wait:["int pcntl_wait(int &status)","Waits on or returns the status of a forked child as defined by the waitpid() system call"],pcntl_waitpid:["int pcntl_waitpid(int pid, int &status, int options)","Waits on or returns the status of a forked child as defined by the waitpid() system call"],pcntl_wexitstatus:["int pcntl_wexitstatus(int status)","Returns the status code of a child's exit"],pcntl_wifexited:["bool pcntl_wifexited(int status)","Returns true if the child status code represents a successful exit"],pcntl_wifsignaled:["bool pcntl_wifsignaled(int status)","Returns true if the child status code represents a process that was terminated due to a signal"],pcntl_wifstopped:["bool pcntl_wifstopped(int status)","Returns true if the child status code represents a stopped process (WUNTRACED must have been used with waitpid)"],pcntl_wstopsig:["int pcntl_wstopsig(int status)","Returns the number of the signal that caused the process to stop who's status code is passed"],pcntl_wtermsig:["int pcntl_wtermsig(int status)","Returns the number of the signal that terminated the process who's status code is passed"],pdo_drivers:["array pdo_drivers()","Return array of available PDO drivers"],pfsockopen:["resource pfsockopen(string hostname, int port [, int errno [, string errstr [, float timeout]]])","Open persistent Internet or Unix domain socket connection"],pg_affected_rows:["int pg_affected_rows(resource result)","Returns the number of affected tuples"],pg_cancel_query:["bool pg_cancel_query(resource connection)","Cancel request"],pg_client_encoding:["string pg_client_encoding([resource connection])","Get the current client encoding"],pg_close:["bool pg_close([resource connection])","Close a PostgreSQL connection"],pg_connect:["resource pg_connect(string connection_string[, int connect_type] | [string host, string port [, string options [, string tty,]]] string database)","Open a PostgreSQL connection"],pg_connection_busy:["bool pg_connection_busy(resource connection)","Get connection is busy or not"],pg_connection_reset:["bool pg_connection_reset(resource connection)","Reset connection (reconnect)"],pg_connection_status:["int pg_connection_status(resource connnection)","Get connection status"],pg_convert:["array pg_convert(resource db, string table, array values[, int options])","Check and convert values for PostgreSQL SQL statement"],pg_copy_from:["bool pg_copy_from(resource connection, string table_name , array rows [, string delimiter [, string null_as]])","Copy table from array"],pg_copy_to:["array pg_copy_to(resource connection, string table_name [, string delimiter [, string null_as]])","Copy table to array"],pg_dbname:["string pg_dbname([resource connection])","Get the database name"],pg_delete:["mixed pg_delete(resource db, string table, array ids[, int options])","Delete records has ids (id=>value)"],pg_end_copy:["bool pg_end_copy([resource connection])","Sync with backend. Completes the Copy command"],pg_escape_bytea:["string pg_escape_bytea([resource connection,] string data)","Escape binary for bytea type"],pg_escape_string:["string pg_escape_string([resource connection,] string data)","Escape string for text/char type"],pg_execute:["resource pg_execute([resource connection,] string stmtname, array params)","Execute a prepared query"],pg_fetch_all:["array pg_fetch_all(resource result)","Fetch all rows into array"],pg_fetch_all_columns:["array pg_fetch_all_columns(resource result [, int column_number])","Fetch all rows into array"],pg_fetch_array:["array pg_fetch_array(resource result [, int row [, int result_type]])","Fetch a row as an array"],pg_fetch_assoc:["array pg_fetch_assoc(resource result [, int row])","Fetch a row as an assoc array"],pg_fetch_object:["object pg_fetch_object(resource result [, int row [, string class_name [, NULL|array ctor_params]]])","Fetch a row as an object"],pg_fetch_result:["mixed pg_fetch_result(resource result, [int row_number,] mixed field_name)","Returns values from a result identifier"],pg_fetch_row:["array pg_fetch_row(resource result [, int row [, int result_type]])","Get a row as an enumerated array"],pg_field_is_null:["int pg_field_is_null(resource result, [int row,] mixed field_name_or_number)","Test if a field is NULL"],pg_field_name:["string pg_field_name(resource result, int field_number)","Returns the name of the field"],pg_field_num:["int pg_field_num(resource result, string field_name)","Returns the field number of the named field"],pg_field_prtlen:["int pg_field_prtlen(resource result, [int row,] mixed field_name_or_number)","Returns the printed length"],pg_field_size:["int pg_field_size(resource result, int field_number)","Returns the internal size of the field"],pg_field_table:["mixed pg_field_table(resource result, int field_number[, bool oid_only])","Returns the name of the table field belongs to, or table's oid if oid_only is true"],pg_field_type:["string pg_field_type(resource result, int field_number)","Returns the type name for the given field"],pg_field_type_oid:["string pg_field_type_oid(resource result, int field_number)","Returns the type oid for the given field"],pg_free_result:["bool pg_free_result(resource result)","Free result memory"],pg_get_notify:["array pg_get_notify([resource connection[, result_type]])","Get asynchronous notification"],pg_get_pid:["int pg_get_pid([resource connection)","Get backend(server) pid"],pg_get_result:["resource pg_get_result(resource connection)","Get asynchronous query result"],pg_host:["string pg_host([resource connection])","Returns the host name associated with the connection"],pg_insert:["mixed pg_insert(resource db, string table, array values[, int options])","Insert values (filed=>value) to table"],pg_last_error:["string pg_last_error([resource connection])","Get the error message string"],pg_last_notice:["string pg_last_notice(resource connection)","Returns the last notice set by the backend"],pg_last_oid:["string pg_last_oid(resource result)","Returns the last object identifier"],pg_lo_close:["bool pg_lo_close(resource large_object)","Close a large object"],pg_lo_create:["mixed pg_lo_create([resource connection],[mixed large_object_oid])","Create a large object"],pg_lo_export:["bool pg_lo_export([resource connection, ] int objoid, string filename)","Export large object direct to filesystem"],pg_lo_import:["int pg_lo_import([resource connection, ] string filename [, mixed oid])","Import large object direct from filesystem"],pg_lo_open:["resource pg_lo_open([resource connection,] int large_object_oid, string mode)","Open a large object and return fd"],pg_lo_read:["string pg_lo_read(resource large_object [, int len])","Read a large object"],pg_lo_read_all:["int pg_lo_read_all(resource large_object)","Read a large object and send straight to browser"],pg_lo_seek:["bool pg_lo_seek(resource large_object, int offset [, int whence])","Seeks position of large object"],pg_lo_tell:["int pg_lo_tell(resource large_object)","Returns current position of large object"],pg_lo_unlink:["bool pg_lo_unlink([resource connection,] string large_object_oid)","Delete a large object"],pg_lo_write:["int pg_lo_write(resource large_object, string buf [, int len])","Write a large object"],pg_meta_data:["array pg_meta_data(resource db, string table)","Get meta_data"],pg_num_fields:["int pg_num_fields(resource result)","Return the number of fields in the result"],pg_num_rows:["int pg_num_rows(resource result)","Return the number of rows in the result"],pg_options:["string pg_options([resource connection])","Get the options associated with the connection"],pg_parameter_status:["string|false pg_parameter_status([resource connection,] string param_name)","Returns the value of a server parameter"],pg_pconnect:["resource pg_pconnect(string connection_string | [string host, string port [, string options [, string tty,]]] string database)","Open a persistent PostgreSQL connection"],pg_ping:["bool pg_ping([resource connection])","Ping database. If connection is bad, try to reconnect."],pg_port:["int pg_port([resource connection])","Return the port number associated with the connection"],pg_prepare:["resource pg_prepare([resource connection,] string stmtname, string query)","Prepare a query for future execution"],pg_put_line:["bool pg_put_line([resource connection,] string query)","Send null-terminated string to backend server"],pg_query:["resource pg_query([resource connection,] string query)","Execute a query"],pg_query_params:["resource pg_query_params([resource connection,] string query, array params)","Execute a query"],pg_result_error:["string pg_result_error(resource result)","Get error message associated with result"],pg_result_error_field:["string pg_result_error_field(resource result, int fieldcode)","Get error message field associated with result"],pg_result_seek:["bool pg_result_seek(resource result, int offset)","Set internal row offset"],pg_result_status:["mixed pg_result_status(resource result[, long result_type])","Get status of query result"],pg_select:["mixed pg_select(resource db, string table, array ids[, int options])","Select records that has ids (id=>value)"],pg_send_execute:["bool pg_send_execute(resource connection, string stmtname, array params)","Executes prevriously prepared stmtname asynchronously"],pg_send_prepare:["bool pg_send_prepare(resource connection, string stmtname, string query)","Asynchronously prepare a query for future execution"],pg_send_query:["bool pg_send_query(resource connection, string query)","Send asynchronous query"],pg_send_query_params:["bool pg_send_query_params(resource connection, string query, array params)","Send asynchronous parameterized query"],pg_set_client_encoding:["int pg_set_client_encoding([resource connection,] string encoding)","Set client encoding"],pg_set_error_verbosity:["int pg_set_error_verbosity([resource connection,] int verbosity)","Set error verbosity"],pg_trace:["bool pg_trace(string filename [, string mode [, resource connection]])","Enable tracing a PostgreSQL connection"],pg_transaction_status:["int pg_transaction_status(resource connnection)","Get transaction status"],pg_tty:["string pg_tty([resource connection])","Return the tty name associated with the connection"],pg_unescape_bytea:["string pg_unescape_bytea(string data)","Unescape binary for bytea type"],pg_untrace:["bool pg_untrace([resource connection])","Disable tracing of a PostgreSQL connection"],pg_update:["mixed pg_update(resource db, string table, array fields, array ids[, int options])","Update table using values (field=>value) and ids (id=>value)"],pg_version:["array pg_version([resource connection])","Returns an array with client, protocol and server version (when available)"],php_egg_logo_guid:["string php_egg_logo_guid(void)","Return the special ID used to request the PHP logo in phpinfo screens"],php_ini_loaded_file:["string php_ini_loaded_file(void)","Return the actual loaded ini filename"],php_ini_scanned_files:["string php_ini_scanned_files(void)","Return comma-separated string of .ini files parsed from the additional ini dir"],php_logo_guid:["string php_logo_guid(void)","Return the special ID used to request the PHP logo in phpinfo screens"],php_real_logo_guid:["string php_real_logo_guid(void)","Return the special ID used to request the PHP logo in phpinfo screens"],php_sapi_name:["string php_sapi_name(void)","Return the current SAPI module name"],php_snmpv3:["void php_snmpv3(INTERNAL_FUNCTION_PARAMETERS, int st)","* * Generic SNMPv3 object fetcher * From here is passed on the the common internal object fetcher. * * st=SNMP_CMD_GET snmp3_get() - query an agent and return a single value. * st=SNMP_CMD_GETNEXT snmp3_getnext() - query an agent and return the next single value. * st=SNMP_CMD_WALK snmp3_walk() - walk the mib and return a single dimensional array * containing the values. * st=SNMP_CMD_REALWALK snmp3_real_walk() - walk the mib and return an * array of oid,value pairs. * st=SNMP_CMD_SET snmp3_set() - query an agent and set a single value *"],php_strip_whitespace:["string php_strip_whitespace(string file_name)","Return source with stripped comments and whitespace"],php_uname:["string php_uname(void)","Return information about the system PHP was built on"],phpcredits:["void phpcredits([int flag])","Prints the list of people who've contributed to the PHP project"],phpinfo:["void phpinfo([int what])","Output a page of useful information about PHP and the current request"],phpversion:["string phpversion([string extension])","Return the current PHP version"],pi:["float pi(void)","Returns an approximation of pi"],png2wbmp:["bool png2wbmp (string f_org, string f_dest, int d_height, int d_width, int threshold)","Convert PNG image to WBMP image"],popen:["resource popen(string command, string mode)","Execute a command and open either a read or a write pipe to it"],posix_access:["bool posix_access(string file [, int mode])","Determine accessibility of a file (POSIX.1 5.6.3)"],posix_ctermid:["string posix_ctermid(void)","Generate terminal path name (POSIX.1, 4.7.1)"],posix_get_last_error:["int posix_get_last_error(void)","Retrieve the error number set by the last posix function which failed."],posix_getcwd:["string posix_getcwd(void)","Get working directory pathname (POSIX.1, 5.2.2)"],posix_getegid:["int posix_getegid(void)","Get the current effective group id (POSIX.1, 4.2.1)"],posix_geteuid:["int posix_geteuid(void)","Get the current effective user id (POSIX.1, 4.2.1)"],posix_getgid:["int posix_getgid(void)","Get the current group id (POSIX.1, 4.2.1)"],posix_getgrgid:["array posix_getgrgid(long gid)","Group database access (POSIX.1, 9.2.1)"],posix_getgrnam:["array posix_getgrnam(string groupname)","Group database access (POSIX.1, 9.2.1)"],posix_getgroups:["array posix_getgroups(void)","Get supplementary group id's (POSIX.1, 4.2.3)"],posix_getlogin:["string posix_getlogin(void)","Get user name (POSIX.1, 4.2.4)"],posix_getpgid:["int posix_getpgid(void)","Get the process group id of the specified process (This is not a POSIX function, but a SVR4ism, so we compile conditionally)"],posix_getpgrp:["int posix_getpgrp(void)","Get current process group id (POSIX.1, 4.3.1)"],posix_getpid:["int posix_getpid(void)","Get the current process id (POSIX.1, 4.1.1)"],posix_getppid:["int posix_getppid(void)","Get the parent process id (POSIX.1, 4.1.1)"],posix_getpwnam:["array posix_getpwnam(string groupname)","User database access (POSIX.1, 9.2.2)"],posix_getpwuid:["array posix_getpwuid(long uid)","User database access (POSIX.1, 9.2.2)"],posix_getrlimit:["array posix_getrlimit(void)","Get system resource consumption limits (This is not a POSIX function, but a BSDism and a SVR4ism. We compile conditionally)"],posix_getsid:["int posix_getsid(void)","Get process group id of session leader (This is not a POSIX function, but a SVR4ism, so be compile conditionally)"],posix_getuid:["int posix_getuid(void)","Get the current user id (POSIX.1, 4.2.1)"],posix_initgroups:["bool posix_initgroups(string name, int base_group_id)","Calculate the group access list for the user specified in name."],posix_isatty:["bool posix_isatty(int fd)","Determine if filedesc is a tty (POSIX.1, 4.7.1)"],posix_kill:["bool posix_kill(int pid, int sig)","Send a signal to a process (POSIX.1, 3.3.2)"],posix_mkfifo:["bool posix_mkfifo(string pathname, int mode)","Make a FIFO special file (POSIX.1, 5.4.2)"],posix_mknod:["bool posix_mknod(string pathname, int mode [, int major [, int minor]])","Make a special or ordinary file (POSIX.1)"],posix_setegid:["bool posix_setegid(long uid)","Set effective group id"],posix_seteuid:["bool posix_seteuid(long uid)","Set effective user id"],posix_setgid:["bool posix_setgid(int uid)","Set group id (POSIX.1, 4.2.2)"],posix_setpgid:["bool posix_setpgid(int pid, int pgid)","Set process group id for job control (POSIX.1, 4.3.3)"],posix_setsid:["int posix_setsid(void)","Create session and set process group id (POSIX.1, 4.3.2)"],posix_setuid:["bool posix_setuid(long uid)","Set user id (POSIX.1, 4.2.2)"],posix_strerror:["string posix_strerror(int errno)","Retrieve the system error message associated with the given errno."],posix_times:["array posix_times(void)","Get process times (POSIX.1, 4.5.2)"],posix_ttyname:["string posix_ttyname(int fd)","Determine terminal device name (POSIX.1, 4.7.2)"],posix_uname:["array posix_uname(void)","Get system name (POSIX.1, 4.4.1)"],pow:["number pow(number base, number exponent)","Returns base raised to the power of exponent. Returns integer result when possible"],preg_filter:["mixed preg_filter(mixed regex, mixed replace, mixed subject [, int limit [, int &count]])","Perform Perl-style regular expression replacement and only return matches."],preg_grep:["array preg_grep(string regex, array input [, int flags])","Searches array and returns entries which match regex"],preg_last_error:["int preg_last_error()","Returns the error code of the last regexp execution."],preg_match:["int preg_match(string pattern, string subject [, array &subpatterns [, int flags [, int offset]]])","Perform a Perl-style regular expression match"],preg_match_all:["int preg_match_all(string pattern, string subject, array &subpatterns [, int flags [, int offset]])","Perform a Perl-style global regular expression match"],preg_quote:["string preg_quote(string str [, string delim_char])","Quote regular expression characters plus an optional character"],preg_replace:["mixed preg_replace(mixed regex, mixed replace, mixed subject [, int limit [, int &count]])","Perform Perl-style regular expression replacement."],preg_replace_callback:["mixed preg_replace_callback(mixed regex, mixed callback, mixed subject [, int limit [, int &count]])","Perform Perl-style regular expression replacement using replacement callback."],preg_split:["array preg_split(string pattern, string subject [, int limit [, int flags]])","Split string into an array using a perl-style regular expression as a delimiter"],prev:["mixed prev(array array_arg)","Move array argument's internal pointer to the previous element and return it"],print:["int print(string arg)","Output a string"],print_r:["mixed print_r(mixed var [, bool return])","Prints out or returns information about the specified variable"],printf:["int printf(string format [, mixed arg1 [, mixed ...]])","Output a formatted string"],proc_close:["int proc_close(resource process)","close a process opened by proc_open"],proc_get_status:["array proc_get_status(resource process)","get information about a process opened by proc_open"],proc_nice:["bool proc_nice(int priority)","Change the priority of the current process"],proc_open:["resource proc_open(string command, array descriptorspec, array &pipes [, string cwd [, array env [, array other_options]]])","Run a process with more control over it's file descriptors"],proc_terminate:["bool proc_terminate(resource process [, long signal])","kill a process opened by proc_open"],property_exists:["bool property_exists(mixed object_or_class, string property_name)","Checks if the object or class has a property"],pspell_add_to_personal:["bool pspell_add_to_personal(int pspell, string word)","Adds a word to a personal list"],pspell_add_to_session:["bool pspell_add_to_session(int pspell, string word)","Adds a word to the current session"],pspell_check:["bool pspell_check(int pspell, string word)","Returns true if word is valid"],pspell_clear_session:["bool pspell_clear_session(int pspell)","Clears the current session"],pspell_config_create:["int pspell_config_create(string language [, string spelling [, string jargon [, string encoding]]])","Create a new config to be used later to create a manager"],pspell_config_data_dir:["bool pspell_config_data_dir(int conf, string directory)","location of language data files"],pspell_config_dict_dir:["bool pspell_config_dict_dir(int conf, string directory)","location of the main word list"],pspell_config_ignore:["bool pspell_config_ignore(int conf, int ignore)","Ignore words <= n chars"],pspell_config_mode:["bool pspell_config_mode(int conf, long mode)","Select mode for config (PSPELL_FAST, PSPELL_NORMAL or PSPELL_BAD_SPELLERS)"],pspell_config_personal:["bool pspell_config_personal(int conf, string personal)","Use a personal dictionary for this config"],pspell_config_repl:["bool pspell_config_repl(int conf, string repl)","Use a personal dictionary with replacement pairs for this config"],pspell_config_runtogether:["bool pspell_config_runtogether(int conf, bool runtogether)","Consider run-together words as valid components"],pspell_config_save_repl:["bool pspell_config_save_repl(int conf, bool save)","Save replacement pairs when personal list is saved for this config"],pspell_new:["int pspell_new(string language [, string spelling [, string jargon [, string encoding [, int mode]]]])","Load a dictionary"],pspell_new_config:["int pspell_new_config(int config)","Load a dictionary based on the given config"],pspell_new_personal:["int pspell_new_personal(string personal, string language [, string spelling [, string jargon [, string encoding [, int mode]]]])","Load a dictionary with a personal wordlist"],pspell_save_wordlist:["bool pspell_save_wordlist(int pspell)","Saves the current (personal) wordlist"],pspell_store_replacement:["bool pspell_store_replacement(int pspell, string misspell, string correct)","Notify the dictionary of a user-selected replacement"],pspell_suggest:["array pspell_suggest(int pspell, string word)","Returns array of suggestions"],putenv:["bool putenv(string setting)","Set the value of an environment variable"],quoted_printable_decode:["string quoted_printable_decode(string str)","Convert a quoted-printable string to an 8 bit string"],quoted_printable_encode:["string quoted_printable_encode(string str) */",'PHP_FUNCTION(quoted_printable_encode) { char *str, *new_str; int str_len; size_t new_str_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) != SUCCESS) { return; } if (!str_len) { RETURN_EMPTY_STRING(); } new_str = (char *)php_quot_print_encode((unsigned char *)str, (size_t)str_len, &new_str_len); RETURN_STRINGL(new_str, new_str_len, 0); } /* }}}'],quotemeta:["string quotemeta(string str)","Quotes meta characters"],rad2deg:["float rad2deg(float number)","Converts the radian number to the equivalent number in degrees"],rand:["int rand([int min, int max])","Returns a random number"],range:["array range(mixed low, mixed high[, int step])","Create an array containing the range of integers or characters from low to high (inclusive)"],rawurldecode:["string rawurldecode(string str)","Decodes URL-encodes string"],rawurlencode:["string rawurlencode(string str)","URL-encodes string"],readdir:["string readdir([resource dir_handle])","Read directory entry from dir_handle"],readfile:["int readfile(string filename [, bool use_include_path[, resource context]])","Output a file or a URL"],readgzfile:["int readgzfile(string filename [, int use_include_path])","Output a .gz-file"],readline:["string readline([string prompt])","Reads a line"],readline_add_history:["bool readline_add_history(string prompt)","Adds a line to the history"],readline_callback_handler_install:["void readline_callback_handler_install(string prompt, mixed callback)","Initializes the readline callback interface and terminal, prints the prompt and returns immediately"],readline_callback_handler_remove:["bool readline_callback_handler_remove()","Removes a previously installed callback handler and restores terminal settings"],readline_callback_read_char:["void readline_callback_read_char()","Informs the readline callback interface that a character is ready for input"],readline_clear_history:["bool readline_clear_history(void)","Clears the history"],readline_completion_function:["bool readline_completion_function(string funcname)","Readline completion function?"],readline_info:["mixed readline_info([string varname [, string newvalue]])","Gets/sets various internal readline variables."],readline_list_history:["array readline_list_history(void)","Lists the history"],readline_on_new_line:["void readline_on_new_line(void)","Inform readline that the cursor has moved to a new line"],readline_read_history:["bool readline_read_history([string filename])","Reads the history"],readline_redisplay:["void readline_redisplay(void)","Ask readline to redraw the display"],readline_write_history:["bool readline_write_history([string filename])","Writes the history"],readlink:["string readlink(string filename)","Return the target of a symbolic link"],realpath:["string realpath(string path)","Return the resolved path"],realpath_cache_get:["bool realpath_cache_get()","Get current size of realpath cache"],realpath_cache_size:["bool realpath_cache_size()","Get current size of realpath cache"],recode_file:["bool recode_file(string request, resource input, resource output)","Recode file input into file output according to request"],recode_string:["string recode_string(string request, string str)","Recode string str according to request string"],register_shutdown_function:["void register_shutdown_function(string function_name)","Register a user-level function to be called on request termination"],register_tick_function:["bool register_tick_function(string function_name [, mixed arg [, mixed ... ]])","Registers a tick callback function"],rename:["bool rename(string old_name, string new_name[, resource context])","Rename a file"],require:["bool require(string path)","Includes and evaluates the specified file, erroring if the file cannot be included"],require_once:["bool require_once(string path)","Includes and evaluates the specified file, erroring if the file cannot be included"],reset:["mixed reset(array array_arg)","Set array argument's internal pointer to the first element and return it"],restore_error_handler:["void restore_error_handler(void)","Restores the previously defined error handler function"],restore_exception_handler:["void restore_exception_handler(void)","Restores the previously defined exception handler function"],restore_include_path:["void restore_include_path()","Restore the value of the include_path configuration option"],rewind:["bool rewind(resource fp)","Rewind the position of a file pointer"],rewinddir:["void rewinddir([resource dir_handle])","Rewind dir_handle back to the start"],rmdir:["bool rmdir(string dirname[, resource context])","Remove a directory"],round:["float round(float number [, int precision [, int mode]])","Returns the number rounded to specified precision"],rsort:["bool rsort(array &array_arg [, int sort_flags])","Sort an array in reverse order"],rtrim:["string rtrim(string str [, string character_mask])","Removes trailing whitespace"],scandir:["array scandir(string dir [, int sorting_order [, resource context]])","List files & directories inside the specified path"],sem_acquire:["bool sem_acquire(resource id)","Acquires the semaphore with the given id, blocking if necessary"],sem_get:["resource sem_get(int key [, int max_acquire [, int perm [, int auto_release]])","Return an id for the semaphore with the given key, and allow max_acquire (default 1) processes to acquire it simultaneously"],sem_release:["bool sem_release(resource id)","Releases the semaphore with the given id"],sem_remove:["bool sem_remove(resource id)","Removes semaphore from Unix systems"],serialize:["string serialize(mixed variable)","Returns a string representation of variable (which can later be unserialized)"],session_cache_expire:["int session_cache_expire([int new_cache_expire])","Return the current cache expire. If new_cache_expire is given, the current cache_expire is replaced with new_cache_expire"],session_cache_limiter:["string session_cache_limiter([string new_cache_limiter])","Return the current cache limiter. If new_cache_limited is given, the current cache_limiter is replaced with new_cache_limiter"],session_decode:["bool session_decode(string data)","Deserializes data and reinitializes the variables"],session_destroy:["bool session_destroy(void)","Destroy the current session and all data associated with it"],session_encode:["string session_encode(void)","Serializes the current setup and returns the serialized representation"],session_get_cookie_params:["array session_get_cookie_params(void)","Return the session cookie parameters"],session_id:["string session_id([string newid])","Return the current session id. If newid is given, the session id is replaced with newid"],session_is_registered:["bool session_is_registered(string varname)","Checks if a variable is registered in session"],session_module_name:["string session_module_name([string newname])","Return the current module name used for accessing session data. If newname is given, the module name is replaced with newname"],session_name:["string session_name([string newname])","Return the current session name. If newname is given, the session name is replaced with newname"],session_regenerate_id:["bool session_regenerate_id([bool delete_old_session])","Update the current session id with a newly generated one. If delete_old_session is set to true, remove the old session."],session_register:["bool session_register(mixed var_names [, mixed ...])","Adds varname(s) to the list of variables which are freezed at the session end"],session_save_path:["string session_save_path([string newname])","Return the current save path passed to module_name. If newname is given, the save path is replaced with newname"],session_set_cookie_params:["void session_set_cookie_params(int lifetime [, string path [, string domain [, bool secure[, bool httponly]]]])","Set session cookie parameters"],session_set_save_handler:["void session_set_save_handler(string open, string close, string read, string write, string destroy, string gc)","Sets user-level functions"],session_start:["bool session_start(void)","Begin session - reinitializes freezed variables, registers browsers etc"],session_unregister:["bool session_unregister(string varname)","Removes varname from the list of variables which are freezed at the session end"],session_unset:["void session_unset(void)","Unset all registered variables"],session_write_close:["void session_write_close(void)","Write session data and end session"],set_error_handler:["string set_error_handler(string error_handler [, int error_types])","Sets a user-defined error handler function. Returns the previously defined error handler, or false on error"],set_exception_handler:["string set_exception_handler(callable exception_handler)","Sets a user-defined exception handler function. Returns the previously defined exception handler, or false on error"],set_include_path:["string set_include_path(string new_include_path)","Sets the include_path configuration option"],set_magic_quotes_runtime:["bool set_magic_quotes_runtime(int new_setting)","Set the current active configuration setting of magic_quotes_runtime and return previous"],set_time_limit:["bool set_time_limit(int seconds)","Sets the maximum time a script can run"],setcookie:["bool setcookie(string name [, string value [, int expires [, string path [, string domain [, bool secure[, bool httponly]]]]]])","Send a cookie"],setlocale:["string setlocale(mixed category, string locale [, string ...])","Set locale information"],setrawcookie:["bool setrawcookie(string name [, string value [, int expires [, string path [, string domain [, bool secure[, bool httponly]]]]]])","Send a cookie with no url encoding of the value"],settype:["bool settype(mixed var, string type)","Set the type of the variable"],sha1:["string sha1(string str [, bool raw_output])","Calculate the sha1 hash of a string"],sha1_file:["string sha1_file(string filename [, bool raw_output])","Calculate the sha1 hash of given filename"],shell_exec:["string shell_exec(string cmd)","Execute command via shell and return complete output as string"],shm_attach:["int shm_attach(int key [, int memsize [, int perm]])","Creates or open a shared memory segment"],shm_detach:["bool shm_detach(resource shm_identifier)","Disconnects from shared memory segment"],shm_get_var:["mixed shm_get_var(resource id, int variable_key)","Returns a variable from shared memory"],shm_has_var:["bool shm_has_var(resource id, int variable_key)","Checks whether a specific entry exists"],shm_put_var:["bool shm_put_var(resource shm_identifier, int variable_key, mixed variable)","Inserts or updates a variable in shared memory"],shm_remove:["bool shm_remove(resource shm_identifier)","Removes shared memory from Unix systems"],shm_remove_var:["bool shm_remove_var(resource id, int variable_key)","Removes variable from shared memory"],shmop_close:["void shmop_close (int shmid)","closes a shared memory segment"],shmop_delete:["bool shmop_delete (int shmid)","mark segment for deletion"],shmop_open:["int shmop_open (int key, string flags, int mode, int size)","gets and attaches a shared memory segment"],shmop_read:["string shmop_read (int shmid, int start, int count)","reads from a shm segment"],shmop_size:["int shmop_size (int shmid)","returns the shm size"],shmop_write:["int shmop_write (int shmid, string data, int offset)","writes to a shared memory segment"],shuffle:["bool shuffle(array array_arg)","Randomly shuffle the contents of an array"],similar_text:["int similar_text(string str1, string str2 [, float percent])","Calculates the similarity between two strings"],simplexml_import_dom:["simplemxml_element simplexml_import_dom(domNode node [, string class_name])","Get a simplexml_element object from dom to allow for processing"],simplexml_load_file:["simplemxml_element simplexml_load_file(string filename [, string class_name [, int options [, string ns [, bool is_prefix]]]])","Load a filename and return a simplexml_element object to allow for processing"],simplexml_load_string:["simplemxml_element simplexml_load_string(string data [, string class_name [, int options [, string ns [, bool is_prefix]]]])","Load a string and return a simplexml_element object to allow for processing"],sin:["float sin(float number)","Returns the sine of the number in radians"],sinh:["float sinh(float number)","Returns the hyperbolic sine of the number, defined as (exp(number) - exp(-number))/2"],sleep:["void sleep(int seconds)","Delay for a given number of seconds"],smfi_addheader:["bool smfi_addheader(string headerf, string headerv)","Adds a header to the current message."],smfi_addrcpt:["bool smfi_addrcpt(string rcpt)","Add a recipient to the message envelope."],smfi_chgheader:["bool smfi_chgheader(string headerf, string headerv)","Changes a header's value for the current message."],smfi_delrcpt:["bool smfi_delrcpt(string rcpt)","Removes the named recipient from the current message's envelope."],smfi_getsymval:["string smfi_getsymval(string macro)","Returns the value of the given macro or NULL if the macro is not defined."],smfi_replacebody:["bool smfi_replacebody(string body)","Replaces the body of the current message. If called more than once, subsequent calls result in data being appended to the new body."],smfi_setflags:["void smfi_setflags(long flags)","Sets the flags describing the actions the filter may take."],smfi_setreply:["bool smfi_setreply(string rcode, string xcode, string message)","Directly set the SMTP error reply code for this connection. This code will be used on subsequent error replies resulting from actions taken by this filter."],smfi_settimeout:["void smfi_settimeout(long timeout)","Sets the number of seconds libmilter will wait for an MTA connection before timing out a socket."],snmp2_get:["string snmp2_get(string host, string community, string object_id [, int timeout [, int retries]])","Fetch a SNMP object"],snmp2_getnext:["string snmp2_getnext(string host, string community, string object_id [, int timeout [, int retries]])","Fetch a SNMP object"],snmp2_real_walk:["array snmp2_real_walk(string host, string community, string object_id [, int timeout [, int retries]])","Return all objects including their respective object id withing the specified one"],snmp2_set:["int snmp2_set(string host, string community, string object_id, string type, mixed value [, int timeout [, int retries]])","Set the value of a SNMP object"],snmp2_walk:["array snmp2_walk(string host, string community, string object_id [, int timeout [, int retries]])","Return all objects under the specified object id"],snmp3_get:["int snmp3_get(string host, string sec_name, string sec_level, string auth_protocol, string auth_passphrase, string priv_protocol, string priv_passphrase, string object_id [, int timeout [, int retries]])","Fetch the value of a SNMP object"],snmp3_getnext:["int snmp3_getnext(string host, string sec_name, string sec_level, string auth_protocol, string auth_passphrase, string priv_protocol, string priv_passphrase, string object_id [, int timeout [, int retries]])","Fetch the value of a SNMP object"],snmp3_real_walk:["int snmp3_real_walk(string host, string sec_name, string sec_level, string auth_protocol, string auth_passphrase, string priv_protocol, string priv_passphrase, string object_id [, int timeout [, int retries]])","Fetch the value of a SNMP object"],snmp3_set:["int snmp3_set(string host, string sec_name, string sec_level, string auth_protocol, string auth_passphrase, string priv_protocol, string priv_passphrase, string object_id, string type, mixed value [, int timeout [, int retries]])","Fetch the value of a SNMP object"],snmp3_walk:["int snmp3_walk(string host, string sec_name, string sec_level, string auth_protocol, string auth_passphrase, string priv_protocol, string priv_passphrase, string object_id [, int timeout [, int retries]])","Fetch the value of a SNMP object"],snmp_get_quick_print:["bool snmp_get_quick_print(void)","Return the current status of quick_print"],snmp_get_valueretrieval:["int snmp_get_valueretrieval()","Return the method how the SNMP values will be returned"],snmp_read_mib:["int snmp_read_mib(string filename)","Reads and parses a MIB file into the active MIB tree."],snmp_set_enum_print:["void snmp_set_enum_print(int enum_print)","Return all values that are enums with their enum value instead of the raw integer"],snmp_set_oid_output_format:["void snmp_set_oid_output_format(int oid_format)","Set the OID output format."],snmp_set_quick_print:["void snmp_set_quick_print(int quick_print)","Return all objects including their respective object id withing the specified one"],snmp_set_valueretrieval:["void snmp_set_valueretrieval(int method)","Specify the method how the SNMP values will be returned"],snmpget:["string snmpget(string host, string community, string object_id [, int timeout [, int retries]])","Fetch a SNMP object"],snmpgetnext:["string snmpgetnext(string host, string community, string object_id [, int timeout [, int retries]])","Fetch a SNMP object"],snmprealwalk:["array snmprealwalk(string host, string community, string object_id [, int timeout [, int retries]])","Return all objects including their respective object id withing the specified one"],snmpset:["int snmpset(string host, string community, string object_id, string type, mixed value [, int timeout [, int retries]])","Set the value of a SNMP object"],snmpwalk:["array snmpwalk(string host, string community, string object_id [, int timeout [, int retries]])","Return all objects under the specified object id"],socket_accept:["resource socket_accept(resource socket)","Accepts a connection on the listening socket fd"],socket_bind:["bool socket_bind(resource socket, string addr [, int port])","Binds an open socket to a listening port, port is only specified in AF_INET family."],socket_clear_error:["void socket_clear_error([resource socket])","Clears the error on the socket or the last error code."],socket_close:["void socket_close(resource socket)","Closes a file descriptor"],socket_connect:["bool socket_connect(resource socket, string addr [, int port])","Opens a connection to addr:port on the socket specified by socket"],socket_create:["resource socket_create(int domain, int type, int protocol)","Creates an endpoint for communication in the domain specified by domain, of type specified by type"],socket_create_listen:["resource socket_create_listen(int port[, int backlog])","Opens a socket on port to accept connections"],socket_create_pair:["bool socket_create_pair(int domain, int type, int protocol, array &fd)","Creates a pair of indistinguishable sockets and stores them in fds."],socket_get_option:["mixed socket_get_option(resource socket, int level, int optname)","Gets socket options for the socket"],socket_getpeername:["bool socket_getpeername(resource socket, string &addr[, int &port])","Queries the remote side of the given socket which may either result in host/port or in a UNIX filesystem path, dependent on its type."],socket_getsockname:["bool socket_getsockname(resource socket, string &addr[, int &port])","Queries the remote side of the given socket which may either result in host/port or in a UNIX filesystem path, dependent on its type."],socket_last_error:["int socket_last_error([resource socket])","Returns the last socket error (either the last used or the provided socket resource)"],socket_listen:["bool socket_listen(resource socket[, int backlog])","Sets the maximum number of connections allowed to be waited for on the socket specified by fd"],socket_read:["string socket_read(resource socket, int length [, int type])","Reads a maximum of length bytes from socket"],socket_recv:["int socket_recv(resource socket, string &buf, int len, int flags)","Receives data from a connected socket"],socket_recvfrom:["int socket_recvfrom(resource socket, string &buf, int len, int flags, string &name [, int &port])","Receives data from a socket, connected or not"],socket_select:["int socket_select(array &read_fds, array &write_fds, array &except_fds, int tv_sec[, int tv_usec])","Runs the select() system call on the sets mentioned with a timeout specified by tv_sec and tv_usec"],socket_send:["int socket_send(resource socket, string buf, int len, int flags)","Sends data to a connected socket"],socket_sendto:["int socket_sendto(resource socket, string buf, int len, int flags, string addr [, int port])","Sends a message to a socket, whether it is connected or not"],socket_set_block:["bool socket_set_block(resource socket)","Sets blocking mode on a socket resource"],socket_set_nonblock:["bool socket_set_nonblock(resource socket)","Sets nonblocking mode on a socket resource"],socket_set_option:["bool socket_set_option(resource socket, int level, int optname, int|array optval)","Sets socket options for the socket"],socket_shutdown:["bool socket_shutdown(resource socket[, int how])","Shuts down a socket for receiving, sending, or both."],socket_strerror:["string socket_strerror(int errno)","Returns a string describing an error"],socket_write:["int socket_write(resource socket, string buf[, int length])","Writes the buffer to the socket resource, length is optional"],solid_fetch_prev:["bool solid_fetch_prev(resource result_id)",""],sort:["bool sort(array &array_arg [, int sort_flags])","Sort an array"],soundex:["string soundex(string str)","Calculate the soundex key of a string"],spl_autoload:["void spl_autoload(string class_name [, string file_extensions])","Default implementation for __autoload()"],spl_autoload_call:["void spl_autoload_call(string class_name)","Try all registerd autoload function to load the requested class"],spl_autoload_extensions:["string spl_autoload_extensions([string file_extensions])","Register and return default file extensions for spl_autoload"],spl_autoload_functions:["false|array spl_autoload_functions()","Return all registered __autoload() functionns"],spl_autoload_register:['bool spl_autoload_register([mixed autoload_function = "spl_autoload" [, throw = true [, prepend]]])',"Register given function as __autoload() implementation"],spl_autoload_unregister:["bool spl_autoload_unregister(mixed autoload_function)","Unregister given function as __autoload() implementation"],spl_classes:["array spl_classes()","Return an array containing the names of all clsses and interfaces defined in SPL"],spl_object_hash:["string spl_object_hash(object obj)","Return hash id for given object"],split:["array split(string pattern, string string [, int limit])","Split string into array by regular expression"],spliti:["array spliti(string pattern, string string [, int limit])","Split string into array by regular expression case-insensitive"],sprintf:["string sprintf(string format [, mixed arg1 [, mixed ...]])","Return a formatted string"],sql_regcase:["string sql_regcase(string string)","Make regular expression for case insensitive match"],sqlite_array_query:["array sqlite_array_query(resource db, string query [ , int result_type [, bool decode_binary]])","Executes a query against a given database and returns an array of arrays."],sqlite_busy_timeout:["void sqlite_busy_timeout(resource db, int ms)","Set busy timeout duration. If ms <= 0, all busy handlers are disabled."],sqlite_changes:["int sqlite_changes(resource db)","Returns the number of rows that were changed by the most recent SQL statement."],sqlite_close:["void sqlite_close(resource db)","Closes an open sqlite database."],sqlite_column:["mixed sqlite_column(resource result, mixed index_or_name [, bool decode_binary])","Fetches a column from the current row of a result set."],sqlite_create_aggregate:["bool sqlite_create_aggregate(resource db, string funcname, mixed step_func, mixed finalize_func[, long num_args])","Registers an aggregate function for queries."],sqlite_create_function:["bool sqlite_create_function(resource db, string funcname, mixed callback[, long num_args])",'Registers a "regular" function for queries.'],sqlite_current:["array sqlite_current(resource result [, int result_type [, bool decode_binary]])","Fetches the current row from a result set as an array."],sqlite_error_string:["string sqlite_error_string(int error_code)","Returns the textual description of an error code."],sqlite_escape_string:["string sqlite_escape_string(string item)","Escapes a string for use as a query parameter."],sqlite_exec:["boolean sqlite_exec(string query, resource db[, string &error_message])","Executes a result-less query against a given database"],sqlite_factory:["object sqlite_factory(string filename [, int mode [, string &error_message]])","Opens a SQLite database and creates an object for it. Will create the database if it does not exist."],sqlite_fetch_all:["array sqlite_fetch_all(resource result [, int result_type [, bool decode_binary]])","Fetches all rows from a result set as an array of arrays."],sqlite_fetch_array:["array sqlite_fetch_array(resource result [, int result_type [, bool decode_binary]])","Fetches the next row from a result set as an array."],sqlite_fetch_column_types:["resource sqlite_fetch_column_types(string table_name, resource db [, int result_type])","Return an array of column types from a particular table."],sqlite_fetch_object:["object sqlite_fetch_object(resource result [, string class_name [, NULL|array ctor_params [, bool decode_binary]]])","Fetches the next row from a result set as an object."],sqlite_fetch_single:["string sqlite_fetch_single(resource result [, bool decode_binary])","Fetches the first column of a result set as a string."],sqlite_field_name:["string sqlite_field_name(resource result, int field_index)","Returns the name of a particular field of a result set."],sqlite_has_prev:["bool sqlite_has_prev(resource result)","* Returns whether a previous row is available."],sqlite_key:["int sqlite_key(resource result)","Return the current row index of a buffered result."],sqlite_last_error:["int sqlite_last_error(resource db)","Returns the error code of the last error for a database."],sqlite_last_insert_rowid:["int sqlite_last_insert_rowid(resource db)","Returns the rowid of the most recently inserted row."],sqlite_libencoding:["string sqlite_libencoding()","Returns the encoding (iso8859 or UTF-8) of the linked SQLite library."],sqlite_libversion:["string sqlite_libversion()","Returns the version of the linked SQLite library."],sqlite_next:["bool sqlite_next(resource result)","Seek to the next row number of a result set."],sqlite_num_fields:["int sqlite_num_fields(resource result)","Returns the number of fields in a result set."],sqlite_num_rows:["int sqlite_num_rows(resource result)","Returns the number of rows in a buffered result set."],sqlite_open:["resource sqlite_open(string filename [, int mode [, string &error_message]])","Opens a SQLite database. Will create the database if it does not exist."],sqlite_popen:["resource sqlite_popen(string filename [, int mode [, string &error_message]])","Opens a persistent handle to a SQLite database. Will create the database if it does not exist."],sqlite_prev:["bool sqlite_prev(resource result)","* Seek to the previous row number of a result set."],sqlite_query:["resource sqlite_query(string query, resource db [, int result_type [, string &error_message]])","Executes a query against a given database and returns a result handle."],sqlite_rewind:["bool sqlite_rewind(resource result)","Seek to the first row number of a buffered result set."],sqlite_seek:["bool sqlite_seek(resource result, int row)","Seek to a particular row number of a buffered result set."],sqlite_single_query:["array sqlite_single_query(resource db, string query [, bool first_row_only [, bool decode_binary]])","Executes a query and returns either an array for one single column or the value of the first row."],sqlite_udf_decode_binary:["string sqlite_udf_decode_binary(string data)","Decode binary encoding on a string parameter passed to an UDF."],sqlite_udf_encode_binary:["string sqlite_udf_encode_binary(string data)","Apply binary encoding (if required) to a string to return from an UDF."],sqlite_unbuffered_query:["resource sqlite_unbuffered_query(string query, resource db [ , int result_type [, string &error_message]])","Executes a query that does not prefetch and buffer all data."],sqlite_valid:["bool sqlite_valid(resource result)","Returns whether more rows are available."],sqrt:["float sqrt(float number)","Returns the square root of the number"],srand:["void srand([int seed])","Seeds random number generator"],sscanf:["mixed sscanf(string str, string format [, string ...])","Implements an ANSI C compatible sscanf"],stat:["array stat(string filename)","Give information about a file"],str_getcsv:["array str_getcsv(string input[, string delimiter[, string enclosure[, string escape]]])","Parse a CSV string into an array"],str_ireplace:["mixed str_ireplace(mixed search, mixed replace, mixed subject [, int &replace_count])","Replaces all occurrences of search in haystack with replace / case-insensitive"],str_pad:["string str_pad(string input, int pad_length [, string pad_string [, int pad_type]])","Returns input string padded on the left or right to specified length with pad_string"],str_repeat:["string str_repeat(string input, int mult)","Returns the input string repeat mult times"],str_replace:["mixed str_replace(mixed search, mixed replace, mixed subject [, int &replace_count])","Replaces all occurrences of search in haystack with replace"],str_rot13:["string str_rot13(string str)","Perform the rot13 transform on a string"],str_shuffle:["void str_shuffle(string str)","Shuffles string. One permutation of all possible is created"],str_split:["array str_split(string str [, int split_length])","Convert a string to an array. If split_length is specified, break the string down into chunks each split_length characters long."],str_word_count:["mixed str_word_count(string str, [int format [, string charlist]])",'Counts the number of words inside a string. If format of 1 is specified, then the function will return an array containing all the words found inside the string. If format of 2 is specified, then the function will return an associated array where the position of the word is the key and the word itself is the value. For the purpose of this function, \'word\' is defined as a locale dependent string containing alphabetic characters, which also may contain, but not start with "\'" and "-" characters.'],strcasecmp:["int strcasecmp(string str1, string str2)","Binary safe case-insensitive string comparison"],strchr:["string strchr(string haystack, string needle)","An alias for strstr"],strcmp:["int strcmp(string str1, string str2)","Binary safe string comparison"],strcoll:["int strcoll(string str1, string str2)","Compares two strings using the current locale"],strcspn:["int strcspn(string str, string mask [, start [, len]])","Finds length of initial segment consisting entirely of characters not found in mask. If start or/and length is provide works like strcspn(substr($s,$start,$len),$bad_chars)"],stream_bucket_append:["void stream_bucket_append(resource brigade, resource bucket)","Append bucket to brigade"],stream_bucket_make_writeable:["object stream_bucket_make_writeable(resource brigade)","Return a bucket object from the brigade for operating on"],stream_bucket_new:["resource stream_bucket_new(resource stream, string buffer)","Create a new bucket for use on the current stream"],stream_bucket_prepend:["void stream_bucket_prepend(resource brigade, resource bucket)","Prepend bucket to brigade"],stream_context_create:["resource stream_context_create([array options[, array params]])","Create a file context and optionally set parameters"],stream_context_get_default:["resource stream_context_get_default([array options])","Get a handle on the default file/stream context and optionally set parameters"],stream_context_get_options:["array stream_context_get_options(resource context|resource stream)","Retrieve options for a stream/wrapper/context"],stream_context_get_params:["array stream_context_get_params(resource context|resource stream)","Get parameters of a file context"],stream_context_set_default:["resource stream_context_set_default(array options)","Set default file/stream context, returns the context as a resource"],stream_context_set_option:["bool stream_context_set_option(resource context|resource stream, string wrappername, string optionname, mixed value)","Set an option for a wrapper"],stream_context_set_params:["bool stream_context_set_params(resource context|resource stream, array options)","Set parameters for a file context"],stream_copy_to_stream:["long stream_copy_to_stream(resource source, resource dest [, long maxlen [, long pos]])","Reads up to maxlen bytes from source stream and writes them to dest stream."],stream_filter_append:["resource stream_filter_append(resource stream, string filtername[, int read_write[, string filterparams]])","Append a filter to a stream"],stream_filter_prepend:["resource stream_filter_prepend(resource stream, string filtername[, int read_write[, string filterparams]])","Prepend a filter to a stream"],stream_filter_register:["bool stream_filter_register(string filtername, string classname)","Registers a custom filter handler class"],stream_filter_remove:["bool stream_filter_remove(resource stream_filter)","Flushes any data in the filter's internal buffer, removes it from the chain, and frees the resource"],stream_get_contents:["string stream_get_contents(resource source [, long maxlen [, long offset]])","Reads all remaining bytes (or up to maxlen bytes) from a stream and returns them as a string."],stream_get_filters:["array stream_get_filters(void)","Returns a list of registered filters"],stream_get_line:["string stream_get_line(resource stream, int maxlen [, string ending])","Read up to maxlen bytes from a stream or until the ending string is found"],stream_get_meta_data:["array stream_get_meta_data(resource fp)","Retrieves header/meta data from streams/file pointers"],stream_get_transports:["array stream_get_transports()","Retrieves list of registered socket transports"],stream_get_wrappers:["array stream_get_wrappers()","Retrieves list of registered stream wrappers"],stream_is_local:["bool stream_is_local(resource stream|string url)",""],stream_resolve_include_path:["string stream_resolve_include_path(string filename)","Determine what file will be opened by calls to fopen() with a relative path"],stream_select:["int stream_select(array &read_streams, array &write_streams, array &except_streams, int tv_sec[, int tv_usec])","Runs the select() system call on the sets of streams with a timeout specified by tv_sec and tv_usec"],stream_set_blocking:["bool stream_set_blocking(resource socket, int mode)","Set blocking/non-blocking mode on a socket or stream"],stream_set_timeout:["bool stream_set_timeout(resource stream, int seconds [, int microseconds])","Set timeout on stream read to seconds + microseonds"],stream_set_write_buffer:["int stream_set_write_buffer(resource fp, int buffer)","Set file write buffer"],stream_socket_accept:["resource stream_socket_accept(resource serverstream, [ double timeout [, string &peername ]])","Accept a client connection from a server socket"],stream_socket_client:["resource stream_socket_client(string remoteaddress [, long &errcode [, string &errstring [, double timeout [, long flags [, resource context]]]]])","Open a client connection to a remote address"],stream_socket_enable_crypto:["int stream_socket_enable_crypto(resource stream, bool enable [, int cryptokind [, resource sessionstream]])","Enable or disable a specific kind of crypto on the stream"],stream_socket_get_name:["string stream_socket_get_name(resource stream, bool want_peer)","Returns either the locally bound or remote name for a socket stream"],stream_socket_pair:["array stream_socket_pair(int domain, int type, int protocol)","Creates a pair of connected, indistinguishable socket streams"],stream_socket_recvfrom:["string stream_socket_recvfrom(resource stream, long amount [, long flags [, string &remote_addr]])","Receives data from a socket stream"],stream_socket_sendto:["long stream_socket_sendto(resouce stream, string data [, long flags [, string target_addr]])","Send data to a socket stream. If target_addr is specified it must be in dotted quad (or [ipv6]) format"],stream_socket_server:["resource stream_socket_server(string localaddress [, long &errcode [, string &errstring [, long flags [, resource context]]]])","Create a server socket bound to localaddress"],stream_socket_shutdown:["int stream_socket_shutdown(resource stream, int how)","causes all or part of a full-duplex connection on the socket associated with stream to be shut down. If how is SHUT_RD, further receptions will be disallowed. If how is SHUT_WR, further transmissions will be disallowed. If how is SHUT_RDWR, further receptions and transmissions will be disallowed."],stream_supports_lock:["bool stream_supports_lock(resource stream)","Tells wether the stream supports locking through flock()."],stream_wrapper_register:["bool stream_wrapper_register(string protocol, string classname[, integer flags])","Registers a custom URL protocol handler class"],stream_wrapper_restore:["bool stream_wrapper_restore(string protocol)","Restore the original protocol handler, overriding if necessary"],stream_wrapper_unregister:["bool stream_wrapper_unregister(string protocol)","Unregister a wrapper for the life of the current request."],strftime:["string strftime(string format [, int timestamp])","Format a local time/date according to locale settings"],strip_tags:["string strip_tags(string str [, string allowable_tags])","Strips HTML and PHP tags from a string"],stripcslashes:["string stripcslashes(string str)","Strips backslashes from a string. Uses C-style conventions"],stripos:["int stripos(string haystack, string needle [, int offset])","Finds position of first occurrence of a string within another, case insensitive"],stripslashes:["string stripslashes(string str)","Strips backslashes from a string"],stristr:["string stristr(string haystack, string needle[, bool part])","Finds first occurrence of a string within another, case insensitive"],strlen:["int strlen(string str)","Get string length"],strnatcasecmp:["int strnatcasecmp(string s1, string s2)","Returns the result of case-insensitive string comparison using 'natural' algorithm"],strnatcmp:["int strnatcmp(string s1, string s2)","Returns the result of string comparison using 'natural' algorithm"],strncasecmp:["int strncasecmp(string str1, string str2, int len)","Binary safe string comparison"],strncmp:["int strncmp(string str1, string str2, int len)","Binary safe string comparison"],strpbrk:["array strpbrk(string haystack, string char_list)","Search a string for any of a set of characters"],strpos:["int strpos(string haystack, string needle [, int offset])","Finds position of first occurrence of a string within another"],strptime:["string strptime(string timestamp, string format)","Parse a time/date generated with strftime()"],strrchr:["string strrchr(string haystack, string needle)","Finds the last occurrence of a character in a string within another"],strrev:["string strrev(string str)","Reverse a string"],strripos:["int strripos(string haystack, string needle [, int offset])","Finds position of last occurrence of a string within another string"],strrpos:["int strrpos(string haystack, string needle [, int offset])","Finds position of last occurrence of a string within another string"],strspn:["int strspn(string str, string mask [, start [, len]])","Finds length of initial segment consisting entirely of characters found in mask. If start or/and length is provided works like strspn(substr($s,$start,$len),$good_chars)"],strstr:["string strstr(string haystack, string needle[, bool part])","Finds first occurrence of a string within another"],strtok:["string strtok([string str,] string token)","Tokenize a string"],strtolower:["string strtolower(string str)","Makes a string lowercase"],strtotime:["int strtotime(string time [, int now ])","Convert string representation of date and time to a timestamp"],strtoupper:["string strtoupper(string str)","Makes a string uppercase"],strtr:["string strtr(string str, string from[, string to])","Translates characters in str using given translation tables"],strval:["string strval(mixed var)","Get the string value of a variable"],substr:["string substr(string str, int start [, int length])","Returns part of a string"],substr_compare:["int substr_compare(string main_str, string str, int offset [, int length [, bool case_sensitivity]])","Binary safe optionally case insensitive comparison of 2 strings from an offset, up to length characters"],substr_count:["int substr_count(string haystack, string needle [, int offset [, int length]])","Returns the number of times a substring occurs in the string"],substr_replace:["mixed substr_replace(mixed str, mixed repl, mixed start [, mixed length])","Replaces part of a string with another string"],sybase_affected_rows:["int sybase_affected_rows([resource link_id])","Get number of affected rows in last query"],sybase_close:["bool sybase_close([resource link_id])","Close Sybase connection"],sybase_connect:["int sybase_connect([string host [, string user [, string password [, string charset [, string appname [, bool new]]]]]])","Open Sybase server connection"],sybase_data_seek:["bool sybase_data_seek(resource result, int offset)","Move internal row pointer"],sybase_deadlock_retry_count:["void sybase_deadlock_retry_count(int retry_count)","Sets deadlock retry count"],sybase_fetch_array:["array sybase_fetch_array(resource result)","Fetch row as array"],sybase_fetch_assoc:["array sybase_fetch_assoc(resource result)","Fetch row as array without numberic indices"],sybase_fetch_field:["object sybase_fetch_field(resource result [, int offset])","Get field information"],sybase_fetch_object:["object sybase_fetch_object(resource result [, mixed object])","Fetch row as object"],sybase_fetch_row:["array sybase_fetch_row(resource result)","Get row as enumerated array"],sybase_field_seek:["bool sybase_field_seek(resource result, int offset)","Set field offset"],sybase_free_result:["bool sybase_free_result(resource result)","Free result memory"],sybase_get_last_message:["string sybase_get_last_message(void)","Returns the last message from server (over min_message_severity)"],sybase_min_client_severity:["void sybase_min_client_severity(int severity)","Sets minimum client severity"],sybase_min_server_severity:["void sybase_min_server_severity(int severity)","Sets minimum server severity"],sybase_num_fields:["int sybase_num_fields(resource result)","Get number of fields in result"],sybase_num_rows:["int sybase_num_rows(resource result)","Get number of rows in result"],sybase_pconnect:["int sybase_pconnect([string host [, string user [, string password [, string charset [, string appname]]]]])","Open persistent Sybase connection"],sybase_query:["int sybase_query(string query [, resource link_id])","Send Sybase query"],sybase_result:["string sybase_result(resource result, int row, mixed field)","Get result data"],sybase_select_db:["bool sybase_select_db(string database [, resource link_id])","Select Sybase database"],sybase_set_message_handler:["bool sybase_set_message_handler(mixed error_func [, resource connection])","Set the error handler, to be called when a server message is raised. If error_func is NULL the handler will be deleted"],sybase_unbuffered_query:["int sybase_unbuffered_query(string query [, resource link_id])","Send Sybase query"],symlink:["int symlink(string target, string link)","Create a symbolic link"],sys_get_temp_dir:["string sys_get_temp_dir()","Returns directory path used for temporary files"],sys_getloadavg:["array sys_getloadavg()",""],syslog:["bool syslog(int priority, string message)","Generate a system log message"],system:["int system(string command [, int &return_value])","Execute an external program and display output"],tan:["float tan(float number)","Returns the tangent of the number in radians"],tanh:["float tanh(float number)","Returns the hyperbolic tangent of the number, defined as sinh(number)/cosh(number)"],tempnam:["string tempnam(string dir, string prefix)","Create a unique filename in a directory"],textdomain:["string textdomain(string domain)",'Set the textdomain to "domain". Returns the current domain'],tidy_access_count:["int tidy_access_count()","Returns the Number of Tidy accessibility warnings encountered for specified document."],tidy_clean_repair:["boolean tidy_clean_repair()","Execute configured cleanup and repair operations on parsed markup"],tidy_config_count:["int tidy_config_count()","Returns the Number of Tidy configuration errors encountered for specified document."],tidy_diagnose:["boolean tidy_diagnose()","Run configured diagnostics on parsed and repaired markup."],tidy_error_count:["int tidy_error_count()","Returns the Number of Tidy errors encountered for specified document."],tidy_get_body:["TidyNode tidy_get_body(resource tidy)","Returns a TidyNode Object starting from the tag of the tidy parse tree"],tidy_get_config:["array tidy_get_config()","Get current Tidy configuarion"],tidy_get_error_buffer:["string tidy_get_error_buffer([boolean detailed])","Return warnings and errors which occured parsing the specified document"],tidy_get_head:["TidyNode tidy_get_head()","Returns a TidyNode Object starting from the tag of the tidy parse tree"],tidy_get_html:["TidyNode tidy_get_html()","Returns a TidyNode Object starting from the tag of the tidy parse tree"],tidy_get_html_ver:["int tidy_get_html_ver()","Get the Detected HTML version for the specified document."],tidy_get_opt_doc:["string tidy_get_opt_doc(tidy resource, string optname)","Returns the documentation for the given option name"],tidy_get_output:["string tidy_get_output()","Return a string representing the parsed tidy markup"],tidy_get_release:["string tidy_get_release()","Get release date (version) for Tidy library"],tidy_get_root:["TidyNode tidy_get_root()","Returns a TidyNode Object representing the root of the tidy parse tree"],tidy_get_status:["int tidy_get_status()","Get status of specfied document."],tidy_getopt:["mixed tidy_getopt(string option)","Returns the value of the specified configuration option for the tidy document."],tidy_is_xhtml:["boolean tidy_is_xhtml()","Indicates if the document is a XHTML document."],tidy_is_xml:["boolean tidy_is_xml()","Indicates if the document is a generic (non HTML/XHTML) XML document."],tidy_parse_file:["boolean tidy_parse_file(string file [, mixed config_options [, string encoding [, bool use_include_path]]])","Parse markup in file or URI"],tidy_parse_string:["bool tidy_parse_string(string input [, mixed config_options [, string encoding]])","Parse a document stored in a string"],tidy_repair_file:["boolean tidy_repair_file(string filename [, mixed config_file [, string encoding [, bool use_include_path]]])","Repair a file using an optionally provided configuration file"],tidy_repair_string:["boolean tidy_repair_string(string data [, mixed config_file [, string encoding]])","Repair a string using an optionally provided configuration file"],tidy_warning_count:["int tidy_warning_count()","Returns the Number of Tidy warnings encountered for specified document."],time:["int time(void)","Return current UNIX timestamp"],time_nanosleep:["mixed time_nanosleep(long seconds, long nanoseconds)","Delay for a number of seconds and nano seconds"],time_sleep_until:["mixed time_sleep_until(float timestamp)","Make the script sleep until the specified time"],timezone_abbreviations_list:["array timezone_abbreviations_list()","Returns associative array containing dst, offset and the timezone name"],timezone_identifiers_list:["array timezone_identifiers_list([long what[, string country]])","Returns numerically index array with all timezone identifiers."],timezone_location_get:["array timezone_location_get()","Returns location information for a timezone, including country code, latitude/longitude and comments"],timezone_name_from_abbr:["string timezone_name_from_abbr(string abbr[, long gmtOffset[, long isdst]])","Returns the timezone name from abbrevation"],timezone_name_get:["string timezone_name_get(DateTimeZone object)","Returns the name of the timezone."],timezone_offset_get:["long timezone_offset_get(DateTimeZone object, DateTime object)","Returns the timezone offset."],timezone_open:["DateTimeZone timezone_open(string timezone)","Returns new DateTimeZone object"],timezone_transitions_get:["array timezone_transitions_get(DateTimeZone object [, long timestamp_begin [, long timestamp_end ]])","Returns numerically indexed array containing associative array for all transitions in the specified range for the timezone."],timezone_version_get:["array timezone_version_get()","Returns the Olson database version number."],tmpfile:["resource tmpfile(void)","Create a temporary file that will be deleted automatically after use"],token_get_all:["array token_get_all(string source)",""],token_name:["string token_name(int type)",""],touch:["bool touch(string filename [, int time [, int atime]])","Set modification time of file"],trigger_error:["void trigger_error(string messsage [, int error_type])","Generates a user-level error/warning/notice message"],trim:["string trim(string str [, string character_mask])","Strips whitespace from the beginning and end of a string"],uasort:["bool uasort(array array_arg, string cmp_function)","Sort an array with a user-defined comparison function and maintain index association"],ucfirst:["string ucfirst(string str)","Make a string's first character lowercase"],ucwords:["string ucwords(string str)","Uppercase the first character of every word in a string"],uksort:["bool uksort(array array_arg, string cmp_function)","Sort an array by keys using a user-defined comparison function"],umask:["int umask([int mask])","Return or change the umask"],uniqid:["string uniqid([string prefix [, bool more_entropy]])","Generates a unique ID"],unixtojd:["int unixtojd([int timestamp])","Convert UNIX timestamp to Julian Day"],unlink:["bool unlink(string filename[, context context])","Delete a file"],unpack:["array unpack(string format, string input)","Unpack binary string into named array elements according to format argument"],unregister_tick_function:["void unregister_tick_function(string function_name)","Unregisters a tick callback function"],unserialize:["mixed unserialize(string variable_representation)","Takes a string representation of variable and recreates it"],unset:["void unset (mixed var [, mixed var])","Unset a given variable"],urldecode:["string urldecode(string str)","Decodes URL-encoded string"],urlencode:["string urlencode(string str)","URL-encodes string"],usleep:["void usleep(int micro_seconds)","Delay for a given number of micro seconds"],usort:["bool usort(array array_arg, string cmp_function)","Sort an array by values using a user-defined comparison function"],utf8_decode:["string utf8_decode(string data)","Converts a UTF-8 encoded string to ISO-8859-1"],utf8_encode:["string utf8_encode(string data)","Encodes an ISO-8859-1 string to UTF-8"],var_dump:["void var_dump(mixed var)","Dumps a string representation of variable to output"],var_export:["mixed var_export(mixed var [, bool return])","Outputs or returns a string representation of a variable"],variant_abs:["mixed variant_abs(mixed left)","Returns the absolute value of a variant"],variant_add:["mixed variant_add(mixed left, mixed right)",'"Adds" two variant values together and returns the result'],variant_and:["mixed variant_and(mixed left, mixed right)","performs a bitwise AND operation between two variants and returns the result"],variant_cast:["object variant_cast(object variant, int type)","Convert a variant into a new variant object of another type"],variant_cat:["mixed variant_cat(mixed left, mixed right)","concatenates two variant values together and returns the result"],variant_cmp:["int variant_cmp(mixed left, mixed right [, int lcid [, int flags]])","Compares two variants"],variant_date_from_timestamp:["object variant_date_from_timestamp(int timestamp)","Returns a variant date representation of a unix timestamp"],variant_date_to_timestamp:["int variant_date_to_timestamp(object variant)","Converts a variant date/time value to unix timestamp"],variant_div:["mixed variant_div(mixed left, mixed right)","Returns the result from dividing two variants"],variant_eqv:["mixed variant_eqv(mixed left, mixed right)","Performs a bitwise equivalence on two variants"],variant_fix:["mixed variant_fix(mixed left)","Returns the integer part ? of a variant"],variant_get_type:["int variant_get_type(object variant)","Returns the VT_XXX type code for a variant"],variant_idiv:["mixed variant_idiv(mixed left, mixed right)","Converts variants to integers and then returns the result from dividing them"],variant_imp:["mixed variant_imp(mixed left, mixed right)","Performs a bitwise implication on two variants"],variant_int:["mixed variant_int(mixed left)","Returns the integer portion of a variant"],variant_mod:["mixed variant_mod(mixed left, mixed right)","Divides two variants and returns only the remainder"],variant_mul:["mixed variant_mul(mixed left, mixed right)","multiplies the values of the two variants and returns the result"],variant_neg:["mixed variant_neg(mixed left)","Performs logical negation on a variant"],variant_not:["mixed variant_not(mixed left)","Performs bitwise not negation on a variant"],variant_or:["mixed variant_or(mixed left, mixed right)","Performs a logical disjunction on two variants"],variant_pow:["mixed variant_pow(mixed left, mixed right)","Returns the result of performing the power function with two variants"],variant_round:["mixed variant_round(mixed left, int decimals)","Rounds a variant to the specified number of decimal places"],variant_set:["void variant_set(object variant, mixed value)","Assigns a new value for a variant object"],variant_set_type:["void variant_set_type(object variant, int type)",'Convert a variant into another type. Variant is modified "in-place"'],variant_sub:["mixed variant_sub(mixed left, mixed right)","subtracts the value of the right variant from the left variant value and returns the result"],variant_xor:["mixed variant_xor(mixed left, mixed right)","Performs a logical exclusion on two variants"],version_compare:["int version_compare(string ver1, string ver2 [, string oper])",'Compares two "PHP-standardized" version number strings'],vfprintf:["int vfprintf(resource stream, string format, array args)","Output a formatted string into a stream"],virtual:["bool virtual(string filename)","Perform an Apache sub-request"],vprintf:["int vprintf(string format, array args)","Output a formatted string"],vsprintf:["string vsprintf(string format, array args)","Return a formatted string"],wddx_add_vars:["int wddx_add_vars(resource packet_id, mixed var_names [, mixed ...])","Serializes given variables and adds them to packet given by packet_id"],wddx_deserialize:["mixed wddx_deserialize(mixed packet)","Deserializes given packet and returns a PHP value"],wddx_packet_end:["string wddx_packet_end(resource packet_id)","Ends specified WDDX packet and returns the string containing the packet"],wddx_packet_start:["resource wddx_packet_start([string comment])","Starts a WDDX packet with optional comment and returns the packet id"],wddx_serialize_value:["string wddx_serialize_value(mixed var [, string comment])","Creates a new packet and serializes the given value"],wddx_serialize_vars:["string wddx_serialize_vars(mixed var_name [, mixed ...])","Creates a new packet and serializes given variables into a struct"],wordwrap:["string wordwrap(string str [, int width [, string break [, boolean cut]]])","Wraps buffer to selected number of characters using string break char"],xml_error_string:["string xml_error_string(int code)","Get XML parser error string"],xml_get_current_byte_index:["int xml_get_current_byte_index(resource parser)","Get current byte index for an XML parser"],xml_get_current_column_number:["int xml_get_current_column_number(resource parser)","Get current column number for an XML parser"],xml_get_current_line_number:["int xml_get_current_line_number(resource parser)","Get current line number for an XML parser"],xml_get_error_code:["int xml_get_error_code(resource parser)","Get XML parser error code"],xml_parse:["int xml_parse(resource parser, string data [, int isFinal])","Start parsing an XML document"],xml_parse_into_struct:["int xml_parse_into_struct(resource parser, string data, array &values [, array &index ])","Parsing a XML document"],xml_parser_create:["resource xml_parser_create([string encoding])","Create an XML parser"],xml_parser_create_ns:["resource xml_parser_create_ns([string encoding [, string sep]])","Create an XML parser"],xml_parser_free:["int xml_parser_free(resource parser)","Free an XML parser"],xml_parser_get_option:["int xml_parser_get_option(resource parser, int option)","Get options from an XML parser"],xml_parser_set_option:["int xml_parser_set_option(resource parser, int option, mixed value)","Set options in an XML parser"],xml_set_character_data_handler:["int xml_set_character_data_handler(resource parser, string hdl)","Set up character data handler"],xml_set_default_handler:["int xml_set_default_handler(resource parser, string hdl)","Set up default handler"],xml_set_element_handler:["int xml_set_element_handler(resource parser, string shdl, string ehdl)","Set up start and end element handlers"],xml_set_end_namespace_decl_handler:["int xml_set_end_namespace_decl_handler(resource parser, string hdl)","Set up character data handler"],xml_set_external_entity_ref_handler:["int xml_set_external_entity_ref_handler(resource parser, string hdl)","Set up external entity reference handler"],xml_set_notation_decl_handler:["int xml_set_notation_decl_handler(resource parser, string hdl)","Set up notation declaration handler"],xml_set_object:["int xml_set_object(resource parser, object &obj)","Set up object which should be used for callbacks"],xml_set_processing_instruction_handler:["int xml_set_processing_instruction_handler(resource parser, string hdl)","Set up processing instruction (PI) handler"],xml_set_start_namespace_decl_handler:["int xml_set_start_namespace_decl_handler(resource parser, string hdl)","Set up character data handler"],xml_set_unparsed_entity_decl_handler:["int xml_set_unparsed_entity_decl_handler(resource parser, string hdl)","Set up unparsed entity declaration handler"],xmlrpc_decode:["array xmlrpc_decode(string xml [, string encoding])","Decodes XML into native PHP types"],xmlrpc_decode_request:["array xmlrpc_decode_request(string xml, string& method [, string encoding])","Decodes XML into native PHP types"],xmlrpc_encode:["string xmlrpc_encode(mixed value)","Generates XML for a PHP value"],xmlrpc_encode_request:["string xmlrpc_encode_request(string method, mixed params [, array output_options])","Generates XML for a method request"],xmlrpc_get_type:["string xmlrpc_get_type(mixed value)","Gets xmlrpc type for a PHP value. Especially useful for base64 and datetime strings"],xmlrpc_is_fault:["bool xmlrpc_is_fault(array)","Determines if an array value represents an XMLRPC fault."],xmlrpc_parse_method_descriptions:["array xmlrpc_parse_method_descriptions(string xml)","Decodes XML into a list of method descriptions"],xmlrpc_server_add_introspection_data:["int xmlrpc_server_add_introspection_data(resource server, array desc)","Adds introspection documentation"],xmlrpc_server_call_method:["mixed xmlrpc_server_call_method(resource server, string xml, mixed user_data [, array output_options])","Parses XML requests and call methods"],xmlrpc_server_create:["resource xmlrpc_server_create(void)","Creates an xmlrpc server"],xmlrpc_server_destroy:["int xmlrpc_server_destroy(resource server)","Destroys server resources"],xmlrpc_server_register_introspection_callback:["bool xmlrpc_server_register_introspection_callback(resource server, string function)","Register a PHP function to generate documentation"],xmlrpc_server_register_method:["bool xmlrpc_server_register_method(resource server, string method_name, string function)","Register a PHP function to handle method matching method_name"],xmlrpc_set_type:["bool xmlrpc_set_type(string value, string type)","Sets xmlrpc type, base64 or datetime, for a PHP string value"],xmlwriter_end_attribute:["bool xmlwriter_end_attribute(resource xmlwriter)","End attribute - returns FALSE on error"],xmlwriter_end_cdata:["bool xmlwriter_end_cdata(resource xmlwriter)","End current CDATA - returns FALSE on error"],xmlwriter_end_comment:["bool xmlwriter_end_comment(resource xmlwriter)","Create end comment - returns FALSE on error"],xmlwriter_end_document:["bool xmlwriter_end_document(resource xmlwriter)","End current document - returns FALSE on error"],xmlwriter_end_dtd:["bool xmlwriter_end_dtd(resource xmlwriter)","End current DTD - returns FALSE on error"],xmlwriter_end_dtd_attlist:["bool xmlwriter_end_dtd_attlist(resource xmlwriter)","End current DTD AttList - returns FALSE on error"],xmlwriter_end_dtd_element:["bool xmlwriter_end_dtd_element(resource xmlwriter)","End current DTD element - returns FALSE on error"],xmlwriter_end_dtd_entity:["bool xmlwriter_end_dtd_entity(resource xmlwriter)","End current DTD Entity - returns FALSE on error"],xmlwriter_end_element:["bool xmlwriter_end_element(resource xmlwriter)","End current element - returns FALSE on error"],xmlwriter_end_pi:["bool xmlwriter_end_pi(resource xmlwriter)","End current PI - returns FALSE on error"],xmlwriter_flush:["mixed xmlwriter_flush(resource xmlwriter [,bool empty])","Output current buffer"],xmlwriter_full_end_element:["bool xmlwriter_full_end_element(resource xmlwriter)","End current element - returns FALSE on error"],xmlwriter_open_memory:["resource xmlwriter_open_memory()","Create new xmlwriter using memory for string output"],xmlwriter_open_uri:["resource xmlwriter_open_uri(resource xmlwriter, string source)","Create new xmlwriter using source uri for output"],xmlwriter_output_memory:["string xmlwriter_output_memory(resource xmlwriter [,bool flush])","Output current buffer as string"],xmlwriter_set_indent:["bool xmlwriter_set_indent(resource xmlwriter, bool indent)","Toggle indentation on/off - returns FALSE on error"],xmlwriter_set_indent_string:["bool xmlwriter_set_indent_string(resource xmlwriter, string indentString)","Set string used for indenting - returns FALSE on error"],xmlwriter_start_attribute:["bool xmlwriter_start_attribute(resource xmlwriter, string name)","Create start attribute - returns FALSE on error"],xmlwriter_start_attribute_ns:["bool xmlwriter_start_attribute_ns(resource xmlwriter, string prefix, string name, string uri)","Create start namespaced attribute - returns FALSE on error"],xmlwriter_start_cdata:["bool xmlwriter_start_cdata(resource xmlwriter)","Create start CDATA tag - returns FALSE on error"],xmlwriter_start_comment:["bool xmlwriter_start_comment(resource xmlwriter)","Create start comment - returns FALSE on error"],xmlwriter_start_document:["bool xmlwriter_start_document(resource xmlwriter, string version, string encoding, string standalone)","Create document tag - returns FALSE on error"],xmlwriter_start_dtd:["bool xmlwriter_start_dtd(resource xmlwriter, string name, string pubid, string sysid)","Create start DTD tag - returns FALSE on error"],xmlwriter_start_dtd_attlist:["bool xmlwriter_start_dtd_attlist(resource xmlwriter, string name)","Create start DTD AttList - returns FALSE on error"],xmlwriter_start_dtd_element:["bool xmlwriter_start_dtd_element(resource xmlwriter, string name)","Create start DTD element - returns FALSE on error"],xmlwriter_start_dtd_entity:["bool xmlwriter_start_dtd_entity(resource xmlwriter, string name, bool isparam)","Create start DTD Entity - returns FALSE on error"],xmlwriter_start_element:["bool xmlwriter_start_element(resource xmlwriter, string name)","Create start element tag - returns FALSE on error"],xmlwriter_start_element_ns:["bool xmlwriter_start_element_ns(resource xmlwriter, string prefix, string name, string uri)","Create start namespaced element tag - returns FALSE on error"],xmlwriter_start_pi:["bool xmlwriter_start_pi(resource xmlwriter, string target)","Create start PI tag - returns FALSE on error"],xmlwriter_text:["bool xmlwriter_text(resource xmlwriter, string content)","Write text - returns FALSE on error"],xmlwriter_write_attribute:["bool xmlwriter_write_attribute(resource xmlwriter, string name, string content)","Write full attribute - returns FALSE on error"],xmlwriter_write_attribute_ns:["bool xmlwriter_write_attribute_ns(resource xmlwriter, string prefix, string name, string uri, string content)","Write full namespaced attribute - returns FALSE on error"],xmlwriter_write_cdata:["bool xmlwriter_write_cdata(resource xmlwriter, string content)","Write full CDATA tag - returns FALSE on error"],xmlwriter_write_comment:["bool xmlwriter_write_comment(resource xmlwriter, string content)","Write full comment tag - returns FALSE on error"],xmlwriter_write_dtd:["bool xmlwriter_write_dtd(resource xmlwriter, string name, string pubid, string sysid, string subset)","Write full DTD tag - returns FALSE on error"],xmlwriter_write_dtd_attlist:["bool xmlwriter_write_dtd_attlist(resource xmlwriter, string name, string content)","Write full DTD AttList tag - returns FALSE on error"],xmlwriter_write_dtd_element:["bool xmlwriter_write_dtd_element(resource xmlwriter, string name, string content)","Write full DTD element tag - returns FALSE on error"],xmlwriter_write_dtd_entity:["bool xmlwriter_write_dtd_entity(resource xmlwriter, string name, string content [, int pe [, string pubid [, string sysid [, string ndataid]]]])","Write full DTD Entity tag - returns FALSE on error"],xmlwriter_write_element:["bool xmlwriter_write_element(resource xmlwriter, string name[, string content])","Write full element tag - returns FALSE on error"],xmlwriter_write_element_ns:["bool xmlwriter_write_element_ns(resource xmlwriter, string prefix, string name, string uri[, string content])","Write full namesapced element tag - returns FALSE on error"],xmlwriter_write_pi:["bool xmlwriter_write_pi(resource xmlwriter, string target, string content)","Write full PI tag - returns FALSE on error"],xmlwriter_write_raw:["bool xmlwriter_write_raw(resource xmlwriter, string content)","Write text - returns FALSE on error"],xsl_xsltprocessor_get_parameter:["string xsl_xsltprocessor_get_parameter(string namespace, string name);",""],xsl_xsltprocessor_has_exslt_support:["bool xsl_xsltprocessor_has_exslt_support();",""],xsl_xsltprocessor_import_stylesheet:["void xsl_xsltprocessor_import_stylesheet(domdocument doc);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html# Since:"],xsl_xsltprocessor_register_php_functions:["void xsl_xsltprocessor_register_php_functions([mixed $restrict]);",""],xsl_xsltprocessor_remove_parameter:["bool xsl_xsltprocessor_remove_parameter(string namespace, string name);",""],xsl_xsltprocessor_set_parameter:["bool xsl_xsltprocessor_set_parameter(string namespace, mixed name [, string value]);",""],xsl_xsltprocessor_set_profiling:["bool xsl_xsltprocessor_set_profiling(string filename) */",'PHP_FUNCTION(xsl_xsltprocessor_set_profiling) { zval *id; xsl_object *intern; char *filename = NULL; int filename_len; DOM_GET_THIS(id); if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "s!", &filename, &filename_len) == SUCCESS) { intern = (xsl_object *)zend_object_store_get_object(id TSRMLS_CC); if (intern->profiling) { efree(intern->profiling); } if (filename != NULL) { intern->profiling = estrndup(filename,filename_len); } else { intern->profiling = NULL; } RETURN_TRUE; } else { WRONG_PARAM_COUNT; } } /* }}} end xsl_xsltprocessor_set_profiling'],xsl_xsltprocessor_transform_to_doc:["domdocument xsl_xsltprocessor_transform_to_doc(domnode doc);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html# Since:"],xsl_xsltprocessor_transform_to_uri:["int xsl_xsltprocessor_transform_to_uri(domdocument doc, string uri);",""],xsl_xsltprocessor_transform_to_xml:["string xsl_xsltprocessor_transform_to_xml(domdocument doc);",""],zend_logo_guid:["string zend_logo_guid(void)","Return the special ID used to request the Zend logo in phpinfo screens"],zend_version:["string zend_version(void)","Get the version of the Zend Engine"],zip_close:["void zip_close(resource zip)","Close a Zip archive"],zip_entry_close:["void zip_entry_close(resource zip_ent)","Close a zip entry"],zip_entry_compressedsize:["int zip_entry_compressedsize(resource zip_entry)","Return the compressed size of a ZZip entry"],zip_entry_compressionmethod:["string zip_entry_compressionmethod(resource zip_entry)","Return a string containing the compression method used on a particular entry"],zip_entry_filesize:["int zip_entry_filesize(resource zip_entry)","Return the actual filesize of a ZZip entry"],zip_entry_name:["string zip_entry_name(resource zip_entry)","Return the name given a ZZip entry"],zip_entry_open:["bool zip_entry_open(resource zip_dp, resource zip_entry [, string mode])","Open a Zip File, pointed by the resource entry"],zip_entry_read:["mixed zip_entry_read(resource zip_entry [, int len])","Read from an open directory entry"],zip_open:["resource zip_open(string filename)","Create new zip using source uri for output"],zip_read:["resource zip_read(resource zip)","Returns the next file in the archive"],zlib_get_coding_type:["string zlib_get_coding_type(void)","Returns the coding type used for output compression"]},i={$_COOKIE:{type:"array"},$_ENV:{type:"array"},$_FILES:{type:"array"},$_GET:{type:"array"},$_POST:{type:"array"},$_REQUEST:{type:"array"},$_SERVER:{type:"array",value:{DOCUMENT_ROOT:1,GATEWAY_INTERFACE:1,HTTP_ACCEPT:1,HTTP_ACCEPT_CHARSET:1,HTTP_ACCEPT_ENCODING:1,HTTP_ACCEPT_LANGUAGE:1,HTTP_CONNECTION:1,HTTP_HOST:1,HTTP_REFERER:1,HTTP_USER_AGENT:1,PATH_TRANSLATED:1,PHP_SELF:1,QUERY_STRING:1,REMOTE_ADDR:1,REMOTE_PORT:1,REQUEST_METHOD:1,REQUEST_URI:1,SCRIPT_FILENAME:1,SCRIPT_NAME:1,SERVER_ADMIN:1,SERVER_NAME:1,SERVER_PORT:1,SERVER_PROTOCOL:1,SERVER_SIGNATURE:1,SERVER_SOFTWARE:1}},$_SESSION:{type:"array"},$GLOBALS:{type:"array"}},o=function(){};(function(){this.getCompletions=function(e,t,n,r){var i=t.getTokenAt(n.row,n.column);if(!i)return[];if(i.type==="identifier")return this.getFunctionCompletions(e,t,n,r);if(s(i,"variable"))return this.getVariableCompletions(e,t,n,r);var o=t.getLine(n.row).substr(0,n.column);return i.type==="string"&&/(\$[\w]*)\[["']([^'"]*)$/i.test(o)?this.getArrayKeyCompletions(e,t,n,r):[]},this.getFunctionCompletions=function(e,t,n,i){var s=Object.keys(r);return s.map(function(e){return{caption:e,snippet:e+"($0)",meta:"php function",score:Number.MAX_VALUE,docHTML:r[e][1]}})},this.getVariableCompletions=function(e,t,n,r){var s=Object.keys(i);return s.map(function(e){return{caption:e,value:e,meta:"php variable",score:Number.MAX_VALUE}})},this.getArrayKeyCompletions=function(e,t,n,r){var s=t.getLine(n.row).substr(0,n.column),o=s.match(/(\$[\w]*)\[["']([^'"]*)$/i)[1];if(!i[o])return[];var u=[];return i[o].type==="array"&&i[o].value&&(u=Object.keys(i[o].value)),u.map(function(e){return{caption:e,value:e,meta:"php array key",score:Number.MAX_VALUE}})}}).call(o.prototype),t.PhpCompletions=o}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/javascript",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/javascript_highlight_rules","ace/mode/matching_brace_outdent","ace/worker/worker_client","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./javascript_highlight_rules").JavaScriptHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../worker/worker_client").WorkerClient,a=e("./behaviour/cstyle").CstyleBehaviour,f=e("./folding/cstyle").FoldMode,l=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new a,this.foldingRules=new f};r.inherits(l,i),function(){this.lineCommentStart="//",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens,o=i.state;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"||e=="no_regex"){var u=t.match(/^.*(?:\bcase\b.*:|[\{\(\[])\s*$/);u&&(r+=n)}else if(e=="doc-start"){if(o=="start"||o=="no_regex")return"";var u=t.match(/^\s*(\/?)\*/);u&&(u[1]&&(r+=" "),r+="* ")}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.createWorker=function(e){var t=new u(["ace"],"ace/mode/javascript_worker","JavaScriptWorker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/javascript"}.call(l.prototype),t.Mode=l}),define("ace/mode/css_completions",["require","exports","module"],function(e,t,n){"use strict";var r={background:{"#$0":1},"background-color":{"#$0":1,transparent:1,fixed:1},"background-image":{"url('/$0')":1},"background-repeat":{repeat:1,"repeat-x":1,"repeat-y":1,"no-repeat":1,inherit:1},"background-position":{bottom:2,center:2,left:2,right:2,top:2,inherit:2},"background-attachment":{scroll:1,fixed:1},"background-size":{cover:1,contain:1},"background-clip":{"border-box":1,"padding-box":1,"content-box":1},"background-origin":{"border-box":1,"padding-box":1,"content-box":1},border:{"solid $0":1,"dashed $0":1,"dotted $0":1,"#$0":1},"border-color":{"#$0":1},"border-style":{solid:2,dashed:2,dotted:2,"double":2,groove:2,hidden:2,inherit:2,inset:2,none:2,outset:2,ridged:2},"border-collapse":{collapse:1,separate:1},bottom:{px:1,em:1,"%":1},clear:{left:1,right:1,both:1,none:1},color:{"#$0":1,"rgb(#$00,0,0)":1},cursor:{"default":1,pointer:1,move:1,text:1,wait:1,help:1,progress:1,"n-resize":1,"ne-resize":1,"e-resize":1,"se-resize":1,"s-resize":1,"sw-resize":1,"w-resize":1,"nw-resize":1},display:{none:1,block:1,inline:1,"inline-block":1,"table-cell":1},"empty-cells":{show:1,hide:1},"float":{left:1,right:1,none:1},"font-family":{Arial:2,"Comic Sans MS":2,Consolas:2,"Courier New":2,Courier:2,Georgia:2,Monospace:2,"Sans-Serif":2,"Segoe UI":2,Tahoma:2,"Times New Roman":2,"Trebuchet MS":2,Verdana:1},"font-size":{px:1,em:1,"%":1},"font-weight":{bold:1,normal:1},"font-style":{italic:1,normal:1},"font-variant":{normal:1,"small-caps":1},height:{px:1,em:1,"%":1},left:{px:1,em:1,"%":1},"letter-spacing":{normal:1},"line-height":{normal:1},"list-style-type":{none:1,disc:1,circle:1,square:1,decimal:1,"decimal-leading-zero":1,"lower-roman":1,"upper-roman":1,"lower-greek":1,"lower-latin":1,"upper-latin":1,georgian:1,"lower-alpha":1,"upper-alpha":1},margin:{px:1,em:1,"%":1},"margin-right":{px:1,em:1,"%":1},"margin-left":{px:1,em:1,"%":1},"margin-top":{px:1,em:1,"%":1},"margin-bottom":{px:1,em:1,"%":1},"max-height":{px:1,em:1,"%":1},"max-width":{px:1,em:1,"%":1},"min-height":{px:1,em:1,"%":1},"min-width":{px:1,em:1,"%":1},overflow:{hidden:1,visible:1,auto:1,scroll:1},"overflow-x":{hidden:1,visible:1,auto:1,scroll:1},"overflow-y":{hidden:1,visible:1,auto:1,scroll:1},padding:{px:1,em:1,"%":1},"padding-top":{px:1,em:1,"%":1},"padding-right":{px:1,em:1,"%":1},"padding-bottom":{px:1,em:1,"%":1},"padding-left":{px:1,em:1,"%":1},"page-break-after":{auto:1,always:1,avoid:1,left:1,right:1},"page-break-before":{auto:1,always:1,avoid:1,left:1,right:1},position:{absolute:1,relative:1,fixed:1,"static":1},right:{px:1,em:1,"%":1},"table-layout":{fixed:1,auto:1},"text-decoration":{none:1,underline:1,"line-through":1,blink:1},"text-align":{left:1,right:1,center:1,justify:1},"text-transform":{capitalize:1,uppercase:1,lowercase:1,none:1},top:{px:1,em:1,"%":1},"vertical-align":{top:1,bottom:1},visibility:{hidden:1,visible:1},"white-space":{nowrap:1,normal:1,pre:1,"pre-line":1,"pre-wrap":1},width:{px:1,em:1,"%":1},"word-spacing":{normal:1},filter:{"alpha(opacity=$0100)":1},"text-shadow":{"$02px 2px 2px #777":1},"text-overflow":{"ellipsis-word":1,clip:1,ellipsis:1},"-moz-border-radius":1,"-moz-border-radius-topright":1,"-moz-border-radius-bottomright":1,"-moz-border-radius-topleft":1,"-moz-border-radius-bottomleft":1,"-webkit-border-radius":1,"-webkit-border-top-right-radius":1,"-webkit-border-top-left-radius":1,"-webkit-border-bottom-right-radius":1,"-webkit-border-bottom-left-radius":1,"-moz-box-shadow":1,"-webkit-box-shadow":1,transform:{"rotate($00deg)":1,"skew($00deg)":1},"-moz-transform":{"rotate($00deg)":1,"skew($00deg)":1},"-webkit-transform":{"rotate($00deg)":1,"skew($00deg)":1}},i=function(){};(function(){this.completionsDefined=!1,this.defineCompletions=function(){if(document){var e=document.createElement("c").style;for(var t in e){if(typeof e[t]!="string")continue;var n=t.replace(/[A-Z]/g,function(e){return"-"+e.toLowerCase()});r.hasOwnProperty(n)||(r[n]=1)}}this.completionsDefined=!0},this.getCompletions=function(e,t,n,r){this.completionsDefined||this.defineCompletions();var i=t.getTokenAt(n.row,n.column);if(!i)return[];if(e==="ruleset"){var s=t.getLine(n.row).substr(0,n.column);return/:[^;]+$/.test(s)?(/([\w\-]+):[^:]*$/.test(s),this.getPropertyValueCompletions(e,t,n,r)):this.getPropertyCompletions(e,t,n,r)}return[]},this.getPropertyCompletions=function(e,t,n,i){var s=Object.keys(r);return s.map(function(e){return{caption:e,snippet:e+": $0",meta:"property",score:Number.MAX_VALUE}})},this.getPropertyValueCompletions=function(e,t,n,i){var s=t.getLine(n.row).substr(0,n.column),o=(/([\w\-]+):[^:]*$/.exec(s)||{})[1];if(!o)return[];var u=[];return o in r&&typeof r[o]=="object"&&(u=Object.keys(r[o])),u.map(function(e){return{caption:e,snippet:e,meta:"property value",score:Number.MAX_VALUE}})}}).call(i.prototype),t.CssCompletions=i}),define("ace/mode/behaviour/css",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/mode/behaviour/cstyle","ace/token_iterator"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("./cstyle").CstyleBehaviour,o=e("../../token_iterator").TokenIterator,u=function(){this.inherit(s),this.add("colon","insertion",function(e,t,n,r,i){if(i===":"){var s=n.getCursorPosition(),u=new o(r,s.row,s.column),a=u.getCurrentToken();a&&a.value.match(/\s+/)&&(a=u.stepBackward());if(a&&a.type==="support.type"){var f=r.doc.getLine(s.row),l=f.substring(s.column,s.column+1);if(l===":")return{text:"",selection:[1,1]};if(!f.substring(s.column).match(/^\s*;/))return{text:":;",selection:[1,1]}}}}),this.add("colon","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s===":"){var u=n.getCursorPosition(),a=new o(r,u.row,u.column),f=a.getCurrentToken();f&&f.value.match(/\s+/)&&(f=a.stepBackward());if(f&&f.type==="support.type"){var l=r.doc.getLine(i.start.row),c=l.substring(i.end.column,i.end.column+1);if(c===";")return i.end.column++,i}}}),this.add("semicolon","insertion",function(e,t,n,r,i){if(i===";"){var s=n.getCursorPosition(),o=r.doc.getLine(s.row),u=o.substring(s.column,s.column+1);if(u===";")return{text:"",selection:[1,1]}}})};r.inherits(u,s),t.CssBehaviour=u}),define("ace/mode/css",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/css_highlight_rules","ace/mode/matching_brace_outdent","ace/worker/worker_client","ace/mode/css_completions","ace/mode/behaviour/css","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./css_highlight_rules").CssHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../worker/worker_client").WorkerClient,a=e("./css_completions").CssCompletions,f=e("./behaviour/css").CssBehaviour,l=e("./folding/cstyle").FoldMode,c=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new f,this.$completer=new a,this.foldingRules=new l};r.inherits(c,i),function(){this.foldingRules="cStyle",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e).tokens;if(i.length&&i[i.length-1].type=="comment")return r;var s=t.match(/^.*\{\s*$/);return s&&(r+=n),r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.getCompletions=function(e,t,n,r){return this.$completer.getCompletions(e,t,n,r)},this.createWorker=function(e){var t=new u(["ace"],"ace/mode/css_worker","Worker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/css"}.call(c.prototype),t.Mode=c}),define("ace/mode/behaviour/xml",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";function u(e,t){return e.type.lastIndexOf(t+".xml")>-1}var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),a=function(){this.add("string_dquotes","insertion",function(e,t,n,r,i){if(i=='"'||i=="'"){var o=i,a=r.doc.getTextRange(n.getSelectionRange());if(a!==""&&a!=="'"&&a!='"'&&n.getWrapBehavioursEnabled())return{text:o+a+o,selection:!1};var f=n.getCursorPosition(),l=r.doc.getLine(f.row),c=l.substring(f.column,f.column+1),h=new s(r,f.row,f.column),p=h.getCurrentToken();if(c==o&&(u(p,"attribute-value")||u(p,"string")))return{text:"",selection:[1,1]};p||(p=h.stepBackward());if(!p)return;while(u(p,"tag-whitespace")||u(p,"whitespace"))p=h.stepBackward();var d=!c||c.match(/\s/);if(u(p,"attribute-equals")&&(d||c==">")||u(p,"decl-attribute-equals")&&(d||c=="?"))return{text:o+o,selection:[1,1]}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&(s=='"'||s=="'")){var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==s)return i.end.column++,i}}),this.add("autoclosing","insertion",function(e,t,n,r,i){if(i==">"){var o=n.getSelectionRange().start,a=new s(r,o.row,o.column),f=a.getCurrentToken()||a.stepBackward();if(!f||!(u(f,"tag-name")||u(f,"tag-whitespace")||u(f,"attribute-name")||u(f,"attribute-equals")||u(f,"attribute-value")))return;if(u(f,"reference.attribute-value"))return;if(u(f,"attribute-value")){var l=f.value.charAt(0);if(l=='"'||l=="'"){var c=f.value.charAt(f.value.length-1),h=a.getCurrentTokenColumn()+f.value.length;if(h>o.column||h==o.column&&l!=c)return}}while(!u(f,"tag-name")){f=a.stepBackward();if(f.value=="<"){f=a.stepForward();break}}var p=a.getCurrentTokenRow(),d=a.getCurrentTokenColumn();if(u(a.stepBackward(),"end-tag-open"))return;var v=f.value;p==o.row&&(v=v.substring(0,o.column-d));if(this.voidElements.hasOwnProperty(v.toLowerCase()))return;return{text:">",selection:[1,1]}}}),this.add("autoindent","insertion",function(e,t,n,r,i){if(i=="\n"){var o=n.getCursorPosition(),u=r.getLine(o.row),a=new s(r,o.row,o.column),f=a.getCurrentToken();if(f&&f.type.indexOf("tag-close")!==-1){if(f.value=="/>")return;while(f&&f.type.indexOf("tag-name")===-1)f=a.stepBackward();if(!f)return;var l=f.value,c=a.getCurrentTokenRow();f=a.stepBackward();if(!f||f.type.indexOf("end-tag")!==-1)return;if(this.voidElements&&!this.voidElements[l]){var h=r.getTokenAt(o.row,o.column+1),u=r.getLine(c),p=this.$getIndent(u),d=p+r.getTabString();return h&&h.value==="-1}var r=e("../../lib/oop"),i=e("../../lib/lang"),s=e("../../range").Range,o=e("./fold_mode").FoldMode,u=e("../../token_iterator").TokenIterator,a=t.FoldMode=function(e,t){o.call(this),this.voidElements=e||{},this.optionalEndTags=r.mixin({},this.voidElements),t&&r.mixin(this.optionalEndTags,t)};r.inherits(a,o);var f=function(){this.tagName="",this.closing=!1,this.selfClosing=!1,this.start={row:0,column:0},this.end={row:0,column:0}};(function(){this.getFoldWidget=function(e,t,n){var r=this._getFirstTagInLine(e,n);return r?r.closing||!r.tagName&&r.selfClosing?t=="markbeginend"?"end":"":!r.tagName||r.selfClosing||this.voidElements.hasOwnProperty(r.tagName.toLowerCase())?"":this._findEndTagInLine(e,n,r.tagName,r.end.column)?"":"start":""},this._getFirstTagInLine=function(e,t){var n=e.getTokens(t),r=new f;for(var i=0;i";break}}return r}if(l(s,"tag-close"))return r.selfClosing=s.value=="/>",r;r.start.column+=s.value.length}return null},this._findEndTagInLine=function(e,t,n,r){var i=e.getTokens(t),s=0;for(var o=0;o",n.end.row=e.getCurrentTokenRow(),n.end.column=e.getCurrentTokenColumn()+t.value.length,e.stepForward(),n;while(t=e.stepForward());return null},this._readTagBackward=function(e){var t=e.getCurrentToken();if(!t)return null;var n=new f;do{if(l(t,"tag-open"))return n.closing=l(t,"end-tag-open"),n.start.row=e.getCurrentTokenRow(),n.start.column=e.getCurrentTokenColumn(),e.stepBackward(),n;l(t,"tag-name")?n.tagName=t.value:l(t,"tag-close")&&(n.selfClosing=t.value=="/>",n.end.row=e.getCurrentTokenRow(),n.end.column=e.getCurrentTokenColumn()+t.value.length)}while(t=e.stepBackward());return null},this._pop=function(e,t){while(e.length){var n=e[e.length-1];if(!t||n.tagName==t.tagName)return e.pop();if(this.optionalEndTags.hasOwnProperty(n.tagName)){e.pop();continue}return null}},this.getFoldWidgetRange=function(e,t,n){var r=this._getFirstTagInLine(e,n);if(!r)return null;var i=r.closing||r.selfClosing,o=[],a;if(!i){var f=new u(e,n,r.start.column),l={row:n,column:r.start.column+r.tagName.length+2};r.start.row==r.end.row&&(l.column=r.end.column);while(a=this._readTagForward(f)){if(a.selfClosing){if(!o.length)return a.start.column+=a.tagName.length+2,a.end.column-=2,s.fromPoints(a.start,a.end);continue}if(a.closing){this._pop(o,a);if(o.length==0)return s.fromPoints(l,a.start)}else o.push(a)}}else{var f=new u(e,n,r.end.column),c={row:n,column:r.start.column};while(a=this._readTagBackward(f)){if(a.selfClosing){if(!o.length)return a.start.column+=a.tagName.length+2,a.end.column-=2,s.fromPoints(a.start,a.end);continue}if(!a.closing){this._pop(o,a);if(o.length==0)return a.start.column+=a.tagName.length+2,a.start.row==a.end.row&&a.start.column-1}function l(e,t){var n=new r(e,t.row,t.column),i=n.getCurrentToken();while(i&&!f(i,"tag-name"))i=n.stepBackward();if(i)return i.value}function c(e,t){var n=new r(e,t.row,t.column),i=n.getCurrentToken();while(i&&!f(i,"attribute-name"))i=n.stepBackward();if(i)return i.value}var r=e("../token_iterator").TokenIterator,i=["accesskey","class","contenteditable","contextmenu","dir","draggable","dropzone","hidden","id","inert","itemid","itemprop","itemref","itemscope","itemtype","lang","spellcheck","style","tabindex","title","translate"],s=["onabort","onblur","oncancel","oncanplay","oncanplaythrough","onchange","onclick","onclose","oncontextmenu","oncuechange","ondblclick","ondrag","ondragend","ondragenter","ondragleave","ondragover","ondragstart","ondrop","ondurationchange","onemptied","onended","onerror","onfocus","oninput","oninvalid","onkeydown","onkeypress","onkeyup","onload","onloadeddata","onloadedmetadata","onloadstart","onmousedown","onmousemove","onmouseout","onmouseover","onmouseup","onmousewheel","onpause","onplay","onplaying","onprogress","onratechange","onreset","onscroll","onseeked","onseeking","onselect","onshow","onstalled","onsubmit","onsuspend","ontimeupdate","onvolumechange","onwaiting"],o=i.concat(s),u={html:{manifest:1},head:{},title:{},base:{href:1,target:1},link:{href:1,hreflang:1,rel:{stylesheet:1,icon:1},media:{all:1,screen:1,print:1},type:{"text/css":1,"image/png":1,"image/jpeg":1,"image/gif":1},sizes:1},meta:{"http-equiv":{"content-type":1},name:{description:1,keywords:1},content:{"text/html; charset=UTF-8":1},charset:1},style:{type:1,media:{all:1,screen:1,print:1},scoped:1},script:{charset:1,type:{"text/javascript":1},src:1,defer:1,async:1},noscript:{href:1},body:{onafterprint:1,onbeforeprint:1,onbeforeunload:1,onhashchange:1,onmessage:1,onoffline:1,onpopstate:1,onredo:1,onresize:1,onstorage:1,onundo:1,onunload:1},section:{},nav:{},article:{pubdate:1},aside:{},h1:{},h2:{},h3:{},h4:{},h5:{},h6:{},header:{},footer:{},address:{},main:{},p:{},hr:{},pre:{},blockquote:{cite:1},ol:{start:1,reversed:1},ul:{},li:{value:1},dl:{},dt:{},dd:{},figure:{},figcaption:{},div:{},a:{href:1,target:{_blank:1,top:1},ping:1,rel:{nofollow:1,alternate:1,author:1,bookmark:1,help:1,license:1,next:1,noreferrer:1,prefetch:1,prev:1,search:1,tag:1},media:1,hreflang:1,type:1},em:{},strong:{},small:{},s:{},cite:{},q:{cite:1},dfn:{},abbr:{},data:{},time:{datetime:1},code:{},"var":{},samp:{},kbd:{},sub:{},sup:{},i:{},b:{},u:{},mark:{},ruby:{},rt:{},rp:{},bdi:{},bdo:{},span:{},br:{},wbr:{},ins:{cite:1,datetime:1},del:{cite:1,datetime:1},img:{alt:1,src:1,height:1,width:1,usemap:1,ismap:1},iframe:{name:1,src:1,height:1,width:1,sandbox:{"allow-same-origin":1,"allow-top-navigation":1,"allow-forms":1,"allow-scripts":1},seamless:{seamless:1}},embed:{src:1,height:1,width:1,type:1},object:{param:1,data:1,type:1,height:1,width:1,usemap:1,name:1,form:1,classid:1},param:{name:1,value:1},video:{src:1,autobuffer:1,autoplay:{autoplay:1},loop:{loop:1},controls:{controls:1},width:1,height:1,poster:1,muted:{muted:1},preload:{auto:1,metadata:1,none:1}},audio:{src:1,autobuffer:1,autoplay:{autoplay:1},loop:{loop:1},controls:{controls:1},muted:{muted:1},preload:{auto:1,metadata:1,none:1}},source:{src:1,type:1,media:1},track:{kind:1,src:1,srclang:1,label:1,"default":1},canvas:{width:1,height:1},map:{name:1},area:{shape:1,coords:1,href:1,hreflang:1,alt:1,target:1,media:1,rel:1,ping:1,type:1},svg:{},math:{},table:{summary:1},caption:{},colgroup:{span:1},col:{span:1},tbody:{},thead:{},tfoot:{},tr:{},td:{headers:1,rowspan:1,colspan:1},th:{headers:1,rowspan:1,colspan:1,scope:1},form:{"accept-charset":1,action:1,autocomplete:1,enctype:{"multipart/form-data":1,"application/x-www-form-urlencoded":1},method:{get:1,post:1},name:1,novalidate:1,target:{_blank:1,top:1}},fieldset:{disabled:1,form:1,name:1},legend:{},label:{form:1,"for":1},input:{type:{text:1,password:1,hidden:1,checkbox:1,submit:1,radio:1,file:1,button:1,reset:1,image:31,color:1,date:1,datetime:1,"datetime-local":1,email:1,month:1,number:1,range:1,search:1,tel:1,time:1,url:1,week:1},accept:1,alt:1,autocomplete:{on:1,off:1},autofocus:{autofocus:1},checked:{checked:1},disabled:{disabled:1},form:1,formaction:1,formenctype:{"application/x-www-form-urlencoded":1,"multipart/form-data":1,"text/plain":1},formmethod:{get:1,post:1},formnovalidate:{formnovalidate:1},formtarget:{_blank:1,_self:1,_parent:1,_top:1},height:1,list:1,max:1,maxlength:1,min:1,multiple:{multiple:1},name:1,pattern:1,placeholder:1,readonly:{readonly:1},required:{required:1},size:1,src:1,step:1,width:1,files:1,value:1},button:{autofocus:1,disabled:{disabled:1},form:1,formaction:1,formenctype:1,formmethod:1,formnovalidate:1,formtarget:1,name:1,value:1,type:{button:1,submit:1}},select:{autofocus:1,disabled:1,form:1,multiple:{multiple:1},name:1,size:1,readonly:{readonly:1}},datalist:{},optgroup:{disabled:1,label:1},option:{disabled:1,selected:1,label:1,value:1},textarea:{autofocus:{autofocus:1},disabled:{disabled:1},form:1,maxlength:1,name:1,placeholder:1,readonly:{readonly:1},required:{required:1},rows:1,cols:1,wrap:{on:1,off:1,hard:1,soft:1}},keygen:{autofocus:1,challenge:{challenge:1},disabled:{disabled:1},form:1,keytype:{rsa:1,dsa:1,ec:1},name:1},output:{"for":1,form:1,name:1},progress:{value:1,max:1},meter:{value:1,min:1,max:1,low:1,high:1,optimum:1},details:{open:1},summary:{},command:{type:1,label:1,icon:1,disabled:1,checked:1,radiogroup:1,command:1},menu:{type:1,label:1},dialog:{open:1}},a=Object.keys(u),h=function(){};(function(){this.getCompletions=function(e,t,n,r){var i=t.getTokenAt(n.row,n.column);if(!i)return[];if(f(i,"tag-name")||f(i,"tag-open")||f(i,"end-tag-open"))return this.getTagCompletions(e,t,n,r);if(f(i,"tag-whitespace")||f(i,"attribute-name"))return this.getAttributeCompletions(e,t,n,r);if(f(i,"attribute-value"))return this.getAttributeValueCompletions(e,t,n,r);var s=t.getLine(n.row).substr(0,n.column);return/&[a-z]*$/i.test(s)?this.getHTMLEntityCompletions(e,t,n,r):[]},this.getTagCompletions=function(e,t,n,r){return a.map(function(e){return{value:e,meta:"tag",score:Number.MAX_VALUE}})},this.getAttributeCompletions=function(e,t,n,r){var i=l(t,n);if(!i)return[];var s=o;return i in u&&(s=s.concat(Object.keys(u[i]))),s.map(function(e){return{caption:e,snippet:e+'="$0"',meta:"attribute",score:Number.MAX_VALUE}})},this.getAttributeValueCompletions=function(e,t,n,r){var i=l(t,n),s=c(t,n);if(!i)return[];var o=[];return i in u&&s in u[i]&&typeof u[i][s]=="object"&&(o=Object.keys(u[i][s])),o.map(function(e){return{caption:e,snippet:e,meta:"attribute value",score:Number.MAX_VALUE}})},this.getHTMLEntityCompletions=function(e,t,n,r){var i=["Aacute;","aacute;","Acirc;","acirc;","acute;","AElig;","aelig;","Agrave;","agrave;","alefsym;","Alpha;","alpha;","amp;","and;","ang;","Aring;","aring;","asymp;","Atilde;","atilde;","Auml;","auml;","bdquo;","Beta;","beta;","brvbar;","bull;","cap;","Ccedil;","ccedil;","cedil;","cent;","Chi;","chi;","circ;","clubs;","cong;","copy;","crarr;","cup;","curren;","Dagger;","dagger;","dArr;","darr;","deg;","Delta;","delta;","diams;","divide;","Eacute;","eacute;","Ecirc;","ecirc;","Egrave;","egrave;","empty;","emsp;","ensp;","Epsilon;","epsilon;","equiv;","Eta;","eta;","ETH;","eth;","Euml;","euml;","euro;","exist;","fnof;","forall;","frac12;","frac14;","frac34;","frasl;","Gamma;","gamma;","ge;","gt;","hArr;","harr;","hearts;","hellip;","Iacute;","iacute;","Icirc;","icirc;","iexcl;","Igrave;","igrave;","image;","infin;","int;","Iota;","iota;","iquest;","isin;","Iuml;","iuml;","Kappa;","kappa;","Lambda;","lambda;","lang;","laquo;","lArr;","larr;","lceil;","ldquo;","le;","lfloor;","lowast;","loz;","lrm;","lsaquo;","lsquo;","lt;","macr;","mdash;","micro;","middot;","minus;","Mu;","mu;","nabla;","nbsp;","ndash;","ne;","ni;","not;","notin;","nsub;","Ntilde;","ntilde;","Nu;","nu;","Oacute;","oacute;","Ocirc;","ocirc;","OElig;","oelig;","Ograve;","ograve;","oline;","Omega;","omega;","Omicron;","omicron;","oplus;","or;","ordf;","ordm;","Oslash;","oslash;","Otilde;","otilde;","otimes;","Ouml;","ouml;","para;","part;","permil;","perp;","Phi;","phi;","Pi;","pi;","piv;","plusmn;","pound;","Prime;","prime;","prod;","prop;","Psi;","psi;","quot;","radic;","rang;","raquo;","rArr;","rarr;","rceil;","rdquo;","real;","reg;","rfloor;","Rho;","rho;","rlm;","rsaquo;","rsquo;","sbquo;","Scaron;","scaron;","sdot;","sect;","shy;","Sigma;","sigma;","sigmaf;","sim;","spades;","sub;","sube;","sum;","sup;","sup1;","sup2;","sup3;","supe;","szlig;","Tau;","tau;","there4;","Theta;","theta;","thetasym;","thinsp;","THORN;","thorn;","tilde;","times;","trade;","Uacute;","uacute;","uArr;","uarr;","Ucirc;","ucirc;","Ugrave;","ugrave;","uml;","upsih;","Upsilon;","upsilon;","Uuml;","uuml;","weierp;","Xi;","xi;","Yacute;","yacute;","yen;","Yuml;","yuml;","Zeta;","zeta;","zwj;","zwnj;"];return i.map(function(e){return{caption:e,snippet:e,meta:"html entity",score:Number.MAX_VALUE}})}}).call(h.prototype),t.HtmlCompletions=h}),define("ace/mode/html",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text","ace/mode/javascript","ace/mode/css","ace/mode/html_highlight_rules","ace/mode/behaviour/xml","ace/mode/folding/html","ace/mode/html_completions","ace/worker/worker_client"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text").Mode,o=e("./javascript").Mode,u=e("./css").Mode,a=e("./html_highlight_rules").HtmlHighlightRules,f=e("./behaviour/xml").XmlBehaviour,l=e("./folding/html").FoldMode,c=e("./html_completions").HtmlCompletions,h=e("../worker/worker_client").WorkerClient,p=["area","base","br","col","embed","hr","img","input","keygen","link","meta","menuitem","param","source","track","wbr"],d=["li","dt","dd","p","rt","rp","optgroup","option","colgroup","td","th"],v=function(e){this.fragmentContext=e&&e.fragmentContext,this.HighlightRules=a,this.$behaviour=new f,this.$completer=new c,this.createModeDelegates({"js-":o,"css-":u}),this.foldingRules=new l(this.voidElements,i.arrayToMap(d))};r.inherits(v,s),function(){this.blockComment={start:""},this.voidElements=i.arrayToMap(p),this.getNextLineIndent=function(e,t,n){return this.$getIndent(t)},this.checkOutdent=function(e,t,n){return!1},this.getCompletions=function(e,t,n,r){return this.$completer.getCompletions(e,t,n,r)},this.createWorker=function(e){if(this.constructor!=v)return;var t=new h(["ace"],"ace/mode/html_worker","Worker");return t.attachToDocument(e.getDocument()),this.fragmentContext&&t.call("setOptions",[{context:this.fragmentContext}]),t.on("error",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/html"}.call(v.prototype),t.Mode=v}),define("ace/mode/php",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/php_highlight_rules","ace/mode/php_highlight_rules","ace/mode/matching_brace_outdent","ace/range","ace/worker/worker_client","ace/mode/php_completions","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle","ace/unicode","ace/mode/html","ace/mode/javascript","ace/mode/css"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./php_highlight_rules").PhpHighlightRules,o=e("./php_highlight_rules").PhpLangHighlightRules,u=e("./matching_brace_outdent").MatchingBraceOutdent,a=e("../range").Range,f=e("../worker/worker_client").WorkerClient,l=e("./php_completions").PhpCompletions,c=e("./behaviour/cstyle").CstyleBehaviour,h=e("./folding/cstyle").FoldMode,p=e("../unicode"),d=e("./html").Mode,v=e("./javascript").Mode,m=e("./css").Mode,g=function(e){this.HighlightRules=o,this.$outdent=new u,this.$behaviour=new c,this.$completer=new l,this.foldingRules=new h};r.inherits(g,i),function(){this.tokenRe=new RegExp("^["+p.packages.L+p.packages.Mn+p.packages.Mc+p.packages.Nd+p.packages.Pc+"_]+","g"),this.nonTokenRe=new RegExp("^(?:[^"+p.packages.L+p.packages.Mn+p.packages.Mc+p.packages.Nd+p.packages.Pc+"_]|\\s])+","g"),this.lineCommentStart=["//","#"],this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens,o=i.state;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"){var u=t.match(/^.*[\{\(\[:]\s*$/);u&&(r+=n)}else if(e=="doc-start"){if(o!="doc-start")return"";var u=t.match(/^\s*(\/?)\*/);u&&(u[1]&&(r+=" "),r+="* ")}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.getCompletions=function(e,t,n,r){return this.$completer.getCompletions(e,t,n,r)},this.$id="ace/mode/php-inline"}.call(g.prototype);var y=function(e){if(e&&e.inline){var t=new g;return t.createWorker=this.createWorker,t.inlinePhp=!0,t}d.call(this),this.HighlightRules=s,this.createModeDelegates({"js-":v,"css-":m,"php-":g}),this.foldingRules.subModes["php-"]=new h};r.inherits(y,d),function(){this.createWorker=function(e){var t=new f(["ace"],"ace/mode/php_worker","PhpWorker");return t.attachToDocument(e.getDocument()),this.inlinePhp&&t.call("setOptions",[{inline:!0}]),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/php"}.call(y.prototype),t.Mode=y}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-plain_text.js b/public/themes/pterodactyl/vendor/ace/mode-plain_text.js new file mode 100644 index 0000000..2e2cfb4 --- /dev/null +++ b/public/themes/pterodactyl/vendor/ace/mode-plain_text.js @@ -0,0 +1 @@ +define("ace/mode/plain_text",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/text_highlight_rules","ace/mode/behaviour"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./text_highlight_rules").TextHighlightRules,o=e("./behaviour").Behaviour,u=function(){this.HighlightRules=s,this.$behaviour=new o};r.inherits(u,i),function(){this.type="text",this.getNextLineIndent=function(e,t,n){return""},this.$id="ace/mode/plain_text"}.call(u.prototype),t.Mode=u}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-properties.js b/public/themes/pterodactyl/vendor/ace/mode-properties.js new file mode 100644 index 0000000..1f77ca3 --- /dev/null +++ b/public/themes/pterodactyl/vendor/ace/mode-properties.js @@ -0,0 +1 @@ +define("ace/mode/properties_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e=/\\u[0-9a-fA-F]{4}|\\/;this.$rules={start:[{token:"comment",regex:/[!#].*$/},{token:"keyword",regex:/[=:]$/},{token:"keyword",regex:/[=:]/,next:"value"},{token:"constant.language.escape",regex:e},{defaultToken:"variable"}],value:[{regex:/\\$/,token:"string",next:"value"},{regex:/$/,token:"string",next:"start"},{token:"constant.language.escape",regex:e},{defaultToken:"string"}]}};r.inherits(s,i),t.PropertiesHighlightRules=s}),define("ace/mode/properties",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/properties_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./properties_highlight_rules").PropertiesHighlightRules,o=function(){this.HighlightRules=s,this.$behaviour=this.$defaultBehaviour};r.inherits(o,i),function(){this.$id="ace/mode/properties"}.call(o.prototype),t.Mode=o}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-python.js b/public/themes/pterodactyl/vendor/ace/mode-python.js new file mode 100644 index 0000000..f4e6c0d --- /dev/null +++ b/public/themes/pterodactyl/vendor/ace/mode-python.js @@ -0,0 +1 @@ +define("ace/mode/python_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e="and|as|assert|break|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|not|or|pass|print|raise|return|try|while|with|yield",t="True|False|None|NotImplemented|Ellipsis|__debug__",n="abs|divmod|input|open|staticmethod|all|enumerate|int|ord|str|any|eval|isinstance|pow|sum|basestring|execfile|issubclass|print|super|binfile|iter|property|tuple|bool|filter|len|range|type|bytearray|float|list|raw_input|unichr|callable|format|locals|reduce|unicode|chr|frozenset|long|reload|vars|classmethod|getattr|map|repr|xrange|cmp|globals|max|reversed|zip|compile|hasattr|memoryview|round|__import__|complex|hash|min|set|apply|delattr|help|next|setattr|buffer|dict|hex|object|slice|coerce|dir|id|oct|sorted|intern",r=this.createKeywordMapper({"invalid.deprecated":"debugger","support.function":n,"constant.language":t,keyword:e},"identifier"),i="(?:r|u|ur|R|U|UR|Ur|uR)?",s="(?:(?:[1-9]\\d*)|(?:0))",o="(?:0[oO]?[0-7]+)",u="(?:0[xX][\\dA-Fa-f]+)",a="(?:0[bB][01]+)",f="(?:"+s+"|"+o+"|"+u+"|"+a+")",l="(?:[eE][+-]?\\d+)",c="(?:\\.\\d+)",h="(?:\\d+)",p="(?:(?:"+h+"?"+c+")|(?:"+h+"\\.))",d="(?:(?:"+p+"|"+h+")"+l+")",v="(?:"+d+"|"+p+")",m="\\\\(x[0-9A-Fa-f]{2}|[0-7]{3}|[\\\\abfnrtv'\"]|U[0-9A-Fa-f]{8}|u[0-9A-Fa-f]{4})";this.$rules={start:[{token:"comment",regex:"#.*$"},{token:"string",regex:i+'"{3}',next:"qqstring3"},{token:"string",regex:i+'"(?=.)',next:"qqstring"},{token:"string",regex:i+"'{3}",next:"qstring3"},{token:"string",regex:i+"'(?=.)",next:"qstring"},{token:"constant.numeric",regex:"(?:"+v+"|\\d+)[jJ]\\b"},{token:"constant.numeric",regex:v},{token:"constant.numeric",regex:f+"[lL]\\b"},{token:"constant.numeric",regex:f+"\\b"},{token:r,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+"}],qqstring3:[{token:"constant.language.escape",regex:m},{token:"string",regex:'"{3}',next:"start"},{defaultToken:"string"}],qstring3:[{token:"constant.language.escape",regex:m},{token:"string",regex:"'{3}",next:"start"},{defaultToken:"string"}],qqstring:[{token:"constant.language.escape",regex:m},{token:"string",regex:"\\\\$",next:"qqstring"},{token:"string",regex:'"|$',next:"start"},{defaultToken:"string"}],qstring:[{token:"constant.language.escape",regex:m},{token:"string",regex:"\\\\$",next:"qstring"},{token:"string",regex:"'|$",next:"start"},{defaultToken:"string"}]}};r.inherits(s,i),t.PythonHighlightRules=s}),define("ace/mode/folding/pythonic",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("./fold_mode").FoldMode,s=t.FoldMode=function(e){this.foldingStartMarker=new RegExp("([\\[{])(?:\\s*)$|("+e+")(?:\\s*)(?:#.*)?$")};r.inherits(s,i),function(){this.getFoldWidgetRange=function(e,t,n){var r=e.getLine(n),i=r.match(this.foldingStartMarker);if(i)return i[1]?this.openingBracketBlock(e,i[1],n,i.index):i[2]?this.indentationBlock(e,n,i.index+i[2].length):this.indentationBlock(e,n)}}.call(s.prototype)}),define("ace/mode/python",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/python_highlight_rules","ace/mode/folding/pythonic","ace/range"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./python_highlight_rules").PythonHighlightRules,o=e("./folding/pythonic").FoldMode,u=e("../range").Range,a=function(){this.HighlightRules=s,this.foldingRules=new o("\\:"),this.$behaviour=this.$defaultBehaviour};r.inherits(a,i),function(){this.lineCommentStart="#",this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().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.getTokenizer().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))},this.$id="ace/mode/python"}.call(a.prototype),t.Mode=a}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-ruby.js b/public/themes/pterodactyl/vendor/ace/mode-ruby.js new file mode 100644 index 0000000..b857c1f --- /dev/null +++ b/public/themes/pterodactyl/vendor/ace/mode-ruby.js @@ -0,0 +1 @@ +define("ace/mode/ruby_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=t.constantOtherSymbol={token:"constant.other.symbol.ruby",regex:"[:](?:[A-Za-z_]|[@$](?=[a-zA-Z0-9_]))[a-zA-Z0-9_]*[!=?]?"},o=t.qString={token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},u=t.qqString={token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},a=t.tString={token:"string",regex:"[`](?:(?:\\\\.)|(?:[^'\\\\]))*?[`]"},f=t.constantNumericHex={token:"constant.numeric",regex:"0[xX][0-9a-fA-F](?:[0-9a-fA-F]|_(?=[0-9a-fA-F]))*\\b"},l=t.constantNumericFloat={token:"constant.numeric",regex:"[+-]?\\d(?:\\d|_(?=\\d))*(?:(?:\\.\\d(?:\\d|_(?=\\d))*)?(?:[eE][+-]?\\d+)?)?\\b"},c=function(){var e="abort|Array|assert|assert_equal|assert_not_equal|assert_same|assert_not_same|assert_nil|assert_not_nil|assert_match|assert_no_match|assert_in_delta|assert_throws|assert_raise|assert_nothing_raised|assert_instance_of|assert_kind_of|assert_respond_to|assert_operator|assert_send|assert_difference|assert_no_difference|assert_recognizes|assert_generates|assert_response|assert_redirected_to|assert_template|assert_select|assert_select_email|assert_select_rjs|assert_select_encoded|css_select|at_exit|attr|attr_writer|attr_reader|attr_accessor|attr_accessible|autoload|binding|block_given?|callcc|caller|catch|chomp|chomp!|chop|chop!|defined?|delete_via_redirect|eval|exec|exit|exit!|fail|Float|flunk|follow_redirect!|fork|form_for|form_tag|format|gets|global_variables|gsub|gsub!|get_via_redirect|host!|https?|https!|include|Integer|lambda|link_to|link_to_unless_current|link_to_function|link_to_remote|load|local_variables|loop|open|open_session|p|print|printf|proc|putc|puts|post_via_redirect|put_via_redirect|raise|rand|raw|readline|readlines|redirect?|request_via_redirect|require|scan|select|set_trace_func|sleep|split|sprintf|srand|String|stylesheet_link_tag|syscall|system|sub|sub!|test|throw|trace_var|trap|untrace_var|atan2|cos|exp|frexp|ldexp|log|log10|sin|sqrt|tan|render|javascript_include_tag|csrf_meta_tag|label_tag|text_field_tag|submit_tag|check_box_tag|content_tag|radio_button_tag|text_area_tag|password_field_tag|hidden_field_tag|fields_for|select_tag|options_for_select|options_from_collection_for_select|collection_select|time_zone_select|select_date|select_time|select_datetime|date_select|time_select|datetime_select|select_year|select_month|select_day|select_hour|select_minute|select_second|file_field_tag|file_field|respond_to|skip_before_filter|around_filter|after_filter|verify|protect_from_forgery|rescue_from|helper_method|redirect_to|before_filter|send_data|send_file|validates_presence_of|validates_uniqueness_of|validates_length_of|validates_format_of|validates_acceptance_of|validates_associated|validates_exclusion_of|validates_inclusion_of|validates_numericality_of|validates_with|validates_each|authenticate_or_request_with_http_basic|authenticate_or_request_with_http_digest|filter_parameter_logging|match|get|post|resources|redirect|scope|assert_routing|translate|localize|extract_locale_from_tld|caches_page|expire_page|caches_action|expire_action|cache|expire_fragment|expire_cache_for|observe|cache_sweeper|has_many|has_one|belongs_to|has_and_belongs_to_many",t="alias|and|BEGIN|begin|break|case|class|def|defined|do|else|elsif|END|end|ensure|__FILE__|finally|for|gem|if|in|__LINE__|module|next|not|or|private|protected|public|redo|rescue|retry|return|super|then|undef|unless|until|when|while|yield",n="true|TRUE|false|FALSE|nil|NIL|ARGF|ARGV|DATA|ENV|RUBY_PLATFORM|RUBY_RELEASE_DATE|RUBY_VERSION|STDERR|STDIN|STDOUT|TOPLEVEL_BINDING",r="$DEBUG|$defout|$FILENAME|$LOAD_PATH|$SAFE|$stdin|$stdout|$stderr|$VERBOSE|$!|root_url|flash|session|cookies|params|request|response|logger|self",i=this.$keywords=this.createKeywordMapper({keyword:t,"constant.language":n,"variable.language":r,"support.function":e,"invalid.deprecated":"debugger"},"identifier");this.$rules={start:[{token:"comment",regex:"#.*$"},{token:"comment",regex:"^=begin(?:$|\\s.*$)",next:"comment"},{token:"string.regexp",regex:"[/](?:(?:\\[(?:\\\\]|[^\\]])+\\])|(?:\\\\/|[^\\]/]))*[/]\\w*\\s*(?=[).,;]|$)"},[{regex:"[{}]",onMatch:function(e,t,n){this.next=e=="{"?this.nextState:"";if(e=="{"&&n.length)return n.unshift("start",t),"paren.lparen";if(e=="}"&&n.length){n.shift(),this.next=n.shift();if(this.next.indexOf("string")!=-1)return"paren.end"}return e=="{"?"paren.lparen":"paren.rparen"},nextState:"start"},{token:"string.start",regex:/"/,push:[{token:"constant.language.escape",regex:/\\(?:[nsrtvfbae'"\\]|c.|C-.|M-.(?:\\C-.)?|[0-7]{3}|x[\da-fA-F]{2}|u[\da-fA-F]{4})/},{token:"paren.start",regex:/#{/,push:"start"},{token:"string.end",regex:/"/,next:"pop"},{defaultToken:"string"}]},{token:"string.start",regex:/`/,push:[{token:"constant.language.escape",regex:/\\(?:[nsrtvfbae'"\\]|c.|C-.|M-.(?:\\C-.)?|[0-7]{3}|x[\da-fA-F]{2}|u[\da-fA-F]{4})/},{token:"paren.start",regex:/#{/,push:"start"},{token:"string.end",regex:/`/,next:"pop"},{defaultToken:"string"}]},{token:"string.start",regex:/'/,push:[{token:"constant.language.escape",regex:/\\['\\]/},{token:"string.end",regex:/'/,next:"pop"},{defaultToken:"string"}]}],{token:"text",regex:"::"},{token:"variable.instance",regex:"@{1,2}[a-zA-Z_\\d]+"},{token:"support.class",regex:"[A-Z][a-zA-Z_\\d]+"},s,f,l,{token:"constant.language.boolean",regex:"(?:true|false)\\b"},{token:i,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"punctuation.separator.key-value",regex:"=>"},{stateName:"heredoc",onMatch:function(e,t,n){var r=e[2]=="-"?"indentedHeredoc":"heredoc",i=e.split(this.splitRegex);return n.push(r,i[3]),[{type:"constant",value:i[1]},{type:"string",value:i[2]},{type:"support.class",value:i[3]},{type:"string",value:i[4]}]},regex:"(<<-?)(['\"`]?)([\\w]+)(['\"`]?)",rules:{heredoc:[{onMatch:function(e,t,n){return e===n[1]?(n.shift(),n.shift(),this.next=n[0]||"start","support.class"):(this.next="","string")},regex:".*$",next:"start"}],indentedHeredoc:[{token:"string",regex:"^ +"},{onMatch:function(e,t,n){return e===n[1]?(n.shift(),n.shift(),this.next=n[0]||"start","support.class"):(this.next="","string")},regex:".*$",next:"start"}]}},{regex:"$",token:"empty",next:function(e,t){return t[0]==="heredoc"||t[0]==="indentedHeredoc"?t[0]:e}},{token:"string.character",regex:"\\B\\?."},{token:"keyword.operator",regex:"!|\\$|%|&|\\*|\\-\\-|\\-|\\+\\+|\\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\\|\\||\\?\\:|\\*=|%=|\\+=|\\-=|&=|\\^=|\\b(?:in|instanceof|new|delete|typeof|void)"},{token:"paren.lparen",regex:"[[({]"},{token:"paren.rparen",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],comment:[{token:"comment",regex:"^=end(?:$|\\s.*$)",next:"start"},{token:"comment",regex:".+"}]},this.normalizeRules()};r.inherits(c,i),t.RubyHighlightRules=c}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define("ace/mode/folding/coffee",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode","ace/range"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("./fold_mode").FoldMode,s=e("../../range").Range,o=t.FoldMode=function(){};r.inherits(o,i),function(){this.getFoldWidgetRange=function(e,t,n){var r=this.indentationBlock(e,n);if(r)return r;var i=/\S/,o=e.getLine(n),u=o.search(i);if(u==-1||o[u]!="#")return;var a=o.length,f=e.getLength(),l=n,c=n;while(++nl){var p=e.getLine(c).length;return new s(l,a,c,p)}},this.getFoldWidget=function(e,t,n){var r=e.getLine(n),i=r.search(/\S/),s=e.getLine(n+1),o=e.getLine(n-1),u=o.search(/\S/),a=s.search(/\S/);if(i==-1)return e.foldWidgets[n-1]=u!=-1&&u=n[1]?(e.length>n[1]&&(r="invalid"),n.shift(),n.shift(),this.next=n.shift()):this.next="",r},regex:/"#*/,next:"start"},{defaultToken:"string.quoted.raw.source.rust"}]},{token:"string.quoted.double.source.rust",regex:'"',push:[{token:"string.quoted.double.source.rust",regex:'"',next:"pop"},{token:"constant.character.escape.source.rust",regex:s},{defaultToken:"string.quoted.double.source.rust"}]},{token:["keyword.source.rust","text","entity.name.function.source.rust"],regex:"\\b(fn)(\\s+)([a-zA-Z_][a-zA-Z0-9_]*)"},{token:"support.constant",regex:"\\b[a-zA-Z_][\\w\\d]*::"},{token:"keyword.source.rust",regex:"\\b(?:abstract|alignof|as|box|break|continue|const|crate|do|else|enum|extern|for|final|if|impl|in|let|loop|macro|match|mod|move|mut|offsetof|override|priv|proc|pub|pure|ref|return|self|sizeof|static|struct|super|trait|type|typeof|unsafe|unsized|use|virtual|where|while|yield)\\b"},{token:"storage.type.source.rust",regex:"\\b(?:Self|isize|usize|char|bool|u8|u16|u32|u64|u128|f16|f32|f64|i8|i16|i32|i64|i128|str|option|either|c_float|c_double|c_void|FILE|fpos_t|DIR|dirent|c_char|c_schar|c_uchar|c_short|c_ushort|c_int|c_uint|c_long|c_ulong|size_t|ptrdiff_t|clock_t|time_t|c_longlong|c_ulonglong|intptr_t|uintptr_t|off_t|dev_t|ino_t|pid_t|mode_t|ssize_t)\\b"},{token:"variable.language.source.rust",regex:"\\bself\\b"},{token:"comment.line.doc.source.rust",regex:"//!.*$"},{token:"comment.line.double-dash.source.rust",regex:"//.*$"},{token:"comment.start.block.source.rust",regex:"/\\*",stateName:"comment",push:[{token:"comment.start.block.source.rust",regex:"/\\*",push:"comment"},{token:"comment.end.block.source.rust",regex:"\\*/",next:"pop"},{defaultToken:"comment.block.source.rust"}]},{token:"keyword.operator",regex:/\$|[-=]>|[-+%^=!&|<>]=?|[*/](?![*/])=?/},{token:"punctuation.operator",regex:/[?:,;.]/},{token:"paren.lparen",regex:/[\[({]/},{token:"paren.rparen",regex:/[\])}]/},{token:"constant.language.source.rust",regex:"\\b(?:true|false|Some|None|Ok|Err)\\b"},{token:"support.constant.source.rust",regex:"\\b(?:EXIT_FAILURE|EXIT_SUCCESS|RAND_MAX|EOF|SEEK_SET|SEEK_CUR|SEEK_END|_IOFBF|_IONBF|_IOLBF|BUFSIZ|FOPEN_MAX|FILENAME_MAX|L_tmpnam|TMP_MAX|O_RDONLY|O_WRONLY|O_RDWR|O_APPEND|O_CREAT|O_EXCL|O_TRUNC|S_IFIFO|S_IFCHR|S_IFBLK|S_IFDIR|S_IFREG|S_IFMT|S_IEXEC|S_IWRITE|S_IREAD|S_IRWXU|S_IXUSR|S_IWUSR|S_IRUSR|F_OK|R_OK|W_OK|X_OK|STDIN_FILENO|STDOUT_FILENO|STDERR_FILENO)\\b"},{token:"meta.preprocessor.source.rust",regex:"\\b\\w\\(\\w\\)*!|#\\[[\\w=\\(\\)_]+\\]\\b"},{token:"constant.numeric.source.rust",regex:/\b(?:0x[a-fA-F0-9_]+|0o[0-7_]+|0b[01_]+|[0-9][0-9_]*(?!\.))(?:[iu](?:size|8|16|32|64|128))?\b/},{token:"constant.numeric.source.rust",regex:/\b(?:[0-9][0-9_]*)(?:\.[0-9][0-9_]*)?(?:[Ee][+-][0-9][0-9_]*)?(?:f32|f64)?\b/}]},this.normalizeRules()};o.metaData={fileTypes:["rs","rc"],foldingStartMarker:"^.*\\bfn\\s*(\\w+\\s*)?\\([^\\)]*\\)(\\s*\\{[^\\}]*)?\\s*$",foldingStopMarker:"^\\s*\\}",name:"Rust",scopeName:"source.rust"},r.inherits(o,i),t.RustHighlightRules=o}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/rust",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/rust_highlight_rules","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./rust_highlight_rules").RustHighlightRules,o=e("./folding/cstyle").FoldMode,u=function(){this.HighlightRules=s,this.foldingRules=new o,this.$behaviour=this.$defaultBehaviour};r.inherits(u,i),function(){this.lineCommentStart="//",this.blockComment={start:"/*",end:"*/",nestable:!0},this.$id="ace/mode/rust"}.call(u.prototype),t.Mode=u}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-sh.js b/public/themes/pterodactyl/vendor/ace/mode-sh.js new file mode 100644 index 0000000..95e556e --- /dev/null +++ b/public/themes/pterodactyl/vendor/ace/mode-sh.js @@ -0,0 +1 @@ +define("ace/mode/sh_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";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|function|declare|readonly",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+"=)",h="(?:\\$(?:SHLVL|\\$|\\!|\\?))",p="(?:"+l+"\\s*\\(\\))";this.$rules={start:[{token:"constant",regex:/\\./},{token:["text","comment"],regex:/(^|\s)(#.*)$/},{token:"string.start",regex:'"',push:[{token:"constant.language.escape",regex:/\\(?:[$`"\\]|$)/},{include:"variables"},{token:"keyword.operator",regex:/`/},{token:"string.end",regex:'"',next:"pop"},{defaultToken:"string"}]},{token:"string",regex:"\\$'",push:[{token:"constant.language.escape",regex:/\\(?:[abeEfnrtv\\'"]|x[a-fA-F\d]{1,2}|u[a-fA-F\d]{4}([a-fA-F\d]{4})?|c.|\d{1,3})/},{token:"string",regex:"'",next:"pop"},{defaultToken:"string"}]},{regex:"<<<",token:"keyword.operator"},{stateName:"heredoc",regex:"(<<-?)(\\s*)(['\"`]?)([\\w\\-]+)(['\"`]?)",onMatch:function(e,t,n){var r=e[2]=="-"?"indentedHeredoc":"heredoc",i=e.split(this.splitRegex);return n.push(r,i[4]),[{type:"constant",value:i[1]},{type:"text",value:i[2]},{type:"string",value:i[3]},{type:"support.class",value:i[4]},{type:"string",value:i[5]}]},rules:{heredoc:[{onMatch:function(e,t,n){return e===n[1]?(n.shift(),n.shift(),this.next=n[0]||"start","support.class"):(this.next="","string")},regex:".*$",next:"start"}],indentedHeredoc:[{token:"string",regex:"^ +"},{onMatch:function(e,t,n){return e===n[1]?(n.shift(),n.shift(),this.next=n[0]||"start","support.class"):(this.next="","string")},regex:".*$",next:"start"}]}},{regex:"$",token:"empty",next:function(e,t){return t[0]==="heredoc"||t[0]==="indentedHeredoc"?t[0]:e}},{token:["keyword","text","text","text","variable"],regex:/(declare|local|readonly)(\s+)(?:(-[fixar]+)(\s+))?([a-zA-Z_][a-zA-Z0-9_]*\b)/},{token:"variable.language",regex:h},{token:"variable",regex:c},{include:"variables"},{token:"support.function",regex:p},{token:"support.function",regex:f},{token:"string",start:"'",end:"'"},{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:"punctuation.operator",regex:";"},{token:"paren.lparen",regex:"[\\[\\(\\{]"},{token:"paren.rparen",regex:"[\\]]"},{token:"paren.rparen",regex:"[\\)\\}]",next:"pop"}],variables:[{token:"variable",regex:/(\$)(\w+)/},{token:["variable","paren.lparen"],regex:/(\$)(\()/,push:"start"},{token:["variable","paren.lparen","keyword.operator","variable","keyword.operator"],regex:/(\$)(\{)([#!]?)(\w+|[*@#?\-$!0_])(:[?+\-=]?|##?|%%?|,,?\/|\^\^?)?/,push:"start"},{token:"variable",regex:/\$[*@#?\-$!0_]/},{token:["variable","paren.lparen"],regex:/(\$)(\{)/,push:"start"}]},this.normalizeRules()};r.inherits(u,i),t.ShHighlightRules=u}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/sh",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/sh_highlight_rules","ace/range","ace/mode/folding/cstyle","ace/mode/behaviour/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./sh_highlight_rules").ShHighlightRules,o=e("../range").Range,u=e("./folding/cstyle").FoldMode,a=e("./behaviour/cstyle").CstyleBehaviour,f=function(){this.HighlightRules=s,this.foldingRules=new u,this.$behaviour=new a};r.inherits(f,i),function(){this.lineCommentStart="#",this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().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.getTokenizer().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 o(n,r.length-i.length,n,r.length))},this.$id="ace/mode/sh"}.call(f.prototype),t.Mode=f}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-smarty.js b/public/themes/pterodactyl/vendor/ace/mode-smarty.js new file mode 100644 index 0000000..98141f4 --- /dev/null +++ b/public/themes/pterodactyl/vendor/ace/mode-smarty.js @@ -0,0 +1 @@ +define("ace/mode/doc_comment_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},s.getTagRule(),{defaultToken:"comment.doc",caseInsensitive:!0}]}};r.inherits(s,i),s.getTagRule=function(e){return{token:"comment.doc.tag.storage.type",regex:"\\b(?:TODO|FIXME|XXX|HACK)\\b"}},s.getStartRule=function(e){return{token:"comment.doc",regex:"\\/\\*(?=\\*)",next:e}},s.getEndRule=function(e){return{token:"comment.doc",regex:"\\*\\/",next:e}},t.DocCommentHighlightRules=s}),define("ace/mode/javascript_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";function a(){var e=o.replace("\\d","\\d\\-"),t={onMatch:function(e,t,n){var r=e.charAt(1)=="/"?2:1;if(r==1)t!=this.nextState?n.unshift(this.next,this.nextState,0):n.unshift(this.next),n[2]++;else if(r==2&&t==this.nextState){n[1]--;if(!n[1]||n[1]<0)n.shift(),n.shift()}return[{type:"meta.tag.punctuation."+(r==1?"":"end-")+"tag-open.xml",value:e.slice(0,r)},{type:"meta.tag.tag-name.xml",value:e.substr(r)}]},regex:"",onMatch:function(e,t,n){return t==n[0]&&n.shift(),e.length==2&&(n[0]==this.nextState&&n[1]--,(!n[1]||n[1]<0)&&n.splice(0,2)),this.next=n[0]||"start",[{type:this.token,value:e}]},nextState:"jsx"},n,f("jsxAttributes"),{token:"entity.other.attribute-name.xml",regex:e},{token:"keyword.operator.attribute-equals.xml",regex:"="},{token:"text.tag-whitespace.xml",regex:"\\s+"},{token:"string.attribute-value.xml",regex:"'",stateName:"jsx_attr_q",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',stateName:"jsx_attr_qq",push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},t],this.$rules.reference=[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}]}function f(e){return[{token:"comment",regex:/\/\*/,next:[i.getTagRule(),{token:"comment",regex:"\\*\\/",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]},{token:"comment",regex:"\\/\\/",next:[i.getTagRule(),{token:"comment",regex:"$|^",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]}]}var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./text_highlight_rules").TextHighlightRules,o="[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*",u=function(e){var t=this.createKeywordMapper({"variable.language":"Array|Boolean|Date|Function|Iterator|Number|Object|RegExp|String|Proxy|Namespace|QName|XML|XMLList|ArrayBuffer|Float32Array|Float64Array|Int16Array|Int32Array|Int8Array|Uint16Array|Uint32Array|Uint8Array|Uint8ClampedArray|Error|EvalError|InternalError|RangeError|ReferenceError|StopIteration|SyntaxError|TypeError|URIError|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|eval|isFinite|isNaN|parseFloat|parseInt|JSON|Math|this|arguments|prototype|window|document",keyword:"const|yield|import|get|set|async|await|break|case|catch|continue|default|delete|do|else|finally|for|function|if|in|of|instanceof|new|return|switch|throw|try|typeof|let|var|while|with|debugger|__parent__|__count__|escape|unescape|with|__proto__|class|enum|extends|super|export|implements|private|public|interface|package|protected|static","storage.type":"const|let|var|function","constant.language":"null|Infinity|NaN|undefined","support.function":"alert","constant.language.boolean":"true|false"},"identifier"),n="case|do|else|finally|in|instanceof|return|throw|try|typeof|yield|void",r="\\\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|u{[0-9a-fA-F]{1,6}}|[0-2][0-7]{0,2}|3[0-7][0-7]?|[4-7][0-7]?|.)";this.$rules={no_regex:[i.getStartRule("doc-start"),f("no_regex"),{token:"string",regex:"'(?=.)",next:"qstring"},{token:"string",regex:'"(?=.)',next:"qqstring"},{token:"constant.numeric",regex:/0(?:[xX][0-9a-fA-F]+|[bB][01]+)\b/},{token:"constant.numeric",regex:/[+-]?\d[\d_]*(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/},{token:["storage.type","punctuation.operator","support.function","punctuation.operator","entity.name.function","text","keyword.operator"],regex:"("+o+")(\\.)(prototype)(\\.)("+o+")(\\s*)(=)",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s+)(\\w+)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","text","entity.name.function","text","paren.lparen"],regex:"(function)(\\s+)("+o+")(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","punctuation.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["text","text","storage.type","text","paren.lparen"],regex:"(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:"keyword",regex:"(?:"+n+")\\b",next:"start"},{token:["support.constant"],regex:/that\b/},{token:["storage.type","punctuation.operator","support.function.firebug"],regex:/(console)(\.)(warn|info|log|error|time|trace|timeEnd|assert)\b/},{token:t,regex:o},{token:"punctuation.operator",regex:/[.](?![.])/,next:"property"},{token:"keyword.operator",regex:/--|\+\+|\.{3}|===|==|=|!=|!==|<+=?|>+=?|!|&&|\|\||\?:|[!$%&*+\-~\/^]=?/,next:"start"},{token:"punctuation.operator",regex:/[?:,;.]/,next:"start"},{token:"paren.lparen",regex:/[\[({]/,next:"start"},{token:"paren.rparen",regex:/[\])}]/},{token:"comment",regex:/^#!.*$/}],property:[{token:"text",regex:"\\s+"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(?:(\\s+)(\\w+))?(\\s*)(\\()",next:"function_arguments"},{token:"punctuation.operator",regex:/[.](?![.])/},{token:"support.function",regex:/(s(?:h(?:ift|ow(?:Mod(?:elessDialog|alDialog)|Help))|croll(?:X|By(?:Pages|Lines)?|Y|To)?|t(?:op|rike)|i(?:n|zeToContent|debar|gnText)|ort|u(?:p|b(?:str(?:ing)?)?)|pli(?:ce|t)|e(?:nd|t(?:Re(?:sizable|questHeader)|M(?:i(?:nutes|lliseconds)|onth)|Seconds|Ho(?:tKeys|urs)|Year|Cursor|Time(?:out)?|Interval|ZOptions|Date|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Date|FullYear)|FullYear|Active)|arch)|qrt|lice|avePreferences|mall)|h(?:ome|andleEvent)|navigate|c(?:har(?:CodeAt|At)|o(?:s|n(?:cat|textual|firm)|mpile)|eil|lear(?:Timeout|Interval)?|a(?:ptureEvents|ll)|reate(?:StyleSheet|Popup|EventObject))|t(?:o(?:GMTString|S(?:tring|ource)|U(?:TCString|pperCase)|Lo(?:caleString|werCase))|est|a(?:n|int(?:Enabled)?))|i(?:s(?:NaN|Finite)|ndexOf|talics)|d(?:isableExternalCapture|ump|etachEvent)|u(?:n(?:shift|taint|escape|watch)|pdateCommands)|j(?:oin|avaEnabled)|p(?:o(?:p|w)|ush|lugins.refresh|a(?:ddings|rse(?:Int|Float)?)|r(?:int|ompt|eference))|e(?:scape|nableExternalCapture|val|lementFromPoint|x(?:p|ec(?:Script|Command)?))|valueOf|UTC|queryCommand(?:State|Indeterm|Enabled|Value)|f(?:i(?:nd|le(?:ModifiedDate|Size|CreatedDate|UpdatedDate)|xed)|o(?:nt(?:size|color)|rward)|loor|romCharCode)|watch|l(?:ink|o(?:ad|g)|astIndexOf)|a(?:sin|nchor|cos|t(?:tachEvent|ob|an(?:2)?)|pply|lert|b(?:s|ort))|r(?:ou(?:nd|teEvents)|e(?:size(?:By|To)|calc|turnValue|place|verse|l(?:oad|ease(?:Capture|Events)))|andom)|g(?:o|et(?:ResponseHeader|M(?:i(?:nutes|lliseconds)|onth)|Se(?:conds|lection)|Hours|Year|Time(?:zoneOffset)?|Da(?:y|te)|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Da(?:y|te)|FullYear)|FullYear|A(?:ttention|llResponseHeaders)))|m(?:in|ove(?:B(?:y|elow)|To(?:Absolute)?|Above)|ergeAttributes|a(?:tch|rgins|x))|b(?:toa|ig|o(?:ld|rderWidths)|link|ack))\b(?=\()/},{token:"support.function.dom",regex:/(s(?:ub(?:stringData|mit)|plitText|e(?:t(?:NamedItem|Attribute(?:Node)?)|lect))|has(?:ChildNodes|Feature)|namedItem|c(?:l(?:ick|o(?:se|neNode))|reate(?:C(?:omment|DATASection|aption)|T(?:Head|extNode|Foot)|DocumentFragment|ProcessingInstruction|E(?:ntityReference|lement)|Attribute))|tabIndex|i(?:nsert(?:Row|Before|Cell|Data)|tem)|open|delete(?:Row|C(?:ell|aption)|T(?:Head|Foot)|Data)|focus|write(?:ln)?|a(?:dd|ppend(?:Child|Data))|re(?:set|place(?:Child|Data)|move(?:NamedItem|Child|Attribute(?:Node)?)?)|get(?:NamedItem|Element(?:sBy(?:Name|TagName|ClassName)|ById)|Attribute(?:Node)?)|blur)\b(?=\()/},{token:"support.constant",regex:/(s(?:ystemLanguage|cr(?:ipts|ollbars|een(?:X|Y|Top|Left))|t(?:yle(?:Sheets)?|atus(?:Text|bar)?)|ibling(?:Below|Above)|ource|uffixes|e(?:curity(?:Policy)?|l(?:ection|f)))|h(?:istory|ost(?:name)?|as(?:h|Focus))|y|X(?:MLDocument|SLDocument)|n(?:ext|ame(?:space(?:s|URI)|Prop))|M(?:IN_VALUE|AX_VALUE)|c(?:haracterSet|o(?:n(?:structor|trollers)|okieEnabled|lorDepth|mp(?:onents|lete))|urrent|puClass|l(?:i(?:p(?:boardData)?|entInformation)|osed|asses)|alle(?:e|r)|rypto)|t(?:o(?:olbar|p)|ext(?:Transform|Indent|Decoration|Align)|ags)|SQRT(?:1_2|2)|i(?:n(?:ner(?:Height|Width)|put)|ds|gnoreCase)|zIndex|o(?:scpu|n(?:readystatechange|Line)|uter(?:Height|Width)|p(?:sProfile|ener)|ffscreenBuffering)|NEGATIVE_INFINITY|d(?:i(?:splay|alog(?:Height|Top|Width|Left|Arguments)|rectories)|e(?:scription|fault(?:Status|Ch(?:ecked|arset)|View)))|u(?:ser(?:Profile|Language|Agent)|n(?:iqueID|defined)|pdateInterval)|_content|p(?:ixelDepth|ort|ersonalbar|kcs11|l(?:ugins|atform)|a(?:thname|dding(?:Right|Bottom|Top|Left)|rent(?:Window|Layer)?|ge(?:X(?:Offset)?|Y(?:Offset)?))|r(?:o(?:to(?:col|type)|duct(?:Sub)?|mpter)|e(?:vious|fix)))|e(?:n(?:coding|abledPlugin)|x(?:ternal|pando)|mbeds)|v(?:isibility|endor(?:Sub)?|Linkcolor)|URLUnencoded|P(?:I|OSITIVE_INFINITY)|f(?:ilename|o(?:nt(?:Size|Family|Weight)|rmName)|rame(?:s|Element)|gColor)|E|whiteSpace|l(?:i(?:stStyleType|n(?:eHeight|kColor))|o(?:ca(?:tion(?:bar)?|lName)|wsrc)|e(?:ngth|ft(?:Context)?)|a(?:st(?:M(?:odified|atch)|Index|Paren)|yer(?:s|X)|nguage))|a(?:pp(?:MinorVersion|Name|Co(?:deName|re)|Version)|vail(?:Height|Top|Width|Left)|ll|r(?:ity|guments)|Linkcolor|bove)|r(?:ight(?:Context)?|e(?:sponse(?:XML|Text)|adyState))|global|x|m(?:imeTypes|ultiline|enubar|argin(?:Right|Bottom|Top|Left))|L(?:N(?:10|2)|OG(?:10E|2E))|b(?:o(?:ttom|rder(?:Width|RightWidth|BottomWidth|Style|Color|TopWidth|LeftWidth))|ufferDepth|elow|ackground(?:Color|Image)))\b/},{token:"identifier",regex:o},{regex:"",token:"empty",next:"no_regex"}],start:[i.getStartRule("doc-start"),f("start"),{token:"string.regexp",regex:"\\/",next:"regex"},{token:"text",regex:"\\s+|^$",next:"start"},{token:"empty",regex:"",next:"no_regex"}],regex:[{token:"regexp.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"string.regexp",regex:"/[sxngimy]*",next:"no_regex"},{token:"invalid",regex:/\{\d+\b,?\d*\}[+*]|[+*$^?][+*]|[$^][?]|\?{3,}/},{token:"constant.language.escape",regex:/\(\?[:=!]|\)|\{\d+\b,?\d*\}|[+*]\?|[()$^+*?.]/},{token:"constant.language.delimiter",regex:/\|/},{token:"constant.language.escape",regex:/\[\^?/,next:"regex_character_class"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp"}],regex_character_class:[{token:"regexp.charclass.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"constant.language.escape",regex:"]",next:"regex"},{token:"constant.language.escape",regex:"-"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp.charachterclass"}],function_arguments:[{token:"variable.parameter",regex:o},{token:"punctuation.operator",regex:"[, ]+"},{token:"punctuation.operator",regex:"$"},{token:"empty",regex:"",next:"no_regex"}],qqstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qqstring"},{token:"string",regex:'"|$',next:"no_regex"},{defaultToken:"string"}],qstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qstring"},{token:"string",regex:"'|$",next:"no_regex"},{defaultToken:"string"}]};if(!e||!e.noES6)this.$rules.no_regex.unshift({regex:"[{}]",onMatch:function(e,t,n){this.next=e=="{"?this.nextState:"";if(e=="{"&&n.length)n.unshift("start",t);else if(e=="}"&&n.length){n.shift(),this.next=n.shift();if(this.next.indexOf("string")!=-1||this.next.indexOf("jsx")!=-1)return"paren.quasi.end"}return e=="{"?"paren.lparen":"paren.rparen"},nextState:"start"},{token:"string.quasi.start",regex:/`/,push:[{token:"constant.language.escape",regex:r},{token:"paren.quasi.start",regex:/\${/,push:"start"},{token:"string.quasi.end",regex:/`/,next:"pop"},{defaultToken:"string.quasi"}]}),(!e||e.jsx!=0)&&a.call(this);this.embedRules(i,"doc-",[i.getEndRule("no_regex")]),this.normalizeRules()};r.inherits(u,s),t.JavaScriptHighlightRules=u}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/javascript",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/javascript_highlight_rules","ace/mode/matching_brace_outdent","ace/worker/worker_client","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./javascript_highlight_rules").JavaScriptHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../worker/worker_client").WorkerClient,a=e("./behaviour/cstyle").CstyleBehaviour,f=e("./folding/cstyle").FoldMode,l=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new a,this.foldingRules=new f};r.inherits(l,i),function(){this.lineCommentStart="//",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens,o=i.state;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"||e=="no_regex"){var u=t.match(/^.*(?:\bcase\b.*:|[\{\(\[])\s*$/);u&&(r+=n)}else if(e=="doc-start"){if(o=="start"||o=="no_regex")return"";var u=t.match(/^\s*(\/?)\*/);u&&(u[1]&&(r+=" "),r+="* ")}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.createWorker=function(e){var t=new u(["ace"],"ace/mode/javascript_worker","JavaScriptWorker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/javascript"}.call(l.prototype),t.Mode=l}),define("ace/mode/css_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text_highlight_rules").TextHighlightRules,o=t.supportType="align-content|align-items|align-self|all|animation|animation-delay|animation-direction|animation-duration|animation-fill-mode|animation-iteration-count|animation-name|animation-play-state|animation-timing-function|backface-visibility|background|background-attachment|background-blend-mode|background-clip|background-color|background-image|background-origin|background-position|background-repeat|background-size|border|border-bottom|border-bottom-color|border-bottom-left-radius|border-bottom-right-radius|border-bottom-style|border-bottom-width|border-collapse|border-color|border-image|border-image-outset|border-image-repeat|border-image-slice|border-image-source|border-image-width|border-left|border-left-color|border-left-style|border-left-width|border-radius|border-right|border-right-color|border-right-style|border-right-width|border-spacing|border-style|border-top|border-top-color|border-top-left-radius|border-top-right-radius|border-top-style|border-top-width|border-width|bottom|box-shadow|box-sizing|caption-side|clear|clip|color|column-count|column-fill|column-gap|column-rule|column-rule-color|column-rule-style|column-rule-width|column-span|column-width|columns|content|counter-increment|counter-reset|cursor|direction|display|empty-cells|filter|flex|flex-basis|flex-direction|flex-flow|flex-grow|flex-shrink|flex-wrap|float|font|font-family|font-size|font-size-adjust|font-stretch|font-style|font-variant|font-weight|hanging-punctuation|height|justify-content|left|letter-spacing|line-height|list-style|list-style-image|list-style-position|list-style-type|margin|margin-bottom|margin-left|margin-right|margin-top|max-height|max-width|min-height|min-width|nav-down|nav-index|nav-left|nav-right|nav-up|opacity|order|outline|outline-color|outline-offset|outline-style|outline-width|overflow|overflow-x|overflow-y|padding|padding-bottom|padding-left|padding-right|padding-top|page-break-after|page-break-before|page-break-inside|perspective|perspective-origin|position|quotes|resize|right|tab-size|table-layout|text-align|text-align-last|text-decoration|text-decoration-color|text-decoration-line|text-decoration-style|text-indent|text-justify|text-overflow|text-shadow|text-transform|top|transform|transform-origin|transform-style|transition|transition-delay|transition-duration|transition-property|transition-timing-function|unicode-bidi|vertical-align|visibility|white-space|width|word-break|word-spacing|word-wrap|z-index",u=t.supportFunction="rgb|rgba|url|attr|counter|counters",a=t.supportConstant="absolute|after-edge|after|all-scroll|all|alphabetic|always|antialiased|armenian|auto|avoid-column|avoid-page|avoid|balance|baseline|before-edge|before|below|bidi-override|block-line-height|block|bold|bolder|border-box|both|bottom|box|break-all|break-word|capitalize|caps-height|caption|center|central|char|circle|cjk-ideographic|clone|close-quote|col-resize|collapse|column|consider-shifts|contain|content-box|cover|crosshair|cubic-bezier|dashed|decimal-leading-zero|decimal|default|disabled|disc|disregard-shifts|distribute-all-lines|distribute-letter|distribute-space|distribute|dotted|double|e-resize|ease-in|ease-in-out|ease-out|ease|ellipsis|end|exclude-ruby|fill|fixed|georgian|glyphs|grid-height|groove|hand|hanging|hebrew|help|hidden|hiragana-iroha|hiragana|horizontal|icon|ideograph-alpha|ideograph-numeric|ideograph-parenthesis|ideograph-space|ideographic|inactive|include-ruby|inherit|initial|inline-block|inline-box|inline-line-height|inline-table|inline|inset|inside|inter-ideograph|inter-word|invert|italic|justify|katakana-iroha|katakana|keep-all|last|left|lighter|line-edge|line-through|line|linear|list-item|local|loose|lower-alpha|lower-greek|lower-latin|lower-roman|lowercase|lr-tb|ltr|mathematical|max-height|max-size|medium|menu|message-box|middle|move|n-resize|ne-resize|newspaper|no-change|no-close-quote|no-drop|no-open-quote|no-repeat|none|normal|not-allowed|nowrap|nw-resize|oblique|open-quote|outset|outside|overline|padding-box|page|pointer|pre-line|pre-wrap|pre|preserve-3d|progress|relative|repeat-x|repeat-y|repeat|replaced|reset-size|ridge|right|round|row-resize|rtl|s-resize|scroll|se-resize|separate|slice|small-caps|small-caption|solid|space|square|start|static|status-bar|step-end|step-start|steps|stretch|strict|sub|super|sw-resize|table-caption|table-cell|table-column-group|table-column|table-footer-group|table-header-group|table-row-group|table-row|table|tb-rl|text-after-edge|text-before-edge|text-bottom|text-size|text-top|text|thick|thin|transparent|underline|upper-alpha|upper-latin|upper-roman|uppercase|use-script|vertical-ideographic|vertical-text|visible|w-resize|wait|whitespace|z-index|zero",f=t.supportConstantColor="aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow",l=t.supportConstantFonts="arial|century|comic|courier|cursive|fantasy|garamond|georgia|helvetica|impact|lucida|symbol|system|tahoma|times|trebuchet|utopia|verdana|webdings|sans-serif|serif|monospace",c=t.numRe="\\-?(?:(?:[0-9]+)|(?:[0-9]*\\.[0-9]+))",h=t.pseudoElements="(\\:+)\\b(after|before|first-letter|first-line|moz-selection|selection)\\b",p=t.pseudoClasses="(:)\\b(active|checked|disabled|empty|enabled|first-child|first-of-type|focus|hover|indeterminate|invalid|last-child|last-of-type|link|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|only-child|only-of-type|required|root|target|valid|visited)\\b",d=function(){var e=this.createKeywordMapper({"support.function":u,"support.constant":a,"support.type":o,"support.constant.color":f,"support.constant.fonts":l},"text",!0);this.$rules={start:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"@.*?{",push:"media"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],media:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"\\}",next:"pop"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],comment:[{token:"comment",regex:"\\*\\/",next:"pop"},{defaultToken:"comment"}],ruleset:[{token:"paren.rparen",regex:"\\}",next:"pop"},{token:"comment",regex:"\\/\\*",push:"comment"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:["constant.numeric","keyword"],regex:"("+c+")(ch|cm|deg|em|ex|fr|gd|grad|Hz|in|kHz|mm|ms|pc|pt|px|rad|rem|s|turn|vh|vm|vw|%)"},{token:"constant.numeric",regex:c},{token:"constant.numeric",regex:"#[a-f0-9]{6}"},{token:"constant.numeric",regex:"#[a-f0-9]{3}"},{token:["punctuation","entity.other.attribute-name.pseudo-element.css"],regex:h},{token:["punctuation","entity.other.attribute-name.pseudo-class.css"],regex:p},{token:["support.function","string","support.function"],regex:"(url\\()(.*)(\\))"},{token:e,regex:"\\-?[a-zA-Z_][a-zA-Z0-9_\\-]*"},{caseInsensitive:!0}]},this.normalizeRules()};r.inherits(d,s),t.CssHighlightRules=d}),define("ace/mode/css_completions",["require","exports","module"],function(e,t,n){"use strict";var r={background:{"#$0":1},"background-color":{"#$0":1,transparent:1,fixed:1},"background-image":{"url('/$0')":1},"background-repeat":{repeat:1,"repeat-x":1,"repeat-y":1,"no-repeat":1,inherit:1},"background-position":{bottom:2,center:2,left:2,right:2,top:2,inherit:2},"background-attachment":{scroll:1,fixed:1},"background-size":{cover:1,contain:1},"background-clip":{"border-box":1,"padding-box":1,"content-box":1},"background-origin":{"border-box":1,"padding-box":1,"content-box":1},border:{"solid $0":1,"dashed $0":1,"dotted $0":1,"#$0":1},"border-color":{"#$0":1},"border-style":{solid:2,dashed:2,dotted:2,"double":2,groove:2,hidden:2,inherit:2,inset:2,none:2,outset:2,ridged:2},"border-collapse":{collapse:1,separate:1},bottom:{px:1,em:1,"%":1},clear:{left:1,right:1,both:1,none:1},color:{"#$0":1,"rgb(#$00,0,0)":1},cursor:{"default":1,pointer:1,move:1,text:1,wait:1,help:1,progress:1,"n-resize":1,"ne-resize":1,"e-resize":1,"se-resize":1,"s-resize":1,"sw-resize":1,"w-resize":1,"nw-resize":1},display:{none:1,block:1,inline:1,"inline-block":1,"table-cell":1},"empty-cells":{show:1,hide:1},"float":{left:1,right:1,none:1},"font-family":{Arial:2,"Comic Sans MS":2,Consolas:2,"Courier New":2,Courier:2,Georgia:2,Monospace:2,"Sans-Serif":2,"Segoe UI":2,Tahoma:2,"Times New Roman":2,"Trebuchet MS":2,Verdana:1},"font-size":{px:1,em:1,"%":1},"font-weight":{bold:1,normal:1},"font-style":{italic:1,normal:1},"font-variant":{normal:1,"small-caps":1},height:{px:1,em:1,"%":1},left:{px:1,em:1,"%":1},"letter-spacing":{normal:1},"line-height":{normal:1},"list-style-type":{none:1,disc:1,circle:1,square:1,decimal:1,"decimal-leading-zero":1,"lower-roman":1,"upper-roman":1,"lower-greek":1,"lower-latin":1,"upper-latin":1,georgian:1,"lower-alpha":1,"upper-alpha":1},margin:{px:1,em:1,"%":1},"margin-right":{px:1,em:1,"%":1},"margin-left":{px:1,em:1,"%":1},"margin-top":{px:1,em:1,"%":1},"margin-bottom":{px:1,em:1,"%":1},"max-height":{px:1,em:1,"%":1},"max-width":{px:1,em:1,"%":1},"min-height":{px:1,em:1,"%":1},"min-width":{px:1,em:1,"%":1},overflow:{hidden:1,visible:1,auto:1,scroll:1},"overflow-x":{hidden:1,visible:1,auto:1,scroll:1},"overflow-y":{hidden:1,visible:1,auto:1,scroll:1},padding:{px:1,em:1,"%":1},"padding-top":{px:1,em:1,"%":1},"padding-right":{px:1,em:1,"%":1},"padding-bottom":{px:1,em:1,"%":1},"padding-left":{px:1,em:1,"%":1},"page-break-after":{auto:1,always:1,avoid:1,left:1,right:1},"page-break-before":{auto:1,always:1,avoid:1,left:1,right:1},position:{absolute:1,relative:1,fixed:1,"static":1},right:{px:1,em:1,"%":1},"table-layout":{fixed:1,auto:1},"text-decoration":{none:1,underline:1,"line-through":1,blink:1},"text-align":{left:1,right:1,center:1,justify:1},"text-transform":{capitalize:1,uppercase:1,lowercase:1,none:1},top:{px:1,em:1,"%":1},"vertical-align":{top:1,bottom:1},visibility:{hidden:1,visible:1},"white-space":{nowrap:1,normal:1,pre:1,"pre-line":1,"pre-wrap":1},width:{px:1,em:1,"%":1},"word-spacing":{normal:1},filter:{"alpha(opacity=$0100)":1},"text-shadow":{"$02px 2px 2px #777":1},"text-overflow":{"ellipsis-word":1,clip:1,ellipsis:1},"-moz-border-radius":1,"-moz-border-radius-topright":1,"-moz-border-radius-bottomright":1,"-moz-border-radius-topleft":1,"-moz-border-radius-bottomleft":1,"-webkit-border-radius":1,"-webkit-border-top-right-radius":1,"-webkit-border-top-left-radius":1,"-webkit-border-bottom-right-radius":1,"-webkit-border-bottom-left-radius":1,"-moz-box-shadow":1,"-webkit-box-shadow":1,transform:{"rotate($00deg)":1,"skew($00deg)":1},"-moz-transform":{"rotate($00deg)":1,"skew($00deg)":1},"-webkit-transform":{"rotate($00deg)":1,"skew($00deg)":1}},i=function(){};(function(){this.completionsDefined=!1,this.defineCompletions=function(){if(document){var e=document.createElement("c").style;for(var t in e){if(typeof e[t]!="string")continue;var n=t.replace(/[A-Z]/g,function(e){return"-"+e.toLowerCase()});r.hasOwnProperty(n)||(r[n]=1)}}this.completionsDefined=!0},this.getCompletions=function(e,t,n,r){this.completionsDefined||this.defineCompletions();var i=t.getTokenAt(n.row,n.column);if(!i)return[];if(e==="ruleset"){var s=t.getLine(n.row).substr(0,n.column);return/:[^;]+$/.test(s)?(/([\w\-]+):[^:]*$/.test(s),this.getPropertyValueCompletions(e,t,n,r)):this.getPropertyCompletions(e,t,n,r)}return[]},this.getPropertyCompletions=function(e,t,n,i){var s=Object.keys(r);return s.map(function(e){return{caption:e,snippet:e+": $0",meta:"property",score:Number.MAX_VALUE}})},this.getPropertyValueCompletions=function(e,t,n,i){var s=t.getLine(n.row).substr(0,n.column),o=(/([\w\-]+):[^:]*$/.exec(s)||{})[1];if(!o)return[];var u=[];return o in r&&typeof r[o]=="object"&&(u=Object.keys(r[o])),u.map(function(e){return{caption:e,snippet:e,meta:"property value",score:Number.MAX_VALUE}})}}).call(i.prototype),t.CssCompletions=i}),define("ace/mode/behaviour/css",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/mode/behaviour/cstyle","ace/token_iterator"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("./cstyle").CstyleBehaviour,o=e("../../token_iterator").TokenIterator,u=function(){this.inherit(s),this.add("colon","insertion",function(e,t,n,r,i){if(i===":"){var s=n.getCursorPosition(),u=new o(r,s.row,s.column),a=u.getCurrentToken();a&&a.value.match(/\s+/)&&(a=u.stepBackward());if(a&&a.type==="support.type"){var f=r.doc.getLine(s.row),l=f.substring(s.column,s.column+1);if(l===":")return{text:"",selection:[1,1]};if(!f.substring(s.column).match(/^\s*;/))return{text:":;",selection:[1,1]}}}}),this.add("colon","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s===":"){var u=n.getCursorPosition(),a=new o(r,u.row,u.column),f=a.getCurrentToken();f&&f.value.match(/\s+/)&&(f=a.stepBackward());if(f&&f.type==="support.type"){var l=r.doc.getLine(i.start.row),c=l.substring(i.end.column,i.end.column+1);if(c===";")return i.end.column++,i}}}),this.add("semicolon","insertion",function(e,t,n,r,i){if(i===";"){var s=n.getCursorPosition(),o=r.doc.getLine(s.row),u=o.substring(s.column,s.column+1);if(u===";")return{text:"",selection:[1,1]}}})};r.inherits(u,s),t.CssBehaviour=u}),define("ace/mode/css",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/css_highlight_rules","ace/mode/matching_brace_outdent","ace/worker/worker_client","ace/mode/css_completions","ace/mode/behaviour/css","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./css_highlight_rules").CssHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../worker/worker_client").WorkerClient,a=e("./css_completions").CssCompletions,f=e("./behaviour/css").CssBehaviour,l=e("./folding/cstyle").FoldMode,c=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new f,this.$completer=new a,this.foldingRules=new l};r.inherits(c,i),function(){this.foldingRules="cStyle",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e).tokens;if(i.length&&i[i.length-1].type=="comment")return r;var s=t.match(/^.*\{\s*$/);return s&&(r+=n),r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.getCompletions=function(e,t,n,r){return this.$completer.getCompletions(e,t,n,r)},this.createWorker=function(e){var t=new u(["ace"],"ace/mode/css_worker","Worker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/css"}.call(c.prototype),t.Mode=c}),define("ace/mode/xml_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(e){var t="[_:a-zA-Z\u00c0-\uffff][-_:.a-zA-Z0-9\u00c0-\uffff]*";this.$rules={start:[{token:"string.cdata.xml",regex:"<\\!\\[CDATA\\[",next:"cdata"},{token:["punctuation.xml-decl.xml","keyword.xml-decl.xml"],regex:"(<\\?)(xml)(?=[\\s])",next:"xml_decl",caseInsensitive:!0},{token:["punctuation.instruction.xml","keyword.instruction.xml"],regex:"(<\\?)("+t+")",next:"processing_instruction"},{token:"comment.xml",regex:"<\\!--",next:"comment"},{token:["xml-pe.doctype.xml","xml-pe.doctype.xml"],regex:"(<\\!)(DOCTYPE)(?=[\\s])",next:"doctype",caseInsensitive:!0},{include:"tag"},{token:"text.end-tag-open.xml",regex:"",next:"start"}],processing_instruction:[{token:"punctuation.instruction.xml",regex:"\\?>",next:"start"},{defaultToken:"instruction.xml"}],doctype:[{include:"whitespace"},{include:"string"},{token:"xml-pe.doctype.xml",regex:">",next:"start"},{token:"xml-pe.xml",regex:"[-_a-zA-Z0-9:]+"},{token:"punctuation.int-subset",regex:"\\[",push:"int_subset"}],int_subset:[{token:"text.xml",regex:"\\s+"},{token:"punctuation.int-subset.xml",regex:"]",next:"pop"},{token:["punctuation.markup-decl.xml","keyword.markup-decl.xml"],regex:"(<\\!)("+t+")",push:[{token:"text",regex:"\\s+"},{token:"punctuation.markup-decl.xml",regex:">",next:"pop"},{include:"string"}]}],cdata:[{token:"string.cdata.xml",regex:"\\]\\]>",next:"start"},{token:"text.xml",regex:"\\s+"},{token:"text.xml",regex:"(?:[^\\]]|\\](?!\\]>))+"}],comment:[{token:"comment.xml",regex:"-->",next:"start"},{defaultToken:"comment.xml"}],reference:[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}],attr_reference:[{token:"constant.language.escape.reference.attribute-value.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}],tag:[{token:["meta.tag.punctuation.tag-open.xml","meta.tag.punctuation.end-tag-open.xml","meta.tag.tag-name.xml"],regex:"(?:(<)|(",next:"start"}]}],tag_whitespace:[{token:"text.tag-whitespace.xml",regex:"\\s+"}],whitespace:[{token:"text.whitespace.xml",regex:"\\s+"}],string:[{token:"string.xml",regex:"'",push:[{token:"string.xml",regex:"'",next:"pop"},{defaultToken:"string.xml"}]},{token:"string.xml",regex:'"',push:[{token:"string.xml",regex:'"',next:"pop"},{defaultToken:"string.xml"}]}],attributes:[{token:"entity.other.attribute-name.xml",regex:"(?:"+t+":)?"+t+""},{token:"keyword.operator.attribute-equals.xml",regex:"="},{include:"tag_whitespace"},{include:"attribute_value"}],attribute_value:[{token:"string.attribute-value.xml",regex:"'",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"attr_reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"attr_reference"},{defaultToken:"string.attribute-value.xml"}]}]},this.constructor===s&&this.normalizeRules()};(function(){this.embedTagRules=function(e,t,n){this.$rules.tag.unshift({token:["meta.tag.punctuation.tag-open.xml","meta.tag."+n+".tag-name.xml"],regex:"(<)("+n+"(?=\\s|>|$))",next:[{include:"attributes"},{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",next:t+"start"}]}),this.$rules[n+"-end"]=[{include:"attributes"},{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",next:"start",onMatch:function(e,t,n){return n.splice(0),this.token}}],this.embedRules(e,t,[{token:["meta.tag.punctuation.end-tag-open.xml","meta.tag."+n+".tag-name.xml"],regex:"(|$))",next:n+"-end"},{token:"string.cdata.xml",regex:"<\\!\\[CDATA\\["},{token:"string.cdata.xml",regex:"\\]\\]>"}])}}).call(i.prototype),r.inherits(s,i),t.XmlHighlightRules=s}),define("ace/mode/html_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/css_highlight_rules","ace/mode/javascript_highlight_rules","ace/mode/xml_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./css_highlight_rules").CssHighlightRules,o=e("./javascript_highlight_rules").JavaScriptHighlightRules,u=e("./xml_highlight_rules").XmlHighlightRules,a=i.createMap({a:"anchor",button:"form",form:"form",img:"image",input:"form",label:"form",option:"form",script:"script",select:"form",textarea:"form",style:"style",table:"table",tbody:"table",td:"table",tfoot:"table",th:"table",tr:"table"}),f=function(){u.call(this),this.addRules({attributes:[{include:"tag_whitespace"},{token:"entity.other.attribute-name.xml",regex:"[-_a-zA-Z0-9:.]+"},{token:"keyword.operator.attribute-equals.xml",regex:"=",push:[{include:"tag_whitespace"},{token:"string.unquoted.attribute-value.html",regex:"[^<>='\"`\\s]+",next:"pop"},{token:"empty",regex:"",next:"pop"}]},{include:"attribute_value"}],tag:[{token:function(e,t){var n=a[t];return["meta.tag.punctuation."+(e=="<"?"":"end-")+"tag-open.xml","meta.tag"+(n?"."+n:"")+".tag-name.xml"]},regex:"(",next:"start"}]}),this.embedTagRules(s,"css-","style"),this.embedTagRules((new o({jsx:!1})).getRules(),"js-","script"),this.constructor===f&&this.normalizeRules()};r.inherits(f,u),t.HtmlHighlightRules=f}),define("ace/mode/behaviour/xml",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";function u(e,t){return e.type.lastIndexOf(t+".xml")>-1}var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),a=function(){this.add("string_dquotes","insertion",function(e,t,n,r,i){if(i=='"'||i=="'"){var o=i,a=r.doc.getTextRange(n.getSelectionRange());if(a!==""&&a!=="'"&&a!='"'&&n.getWrapBehavioursEnabled())return{text:o+a+o,selection:!1};var f=n.getCursorPosition(),l=r.doc.getLine(f.row),c=l.substring(f.column,f.column+1),h=new s(r,f.row,f.column),p=h.getCurrentToken();if(c==o&&(u(p,"attribute-value")||u(p,"string")))return{text:"",selection:[1,1]};p||(p=h.stepBackward());if(!p)return;while(u(p,"tag-whitespace")||u(p,"whitespace"))p=h.stepBackward();var d=!c||c.match(/\s/);if(u(p,"attribute-equals")&&(d||c==">")||u(p,"decl-attribute-equals")&&(d||c=="?"))return{text:o+o,selection:[1,1]}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&(s=='"'||s=="'")){var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==s)return i.end.column++,i}}),this.add("autoclosing","insertion",function(e,t,n,r,i){if(i==">"){var o=n.getSelectionRange().start,a=new s(r,o.row,o.column),f=a.getCurrentToken()||a.stepBackward();if(!f||!(u(f,"tag-name")||u(f,"tag-whitespace")||u(f,"attribute-name")||u(f,"attribute-equals")||u(f,"attribute-value")))return;if(u(f,"reference.attribute-value"))return;if(u(f,"attribute-value")){var l=f.value.charAt(0);if(l=='"'||l=="'"){var c=f.value.charAt(f.value.length-1),h=a.getCurrentTokenColumn()+f.value.length;if(h>o.column||h==o.column&&l!=c)return}}while(!u(f,"tag-name")){f=a.stepBackward();if(f.value=="<"){f=a.stepForward();break}}var p=a.getCurrentTokenRow(),d=a.getCurrentTokenColumn();if(u(a.stepBackward(),"end-tag-open"))return;var v=f.value;p==o.row&&(v=v.substring(0,o.column-d));if(this.voidElements.hasOwnProperty(v.toLowerCase()))return;return{text:">",selection:[1,1]}}}),this.add("autoindent","insertion",function(e,t,n,r,i){if(i=="\n"){var o=n.getCursorPosition(),u=r.getLine(o.row),a=new s(r,o.row,o.column),f=a.getCurrentToken();if(f&&f.type.indexOf("tag-close")!==-1){if(f.value=="/>")return;while(f&&f.type.indexOf("tag-name")===-1)f=a.stepBackward();if(!f)return;var l=f.value,c=a.getCurrentTokenRow();f=a.stepBackward();if(!f||f.type.indexOf("end-tag")!==-1)return;if(this.voidElements&&!this.voidElements[l]){var h=r.getTokenAt(o.row,o.column+1),u=r.getLine(c),p=this.$getIndent(u),d=p+r.getTabString();return h&&h.value==="-1}var r=e("../../lib/oop"),i=e("../../lib/lang"),s=e("../../range").Range,o=e("./fold_mode").FoldMode,u=e("../../token_iterator").TokenIterator,a=t.FoldMode=function(e,t){o.call(this),this.voidElements=e||{},this.optionalEndTags=r.mixin({},this.voidElements),t&&r.mixin(this.optionalEndTags,t)};r.inherits(a,o);var f=function(){this.tagName="",this.closing=!1,this.selfClosing=!1,this.start={row:0,column:0},this.end={row:0,column:0}};(function(){this.getFoldWidget=function(e,t,n){var r=this._getFirstTagInLine(e,n);return r?r.closing||!r.tagName&&r.selfClosing?t=="markbeginend"?"end":"":!r.tagName||r.selfClosing||this.voidElements.hasOwnProperty(r.tagName.toLowerCase())?"":this._findEndTagInLine(e,n,r.tagName,r.end.column)?"":"start":""},this._getFirstTagInLine=function(e,t){var n=e.getTokens(t),r=new f;for(var i=0;i";break}}return r}if(l(s,"tag-close"))return r.selfClosing=s.value=="/>",r;r.start.column+=s.value.length}return null},this._findEndTagInLine=function(e,t,n,r){var i=e.getTokens(t),s=0;for(var o=0;o",n.end.row=e.getCurrentTokenRow(),n.end.column=e.getCurrentTokenColumn()+t.value.length,e.stepForward(),n;while(t=e.stepForward());return null},this._readTagBackward=function(e){var t=e.getCurrentToken();if(!t)return null;var n=new f;do{if(l(t,"tag-open"))return n.closing=l(t,"end-tag-open"),n.start.row=e.getCurrentTokenRow(),n.start.column=e.getCurrentTokenColumn(),e.stepBackward(),n;l(t,"tag-name")?n.tagName=t.value:l(t,"tag-close")&&(n.selfClosing=t.value=="/>",n.end.row=e.getCurrentTokenRow(),n.end.column=e.getCurrentTokenColumn()+t.value.length)}while(t=e.stepBackward());return null},this._pop=function(e,t){while(e.length){var n=e[e.length-1];if(!t||n.tagName==t.tagName)return e.pop();if(this.optionalEndTags.hasOwnProperty(n.tagName)){e.pop();continue}return null}},this.getFoldWidgetRange=function(e,t,n){var r=this._getFirstTagInLine(e,n);if(!r)return null;var i=r.closing||r.selfClosing,o=[],a;if(!i){var f=new u(e,n,r.start.column),l={row:n,column:r.start.column+r.tagName.length+2};r.start.row==r.end.row&&(l.column=r.end.column);while(a=this._readTagForward(f)){if(a.selfClosing){if(!o.length)return a.start.column+=a.tagName.length+2,a.end.column-=2,s.fromPoints(a.start,a.end);continue}if(a.closing){this._pop(o,a);if(o.length==0)return s.fromPoints(l,a.start)}else o.push(a)}}else{var f=new u(e,n,r.end.column),c={row:n,column:r.start.column};while(a=this._readTagBackward(f)){if(a.selfClosing){if(!o.length)return a.start.column+=a.tagName.length+2,a.end.column-=2,s.fromPoints(a.start,a.end);continue}if(!a.closing){this._pop(o,a);if(o.length==0)return a.start.column+=a.tagName.length+2,a.start.row==a.end.row&&a.start.column-1}function l(e,t){var n=new r(e,t.row,t.column),i=n.getCurrentToken();while(i&&!f(i,"tag-name"))i=n.stepBackward();if(i)return i.value}function c(e,t){var n=new r(e,t.row,t.column),i=n.getCurrentToken();while(i&&!f(i,"attribute-name"))i=n.stepBackward();if(i)return i.value}var r=e("../token_iterator").TokenIterator,i=["accesskey","class","contenteditable","contextmenu","dir","draggable","dropzone","hidden","id","inert","itemid","itemprop","itemref","itemscope","itemtype","lang","spellcheck","style","tabindex","title","translate"],s=["onabort","onblur","oncancel","oncanplay","oncanplaythrough","onchange","onclick","onclose","oncontextmenu","oncuechange","ondblclick","ondrag","ondragend","ondragenter","ondragleave","ondragover","ondragstart","ondrop","ondurationchange","onemptied","onended","onerror","onfocus","oninput","oninvalid","onkeydown","onkeypress","onkeyup","onload","onloadeddata","onloadedmetadata","onloadstart","onmousedown","onmousemove","onmouseout","onmouseover","onmouseup","onmousewheel","onpause","onplay","onplaying","onprogress","onratechange","onreset","onscroll","onseeked","onseeking","onselect","onshow","onstalled","onsubmit","onsuspend","ontimeupdate","onvolumechange","onwaiting"],o=i.concat(s),u={html:{manifest:1},head:{},title:{},base:{href:1,target:1},link:{href:1,hreflang:1,rel:{stylesheet:1,icon:1},media:{all:1,screen:1,print:1},type:{"text/css":1,"image/png":1,"image/jpeg":1,"image/gif":1},sizes:1},meta:{"http-equiv":{"content-type":1},name:{description:1,keywords:1},content:{"text/html; charset=UTF-8":1},charset:1},style:{type:1,media:{all:1,screen:1,print:1},scoped:1},script:{charset:1,type:{"text/javascript":1},src:1,defer:1,async:1},noscript:{href:1},body:{onafterprint:1,onbeforeprint:1,onbeforeunload:1,onhashchange:1,onmessage:1,onoffline:1,onpopstate:1,onredo:1,onresize:1,onstorage:1,onundo:1,onunload:1},section:{},nav:{},article:{pubdate:1},aside:{},h1:{},h2:{},h3:{},h4:{},h5:{},h6:{},header:{},footer:{},address:{},main:{},p:{},hr:{},pre:{},blockquote:{cite:1},ol:{start:1,reversed:1},ul:{},li:{value:1},dl:{},dt:{},dd:{},figure:{},figcaption:{},div:{},a:{href:1,target:{_blank:1,top:1},ping:1,rel:{nofollow:1,alternate:1,author:1,bookmark:1,help:1,license:1,next:1,noreferrer:1,prefetch:1,prev:1,search:1,tag:1},media:1,hreflang:1,type:1},em:{},strong:{},small:{},s:{},cite:{},q:{cite:1},dfn:{},abbr:{},data:{},time:{datetime:1},code:{},"var":{},samp:{},kbd:{},sub:{},sup:{},i:{},b:{},u:{},mark:{},ruby:{},rt:{},rp:{},bdi:{},bdo:{},span:{},br:{},wbr:{},ins:{cite:1,datetime:1},del:{cite:1,datetime:1},img:{alt:1,src:1,height:1,width:1,usemap:1,ismap:1},iframe:{name:1,src:1,height:1,width:1,sandbox:{"allow-same-origin":1,"allow-top-navigation":1,"allow-forms":1,"allow-scripts":1},seamless:{seamless:1}},embed:{src:1,height:1,width:1,type:1},object:{param:1,data:1,type:1,height:1,width:1,usemap:1,name:1,form:1,classid:1},param:{name:1,value:1},video:{src:1,autobuffer:1,autoplay:{autoplay:1},loop:{loop:1},controls:{controls:1},width:1,height:1,poster:1,muted:{muted:1},preload:{auto:1,metadata:1,none:1}},audio:{src:1,autobuffer:1,autoplay:{autoplay:1},loop:{loop:1},controls:{controls:1},muted:{muted:1},preload:{auto:1,metadata:1,none:1}},source:{src:1,type:1,media:1},track:{kind:1,src:1,srclang:1,label:1,"default":1},canvas:{width:1,height:1},map:{name:1},area:{shape:1,coords:1,href:1,hreflang:1,alt:1,target:1,media:1,rel:1,ping:1,type:1},svg:{},math:{},table:{summary:1},caption:{},colgroup:{span:1},col:{span:1},tbody:{},thead:{},tfoot:{},tr:{},td:{headers:1,rowspan:1,colspan:1},th:{headers:1,rowspan:1,colspan:1,scope:1},form:{"accept-charset":1,action:1,autocomplete:1,enctype:{"multipart/form-data":1,"application/x-www-form-urlencoded":1},method:{get:1,post:1},name:1,novalidate:1,target:{_blank:1,top:1}},fieldset:{disabled:1,form:1,name:1},legend:{},label:{form:1,"for":1},input:{type:{text:1,password:1,hidden:1,checkbox:1,submit:1,radio:1,file:1,button:1,reset:1,image:31,color:1,date:1,datetime:1,"datetime-local":1,email:1,month:1,number:1,range:1,search:1,tel:1,time:1,url:1,week:1},accept:1,alt:1,autocomplete:{on:1,off:1},autofocus:{autofocus:1},checked:{checked:1},disabled:{disabled:1},form:1,formaction:1,formenctype:{"application/x-www-form-urlencoded":1,"multipart/form-data":1,"text/plain":1},formmethod:{get:1,post:1},formnovalidate:{formnovalidate:1},formtarget:{_blank:1,_self:1,_parent:1,_top:1},height:1,list:1,max:1,maxlength:1,min:1,multiple:{multiple:1},name:1,pattern:1,placeholder:1,readonly:{readonly:1},required:{required:1},size:1,src:1,step:1,width:1,files:1,value:1},button:{autofocus:1,disabled:{disabled:1},form:1,formaction:1,formenctype:1,formmethod:1,formnovalidate:1,formtarget:1,name:1,value:1,type:{button:1,submit:1}},select:{autofocus:1,disabled:1,form:1,multiple:{multiple:1},name:1,size:1,readonly:{readonly:1}},datalist:{},optgroup:{disabled:1,label:1},option:{disabled:1,selected:1,label:1,value:1},textarea:{autofocus:{autofocus:1},disabled:{disabled:1},form:1,maxlength:1,name:1,placeholder:1,readonly:{readonly:1},required:{required:1},rows:1,cols:1,wrap:{on:1,off:1,hard:1,soft:1}},keygen:{autofocus:1,challenge:{challenge:1},disabled:{disabled:1},form:1,keytype:{rsa:1,dsa:1,ec:1},name:1},output:{"for":1,form:1,name:1},progress:{value:1,max:1},meter:{value:1,min:1,max:1,low:1,high:1,optimum:1},details:{open:1},summary:{},command:{type:1,label:1,icon:1,disabled:1,checked:1,radiogroup:1,command:1},menu:{type:1,label:1},dialog:{open:1}},a=Object.keys(u),h=function(){};(function(){this.getCompletions=function(e,t,n,r){var i=t.getTokenAt(n.row,n.column);if(!i)return[];if(f(i,"tag-name")||f(i,"tag-open")||f(i,"end-tag-open"))return this.getTagCompletions(e,t,n,r);if(f(i,"tag-whitespace")||f(i,"attribute-name"))return this.getAttributeCompletions(e,t,n,r);if(f(i,"attribute-value"))return this.getAttributeValueCompletions(e,t,n,r);var s=t.getLine(n.row).substr(0,n.column);return/&[a-z]*$/i.test(s)?this.getHTMLEntityCompletions(e,t,n,r):[]},this.getTagCompletions=function(e,t,n,r){return a.map(function(e){return{value:e,meta:"tag",score:Number.MAX_VALUE}})},this.getAttributeCompletions=function(e,t,n,r){var i=l(t,n);if(!i)return[];var s=o;return i in u&&(s=s.concat(Object.keys(u[i]))),s.map(function(e){return{caption:e,snippet:e+'="$0"',meta:"attribute",score:Number.MAX_VALUE}})},this.getAttributeValueCompletions=function(e,t,n,r){var i=l(t,n),s=c(t,n);if(!i)return[];var o=[];return i in u&&s in u[i]&&typeof u[i][s]=="object"&&(o=Object.keys(u[i][s])),o.map(function(e){return{caption:e,snippet:e,meta:"attribute value",score:Number.MAX_VALUE}})},this.getHTMLEntityCompletions=function(e,t,n,r){var i=["Aacute;","aacute;","Acirc;","acirc;","acute;","AElig;","aelig;","Agrave;","agrave;","alefsym;","Alpha;","alpha;","amp;","and;","ang;","Aring;","aring;","asymp;","Atilde;","atilde;","Auml;","auml;","bdquo;","Beta;","beta;","brvbar;","bull;","cap;","Ccedil;","ccedil;","cedil;","cent;","Chi;","chi;","circ;","clubs;","cong;","copy;","crarr;","cup;","curren;","Dagger;","dagger;","dArr;","darr;","deg;","Delta;","delta;","diams;","divide;","Eacute;","eacute;","Ecirc;","ecirc;","Egrave;","egrave;","empty;","emsp;","ensp;","Epsilon;","epsilon;","equiv;","Eta;","eta;","ETH;","eth;","Euml;","euml;","euro;","exist;","fnof;","forall;","frac12;","frac14;","frac34;","frasl;","Gamma;","gamma;","ge;","gt;","hArr;","harr;","hearts;","hellip;","Iacute;","iacute;","Icirc;","icirc;","iexcl;","Igrave;","igrave;","image;","infin;","int;","Iota;","iota;","iquest;","isin;","Iuml;","iuml;","Kappa;","kappa;","Lambda;","lambda;","lang;","laquo;","lArr;","larr;","lceil;","ldquo;","le;","lfloor;","lowast;","loz;","lrm;","lsaquo;","lsquo;","lt;","macr;","mdash;","micro;","middot;","minus;","Mu;","mu;","nabla;","nbsp;","ndash;","ne;","ni;","not;","notin;","nsub;","Ntilde;","ntilde;","Nu;","nu;","Oacute;","oacute;","Ocirc;","ocirc;","OElig;","oelig;","Ograve;","ograve;","oline;","Omega;","omega;","Omicron;","omicron;","oplus;","or;","ordf;","ordm;","Oslash;","oslash;","Otilde;","otilde;","otimes;","Ouml;","ouml;","para;","part;","permil;","perp;","Phi;","phi;","Pi;","pi;","piv;","plusmn;","pound;","Prime;","prime;","prod;","prop;","Psi;","psi;","quot;","radic;","rang;","raquo;","rArr;","rarr;","rceil;","rdquo;","real;","reg;","rfloor;","Rho;","rho;","rlm;","rsaquo;","rsquo;","sbquo;","Scaron;","scaron;","sdot;","sect;","shy;","Sigma;","sigma;","sigmaf;","sim;","spades;","sub;","sube;","sum;","sup;","sup1;","sup2;","sup3;","supe;","szlig;","Tau;","tau;","there4;","Theta;","theta;","thetasym;","thinsp;","THORN;","thorn;","tilde;","times;","trade;","Uacute;","uacute;","uArr;","uarr;","Ucirc;","ucirc;","Ugrave;","ugrave;","uml;","upsih;","Upsilon;","upsilon;","Uuml;","uuml;","weierp;","Xi;","xi;","Yacute;","yacute;","yen;","Yuml;","yuml;","Zeta;","zeta;","zwj;","zwnj;"];return i.map(function(e){return{caption:e,snippet:e,meta:"html entity",score:Number.MAX_VALUE}})}}).call(h.prototype),t.HtmlCompletions=h}),define("ace/mode/html",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text","ace/mode/javascript","ace/mode/css","ace/mode/html_highlight_rules","ace/mode/behaviour/xml","ace/mode/folding/html","ace/mode/html_completions","ace/worker/worker_client"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text").Mode,o=e("./javascript").Mode,u=e("./css").Mode,a=e("./html_highlight_rules").HtmlHighlightRules,f=e("./behaviour/xml").XmlBehaviour,l=e("./folding/html").FoldMode,c=e("./html_completions").HtmlCompletions,h=e("../worker/worker_client").WorkerClient,p=["area","base","br","col","embed","hr","img","input","keygen","link","meta","menuitem","param","source","track","wbr"],d=["li","dt","dd","p","rt","rp","optgroup","option","colgroup","td","th"],v=function(e){this.fragmentContext=e&&e.fragmentContext,this.HighlightRules=a,this.$behaviour=new f,this.$completer=new c,this.createModeDelegates({"js-":o,"css-":u}),this.foldingRules=new l(this.voidElements,i.arrayToMap(d))};r.inherits(v,s),function(){this.blockComment={start:""},this.voidElements=i.arrayToMap(p),this.getNextLineIndent=function(e,t,n){return this.$getIndent(t)},this.checkOutdent=function(e,t,n){return!1},this.getCompletions=function(e,t,n,r){return this.$completer.getCompletions(e,t,n,r)},this.createWorker=function(e){if(this.constructor!=v)return;var t=new h(["ace"],"ace/mode/html_worker","Worker");return t.attachToDocument(e.getDocument()),this.fragmentContext&&t.call("setOptions",[{context:this.fragmentContext}]),t.on("error",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/html"}.call(v.prototype),t.Mode=v}),define("ace/mode/smarty_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/html_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./html_highlight_rules").HtmlHighlightRules,s=function(){i.call(this);var e={start:[{include:"#comments"},{include:"#blocks"}],"#blocks":[{token:"punctuation.section.embedded.begin.smarty",regex:"\\{%?",push:[{token:"punctuation.section.embedded.end.smarty",regex:"%?\\}",next:"pop"},{include:"#strings"},{include:"#variables"},{include:"#lang"},{defaultToken:"source.smarty"}]}],"#comments":[{token:["punctuation.definition.comment.smarty","comment.block.smarty"],regex:"(\\{%?)(\\*)",push:[{token:"comment.block.smarty",regex:"\\*%?\\}",next:"pop"},{defaultToken:"comment.block.smarty"}]}],"#lang":[{token:"keyword.operator.smarty",regex:"(?:!=|!|<=|>=|<|>|===|==|%|&&|\\|\\|)|\\b(?:and|or|eq|neq|ne|gte|gt|ge|lte|lt|le|not|mod)\\b"},{token:"constant.language.smarty",regex:"\\b(?:TRUE|FALSE|true|false)\\b"},{token:"keyword.control.smarty",regex:"\\b(?:if|else|elseif|foreach|foreachelse|section|switch|case|break|default)\\b"},{token:"variable.parameter.smarty",regex:"\\b[a-zA-Z]+="},{token:"support.function.built-in.smarty",regex:"\\b(?:capture|config_load|counter|cycle|debug|eval|fetch|include_php|include|insert|literal|math|strip|rdelim|ldelim|assign|constant|block|html_[a-z_]*)\\b"},{token:"support.function.variable-modifier.smarty",regex:"\\|(?:capitalize|cat|count_characters|count_paragraphs|count_sentences|count_words|date_format|default|escape|indent|lower|nl2br|regex_replace|replace|spacify|string_format|strip_tags|strip|truncate|upper|wordwrap)"}],"#strings":[{token:"punctuation.definition.string.begin.smarty",regex:"'",push:[{token:"punctuation.definition.string.end.smarty",regex:"'",next:"pop"},{token:"constant.character.escape.smarty",regex:"\\\\."},{defaultToken:"string.quoted.single.smarty"}]},{token:"punctuation.definition.string.begin.smarty",regex:'"',push:[{token:"punctuation.definition.string.end.smarty",regex:'"',next:"pop"},{token:"constant.character.escape.smarty",regex:"\\\\."},{defaultToken:"string.quoted.double.smarty"}]}],"#variables":[{token:["punctuation.definition.variable.smarty","variable.other.global.smarty"],regex:"\\b(\\$)(Smarty\\.)"},{token:["punctuation.definition.variable.smarty","variable.other.smarty"],regex:"(\\$)([a-zA-Z_][a-zA-Z0-9_]*)\\b"},{token:["keyword.operator.smarty","variable.other.property.smarty"],regex:"(->)([a-zA-Z_][a-zA-Z0-9_]*)\\b"},{token:["keyword.operator.smarty","meta.function-call.object.smarty","punctuation.definition.variable.smarty","variable.other.smarty","punctuation.definition.variable.smarty"],regex:"(->)([a-zA-Z_][a-zA-Z0-9_]*)(\\()(.*?)(\\))"}]},t=e.start;for(var n in this.$rules)this.$rules[n].unshift.apply(this.$rules[n],t);Object.keys(e).forEach(function(t){this.$rules[t]||(this.$rules[t]=e[t])},this),this.normalizeRules()};s.metaData={fileTypes:["tpl"],foldingStartMarker:"\\{%?",foldingStopMarker:"%?\\}",name:"Smarty",scopeName:"text.html.smarty"},r.inherits(s,i),t.SmartyHighlightRules=s}),define("ace/mode/smarty",["require","exports","module","ace/lib/oop","ace/mode/html","ace/mode/smarty_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./html").Mode,s=e("./smarty_highlight_rules").SmartyHighlightRules,o=function(){i.call(this),this.HighlightRules=s};r.inherits(o,i),function(){this.$id="ace/mode/smarty"}.call(o.prototype),t.Mode=o}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-sql.js b/public/themes/pterodactyl/vendor/ace/mode-sql.js new file mode 100644 index 0000000..3441609 --- /dev/null +++ b/public/themes/pterodactyl/vendor/ace/mode-sql.js @@ -0,0 +1 @@ +define("ace/mode/sql_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e="select|insert|update|delete|from|where|and|or|group|by|order|limit|offset|having|as|case|when|else|end|type|left|right|join|on|outer|desc|asc|union|create|table|primary|key|if|foreign|not|references|default|null|inner|cross|natural|database|drop|grant",t="true|false",n="avg|count|first|last|max|min|sum|ucase|lcase|mid|len|round|rank|now|format|coalesce|ifnull|isnull|nvl",r="int|numeric|decimal|date|varchar|char|bigint|float|double|bit|binary|text|set|timestamp|money|real|number|integer",i=this.createKeywordMapper({"support.function":n,keyword:e,"constant.language":t,"storage.type":r},"identifier",!0);this.$rules={start:[{token:"comment",regex:"--.*$"},{token:"comment",start:"/\\*",end:"\\*/"},{token:"string",regex:'".*?"'},{token:"string",regex:"'.*?'"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:i,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+"}]},this.normalizeRules()};r.inherits(s,i),t.SqlHighlightRules=s}),define("ace/mode/sql",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/sql_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./sql_highlight_rules").SqlHighlightRules,o=function(){this.HighlightRules=s,this.$behaviour=this.$defaultBehaviour};r.inherits(o,i),function(){this.lineCommentStart="--",this.$id="ace/mode/sql"}.call(o.prototype),t.Mode=o}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-xml.js b/public/themes/pterodactyl/vendor/ace/mode-xml.js new file mode 100644 index 0000000..44452a4 --- /dev/null +++ b/public/themes/pterodactyl/vendor/ace/mode-xml.js @@ -0,0 +1 @@ +define("ace/mode/xml_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(e){var t="[_:a-zA-Z\u00c0-\uffff][-_:.a-zA-Z0-9\u00c0-\uffff]*";this.$rules={start:[{token:"string.cdata.xml",regex:"<\\!\\[CDATA\\[",next:"cdata"},{token:["punctuation.xml-decl.xml","keyword.xml-decl.xml"],regex:"(<\\?)(xml)(?=[\\s])",next:"xml_decl",caseInsensitive:!0},{token:["punctuation.instruction.xml","keyword.instruction.xml"],regex:"(<\\?)("+t+")",next:"processing_instruction"},{token:"comment.xml",regex:"<\\!--",next:"comment"},{token:["xml-pe.doctype.xml","xml-pe.doctype.xml"],regex:"(<\\!)(DOCTYPE)(?=[\\s])",next:"doctype",caseInsensitive:!0},{include:"tag"},{token:"text.end-tag-open.xml",regex:"",next:"start"}],processing_instruction:[{token:"punctuation.instruction.xml",regex:"\\?>",next:"start"},{defaultToken:"instruction.xml"}],doctype:[{include:"whitespace"},{include:"string"},{token:"xml-pe.doctype.xml",regex:">",next:"start"},{token:"xml-pe.xml",regex:"[-_a-zA-Z0-9:]+"},{token:"punctuation.int-subset",regex:"\\[",push:"int_subset"}],int_subset:[{token:"text.xml",regex:"\\s+"},{token:"punctuation.int-subset.xml",regex:"]",next:"pop"},{token:["punctuation.markup-decl.xml","keyword.markup-decl.xml"],regex:"(<\\!)("+t+")",push:[{token:"text",regex:"\\s+"},{token:"punctuation.markup-decl.xml",regex:">",next:"pop"},{include:"string"}]}],cdata:[{token:"string.cdata.xml",regex:"\\]\\]>",next:"start"},{token:"text.xml",regex:"\\s+"},{token:"text.xml",regex:"(?:[^\\]]|\\](?!\\]>))+"}],comment:[{token:"comment.xml",regex:"-->",next:"start"},{defaultToken:"comment.xml"}],reference:[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}],attr_reference:[{token:"constant.language.escape.reference.attribute-value.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}],tag:[{token:["meta.tag.punctuation.tag-open.xml","meta.tag.punctuation.end-tag-open.xml","meta.tag.tag-name.xml"],regex:"(?:(<)|(",next:"start"}]}],tag_whitespace:[{token:"text.tag-whitespace.xml",regex:"\\s+"}],whitespace:[{token:"text.whitespace.xml",regex:"\\s+"}],string:[{token:"string.xml",regex:"'",push:[{token:"string.xml",regex:"'",next:"pop"},{defaultToken:"string.xml"}]},{token:"string.xml",regex:'"',push:[{token:"string.xml",regex:'"',next:"pop"},{defaultToken:"string.xml"}]}],attributes:[{token:"entity.other.attribute-name.xml",regex:"(?:"+t+":)?"+t+""},{token:"keyword.operator.attribute-equals.xml",regex:"="},{include:"tag_whitespace"},{include:"attribute_value"}],attribute_value:[{token:"string.attribute-value.xml",regex:"'",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"attr_reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"attr_reference"},{defaultToken:"string.attribute-value.xml"}]}]},this.constructor===s&&this.normalizeRules()};(function(){this.embedTagRules=function(e,t,n){this.$rules.tag.unshift({token:["meta.tag.punctuation.tag-open.xml","meta.tag."+n+".tag-name.xml"],regex:"(<)("+n+"(?=\\s|>|$))",next:[{include:"attributes"},{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",next:t+"start"}]}),this.$rules[n+"-end"]=[{include:"attributes"},{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",next:"start",onMatch:function(e,t,n){return n.splice(0),this.token}}],this.embedRules(e,t,[{token:["meta.tag.punctuation.end-tag-open.xml","meta.tag."+n+".tag-name.xml"],regex:"(|$))",next:n+"-end"},{token:"string.cdata.xml",regex:"<\\!\\[CDATA\\["},{token:"string.cdata.xml",regex:"\\]\\]>"}])}}).call(i.prototype),r.inherits(s,i),t.XmlHighlightRules=s}),define("ace/mode/behaviour/xml",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";function u(e,t){return e.type.lastIndexOf(t+".xml")>-1}var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),a=function(){this.add("string_dquotes","insertion",function(e,t,n,r,i){if(i=='"'||i=="'"){var o=i,a=r.doc.getTextRange(n.getSelectionRange());if(a!==""&&a!=="'"&&a!='"'&&n.getWrapBehavioursEnabled())return{text:o+a+o,selection:!1};var f=n.getCursorPosition(),l=r.doc.getLine(f.row),c=l.substring(f.column,f.column+1),h=new s(r,f.row,f.column),p=h.getCurrentToken();if(c==o&&(u(p,"attribute-value")||u(p,"string")))return{text:"",selection:[1,1]};p||(p=h.stepBackward());if(!p)return;while(u(p,"tag-whitespace")||u(p,"whitespace"))p=h.stepBackward();var d=!c||c.match(/\s/);if(u(p,"attribute-equals")&&(d||c==">")||u(p,"decl-attribute-equals")&&(d||c=="?"))return{text:o+o,selection:[1,1]}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&(s=='"'||s=="'")){var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==s)return i.end.column++,i}}),this.add("autoclosing","insertion",function(e,t,n,r,i){if(i==">"){var o=n.getSelectionRange().start,a=new s(r,o.row,o.column),f=a.getCurrentToken()||a.stepBackward();if(!f||!(u(f,"tag-name")||u(f,"tag-whitespace")||u(f,"attribute-name")||u(f,"attribute-equals")||u(f,"attribute-value")))return;if(u(f,"reference.attribute-value"))return;if(u(f,"attribute-value")){var l=f.value.charAt(0);if(l=='"'||l=="'"){var c=f.value.charAt(f.value.length-1),h=a.getCurrentTokenColumn()+f.value.length;if(h>o.column||h==o.column&&l!=c)return}}while(!u(f,"tag-name")){f=a.stepBackward();if(f.value=="<"){f=a.stepForward();break}}var p=a.getCurrentTokenRow(),d=a.getCurrentTokenColumn();if(u(a.stepBackward(),"end-tag-open"))return;var v=f.value;p==o.row&&(v=v.substring(0,o.column-d));if(this.voidElements.hasOwnProperty(v.toLowerCase()))return;return{text:">",selection:[1,1]}}}),this.add("autoindent","insertion",function(e,t,n,r,i){if(i=="\n"){var o=n.getCursorPosition(),u=r.getLine(o.row),a=new s(r,o.row,o.column),f=a.getCurrentToken();if(f&&f.type.indexOf("tag-close")!==-1){if(f.value=="/>")return;while(f&&f.type.indexOf("tag-name")===-1)f=a.stepBackward();if(!f)return;var l=f.value,c=a.getCurrentTokenRow();f=a.stepBackward();if(!f||f.type.indexOf("end-tag")!==-1)return;if(this.voidElements&&!this.voidElements[l]){var h=r.getTokenAt(o.row,o.column+1),u=r.getLine(c),p=this.$getIndent(u),d=p+r.getTabString();return h&&h.value==="-1}var r=e("../../lib/oop"),i=e("../../lib/lang"),s=e("../../range").Range,o=e("./fold_mode").FoldMode,u=e("../../token_iterator").TokenIterator,a=t.FoldMode=function(e,t){o.call(this),this.voidElements=e||{},this.optionalEndTags=r.mixin({},this.voidElements),t&&r.mixin(this.optionalEndTags,t)};r.inherits(a,o);var f=function(){this.tagName="",this.closing=!1,this.selfClosing=!1,this.start={row:0,column:0},this.end={row:0,column:0}};(function(){this.getFoldWidget=function(e,t,n){var r=this._getFirstTagInLine(e,n);return r?r.closing||!r.tagName&&r.selfClosing?t=="markbeginend"?"end":"":!r.tagName||r.selfClosing||this.voidElements.hasOwnProperty(r.tagName.toLowerCase())?"":this._findEndTagInLine(e,n,r.tagName,r.end.column)?"":"start":""},this._getFirstTagInLine=function(e,t){var n=e.getTokens(t),r=new f;for(var i=0;i";break}}return r}if(l(s,"tag-close"))return r.selfClosing=s.value=="/>",r;r.start.column+=s.value.length}return null},this._findEndTagInLine=function(e,t,n,r){var i=e.getTokens(t),s=0;for(var o=0;o",n.end.row=e.getCurrentTokenRow(),n.end.column=e.getCurrentTokenColumn()+t.value.length,e.stepForward(),n;while(t=e.stepForward());return null},this._readTagBackward=function(e){var t=e.getCurrentToken();if(!t)return null;var n=new f;do{if(l(t,"tag-open"))return n.closing=l(t,"end-tag-open"),n.start.row=e.getCurrentTokenRow(),n.start.column=e.getCurrentTokenColumn(),e.stepBackward(),n;l(t,"tag-name")?n.tagName=t.value:l(t,"tag-close")&&(n.selfClosing=t.value=="/>",n.end.row=e.getCurrentTokenRow(),n.end.column=e.getCurrentTokenColumn()+t.value.length)}while(t=e.stepBackward());return null},this._pop=function(e,t){while(e.length){var n=e[e.length-1];if(!t||n.tagName==t.tagName)return e.pop();if(this.optionalEndTags.hasOwnProperty(n.tagName)){e.pop();continue}return null}},this.getFoldWidgetRange=function(e,t,n){var r=this._getFirstTagInLine(e,n);if(!r)return null;var i=r.closing||r.selfClosing,o=[],a;if(!i){var f=new u(e,n,r.start.column),l={row:n,column:r.start.column+r.tagName.length+2};r.start.row==r.end.row&&(l.column=r.end.column);while(a=this._readTagForward(f)){if(a.selfClosing){if(!o.length)return a.start.column+=a.tagName.length+2,a.end.column-=2,s.fromPoints(a.start,a.end);continue}if(a.closing){this._pop(o,a);if(o.length==0)return s.fromPoints(l,a.start)}else o.push(a)}}else{var f=new u(e,n,r.end.column),c={row:n,column:r.start.column};while(a=this._readTagBackward(f)){if(a.selfClosing){if(!o.length)return a.start.column+=a.tagName.length+2,a.end.column-=2,s.fromPoints(a.start,a.end);continue}if(!a.closing){this._pop(o,a);if(o.length==0)return a.start.column+=a.tagName.length+2,a.start.row==a.end.row&&a.start.column"},this.createWorker=function(e){var t=new f(["ace"],"ace/mode/xml_worker","Worker");return t.attachToDocument(e.getDocument()),t.on("error",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/xml"}.call(l.prototype),t.Mode=l}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-yaml.js b/public/themes/pterodactyl/vendor/ace/mode-yaml.js new file mode 100644 index 0000000..64a1bdc --- /dev/null +++ b/public/themes/pterodactyl/vendor/ace/mode-yaml.js @@ -0,0 +1 @@ +define("ace/mode/yaml_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment",regex:"#.*$"},{token:"list.markup",regex:/^(?:-{3}|\.{3})\s*(?=#|$)/},{token:"list.markup",regex:/^\s*[\-?](?:$|\s)/},{token:"constant",regex:"!![\\w//]+"},{token:"constant.language",regex:"[&\\*][a-zA-Z0-9-_]+"},{token:["meta.tag","keyword"],regex:/^(\s*\w.*?)(:(?:\s+|$))/},{token:["meta.tag","keyword"],regex:/(\w+?)(\s*:(?:\s+|$))/},{token:"keyword.operator",regex:"<<\\w*:\\w*"},{token:"keyword.operator",regex:"-\\s*(?=[{])"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:"[|>][-+\\d\\s]*$",next:"qqstring"},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"constant.numeric",regex:/(\b|[+\-\.])[\d_]+(?:(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)/},{token:"constant.numeric",regex:/[+\-]?\.inf\b|NaN\b|0x[\dA-Fa-f_]+|0b[10_]+/},{token:"constant.language.boolean",regex:"\\b(?:true|false|TRUE|FALSE|True|False|yes|no)\\b"},{token:"paren.lparen",regex:"[[({]"},{token:"paren.rparen",regex:"[\\])}]"}],qqstring:[{token:"string",regex:"(?=(?:(?:\\\\.)|(?:[^:]))*?:)",next:"start"},{token:"string",regex:".+"}]}};r.inherits(s,i),t.YamlHighlightRules=s}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define("ace/mode/folding/coffee",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode","ace/range"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("./fold_mode").FoldMode,s=e("../../range").Range,o=t.FoldMode=function(){};r.inherits(o,i),function(){this.getFoldWidgetRange=function(e,t,n){var r=this.indentationBlock(e,n);if(r)return r;var i=/\S/,o=e.getLine(n),u=o.search(i);if(u==-1||o[u]!="#")return;var a=o.length,f=e.getLength(),l=n,c=n;while(++nl){var p=e.getLine(c).length;return new s(l,a,c,p)}},this.getFoldWidget=function(e,t,n){var r=e.getLine(n),i=r.search(/\S/),s=e.getLine(n+1),o=e.getLine(n-1),u=o.search(/\S/),a=s.search(/\S/);if(i==-1)return e.foldWidgets[n-1]=u!=-1&&u0){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;n ["+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.row=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))}}}),define("ace/lib/event_emitter",["require","exports","module"],function(e,t,n){"use strict";var r={},i=function(){this.propagationStopped=!0},s=function(){this.defaultPrevented=!0};r._emit=r._dispatchEvent=function(e,t){this._eventRegistry||(this._eventRegistry={}),this._defaultHandlers||(this._defaultHandlers={});var n=this._eventRegistry[e]||[],r=this._defaultHandlers[e];if(!n.length&&!r)return;if(typeof t!="object"||!t)t={};t.type||(t.type=e),t.stopPropagation||(t.stopPropagation=i),t.preventDefault||(t.preventDefault=s),n=n.slice();for(var o=0;othis.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)}),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,i=e.start.row,s=e.start.column,o=0,u=0;do{o=u,u+=t-1;var a=n.slice(o,u);if(u>r){e.lines=a,e.start.row=i+o,e.start.column=s;break}a.push(""),this.applyDelta({start:this.pos(i+o,s),end:this.pos(i+u,s=0),action:e.action,lines:a},!0)}while(!0)},this.revertDelta=function(e){this.applyDelta({start:this.clonePos(e.start),end:this.clonePos(e.end),action:e.action=="insert"?"remove":"insert",lines:e.lines.slice()})},this.indexToPosition=function(e,t){var n=this.$lines||this.getAllLines(),r=this.getNewLineCharacter().length;for(var i=t||0,s=n.length;i=0&&this._ltIndex-1&&!t[u.type].hide&&(u.channel=t[u.type].channel,this._token=u,this._lt.push(u),this._ltIndexCache.push(this._lt.length-this._ltIndex+i),this._lt.length>5&&this._lt.shift(),this._ltIndexCache.length>5&&this._ltIndexCache.shift(),this._ltIndex=this._lt.length),a=t[u.type],a&&(a.hide||a.channel!==undefined&&e!==a.channel)?this.get(e):u.type},LA:function(e){var t=e,n;if(e>0){if(e>5)throw new Error("Too much lookahead.");while(t)n=this.get(),t--;while(tthis._tokenData.length?"UNKNOWN_TOKEN":this._tokenData[e].name},tokenType:function(e){return this._tokenData[e]||-1},unget:function(){if(!this._ltIndexCache.length)throw new Error("Too much lookahead.");this._ltIndex-=this._ltIndexCache.pop(),this._token=this._lt[this._ltIndex-1]}},parserlib.util={StringReader:t,SyntaxError:n,SyntaxUnit:r,EventTarget:e,TokenStreamBase:i}})(),function(){function Combinator(e,t,n){SyntaxUnit.call(this,e,t,n,Parser.COMBINATOR_TYPE),this.type="unknown",/^\s+$/.test(e)?this.type="descendant":e==">"?this.type="child":e=="+"?this.type="adjacent-sibling":e=="~"&&(this.type="sibling")}function MediaFeature(e,t){SyntaxUnit.call(this,"("+e+(t!==null?":"+t:"")+")",e.startLine,e.startCol,Parser.MEDIA_FEATURE_TYPE),this.name=e,this.value=t}function MediaQuery(e,t,n,r,i){SyntaxUnit.call(this,(e?e+" ":"")+(t?t:"")+(t&&n.length>0?" and ":"")+n.join(" and "),r,i,Parser.MEDIA_QUERY_TYPE),this.modifier=e,this.mediaType=t,this.features=n}function Parser(e){EventTarget.call(this),this.options=e||{},this._tokenStream=null}function PropertyName(e,t,n,r){SyntaxUnit.call(this,e,n,r,Parser.PROPERTY_NAME_TYPE),this.hack=t}function PropertyValue(e,t,n){SyntaxUnit.call(this,e.join(" "),t,n,Parser.PROPERTY_VALUE_TYPE),this.parts=e}function PropertyValueIterator(e){this._i=0,this._parts=e.parts,this._marks=[],this.value=e}function PropertyValuePart(text,line,col){SyntaxUnit.call(this,text,line,col,Parser.PROPERTY_VALUE_PART_TYPE),this.type="unknown";var temp;if(/^([+\-]?[\d\.]+)([a-z]+)$/i.test(text)){this.type="dimension",this.value=+RegExp.$1,this.units=RegExp.$2;switch(this.units.toLowerCase()){case"em":case"rem":case"ex":case"px":case"cm":case"mm":case"in":case"pt":case"pc":case"ch":case"vh":case"vw":case"vmax":case"vmin":this.type="length";break;case"deg":case"rad":case"grad":this.type="angle";break;case"ms":case"s":this.type="time";break;case"hz":case"khz":this.type="frequency";break;case"dpi":case"dpcm":this.type="resolution"}}else/^([+\-]?[\d\.]+)%$/i.test(text)?(this.type="percentage",this.value=+RegExp.$1):/^([+\-]?\d+)$/i.test(text)?(this.type="integer",this.value=+RegExp.$1):/^([+\-]?[\d\.]+)$/i.test(text)?(this.type="number",this.value=+RegExp.$1):/^#([a-f0-9]{3,6})/i.test(text)?(this.type="color",temp=RegExp.$1,temp.length==3?(this.red=parseInt(temp.charAt(0)+temp.charAt(0),16),this.green=parseInt(temp.charAt(1)+temp.charAt(1),16),this.blue=parseInt(temp.charAt(2)+temp.charAt(2),16)):(this.red=parseInt(temp.substring(0,2),16),this.green=parseInt(temp.substring(2,4),16),this.blue=parseInt(temp.substring(4,6),16))):/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i.test(text)?(this.type="color",this.red=+RegExp.$1,this.green=+RegExp.$2,this.blue=+RegExp.$3):/^rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/i.test(text)?(this.type="color",this.red=+RegExp.$1*255/100,this.green=+RegExp.$2*255/100,this.blue=+RegExp.$3*255/100):/^rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*([\d\.]+)\s*\)/i.test(text)?(this.type="color",this.red=+RegExp.$1,this.green=+RegExp.$2,this.blue=+RegExp.$3,this.alpha=+RegExp.$4):/^rgba\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*,\s*([\d\.]+)\s*\)/i.test(text)?(this.type="color",this.red=+RegExp.$1*255/100,this.green=+RegExp.$2*255/100,this.blue=+RegExp.$3*255/100,this.alpha=+RegExp.$4):/^hsl\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/i.test(text)?(this.type="color",this.hue=+RegExp.$1,this.saturation=+RegExp.$2/100,this.lightness=+RegExp.$3/100):/^hsla\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*,\s*([\d\.]+)\s*\)/i.test(text)?(this.type="color",this.hue=+RegExp.$1,this.saturation=+RegExp.$2/100,this.lightness=+RegExp.$3/100,this.alpha=+RegExp.$4):/^url\(["']?([^\)"']+)["']?\)/i.test(text)?(this.type="uri",this.uri=RegExp.$1):/^([^\(]+)\(/i.test(text)?(this.type="function",this.name=RegExp.$1,this.value=text):/^["'][^"']*["']/.test(text)?(this.type="string",this.value=eval(text)):Colors[text.toLowerCase()]?(this.type="color",temp=Colors[text.toLowerCase()].substring(1),this.red=parseInt(temp.substring(0,2),16),this.green=parseInt(temp.substring(2,4),16),this.blue=parseInt(temp.substring(4,6),16)):/^[\,\/]$/.test(text)?(this.type="operator",this.value=text):/^[a-z\-_\u0080-\uFFFF][a-z0-9\-_\u0080-\uFFFF]*$/i.test(text)&&(this.type="identifier",this.value=text)}function Selector(e,t,n){SyntaxUnit.call(this,e.join(" "),t,n,Parser.SELECTOR_TYPE),this.parts=e,this.specificity=Specificity.calculate(this)}function SelectorPart(e,t,n,r,i){SyntaxUnit.call(this,n,r,i,Parser.SELECTOR_PART_TYPE),this.elementName=e,this.modifiers=t}function SelectorSubPart(e,t,n,r){SyntaxUnit.call(this,e,n,r,Parser.SELECTOR_SUB_PART_TYPE),this.type=t,this.args=[]}function Specificity(e,t,n,r){this.a=e,this.b=t,this.c=n,this.d=r}function isHexDigit(e){return e!==null&&h.test(e)}function isDigit(e){return e!==null&&/\d/.test(e)}function isWhitespace(e){return e!==null&&/\s/.test(e)}function isNewLine(e){return e!==null&&nl.test(e)}function isNameStart(e){return e!==null&&/[a-z_\u0080-\uFFFF\\]/i.test(e)}function isNameChar(e){return e!==null&&(isNameStart(e)||/[0-9\-\\]/.test(e))}function isIdentStart(e){return e!==null&&(isNameStart(e)||/\-\\/.test(e))}function mix(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);return e}function TokenStream(e){TokenStreamBase.call(this,e,Tokens)}function ValidationError(e,t,n){this.col=n,this.line=t,this.message=e}var EventTarget=parserlib.util.EventTarget,TokenStreamBase=parserlib.util.TokenStreamBase,StringReader=parserlib.util.StringReader,SyntaxError=parserlib.util.SyntaxError,SyntaxUnit=parserlib.util.SyntaxUnit,Colors={aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgrey:"#a9a9a9",darkgreen:"#006400",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkslategrey:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",grey:"#808080",green:"#008000",greenyellow:"#adff2f",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgray:"#d3d3d3",lightgrey:"#d3d3d3",lightgreen:"#90ee90",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370d8",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#d87093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",slategrey:"#708090",snow:"#fffafa",springgreen:"#00ff7f",steelblue:"#4682b4",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",tomato:"#ff6347",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32",activeBorder:"Active window border.",activecaption:"Active window caption.",appworkspace:"Background color of multiple document interface.",background:"Desktop background.",buttonface:"The face background color for 3-D elements that appear 3-D due to one layer of surrounding border.",buttonhighlight:"The color of the border facing the light source for 3-D elements that appear 3-D due to one layer of surrounding border.",buttonshadow:"The color of the border away from the light source for 3-D elements that appear 3-D due to one layer of surrounding border.",buttontext:"Text on push buttons.",captiontext:"Text in caption, size box, and scrollbar arrow box.",graytext:"Grayed (disabled) text. This color is set to #000 if the current display driver does not support a solid gray color.",greytext:"Greyed (disabled) text. This color is set to #000 if the current display driver does not support a solid grey color.",highlight:"Item(s) selected in a control.",highlighttext:"Text of item(s) selected in a control.",inactiveborder:"Inactive window border.",inactivecaption:"Inactive window caption.",inactivecaptiontext:"Color of text in an inactive caption.",infobackground:"Background color for tooltip controls.",infotext:"Text color for tooltip controls.",menu:"Menu background.",menutext:"Text in menus.",scrollbar:"Scroll bar gray area.",threeddarkshadow:"The color of the darker (generally outer) of the two borders away from the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",threedface:"The face background color for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",threedhighlight:"The color of the lighter (generally outer) of the two borders facing the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",threedlightshadow:"The color of the darker (generally inner) of the two borders facing the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",threedshadow:"The color of the lighter (generally inner) of the two borders away from the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",window:"Window background.",windowframe:"Window frame.",windowtext:"Text in windows."};Combinator.prototype=new SyntaxUnit,Combinator.prototype.constructor=Combinator,MediaFeature.prototype=new SyntaxUnit,MediaFeature.prototype.constructor=MediaFeature,MediaQuery.prototype=new SyntaxUnit,MediaQuery.prototype.constructor=MediaQuery,Parser.DEFAULT_TYPE=0,Parser.COMBINATOR_TYPE=1,Parser.MEDIA_FEATURE_TYPE=2,Parser.MEDIA_QUERY_TYPE=3,Parser.PROPERTY_NAME_TYPE=4,Parser.PROPERTY_VALUE_TYPE=5,Parser.PROPERTY_VALUE_PART_TYPE=6,Parser.SELECTOR_TYPE=7,Parser.SELECTOR_PART_TYPE=8,Parser.SELECTOR_SUB_PART_TYPE=9,Parser.prototype=function(){var e=new EventTarget,t,n={constructor:Parser,DEFAULT_TYPE:0,COMBINATOR_TYPE:1,MEDIA_FEATURE_TYPE:2,MEDIA_QUERY_TYPE:3,PROPERTY_NAME_TYPE:4,PROPERTY_VALUE_TYPE:5,PROPERTY_VALUE_PART_TYPE:6,SELECTOR_TYPE:7,SELECTOR_PART_TYPE:8,SELECTOR_SUB_PART_TYPE:9,_stylesheet:function(){var e=this._tokenStream,t=null,n,r,i;this.fire("startstylesheet"),this._charset(),this._skipCruft();while(e.peek()==Tokens.IMPORT_SYM)this._import(),this._skipCruft();while(e.peek()==Tokens.NAMESPACE_SYM)this._namespace(),this._skipCruft();i=e.peek();while(i>Tokens.EOF){try{switch(i){case Tokens.MEDIA_SYM:this._media(),this._skipCruft();break;case Tokens.PAGE_SYM:this._page(),this._skipCruft();break;case Tokens.FONT_FACE_SYM:this._font_face(),this._skipCruft();break;case Tokens.KEYFRAMES_SYM:this._keyframes(),this._skipCruft();break;case Tokens.VIEWPORT_SYM:this._viewport(),this._skipCruft();break;case Tokens.UNKNOWN_SYM:e.get();if(!!this.options.strict)throw new SyntaxError("Unknown @ rule.",e.LT(0).startLine,e.LT(0).startCol);this.fire({type:"error",error:null,message:"Unknown @ rule: "+e.LT(0).value+".",line:e.LT(0).startLine,col:e.LT(0).startCol}),n=0;while(e.advance([Tokens.LBRACE,Tokens.RBRACE])==Tokens.LBRACE)n++;while(n)e.advance([Tokens.RBRACE]),n--;break;case Tokens.S:this._readWhitespace();break;default:if(!this._ruleset())switch(i){case Tokens.CHARSET_SYM:throw r=e.LT(1),this._charset(!1),new SyntaxError("@charset not allowed here.",r.startLine,r.startCol);case Tokens.IMPORT_SYM:throw r=e.LT(1),this._import(!1),new SyntaxError("@import not allowed here.",r.startLine,r.startCol);case Tokens.NAMESPACE_SYM:throw r=e.LT(1),this._namespace(!1),new SyntaxError("@namespace not allowed here.",r.startLine,r.startCol);default:e.get(),this._unexpectedToken(e.token())}}}catch(s){if(!(s instanceof SyntaxError&&!this.options.strict))throw s;this.fire({type:"error",error:s,message:s.message,line:s.line,col:s.col})}i=e.peek()}i!=Tokens.EOF&&this._unexpectedToken(e.token()),this.fire("endstylesheet")},_charset:function(e){var t=this._tokenStream,n,r,i,s;t.match(Tokens.CHARSET_SYM)&&(i=t.token().startLine,s=t.token().startCol,this._readWhitespace(),t.mustMatch(Tokens.STRING),r=t.token(),n=r.value,this._readWhitespace(),t.mustMatch(Tokens.SEMICOLON),e!==!1&&this.fire({type:"charset",charset:n,line:i,col:s}))},_import:function(e){var t=this._tokenStream,n,r,i,s=[];t.mustMatch(Tokens.IMPORT_SYM),i=t.token(),this._readWhitespace(),t.mustMatch([Tokens.STRING,Tokens.URI]),r=t.token().value.replace(/^(?:url\()?["']?([^"']+?)["']?\)?$/,"$1"),this._readWhitespace(),s=this._media_query_list(),t.mustMatch(Tokens.SEMICOLON),this._readWhitespace(),e!==!1&&this.fire({type:"import",uri:r,media:s,line:i.startLine,col:i.startCol})},_namespace:function(e){var t=this._tokenStream,n,r,i,s;t.mustMatch(Tokens.NAMESPACE_SYM),n=t.token().startLine,r=t.token().startCol,this._readWhitespace(),t.match(Tokens.IDENT)&&(i=t.token().value,this._readWhitespace()),t.mustMatch([Tokens.STRING,Tokens.URI]),s=t.token().value.replace(/(?:url\()?["']([^"']+)["']\)?/,"$1"),this._readWhitespace(),t.mustMatch(Tokens.SEMICOLON),this._readWhitespace(),e!==!1&&this.fire({type:"namespace",prefix:i,uri:s,line:n,col:r})},_media:function(){var e=this._tokenStream,t,n,r;e.mustMatch(Tokens.MEDIA_SYM),t=e.token().startLine,n=e.token().startCol,this._readWhitespace(),r=this._media_query_list(),e.mustMatch(Tokens.LBRACE),this._readWhitespace(),this.fire({type:"startmedia",media:r,line:t,col:n});for(;;)if(e.peek()==Tokens.PAGE_SYM)this._page();else if(e.peek()==Tokens.FONT_FACE_SYM)this._font_face();else if(e.peek()==Tokens.VIEWPORT_SYM)this._viewport();else if(!this._ruleset())break;e.mustMatch(Tokens.RBRACE),this._readWhitespace(),this.fire({type:"endmedia",media:r,line:t,col:n})},_media_query_list:function(){var e=this._tokenStream,t=[];this._readWhitespace(),(e.peek()==Tokens.IDENT||e.peek()==Tokens.LPAREN)&&t.push(this._media_query());while(e.match(Tokens.COMMA))this._readWhitespace(),t.push(this._media_query());return t},_media_query:function(){var e=this._tokenStream,t=null,n=null,r=null,i=[];e.match(Tokens.IDENT)&&(n=e.token().value.toLowerCase(),n!="only"&&n!="not"?(e.unget(),n=null):r=e.token()),this._readWhitespace(),e.peek()==Tokens.IDENT?(t=this._media_type(),r===null&&(r=e.token())):e.peek()==Tokens.LPAREN&&(r===null&&(r=e.LT(1)),i.push(this._media_expression()));if(t===null&&i.length===0)return null;this._readWhitespace();while(e.match(Tokens.IDENT))e.token().value.toLowerCase()!="and"&&this._unexpectedToken(e.token()),this._readWhitespace(),i.push(this._media_expression());return new MediaQuery(n,t,i,r.startLine,r.startCol)},_media_type:function(){return this._media_feature()},_media_expression:function(){var e=this._tokenStream,t=null,n,r=null;return e.mustMatch(Tokens.LPAREN),t=this._media_feature(),this._readWhitespace(),e.match(Tokens.COLON)&&(this._readWhitespace(),n=e.LT(1),r=this._expression()),e.mustMatch(Tokens.RPAREN),this._readWhitespace(),new MediaFeature(t,r?new SyntaxUnit(r,n.startLine,n.startCol):null)},_media_feature:function(){var e=this._tokenStream;return e.mustMatch(Tokens.IDENT),SyntaxUnit.fromToken(e.token())},_page:function(){var e=this._tokenStream,t,n,r=null,i=null;e.mustMatch(Tokens.PAGE_SYM),t=e.token().startLine,n=e.token().startCol,this._readWhitespace(),e.match(Tokens.IDENT)&&(r=e.token().value,r.toLowerCase()==="auto"&&this._unexpectedToken(e.token())),e.peek()==Tokens.COLON&&(i=this._pseudo_page()),this._readWhitespace(),this.fire({type:"startpage",id:r,pseudo:i,line:t,col:n}),this._readDeclarations(!0,!0),this.fire({type:"endpage",id:r,pseudo:i,line:t,col:n})},_margin:function(){var e=this._tokenStream,t,n,r=this._margin_sym();return r?(t=e.token().startLine,n=e.token().startCol,this.fire({type:"startpagemargin",margin:r,line:t,col:n}),this._readDeclarations(!0),this.fire({type:"endpagemargin",margin:r,line:t,col:n}),!0):!1},_margin_sym:function(){var e=this._tokenStream;return e.match([Tokens.TOPLEFTCORNER_SYM,Tokens.TOPLEFT_SYM,Tokens.TOPCENTER_SYM,Tokens.TOPRIGHT_SYM,Tokens.TOPRIGHTCORNER_SYM,Tokens.BOTTOMLEFTCORNER_SYM,Tokens.BOTTOMLEFT_SYM,Tokens.BOTTOMCENTER_SYM,Tokens.BOTTOMRIGHT_SYM,Tokens.BOTTOMRIGHTCORNER_SYM,Tokens.LEFTTOP_SYM,Tokens.LEFTMIDDLE_SYM,Tokens.LEFTBOTTOM_SYM,Tokens.RIGHTTOP_SYM,Tokens.RIGHTMIDDLE_SYM,Tokens.RIGHTBOTTOM_SYM])?SyntaxUnit.fromToken(e.token()):null},_pseudo_page:function(){var e=this._tokenStream;return e.mustMatch(Tokens.COLON),e.mustMatch(Tokens.IDENT),e.token().value},_font_face:function(){var e=this._tokenStream,t,n;e.mustMatch(Tokens.FONT_FACE_SYM),t=e.token().startLine,n=e.token().startCol,this._readWhitespace(),this.fire({type:"startfontface",line:t,col:n}),this._readDeclarations(!0),this.fire({type:"endfontface",line:t,col:n})},_viewport:function(){var e=this._tokenStream,t,n;e.mustMatch(Tokens.VIEWPORT_SYM),t=e.token().startLine,n=e.token().startCol,this._readWhitespace(),this.fire({type:"startviewport",line:t,col:n}),this._readDeclarations(!0),this.fire({type:"endviewport",line:t,col:n})},_operator:function(e){var t=this._tokenStream,n=null;if(t.match([Tokens.SLASH,Tokens.COMMA])||e&&t.match([Tokens.PLUS,Tokens.STAR,Tokens.MINUS]))n=t.token(),this._readWhitespace();return n?PropertyValuePart.fromToken(n):null},_combinator:function(){var e=this._tokenStream,t=null,n;return e.match([Tokens.PLUS,Tokens.GREATER,Tokens.TILDE])&&(n=e.token(),t=new Combinator(n.value,n.startLine,n.startCol),this._readWhitespace()),t},_unary_operator:function(){var e=this._tokenStream;return e.match([Tokens.MINUS,Tokens.PLUS])?e.token().value:null},_property:function(){var e=this._tokenStream,t=null,n=null,r,i,s,o;return e.peek()==Tokens.STAR&&this.options.starHack&&(e.get(),i=e.token(),n=i.value,s=i.startLine,o=i.startCol),e.match(Tokens.IDENT)&&(i=e.token(),r=i.value,r.charAt(0)=="_"&&this.options.underscoreHack&&(n="_",r=r.substring(1)),t=new PropertyName(r,n,s||i.startLine,o||i.startCol),this._readWhitespace()),t},_ruleset:function(){var e=this._tokenStream,t,n;try{n=this._selectors_group()}catch(r){if(r instanceof SyntaxError&&!this.options.strict){this.fire({type:"error",error:r,message:r.message,line:r.line,col:r.col}),t=e.advance([Tokens.RBRACE]);if(t!=Tokens.RBRACE)throw r;return!0}throw r}return n&&(this.fire({type:"startrule",selectors:n,line:n[0].line,col:n[0].col}),this._readDeclarations(!0),this.fire({type:"endrule",selectors:n,line:n[0].line,col:n[0].col})),n},_selectors_group:function(){var e=this._tokenStream,t=[],n;n=this._selector();if(n!==null){t.push(n);while(e.match(Tokens.COMMA))this._readWhitespace(),n=this._selector(),n!==null?t.push(n):this._unexpectedToken(e.LT(1))}return t.length?t:null},_selector:function(){var e=this._tokenStream,t=[],n=null,r=null,i=null;n=this._simple_selector_sequence();if(n===null)return null;t.push(n);do{r=this._combinator();if(r!==null)t.push(r),n=this._simple_selector_sequence(),n===null?this._unexpectedToken(e.LT(1)):t.push(n);else{if(!this._readWhitespace())break;i=new Combinator(e.token().value,e.token().startLine,e.token().startCol),r=this._combinator(),n=this._simple_selector_sequence(),n===null?r!==null&&this._unexpectedToken(e.LT(1)):(r!==null?t.push(r):t.push(i),t.push(n))}}while(!0);return new Selector(t,t[0].line,t[0].col)},_simple_selector_sequence:function(){var e=this._tokenStream,t=null,n=[],r="",i=[function(){return e.match(Tokens.HASH)?new SelectorSubPart(e.token().value,"id",e.token().startLine,e.token().startCol):null},this._class,this._attrib,this._pseudo,this._negation],s=0,o=i.length,u=null,a=!1,f,l;f=e.LT(1).startLine,l=e.LT(1).startCol,t=this._type_selector(),t||(t=this._universal()),t!==null&&(r+=t);for(;;){if(e.peek()===Tokens.S)break;while(s1&&e.unget()),null)},_class:function(){var e=this._tokenStream,t;return e.match(Tokens.DOT)?(e.mustMatch(Tokens.IDENT),t=e.token(),new SelectorSubPart("."+t.value,"class",t.startLine,t.startCol-1)):null},_element_name:function(){var e=this._tokenStream,t;return e.match(Tokens.IDENT)?(t=e.token(),new SelectorSubPart(t.value,"elementName",t.startLine,t.startCol)):null},_namespace_prefix:function(){var e=this._tokenStream,t="";if(e.LA(1)===Tokens.PIPE||e.LA(2)===Tokens.PIPE)e.match([Tokens.IDENT,Tokens.STAR])&&(t+=e.token().value),e.mustMatch(Tokens.PIPE),t+="|";return t.length?t:null},_universal:function(){var e=this._tokenStream,t="",n;return n=this._namespace_prefix(),n&&(t+=n),e.match(Tokens.STAR)&&(t+="*"),t.length?t:null},_attrib:function(){var e=this._tokenStream,t=null,n,r;return e.match(Tokens.LBRACKET)?(r=e.token(),t=r.value,t+=this._readWhitespace(),n=this._namespace_prefix(),n&&(t+=n),e.mustMatch(Tokens.IDENT),t+=e.token().value,t+=this._readWhitespace(),e.match([Tokens.PREFIXMATCH,Tokens.SUFFIXMATCH,Tokens.SUBSTRINGMATCH,Tokens.EQUALS,Tokens.INCLUDES,Tokens.DASHMATCH])&&(t+=e.token().value,t+=this._readWhitespace(),e.mustMatch([Tokens.IDENT,Tokens.STRING]),t+=e.token().value,t+=this._readWhitespace()),e.mustMatch(Tokens.RBRACKET),new SelectorSubPart(t+"]","attribute",r.startLine,r.startCol)):null},_pseudo:function(){var e=this._tokenStream,t=null,n=":",r,i;return e.match(Tokens.COLON)&&(e.match(Tokens.COLON)&&(n+=":"),e.match(Tokens.IDENT)?(t=e.token().value,r=e.token().startLine,i=e.token().startCol-n.length):e.peek()==Tokens.FUNCTION&&(r=e.LT(1).startLine,i=e.LT(1).startCol-n.length,t=this._functional_pseudo()),t&&(t=new SelectorSubPart(n+t,"pseudo",r,i))),t},_functional_pseudo:function(){var e=this._tokenStream,t=null;return e.match(Tokens.FUNCTION)&&(t=e.token().value,t+=this._readWhitespace(),t+=this._expression(),e.mustMatch(Tokens.RPAREN),t+=")"),t},_expression:function(){var e=this._tokenStream,t="";while(e.match([Tokens.PLUS,Tokens.MINUS,Tokens.DIMENSION,Tokens.NUMBER,Tokens.STRING,Tokens.IDENT,Tokens.LENGTH,Tokens.FREQ,Tokens.ANGLE,Tokens.TIME,Tokens.RESOLUTION,Tokens.SLASH]))t+=e.token().value,t+=this._readWhitespace();return t.length?t:null},_negation:function(){var e=this._tokenStream,t,n,r="",i,s=null;return e.match(Tokens.NOT)&&(r=e.token().value,t=e.token().startLine,n=e.token().startCol,r+=this._readWhitespace(),i=this._negation_arg(),r+=i,r+=this._readWhitespace(),e.match(Tokens.RPAREN),r+=e.token().value,s=new SelectorSubPart(r,"not",t,n),s.args.push(i)),s},_negation_arg:function(){var e=this._tokenStream,t=[this._type_selector,this._universal,function(){return e.match(Tokens.HASH)?new SelectorSubPart(e.token().value,"id",e.token().startLine,e.token().startCol):null},this._class,this._attrib,this._pseudo],n=null,r=0,i=t.length,s,o,u,a;o=e.LT(1).startLine,u=e.LT(1).startCol;while(r0?new PropertyValue(n,n[0].line,n[0].col):null},_term:function(e){var t=this._tokenStream,n=null,r=null,i=null,s,o,u;return n=this._unary_operator(),n!==null&&(o=t.token().startLine,u=t.token().startCol),t.peek()==Tokens.IE_FUNCTION&&this.options.ieFilters?(r=this._ie_function(),n===null&&(o=t.token().startLine,u=t.token().startCol)):e&&t.match([Tokens.LPAREN,Tokens.LBRACE,Tokens.LBRACKET])?(s=t.token(),i=s.endChar,r=s.value+this._expr(e).text,n===null&&(o=t.token().startLine,u=t.token().startCol),t.mustMatch(Tokens.type(i)),r+=i,this._readWhitespace()):t.match([Tokens.NUMBER,Tokens.PERCENTAGE,Tokens.LENGTH,Tokens.ANGLE,Tokens.TIME,Tokens.FREQ,Tokens.STRING,Tokens.IDENT,Tokens.URI,Tokens.UNICODE_RANGE])?(r=t.token().value,n===null&&(o=t.token().startLine,u=t.token().startCol),this._readWhitespace()):(s=this._hexcolor(),s===null?(n===null&&(o=t.LT(1).startLine,u=t.LT(1).startCol),r===null&&(t.LA(3)==Tokens.EQUALS&&this.options.ieFilters?r=this._ie_function():r=this._function())):(r=s.value,n===null&&(o=s.startLine,u=s.startCol))),r!==null?new PropertyValuePart(n!==null?n+r:r,o,u):null},_function:function(){var e=this._tokenStream,t=null,n=null,r;if(e.match(Tokens.FUNCTION)){t=e.token().value,this._readWhitespace(),n=this._expr(!0),t+=n;if(this.options.ieFilters&&e.peek()==Tokens.EQUALS)do{this._readWhitespace()&&(t+=e.token().value),e.LA(0)==Tokens.COMMA&&(t+=e.token().value),e.match(Tokens.IDENT),t+=e.token().value,e.match(Tokens.EQUALS),t+=e.token().value,r=e.peek();while(r!=Tokens.COMMA&&r!=Tokens.S&&r!=Tokens.RPAREN)e.get(),t+=e.token().value,r=e.peek()}while(e.match([Tokens.COMMA,Tokens.S]));e.match(Tokens.RPAREN),t+=")",this._readWhitespace()}return t},_ie_function:function(){var e=this._tokenStream,t=null,n=null,r;if(e.match([Tokens.IE_FUNCTION,Tokens.FUNCTION])){t=e.token().value;do{this._readWhitespace()&&(t+=e.token().value),e.LA(0)==Tokens.COMMA&&(t+=e.token().value),e.match(Tokens.IDENT),t+=e.token().value,e.match(Tokens.EQUALS),t+=e.token().value,r=e.peek();while(r!=Tokens.COMMA&&r!=Tokens.S&&r!=Tokens.RPAREN)e.get(),t+=e.token().value,r=e.peek()}while(e.match([Tokens.COMMA,Tokens.S]));e.match(Tokens.RPAREN),t+=")",this._readWhitespace()}return t},_hexcolor:function(){var e=this._tokenStream,t=null,n;if(e.match(Tokens.HASH)){t=e.token(),n=t.value;if(!/#[a-f0-9]{3,6}/i.test(n))throw new SyntaxError("Expected a hex color but found '"+n+"' at line "+t.startLine+", col "+t.startCol+".",t.startLine,t.startCol);this._readWhitespace()}return t},_keyframes:function(){var e=this._tokenStream,t,n,r,i="";e.mustMatch(Tokens.KEYFRAMES_SYM),t=e.token(),/^@\-([^\-]+)\-/.test(t.value)&&(i=RegExp.$1),this._readWhitespace(),r=this._keyframe_name(),this._readWhitespace(),e.mustMatch(Tokens.LBRACE),this.fire({type:"startkeyframes",name:r,prefix:i,line:t.startLine,col:t.startCol}),this._readWhitespace(),n=e.peek();while(n==Tokens.IDENT||n==Tokens.PERCENTAGE)this._keyframe_rule(),this._readWhitespace(),n=e.peek();this.fire({type:"endkeyframes",name:r,prefix:i,line:t.startLine,col:t.startCol}),this._readWhitespace(),e.mustMatch(Tokens.RBRACE)},_keyframe_name:function(){var e=this._tokenStream,t;return e.mustMatch([Tokens.IDENT,Tokens.STRING]),SyntaxUnit.fromToken(e.token())},_keyframe_rule:function(){var e=this._tokenStream,t,n=this._key_list();this.fire({type:"startkeyframerule",keys:n,line:n[0].line,col:n[0].col}),this._readDeclarations(!0),this.fire({type:"endkeyframerule",keys:n,line:n[0].line,col:n[0].col})},_key_list:function(){var e=this._tokenStream,t,n,r=[];r.push(this._key()),this._readWhitespace();while(e.match(Tokens.COMMA))this._readWhitespace(),r.push(this._key()),this._readWhitespace();return r},_key:function(){var e=this._tokenStream,t;if(e.match(Tokens.PERCENTAGE))return SyntaxUnit.fromToken(e.token());if(e.match(Tokens.IDENT)){t=e.token();if(/from|to/i.test(t.value))return SyntaxUnit.fromToken(t);e.unget()}this._unexpectedToken(e.LT(1))},_skipCruft:function(){while(this._tokenStream.match([Tokens.S,Tokens.CDO,Tokens.CDC]));},_readDeclarations:function(e,t){var n=this._tokenStream,r;this._readWhitespace(),e&&n.mustMatch(Tokens.LBRACE),this._readWhitespace();try{for(;;){if(!(n.match(Tokens.SEMICOLON)||t&&this._margin())){if(!this._declaration())break;if(!n.match(Tokens.SEMICOLON))break}this._readWhitespace()}n.mustMatch(Tokens.RBRACE),this._readWhitespace()}catch(i){if(!(i instanceof SyntaxError&&!this.options.strict))throw i;this.fire({type:"error",error:i,message:i.message,line:i.line,col:i.col}),r=n.advance([Tokens.SEMICOLON,Tokens.RBRACE]);if(r==Tokens.SEMICOLON)this._readDeclarations(!1,t);else if(r!=Tokens.RBRACE)throw i}},_readWhitespace:function(){var e=this._tokenStream,t="";while(e.match(Tokens.S))t+=e.token().value;return t},_unexpectedToken:function(e){throw new SyntaxError("Unexpected token '"+e.value+"' at line "+e.startLine+", col "+e.startCol+".",e.startLine,e.startCol)},_verifyEnd:function(){this._tokenStream.LA(1)!=Tokens.EOF&&this._unexpectedToken(this._tokenStream.LT(1))},_validateProperty:function(e,t){Validation.validate(e,t)},parse:function(e){this._tokenStream=new TokenStream(e,Tokens),this._stylesheet()},parseStyleSheet:function(e){return this.parse(e)},parseMediaQuery:function(e){this._tokenStream=new TokenStream(e,Tokens);var t=this._media_query();return this._verifyEnd(),t},parsePropertyValue:function(e){this._tokenStream=new TokenStream(e,Tokens),this._readWhitespace();var t=this._expr();return this._readWhitespace(),this._verifyEnd(),t},parseRule:function(e){this._tokenStream=new TokenStream(e,Tokens),this._readWhitespace();var t=this._ruleset();return this._readWhitespace(),this._verifyEnd(),t},parseSelector:function(e){this._tokenStream=new TokenStream(e,Tokens),this._readWhitespace();var t=this._selector();return this._readWhitespace(),this._verifyEnd(),t},parseStyleAttribute:function(e){e+="}",this._tokenStream=new TokenStream(e,Tokens),this._readDeclarations()}};for(t in n)n.hasOwnProperty(t)&&(e[t]=n[t]);return e}();var Properties={"align-items":"flex-start | flex-end | center | baseline | stretch","align-content":"flex-start | flex-end | center | space-between | space-around | stretch","align-self":"auto | flex-start | flex-end | center | baseline | stretch","-webkit-align-items":"flex-start | flex-end | center | baseline | stretch","-webkit-align-content":"flex-start | flex-end | center | space-between | space-around | stretch","-webkit-align-self":"auto | flex-start | flex-end | center | baseline | stretch","alignment-adjust":"auto | baseline | before-edge | text-before-edge | middle | central | after-edge | text-after-edge | ideographic | alphabetic | hanging | mathematical | | ","alignment-baseline":"baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical",animation:1,"animation-delay":{multi:"