Repository: sosedoff/pgweb Branch: main Commit: ee2e54bfcf28 Files: 135 Total size: 1.0 MB Directory structure: gitextract_uzqtco04/ ├── .claude/ │ └── settings.local.json ├── .dockerignore ├── .gitattributes ├── .github/ │ └── workflows/ │ ├── checks.yml │ ├── deploy.yml │ ├── docker.yml │ └── release.yml ├── .gitignore ├── .golangci.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── Makefile ├── Procfile ├── README.md ├── SCREENS.md ├── config/ │ ├── examples/ │ │ ├── connect_backend_go/ │ │ │ ├── README.md │ │ │ └── main.go │ │ └── connect_backend_ruby/ │ │ ├── Gemfile │ │ ├── README.md │ │ ├── config.ru │ │ └── main.rb │ ├── pgweb.freebsd_rc │ ├── pgweb.service │ ├── pgweb_initd.conf │ └── pgweb_upstart.conf ├── data/ │ ├── bookmark.toml │ ├── bookmark_invalid_ssl.toml │ ├── bookmark_url.toml │ ├── bookmark_with_ssh.toml │ ├── booktown.sql │ ├── invalid.toml │ ├── lc_example1.sql │ ├── lc_example2.sql │ ├── lc_invalid_meta.sql │ ├── lc_no_meta.sql │ ├── passfile │ └── roach.sql ├── docker-compose-pg.yml ├── docker-compose.yml ├── fly.toml ├── go.mod ├── go.sum ├── main.go ├── pkg/ │ ├── api/ │ │ ├── api.go │ │ ├── api_test.go │ │ ├── errors.go │ │ ├── helpers.go │ │ ├── helpers_test.go │ │ ├── logger.go │ │ ├── logger_test.go │ │ ├── middleware.go │ │ ├── routes.go │ │ ├── session_manager.go │ │ ├── session_manager_test.go │ │ └── types.go │ ├── bookmarks/ │ │ ├── bookmarks.go │ │ ├── bookmarks_test.go │ │ ├── manager.go │ │ └── manager_test.go │ ├── cli/ │ │ └── cli.go │ ├── client/ │ │ ├── client.go │ │ ├── client_test.go │ │ ├── codec.go │ │ ├── codec_test.go │ │ ├── dump.go │ │ ├── dump_test.go │ │ ├── result.go │ │ ├── result_test.go │ │ ├── tunnel.go │ │ ├── util.go │ │ └── util_test.go │ ├── command/ │ │ ├── options.go │ │ ├── options_test.go │ │ └── version.go │ ├── connect/ │ │ ├── backend.go │ │ ├── backend_test.go │ │ └── types.go │ ├── connection/ │ │ ├── connection_string.go │ │ ├── connection_string_test.go │ │ ├── port.go │ │ └── port_test.go │ ├── history/ │ │ └── history.go │ ├── metrics/ │ │ ├── handler.go │ │ ├── metrics.go │ │ └── server.go │ ├── queries/ │ │ ├── field.go │ │ ├── field_test.go │ │ ├── metadata.go │ │ ├── metadata_test.go │ │ ├── query.go │ │ ├── query_test.go │ │ ├── store.go │ │ └── store_test.go │ ├── shared/ │ │ └── ssh_info.go │ ├── statements/ │ │ ├── sql/ │ │ │ ├── databases.sql │ │ │ ├── estimated_row_count.sql │ │ │ ├── function.sql │ │ │ ├── info.sql │ │ │ ├── info_simple.sql │ │ │ ├── materialized_view.sql │ │ │ ├── objects.sql │ │ │ ├── schemas.sql │ │ │ ├── settings.sql │ │ │ ├── table_constraints.sql │ │ │ ├── table_indexes.sql │ │ │ ├── table_info.sql │ │ │ ├── table_info_cockroach.sql │ │ │ ├── table_schema.sql │ │ │ └── tables_stats.sql │ │ └── sql.go │ └── util/ │ └── profiler.go ├── script/ │ ├── build_all.sh │ ├── check_formatting.sh │ ├── package.sh │ ├── test_all.sh │ ├── test_cockroach.sh │ └── update_homebrew.sh └── static/ ├── css/ │ ├── app.css │ ├── bootstrap.css │ └── font-awesome.css ├── data.go ├── fonts/ │ └── FontAwesome.otf ├── index.html └── js/ ├── ace-pgsql.js ├── ace.js ├── app.js ├── base64.js ├── bootstrap-contextmenu.js ├── bootstrap-dropdown.js ├── ext-language_tools.js ├── jquery.js ├── theme-tomorrow.js └── utils.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .claude/settings.local.json ================================================ { "permissions": { "allow": [ "Bash(grep:*)", "Bash(find:*)", "Bash(git checkout:*)", "Bash(go test:*)", "Bash(go build:*)", "Bash(make:*)" ] } } ================================================ FILE: .dockerignore ================================================ .github bin/ ./pgweb ================================================ FILE: .gitattributes ================================================ bindata.go -diff ================================================ FILE: .github/workflows/checks.yml ================================================ name: checks on: push: branches: - main paths-ignore: - '**.md' pull_request: types: - opened - synchronize paths-ignore: - '**.md' env: GO_VERSION: "1.25" concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: tests: name: tests runs-on: ubuntu-latest timeout-minutes: 40 strategy: matrix: pg_version: [9.6, 10, 11, 12, 13, 14, 15, 16, 17, 18] services: postgres: image: postgres:${{ matrix.pg_version }} env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DB: booktown ports: - 5432:5432 options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - name: Install latest Postgres client run: | sudo rm -f /etc/apt/sources.list.d/pgdg.list curl --silent https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add echo "deb http://apt.postgresql.org/pub/repos/apt/ `lsb_release -cs`-pgdg main" | sudo tee /etc/apt/sources.list.d/pgdg.list sudo apt-get update && sudo apt-get install -y postgresql-client-18 - uses: actions/checkout@v3 with: fetch-depth: 0 - uses: actions/setup-go@v3 with: go-version: ${{ env.GO_VERSION }} - run: go mod download - run: make test env: MallocNanoZone: 0 # https://github.com/golang/go/issues/49138 PGHOST: localhost PGUSER: postgres PGPASSWORD: postgres PGDATABASE: booktown tests-windows: runs-on: windows-latest timeout-minutes: 30 steps: - uses: actions/checkout@v3 with: fetch-depth: 0 - uses: actions/setup-go@v3 with: go-version: ${{ env.GO_VERSION }} - run: go mod download - run: make test lint: runs-on: ubuntu-latest timeout-minutes: 10 steps: - uses: actions/setup-go@v3 with: go-version: ${{ env.GO_VERSION }} - uses: actions/checkout@v3 with: fetch-depth: 0 - name: golangci-lint uses: golangci/golangci-lint-action@v8 with: version: v2.7.2 fmt: name: fmt runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 with: fetch-depth: 0 - uses: actions/setup-go@v3 with: go-version: ${{ env.GO_VERSION }} - run: go mod download - run: script/check_formatting.sh ================================================ FILE: .github/workflows/deploy.yml ================================================ name: demo deploy on: push: branches: - main env: FLY_API_TOKEN: ${{ secrets.FLY_TOKEN }} jobs: deploy: name: Deploy to Fly runs-on: ubuntu-latest timeout-minutes: 10 steps: - uses: actions/checkout@v3 - uses: superfly/flyctl-actions/setup-flyctl@master - run: flyctl deploy --remote-only ================================================ FILE: .github/workflows/docker.yml ================================================ name: docker on: push: branches: - main env: GO_VERSION: "1.25" CGO_ENABLED: 0 IMAGE_REPOSITORY: sosedoff/pgweb jobs: docker-build: name: docker images runs-on: ubuntu-latest timeout-minutes: 30 steps: - name: Checkout code uses: actions/checkout@v3 - name: Set up QEMU uses: docker/setup-qemu-action@v2 - name: Configure docker build context uses: docker/setup-buildx-action@v2 - name: Build docker images uses: docker/build-push-action@v2 with: context: . push: false tags: pgweb:latest platforms: linux/amd64,linux/arm64,linux/arm/v7 build-args: | "CGO_ENABLED=${{ env.CGO_ENABLED }}" ================================================ FILE: .github/workflows/release.yml ================================================ name: release on: push: tags: - "v*" env: GO_VERSION: "1.25" CGO_ENABLED: 0 DOCKER_REPOSITORY: sosedoff/pgweb GHCR_REPOSITORY: sosedoff/pgweb jobs: docker-release: name: Publish Docker images runs-on: ubuntu-latest timeout-minutes: 30 steps: - name: Checkout code uses: actions/checkout@v2 - name: Set up QEMU uses: docker/setup-qemu-action@v2 - name: Configure docker build context uses: docker/setup-buildx-action@v2 - name: Set reference tags id: refs run: | echo ::set-output name=SOURCE_NAME::${GITHUB_REF#refs/*/} echo ::set-output name=SOURCE_BRANCH::${GITHUB_REF#refs/heads/} echo ::set-output name=SOURCE_TAG::${GITHUB_REF#refs/tags/v} - name: Login to Docker Hub uses: docker/login-action@v2 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Login to Github Container Registry uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GH_TOKEN }} - name: Build and push docker images uses: docker/build-push-action@v2 with: context: . push: true tags: | ${{ env.DOCKER_REPOSITORY }}:${{ steps.refs.outputs.SOURCE_TAG }} ${{ env.DOCKER_REPOSITORY }}:latest ghcr.io/${{ env.GHCR_REPOSITORY }}:${{ steps.refs.outputs.SOURCE_TAG }} ghcr.io/${{ env.GHCR_REPOSITORY }}:latest platforms: linux/amd64,linux/arm64,linux/arm/v7 ================================================ FILE: .gitignore ================================================ .DS_Store .idea .env .envrc pgweb bin tmp/ cover.out ================================================ FILE: .golangci.yml ================================================ version: "2" linters: disable: - errcheck settings: staticcheck: checks: ["all", "-ST1000", "-ST1003", "-ST1016", "-ST1020", "-ST1021", "-ST1022", "-ST1005", "-QF1004"] ================================================ FILE: CHANGELOG.md ================================================ ## Changelog Current [release](https://github.com/sosedoff/pgweb/releases) is `0.17.0`. ## Next - `NEW` Add PGWEB_BOOKMARKS_DIR environment variable to configure bookmarks directory ## 0.17.0 - 2025-11-22 - `NEW` Update Dockerfile to use Golang 1.24 image, GH-821 - `NEW` Bump go crypto package to 0.44.x, GH-820, GH-823 - `NEW` Add PostgreSQL 18 support to Github Actions, GH-816 - `NEW` Connect backend refactor, GH-801 - `NEW` Add server settings view, GH-768 - `NEW` Add exec time of empty queries, GH-763 - `FIX` Minor typos, GH-764, GH-786 - `FIX` Shorten git revision printed in the --version output, GH-770 ## 0.16.2 - 2024-11-02 - `FIX` Build a new Docker image with PostgreSQL 17 support - `FIX` Run CI against PostgreSQL 17, GH-758 - `FIX` Rename master to main branch, GH-750 ## 0.16.1 - 2024-09-07 - `FIX` Remove linux/arm/v5 from docker release action, GH-742 ## 0.16.0 - 2024-06-04 - `NEW` Allow database stats downloads, GH-738 - `NEW` Add analyze table action, GH-737 - `NEW` Bump postgres version used in docker compose to 15, GH-729 - `NEW` Build on Go 1.22, GH-726 - `FIX` SSH tunnel cleanup and parse fixup, GH-731 - `FIX` Drop linux/arm/v5 from docker build, GH-728 - `FIX` Propagate CGO_ENABLED environment variable to docker build, GH-724 ## 0.15.0 - 2024-03-14 - `NEW` Add support for a bookmarks-only mode, GH-716 - `FIX` Fix missing indexes by quoting schema/table name to ::regclass, GH-711 - `FIX` Continue on parseJSON error, GH-708 ## 0.14.3 - 2024-01-28 - `NEW` Allow retrying a connection on startup, GH-695 - `NEW` Allow setting readonly mode in bookmarks, GH-707 - `FIX` Add UPDATE to list of restricted keywords in read-only mode, GH-697 ## 0.14.2 - 2023-10-29 - `NEW` Execute tests using PostgreSQL 16, GH-691 - `FIX` Unclosed database sessions and tunnels, GH-688 - `FIX` Use pg_table_size for table stats query, GH-685 - `FIX` Use `HasSuffix` to correctly determine URL prefix, GH-684 ## 0.14.1 - 2023-06-17 - `NEW` Add process start time metric, GH-675 - `NEW` Configure pgweb user for docker container, GH-674 - `NEW` Updated dockerfile, GH-645 - `FIX` Fix typo in the healthy metric, GH-657 - `NEW` Use entrypoint instead of cmd in in dockerfile, GH-654 ## 0.14.0 - 2023-02-21 - `FIX` History page query loading fixup, GH-632 - `NEW` Display cell content via context menu, GH-634 - `NEW` Handle support/permissions errors in info call, GH-635 - `NEW` Show error message when API calls fail, GH-636 - `NEW` Add bookmark options to load username/password from env vars, GH-638 - `NEW` Add context menu to display database tables stats, GH-639 - `NEW` Added Local Queries feature, GH-641 - `FIX` Ensure that objects are sorted by schema and name, GH-648 - `FIX` Fetch local queries on db connect, GH-650 ## 0.13.1 - 2022-12-27 - Fix connect flow when `~/.pgweb/bookmarks` directory is not available, GH-631 ## 0.13.0 - 2022-12-25 - Add support for .pgpass file, GH-617 - Request logging additions (request id, forwarded user), GH-618 - Establish connections using bookmark ID only, GH-619 - Display empty schemas on the sidebar, GH-621 - Configure timeout and retries when testing connection status, GH-623 - Setup basic prom metrics endpoint, GH-624 - Add default connect_timeout option to connection string, GH-626 - Add duration_ms to log entries, GH-628 - Add query execution stats to api endpoint, GH-629 ## 0.12.0 - 2022-12-13 - Deprecate usage of Gox for binary builds, GH-571 - Add netcat install in dockerfile to provide a way to healthcheck, GH-572 - Install latest postgres client in docker image, GH-577 - Add support for `PGWEB_` prefix environment variables, GH-585 - Fix export URL generation, refactor export code, GH-588 - Add logrus-based request logger, GH-589 - Configure logger for connect backend, GH-591 - Set LDFLAGS for make build/release commands, GH-592 - Add internal sessions manager, GH-593 - Include index size on the index list view, GH-595 - Fix flaky backend connection test, GH-596 - Add ability to view and copy views/materialized views definitions, GH-594 - Enable dev assets mode with PGWEB_ASSETS_DEVMODE env var, GH-597 - Make query input box resizable, GH-599 - Deprecate Heroku demo deployments and switch to Fly, GH-600 - Handle returning values in update/delete queries, GH-601 - Fix panic with invalid time marshaling, GH-602 - Configure logging level and format, GH-605 - Use go embed to load queries from static files, GH-607 - Switch go build target to 1.19, GH-603 - Add support for user functions, GH-608 - Implement global query timeout option, GH-609 - Switch windows tests from Appveyor to Github Actions, GH-611 - Fix activity endpoint panic when server version is not detected, GH-612 ## 0.11.12 - 2022-07-05 - Update base docker image (alpine), update deps, GH-558 - Refactor docker images building, include ARM, GH-568 ## 0.11.11 - 2022-03-29 - Auto-detect the query from the query source based on user selection, GH-547 - Added binary codec base58 as well as improving the help for --binary-codec flag, GH-548 - Change binary codec back to none, GH-555 ## 0.11.10 - 2022-01-20 - Removes alert on column copy value, GH-536 - Migrate test suite to Github Action, GH-540 - Serialize binary bytea cols into hex/base64, GH-537 - Include build time into version string, GH-541 - Explain analyze dropdown button, GH-532 - Switch to go 1.17, GH-543 - Use HTTP 302 status code for successful backend redirect, GH-544 - Add connect backend tests, GH-546 ## 0.11.9 - 2021-11-08 - Releases are built on Go 1.17 - Build time correction, GH-521 - Fix broken assets URL path prefix, GH-525 - Update docker build image to alpine:3.14, GH-522 - Upgrade gin dependency to v1.7.4, GH-527 - Add FreeBSD startup script, GH-520 ## 0.11.8 - 2021-07-07 - Releases are built with Go 1.16 - Add ARM64 v7 build target, GH-497 - Switch to Go modules for dependency management, GH-509 - Switch to Go embed for static assets management, GH-510 - Add Darwin/ARM64 build target (Apple Silicon), GH-513 ## 0.11.7 - 2020-10-18 - Releases are built with Go 1.15 - Show results row context menu on custom query results, GH-457 - Do not terminate if local authentication failed on start, GH-463 - Do not show other databases if session is locked, GH-470 - Strip debug information from binary to reduce size, GH-489 - Disable autocomplete on database search field, GH-492 - Improve windows connection error matching during start, GH-493 ## 0.11.6 - 2020-02-19 - Add CLI options for SSL key, cert and root certs, GH-452 - Remove double click action on cell, GH-455 ## 0.11.5 - 2019-12-16 - Add basic SQL keyword autocompletion, GH-443 - SSH Private Key handling update (encrypted keys are supported now), GH-445 - Include Go version into `pgweb --version` output, GH-447 - Fix long table name bug in the sidebar, GH-448 - Add SQL objects (table,views,etc) autocompletion, GH-449 - Include Go version into info API endpoint, GH-450 ## 0.11.4 - 2019-10-05 - Fix SQL export filename, GH-438 - Update Docker image to alpine:3.10, GH-439 - Drop unsupported pg_dump options from connection string, GH-441 - Misc code cleanup and formatting, GH-442 ## 0.11.3 - 2019-07-24 - Misc: add script to update homebrew formula version, GH-423 - Destructive keyword restriction in read-only mode, GH-421 - Make database object searchable in sidebar, GH-434 - Update lib/pg to 1.1.1, GH-435 ## 0.11.2 - 2019-02-15 - Fix table row estimation query for camelcase schemas, GH-414 ## 0.11.1 - 2019-01-28 - Typo fixes - Add Base64 javascript encoder/decoder to replace deprecated window.atob call, GH-405 - Fix startup error when DATABASE_URL is set, GH-406 - Fix user auto detection when USER env var is not set, GH-408 - Switch bindata dependency to use maintained fork: github.com/go-bindata/go-bindata, GH-409 ## 0.11.0 - 2018-12-24 - Tweak sidebar database object counters styles, GH-400 - Do not exit with error if local server is not running, GH-399 - Fix SSH host verification check, GH-398 - Scope activity list to current database only, GH-397 - Show current release version and check for updates, GH-396 - Force switch back to default connection settings view, GH-395 - Fix row count estimation bug, GH-394 - Print out failed query SQL and args with --debug flag, GH-393 ## 0.10.0 - 2018-11-28 - Fixes relation not found errors when dealing with table names that have uppercase characters, GH-356 - Dockerfile updates, GH-357 - Check if pg_dump is available before running database export, GH-358 - Improvements to CockroachDB integration, GH-365 - Add EstimatedTableRowsCount to avoid count in large tables, GH-366 - Automatically set table filter option to 'equals' if its not set, GH-370 - Dependencies update and switch to dep, GH-375 - Add column context menu item to get numeric stats, GH-377 - Fix issues with connection string builder, GH-378 - Include rows count to numeric stats view on table column, GH-379 - Make localhost to be a default db host, GH-380 - Clear out connection settings/bookmark on login screen when running in session/connect mode - Add table row context menu with actions, GH-381 - Allow settings url prefix with URL_PREFIX env var, GH-387 - Fix JSON marshal panic when dealing with NaN values, GH-388 - Fix startup behavior when user did not provide a database name, GH-389 ## 0.9.12 - 2018-04-23 - Add link to view database connection string format on login page - Include constraint name under "constraints" tab, GH-343 - Misc CI and config changes ## 0.9.11 - 2017-12-07 - Fix ssl mode for the connection url in the bookmarks, GH-320 - Add support for CORS, GH-321 - Fix custom query results counter for empty queries, GH-322 - Reorganize the table context menu, GH-323 - Disable database connection string text field autocomplete, GH-327 - Add db prefix to the table export files, GH-329 - Add database view context menu with export actions, GH-330 ## 0.9.10 - 2017-11-03 - Make idle connection timeout configurable, [GH-282] - Fix panics when sshinfo is not set on bookmarks, [GH-296] - Dot now allow using startup bookmark in multi-session mode, [GH-300] - Add ability to copy table name from the sidebar, [GH-301] ## 0.9.9 - 2017-09-28 - Automatically format JSON data exports, GH-255 - Update Docker image to alpine:3.6, GH-256 - Print out PostgreSQL server version on start in a single-session mode, GH-264 - Record last query timestamp for the client connection, GH-265 - Add context menu for table headers in browse mode (copy name, see unique values), GH-268 - Add ability to export current database dump, GH-270 - Automatically open pgweb in browser on start if its already running, GH-272 - Connect to the database with credentials provided by a third-party backend, GH-266 - Automatically close idle sessions (no activity in 1 hour), GH-275 - Allow connecting via SSH with a custom private key and other fixes, GH-277 - Add options to disable SSH connections, GH-279 ## 0.9.8 - 2017-08-04 - Fixed error checking in the API, GH-234 - Fixed activity tab to support PG 9.x versions, GH-237 - Remember sort column and order for pagination, GH-240 - Use `sslmode=disable` for bookmarks without sslmode option, GH-244 - Javascript fixes for IE9-11, GH-245 - Require confirmation for the disconnect, GH-246 - Clean the results table on manual disconnect ## 0.9.7 - 2017-04-04 - Fixed issue with locked session and empty db url, GH-206 - Fixed path rewrite on DB change, GH-212 - Upgraded dependencies, GH-217 - Added ability to specify bookmarks path, GH-218 - Added counter for the number of rows from a custom SQL query, GH-224 - Added new behavior for removing table rows view on custom SQL query page, GH-225 ## 0.9.6 - 2016-11-18 - Fixed bug in query base64-encoding, GH-186 - Fixed rows pagination visibility bug, GH-190 - Fixed issue with query order escaping, GH-191 - Fixed invalid query selection for explain command, GH-198 - Fixed issue with empty sidebar, now it shows empty state, GH-202 - Added new flag --readonly to enable read only transaction mode, GH-193 - Added ability to kill any running query, GH-194 - Added session database connection locking, GH-195 - Added ability to switch between databases, GH-196 - Added feature to keep last selected tab when switching between tables, GH-197 - Added new flag --bookmark (-b) to specify server connection from bookmark, GH-201 ## 0.9.5 - 2016-10-01 - Only view schema with USAGE privileges, GH-167 - Fixed broken export to CSV/JSON/XML if hashmark in URL, GH-175 - Added example service configuration for systemd, GH-177 - Allow setting auth user and pass using variables ## 0.9.4 - 2016-07-29 - Fixes CSV/JSON/XML export buttons when pgweb is running with url prefix, GH-170 ## 0.9.3 - 2016-06-30 - Uses Go 1.6 for development, GH-155 - Fixes timestamp formatting in CSV export, GH-163 - Included PostgreSQL 9.6 for integration testing - Switches docker image to Alpine to reduce image size - Adds support for ARMv5 ## 0.9.2 - 2016-03-01 - Fixes bug with unsafe base64 encoded sql queries - Fixes issue with session id not being included in multi-session mode - Fixes visual issue with long table names in sidebar - Fixes visual issue with a scrollbar in table information widget - Fixes issue with database connection form being reset by clicking on 'cancel' button - Adds ability to close connection - Adds display message for number of affected rows for update/delete queries, GH-133 - Adds web server url prefix as a CLI option, GH-135 ## 0.9.1 - 2016-01-25 - Fixes bug with tables context menu - Fixes JS bug when query returns no rows - Fixes bug with switching between different connection modes - Adds AJAX timeout to 5s - Adds sidebar reload action on any CREATE/DROP action ## 0.9.0 - 2016-01-19 - Add support for multiple schemas. GH-112 - Add support for native ssh tunnes. GH-114 - Add materialized views to list of schema objects - Adds a few design tweaks and cleanups - Fixes bug with nil result set when fetching rows ## 0.8.0 - 2016-01-11 - Fixes bug with bigint conversions in javascript. Now bigints are encoded as strings. GH-109 - Adds pagination and simple column filtering to table rows browser. GH-110 - Adds ability to use pgweb with multiple database sessions. GH-111 - Adds a few design tweaks and cleanups ## 0.7.0 - 2016-01-05 - Adds sequences to the sidebar panel - GH-100 - Adds table constrains view - GH-104 - Adds ability to export table and query rows as JSON/XML - GH-107 - Updates to UI theme and SQL editor ## 0.6.3 - 2015-08-16 - Adds PostgreSQL password escaping in web ui, GH-96 - Adds base64 query encoding for CSV export, GH-95 - Adds automatic saving of last executed query to localStorage - Adds request middleware to log incoming form params in debug mode ## 0.6.2 - 2015-07-15 - Adds ability to specify connection strings prefixed by `postgresql://`, [GH-92] - Updates configuration for Heroku, [GH-89], [GH-90] - Updates postgresql library dependency to latest, [GH-91] - Fixes password field to not display plaintext passwords, [GH-87] ## 0.6.1 - 2015-06-18 - This release is repackage-release targeted to fix binary downloads ## 0.6.0 - 2015-05-31 - Adds ability to execute only selected SQL query in run command view, [GH-85] - Adds ability to delete/truncate table via context many on sidebar view - Adds ability to export table contents to CSV via context menu on sidebar view - Changes sidebar color scheme to a lighter and better looking one ## 0.5.3 - 2015-05-06 - Changes default server port from 8080 to 8081 to avoil conflict with RethinkDB - Changes styles for table rows and connection settings window - Adds highlighting styles for columns with sort order - Adds git sha into program version output - Add new endpoint /api/info to get build details ## 0.5.2 - 2015-04-13 - Adds a new endpoint /activity that returns active queries - Adds tab to view active queries - Adds column sorting when browsing table contents - Fixes SQL query view when switching to table structure view ## 0.5.1 - 2015-02-23 - Upgrades Gin framework dependency to 0.5.0 - Fixes server crash if another pgweb server is running ## 0.5.0 - 2015-01-13 - Adds Go 1.4 support - Adds connection string printing in debug mode - Adds initial bookmarks support - Adds /api prefix for all API calls - Adds makefile usage task - Adds windows CI to verify build process - Adds example sql database to codebase - Adds timestamped filenames when exporting results to CSV [GH-75] - Adds connection checking on each request to prevent api panics - Adds timestamps to query history records - Adds current database name to the sidebar - Adds button to refresh tables list to the sidebar - Updates all application dependencies - Changes /api/info endpoint to /api/connection - Fixes issues with connection string/options parsing - Fixes capitalized column names in table view - Fixes connection string validation in /api/connect endpoint ## 0.4.1 - 2014-12-01 - Adds pgweb version on start [GH-65] - Adds user detection from OS environment - Adds simple memory profiles with --debug option - Adds the session user and search path in connection info [GH-67] - Adds table list reloading after CREATE/DROP TABLE queries [GH-69] - Adds font awesome icons for the sidebar - Removes query recording for internal queries [GH-67] - Fixes default sslmode. Its not longer set to "disable" - Fixes cells cropping on table indexes view - Fixes connection URL generation using web interface - Fixes SQL statements for table row count [GH-67] - Fixes /tables JSON response if database does not have any tables ## 0.4.0 - 2014-11-11 - Adds query escaping when exporting results to CSV [GH-38] - Adds keyboard shortcut (ctrl+e, command+e on mac) for query explain action - Adds HTTP basic authentication with --auth-user and --auth-pass flags - Adds -skip-open/-s flag to disable automatic browser launch - Adds --bind option to specify server listen hostname/ip - Adds ssl mode parameters to url if ssl flag is set and not defined in the url - Adds dependency management with Godep - Adds Docker support - Adds Heroku support - Adds ability to connect to databases with no tables - Adds precompiled assets into repository to simplify development - Adds a connection details view - Adds a new interface to specify connection settings or make a new connection - Adds page favicon - Adds ability to present cell data as text area by double clicking on it - Fixes styles for query explain results - Fixes sidebar navigation scrolling styles [GH-12] - Fixes sidebar table name styles to support long names ## 0.3.1 - 2014-10-28 - Adds proper exit code when printing version via -v/--version flag - Adds --version and --debug long flag names - Adds double quotes for table name when fetching table contents - Adds support for DATABASE_URL environment variable if no --url is set - Adds proper usage of jQuery .prop method - Adds --pass flag to specify connection password - Fixes --ssl flag usage, previous value was hardcoded ## 0.3.0 - 2014-10-26 - Renamed `make deps` to `make setup` and fix issues with bootstrapping - Removed hardcoded url for CSV export, it now detects application host:port - Improved query history view table styles - Moved table information view to the sidebar - Added --listen flag to specify web server port, still defaults to 8080 ## 0.2.0 - 2014-10-23 - Design tweaks - Automatically opens browser on OSX systems - Adds query explain functionality - Adds export to CSV ## 0.1.0 - 2014-10-14 - Initial release ================================================ FILE: CONTRIBUTING.md ================================================ - Fork repository - Create a new git branch - Make changes - Run tests: `make test` - Run tests against all supported PostreSQL versions: `make test-all` (optional) - If you change frontend code (js/css) make sure to rebuild assets: `make assets` - Open a new pull request ================================================ FILE: Dockerfile ================================================ # ------------------------------------------------------------------------------ # Builder Stage # ------------------------------------------------------------------------------ FROM golang:1.25-trixie AS build # Set default build argument for CGO_ENABLED ARG CGO_ENABLED=0 ENV CGO_ENABLED=${CGO_ENABLED} WORKDIR /build RUN git config --global --add safe.directory /build COPY go.mod go.sum ./ RUN go mod download COPY Makefile main.go ./ COPY static/ static/ COPY pkg/ pkg/ COPY .git/ . RUN make build # ------------------------------------------------------------------------------ # Fetch signing key # ------------------------------------------------------------------------------ FROM debian:trixie-slim AS keyring ADD https://www.postgresql.org/media/keys/ACCC4CF8.asc keyring.asc RUN apt-get update && \ apt-get install -qq --no-install-recommends gpg RUN gpg -o keyring.pgp --dearmor keyring.asc # ------------------------------------------------------------------------------ # Release Stage # ------------------------------------------------------------------------------ FROM debian:trixie-slim ARG keyring=/usr/share/keyrings/postgresql-archive-keyring.pgp COPY --from=keyring /keyring.pgp $keyring RUN . /etc/os-release && \ echo "deb [signed-by=${keyring}] http://apt.postgresql.org/pub/repos/apt/ ${VERSION_CODENAME}-pgdg main" > /etc/apt/sources.list.d/pgdg.list && \ apt-get update && \ apt-get install -qq --no-install-recommends ca-certificates openssl netcat-openbsd curl postgresql-client COPY --from=build /build/pgweb /usr/bin/pgweb RUN useradd --uid 1000 --no-create-home --shell /bin/false pgweb USER pgweb EXPOSE 8081 ENTRYPOINT ["/usr/bin/pgweb", "--bind=0.0.0.0", "--listen=8081"] ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2014-2024 Dan Sosedoff 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. ================================================ FILE: Makefile ================================================ PKG = github.com/sosedoff/pgweb GIT_COMMIT ?= $(shell git rev-parse --short=8 HEAD) BUILD_TIME ?= $(shell date -u +"%Y-%m-%dT%H:%M:%SZ" | tr -d '\n') GO_VERSION ?= $(shell go version | awk {'print $$3'}) DOCKER_RELEASE_TAG = "sosedoff/pgweb:$(shell git describe --abbrev=0 --tags | sed 's/v//')" DOCKER_LATEST_TAG = "sosedoff/pgweb:latest" LDFLAGS = -s -w LDFLAGS += -X $(PKG)/pkg/command.GitCommit=$(GIT_COMMIT) LDFLAGS += -X $(PKG)/pkg/command.BuildTime=$(BUILD_TIME) LDFLAGS += -X $(PKG)/pkg/command.GoVersion=$(GO_VERSION) usage: @echo "" @echo "Task : Description" @echo "----------------- : -------------------" @echo "make dev : Generate development build" @echo "make build : Generate production build for current OS" @echo "make release : Generate binaries for all supported OSes" @echo "make test : Execute test suite" @echo "make test-all : Execute test suite on multiple PG versions" @echo "make lint : Execute code linter" @echo "make clean : Remove all build files and reset assets" @echo "make docker : Build docker image" @echo "make docker-release : Build and tag docker image" @echo "make docker-push : Push docker images to registry" @echo "" test: go test -v -race -cover ./pkg/... test-all: @./script/test_all.sh @./script/test_cockroach.sh lint: golangci-lint run dev: go build @echo "You can now execute ./pgweb" build: go build -ldflags '${LDFLAGS}' @echo "You can now execute ./pgweb" install: go install -ldflags '${LDFLAGS}' @echo "You can now execute pgweb" release: clean @echo "Building binaries..." @LDFLAGS='${LDFLAGS}' ./script/build_all.sh clean: @echo "Removing all artifacts" @rm -rf ./pgweb ./bin/* docker: docker build --no-cache -t pgweb . docker-run: docker run --rm -p 8081:8081 -it pgweb docker-release: docker build --no-cache -t $(DOCKER_RELEASE_TAG) . docker tag $(DOCKER_RELEASE_TAG) $(DOCKER_LATEST_TAG) docker images $(DOCKER_RELEASE_TAG) docker-push: docker push $(DOCKER_RELEASE_TAG) docker push $(DOCKER_LATEST_TAG) ================================================ FILE: Procfile ================================================ web: pgweb --url=$DATABASE_URL --listen=$PORT --bind=0.0.0.0 --auth-user=$AUTH_USER --auth-pass=$AUTH_PASS ================================================ FILE: README.md ================================================ # pgweb Simple web-based and cross platform PostgreSQL database explorer. [![Release](https://img.shields.io/github/release/sosedoff/pgweb.svg?label=Release)](https://github.com/sosedoff/pgweb/releases) [![Linux Build](https://github.com/sosedoff/pgweb/actions/workflows/checks.yml/badge.svg)](https://github.com/sosedoff/pgweb/actions?query=branch%3Amain) [![Go Report Card](https://goreportcard.com/badge/github.com/sosedoff/pgweb)](https://goreportcard.com/report/github.com/sosedoff/pgweb) [![GoDoc](https://godoc.org/github.com/sosedoff/pgweb?status.svg)](https://godoc.org/github.com/sosedoff/pgweb) [![Docker Pulls](https://img.shields.io/docker/pulls/sosedoff/pgweb.svg)](https://hub.docker.com/r/sosedoff/pgweb/) ## Overview Pgweb is a web-based database explorer for PostgreSQL, written in Go, and works on Mac, Linux and Windows machines. Distributed as a simple binary with zero dependencies. Very easy to use and packs just the right amount of features. [See application screenshots](SCREENS.md) ## Features - Cross-platform: Mac/Linux/Windows (64bit). - Simple installation (distributed as a single binary). - Zero dependencies. - Works with PostgreSQL 9.1+. - Supports native SSH tunnels. - Multiple database sessions. - Execute and analyze custom SQL queries. - Table and query data export to CSV/JSON/XML. - Query history. - Server bookmarks. Visit [WIKI](https://github.com/sosedoff/pgweb/wiki) for more details. ## Demo Visit https://pgweb-demo.fly.dev/ to see Pgweb in action. ## Installation - [Precompiled binaries](https://github.com/sosedoff/pgweb/releases) for supported operating systems are available. - [More installation options](https://github.com/sosedoff/pgweb/wiki/Installation) ## Usage Start server: ``` pgweb ``` You can also provide connection flags: ``` pgweb --host localhost --user myuser --db mydb ``` Connection URL scheme is also supported: ``` pgweb --url postgres://user:password@host:port/database?sslmode=[mode] pgweb --url "postgres:///database?host=/absolute/path/to/unix/socket/dir" ``` ### Multiple database sessions To enable multiple database sessions in pgweb, start the server with: ``` pgweb --sessions ``` Or set environment variable: ``` PGWEB_SESSIONS=1 pgweb ``` ## Testing Before running tests, make sure you have PostgreSQL server running on `localhost:5432` interface. Also, you must have `postgres` user that could create new databases in your local environment. Pgweb server should not be running at the same time. Execute test suite: ``` make test ``` If you're using Docker locally, you might also run pgweb test suite against all supported PostgreSQL version with a single command: ``` make test-all ``` ## Contribute - Fork this repository - Create a new feature branch for a new functionality or bugfix - Commit your changes - Execute test suite - Push your code and open a new pull request - Use [issues](https://github.com/sosedoff/pgweb/issues) for any questions - Check [wiki](https://github.com/sosedoff/pgweb/wiki) for extra documentation ## License The MIT License (MIT). See [LICENSE](LICENSE) file for more details. ================================================ FILE: SCREENS.md ================================================ # Screenshots ### Connect ### Browse table rows ### Write SQL queries ================================================ FILE: config/examples/connect_backend_go/README.md ================================================ # connect-backend-go Example Golang backend for Pgweb Connect feature ## Usage Run the backend: ```bash go run main.go ``` Configure pgweb: ```bash pgweb --sessions --connect-backend=http://localhost:4567 --connect-token=test ``` ================================================ FILE: config/examples/connect_backend_go/main.go ================================================ package main import ( "encoding/json" "fmt" "log" "net/http" ) type BackendRequest struct { Resource string `json:"resource"` Token string `json:"token"` Headers map[string]string `json:"headers"` } type BackendResponse struct { DatabaseURL string `json:"database_url"` } func main() { resources := map[string]string{ "id1": "postgres://localhost:5432/db1?sslmode=disable", "id2": "postgres://localhost:5432/db2?sslmode=disable", "id3": "postgres://localhost:5432/db3?sslmode=disable", } http.HandleFunc("/", func(rw http.ResponseWriter, req *http.Request) { backendReq := BackendRequest{} if err := json.NewDecoder(req.Body).Decode(&backendReq); err != nil { rw.WriteHeader(400) fmt.Fprintf(rw, "error while parsing request: %v", err) return } res, ok := resources[backendReq.Resource] if !ok { rw.WriteHeader(404) return } resp := BackendResponse{ DatabaseURL: res, } json.NewEncoder(rw).Encode(resp) }) if err := http.ListenAndServe(":4567", nil); err != nil { log.Fatal(err) } } ================================================ FILE: config/examples/connect_backend_ruby/Gemfile ================================================ source "https://rubygems.org" gem "sinatra" gem "json" gem "puma" gem "rackup" ================================================ FILE: config/examples/connect_backend_ruby/README.md ================================================ # connect-backend-ruby Example Ruby backend for Pgweb Connect feature ## Usage Install and run the backend: ```bash bundle install ruby main.rb ``` Configure pgweb: ```bash pgweb --sessions --connect-backend=http://localhost:4567 --connect-token=test ``` ================================================ FILE: config/examples/connect_backend_ruby/config.ru ================================================ require "bundler/setup" require "./main" run Sinatra::Application ================================================ FILE: config/examples/connect_backend_ruby/main.rb ================================================ require "bundler/setup" require "sinatra" # Authentication token $token = "test" # List of all available resources $resources = { "id1" => "postgres://localhost:5432/db1?sslmode=disable", "id2" => "postgres://localhost:5432/db2?sslmode=disable", "id3" => "postgres://localhost:5432/db3?sslmode=disable" } helpers do def error(code, message) halt(code, JSON.dump(error: message)) end end before do content_type :json end post "/" do req = JSON.load(request.body) || {} unless req["resource"] halt 404, "Resource ID required" end # Check the resource resource = $resources[req["resource"]] if !resource halt 404, "Invalid resource ID" end # Return connection credentials JSON.dump( database_url: resource ) end ================================================ FILE: config/pgweb.freebsd_rc ================================================ #!/bin/sh # # $FreeBSD: $ # # PROVIDE: pgweb # REQUIRE: NETWORKING # KEYWORD: # # Add the following lines to /etc/rc.conf to enable pgweb: # pgweb_enable="YES" # # pgweb_enable (bool): Set to YES to enable pgweb # Default: NO # pgweb_bind (str): HTTP server host # Default: localhost # pgweb_listen (str): HTTP server listen port # Default: 8081 # pgweb_user (str): pgweb daemon user # Default: www # pgweb_group (str): pgweb daemon group # Default: www . /etc/rc.subr name="pgweb" rcvar="pgweb_enable" load_rc_config $name : ${pgweb_user:="www"} : ${pgweb_group:="www"} : ${pgweb_enable:="NO"} : ${pgweb_bind:="localhost"} : ${pgweb_flags=""} : ${pgweb_facility:="daemon"} : ${pgweb_priority:="debug"} : ${pgweb_listen:="8081"} procname="/usr/local/bin/${name}" pidfile="/var/run/${name}.pid" start_precmd="${name}_precmd" command=/usr/sbin/daemon command_args="-S -l ${pgweb_facility} -s ${pgweb_priority} -T ${name} -t ${name} -p ${pidfile} \ ${procname} --bind=${pgweb_bind} --listen=${pgweb_listen} ${pgweb_flags}" pgweb_precmd() { install -o ${pgweb_user} /dev/null ${pidfile} } run_rc_command "$1" ================================================ FILE: config/pgweb.service ================================================ [Unit] Description=pgweb - Cross-platform client for PostgreSQL databases After=network.target [Service] Type=simple ExecStart=/usr/bin/pgweb --bind=0.0.0.0 --listen=8081 Restart=on-abort [Install] WantedBy=multi-user.target ================================================ FILE: config/pgweb_initd.conf ================================================ #!/bin/sh ### BEGIN INIT INFO # Provides: pgweb # Required-Start: $remote_fs $syslog # Required-Stop: $remote_fs $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Start daemon at boot time # Description: Enable service provided by daemon. ### END INIT INFO # Installation instructions (originally written for Debian) # # Save this script into /etc/init.d/pgweb file, make it executable, # and install it into the boot sequence: # # chmod 755 /etc/init.d/pgweb # update-rc.d pgweb defaults # # This script assumes that pgweb binary is located at /home/pgweb/, and that # there's a bookmark 'server' in /home/pgweb/.pgweb/bookmarks/. # NAME="pgweb" PIDFILE="/var/run/$NAME.pid" USER="pgweb" # Linux system user SU="su $USER -s /bin/bash" TIMEOUT=5 # Time in seconds to wait postgresql to show up case "$1" in start) if [ -f $PIDFILE ]; then echo "Already running... cat $PIDFILE" exit 0 fi # Wait postgresql to show up while ! test -f /var/run/postgresql/*main.pid do sleep 1 TIMEOUT=`expr $TIMEOUT - 1` if test $TIMEOUT -eq 0; then exit 1 fi done # Ready to start pgweb PID=`$SU -c '/home/pgweb/pgweb -s -b server >/dev/null & echo $!'` # Note! Logs are lost. if [ -z $PID ]; then exit 1 else echo $PID > $PIDFILE fi ;; stop) PID=`cat $PIDFILE` kill $PID && rm $PIDFILE ;; *) echo "Usage: /etc/init.d/$NAME {start|stop}" exit 1 ;; esac exit 0 ================================================ FILE: config/pgweb_upstart.conf ================================================ description "PgWeb as a Service for Ubuntu 14.04 With Upstart" start on runlevel [2345] stop on runlevel [!2345] respawn setuid youruser setgid www-data exec /usr/bin/pgweb --bind=0.0.0.0 --listen=8081 ================================================ FILE: data/bookmark.toml ================================================ host = "localhost" port = 5432 user = "postgres" database = "mydatabase" ssl = "disable" ================================================ FILE: data/bookmark_invalid_ssl.toml ================================================ host = "localhost" port = 5432 user = "postgres" database = "mydatabase" ssl = "disabled" ================================================ FILE: data/bookmark_url.toml ================================================ url = "postgres://username:password@host:port/database?sslmode=disable" ================================================ FILE: data/bookmark_with_ssh.toml ================================================ host = "localhost" port = 5432 user = "postgres" database = "mydatabase" ssl = "disable" [SSH] host = "ssh-host" user = "ssh-user" password = "ssh-password" key = "/path/to/key-file" keypassword = "key-file-password" ================================================ FILE: data/booktown.sql ================================================ -- -- Selected TOC Entries: -- -- -- TOC Entry ID 1 (OID 0) -- -- Name: booktown Type: DATABASE Owner: postgres -- DROP DATABASE IF EXISTS "booktown"; CREATE DATABASE "booktown"; -- \connect booktown postgres -- -- TOC Entry ID 2 (OID 2991542) -- -- Name: DATABASE "booktown" Type: COMMENT Owner: -- CREATE TABLE "dummies" ( "id" integer NOT NULL, "isDummy" boolean ); INSERT INTO "dummies" VALUES (1, true); INSERT INTO "dummies" VALUES (2, true); COMMENT ON DATABASE "booktown" IS 'The Book Town Database.'; -- -- TOC Entry ID 33 (OID 3629264) -- -- Name: books Type: TABLE Owner: manager -- CREATE TABLE "books" ( "id" integer NOT NULL, "title" text NOT NULL, "author_id" integer, "subject_id" integer, Constraint "books_id_pkey" Primary Key ("id") ); -- -- TOC Entry ID 47 (OID 2991733) -- -- Name: "plpgsql_call_handler" () Type: FUNCTION Owner: postgres -- CREATE FUNCTION "plpgsql_call_handler" () RETURNS opaque AS '/usr/local/pgsql/lib/plpgsql.so', 'plpgsql_call_handler' LANGUAGE 'C'; -- -- TOC Entry ID 48 (OID 2991734) -- -- Name: plpgsql Type: PROCEDURAL LANGUAGE Owner: -- CREATE TRUSTED PROCEDURAL LANGUAGE 'plpgsql' HANDLER "plpgsql_call_handler" LANCOMPILER 'PL/pgSQL'; -- -- TOC Entry ID 51 (OID 2991735) -- -- Name: "audit_bk" (integer) Type: FUNCTION Owner: postgres -- CREATE FUNCTION "audit_bk" (integer) RETURNS integer AS ' DECLARE key ALIAS FOR $1; table_data inventory%ROWTYPE; BEGIN INSERT INTO inventory_audit SELECT table_data WHERE sort_key=key; IF NOT FOUND THEN RAISE EXCEPTION ''View'' || key || '' not found ''; END IF; return 1; end; ' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 52 (OID 2991736) -- -- Name: "audit" (integer) Type: FUNCTION Owner: postgres -- CREATE FUNCTION "audit" (integer) RETURNS integer AS ' DECLARE key ALIAS FOR $1; table_data inventory%ROWTYPE; BEGIN INSERT INTO inventory_audit SELECT table_data WHERE sort_key=key; IF NOT FOUND THEN RAISE EXCEPTION ''View'' || key || '' not found ''; END IF; return 1; end; ' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 53 (OID 2991737) -- -- Name: "auditbk" () Type: FUNCTION Owner: postgres -- CREATE FUNCTION "auditbk" () RETURNS integer AS ' DECLARE key ALIAS FOR $1; table_data inventory%ROWTYPE; BEGIN INSERT INTO inventory_audit SELECT table_data WHERE sort_key=key; IF NOT FOUND THEN RAISE EXCEPTION ''View'' || key || '' not found ''; END IF; return 1; end; ' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 54 (OID 2991738) -- -- Name: "audit_bk1" () Type: FUNCTION Owner: postgres -- CREATE FUNCTION "audit_bk1" () RETURNS opaque AS ' DECLARE key ALIAS FOR $1; table_data inventory%ROWTYPE; BEGIN INSERT INTO inventory_audit SELECT table_data WHERE sort_key=key; IF NOT FOUND THEN RAISE EXCEPTION ''View'' || key || '' not found ''; END IF; return 1; end; ' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 73 (OID 2991835) -- -- Name: "test_check_a_id" () Type: FUNCTION Owner: example -- CREATE FUNCTION "test_check_a_id" () RETURNS opaque AS ' BEGIN -- checks to make sure the author id -- inserted is not left blank or less than 100 IF NEW.a_id ISNULL THEN RAISE EXCEPTION ''The author id cannot be left blank!''; ELSE IF NEW.a_id < 100 THEN RAISE EXCEPTION ''Please insert a valid author id.''; ELSE RETURN NEW; END IF; END IF; END; ' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 66 (OID 2992619) -- -- Name: "audit_test" () Type: FUNCTION Owner: example -- CREATE FUNCTION "audit_test" () RETURNS opaque AS ' BEGIN IF TG_OP = ''INSERT'' OR TG_OP = ''UPDATE'' THEN NEW.user_aud := current_user; NEW.mod_time := ''NOW''; INSERT INTO inventory_audit SELECT * FROM inventory WHERE prod_id=NEW.prod_id; RETURN NEW; ELSE if TG_OP = ''DELETE'' THEN INSERT INTO inventory_audit SELECT *, current_user, ''NOW'' FROM inventory WHERE prod_id=OLD.prod_id; RETURN OLD; END IF; END IF; END; ' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 67 (OID 3000878) -- -- Name: "first" () Type: FUNCTION Owner: example -- CREATE FUNCTION "first" () RETURNS integer AS ' DecLarE oNe IntEgER := 1; bEGiN ReTUrn oNE; eNd; ' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 68 (OID 3000881) -- -- Name: "test" (integer) Type: FUNCTION Owner: example -- CREATE FUNCTION "test" (integer) RETURNS integer AS ' DECLARE -- defines the variable as ALIAS variable ALIAS FOR $1; BEGIN -- displays the variable after multiplying it by two return variable * 2.0; END; ' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 69 (OID 3000991) -- -- Name: "you_me" (integer) Type: FUNCTION Owner: example -- CREATE FUNCTION "you_me" (integer) RETURNS integer AS ' DECLARE RENAME $1 TO user_no; --you INTEGER := 5; BEGIN return user_no; END;' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 62 (OID 3001136) -- -- Name: "count_by_two" (integer) Type: FUNCTION Owner: example -- CREATE FUNCTION "count_by_two" (integer) RETURNS integer AS ' DECLARE userNum ALIAS FOR $1; i integer; BEGIN i := 1; WHILE userNum[1] < 20 LOOP i = i+1; return userNum; END LOOP; END; ' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 63 (OID 3001139) -- -- Name: "me" () Type: FUNCTION Owner: example -- CREATE FUNCTION "me" () RETURNS text AS ' DECLARE you text := ''testing''; RENAME you to me; BEGIN return me; END;' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 64 (OID 3001149) -- -- Name: "display_cust" (integer) Type: FUNCTION Owner: example -- CREATE FUNCTION "display_cust" (integer) RETURNS text AS ' DECLARE -- declares an alias name for input cust_num ALIAS FOR $1; -- declares a row type cust_info customer%ROWTYPE; BEGIN -- puts information into the newly declared rowtype SELECT into cust_info * FROM customer WHERE cust_id=cust_num; -- displays the customer lastname -- extracted from the rowtype return cust_info.lastname; END; ' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 65 (OID 3001151) -- -- Name: "mixed" () Type: FUNCTION Owner: example -- CREATE FUNCTION "mixed" () RETURNS integer AS ' DecLarE --assigns 1 to the oNe variable oNe IntEgER := 1; bEGiN --displays the value of oNe ReTUrn oNe; eNd; ' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 12 (OID 3117548) -- -- Name: publishers Type: TABLE Owner: postgres -- CREATE TABLE "publishers" ( "id" integer NOT NULL, "name" text, "address" text, Constraint "publishers_pkey" Primary Key ("id") ); -- -- TOC Entry ID 55 (OID 3117729) -- -- Name: "compound_word" (text,text) Type: FUNCTION Owner: example -- CREATE FUNCTION "compound_word" (text,text) RETURNS text AS ' DECLARE -- defines an alias name for the two input values word1 ALIAS FOR $1; word2 ALIAS FOR $2; BEGIN -- displays the resulting joined words RETURN word1 || word2; END; ' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 56 (OID 3117787) -- -- Name: "givename" () Type: FUNCTION Owner: example -- CREATE FUNCTION "givename" () RETURNS opaque AS ' DECLARE tablename text; BEGIN tablename = TG_RELNAME; INSERT INTO INVENTORY values (123, tablename); return old; END; ' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 14 (OID 3389594) -- -- Name: authors Type: TABLE Owner: manager -- CREATE TABLE "authors" ( "id" integer NOT NULL, "last_name" text, "first_name" text, Constraint "authors_pkey" Primary Key ("id") ); -- -- TOC Entry ID 15 (OID 3389632) -- -- Name: states Type: TABLE Owner: postgres -- CREATE TABLE "states" ( "id" integer NOT NULL, "name" text, "abbreviation" character(2), Constraint "state_pkey" Primary Key ("id") ); -- -- TOC Entry ID 16 (OID 3389702) -- -- Name: my_list Type: TABLE Owner: postgres -- CREATE TABLE "my_list" ( "todos" text ); -- -- TOC Entry ID 17 (OID 3390348) -- -- Name: stock Type: TABLE Owner: postgres -- CREATE TABLE "stock" ( "isbn" text NOT NULL, "cost" numeric(5,2), "retail" numeric(5,2), "stock" integer, Constraint "stock_pkey" Primary Key ("isbn") ); -- -- TOC Entry ID 4 (OID 3390416) -- -- Name: subject_ids Type: SEQUENCE Owner: postgres -- CREATE SEQUENCE "subject_ids" start 0 increment 1 maxvalue 2147483647 minvalue 0 cache 1 ; -- -- TOC Entry ID 19 (OID 3390653) -- -- Name: numeric_values Type: TABLE Owner: postgres -- CREATE TABLE "numeric_values" ( "num" numeric(30,6) ); -- -- TOC Entry ID 20 (OID 3390866) -- -- Name: daily_inventory Type: TABLE Owner: postgres -- CREATE TABLE "daily_inventory" ( "isbn" text, "is_stocked" boolean ); -- -- TOC Entry ID 21 (OID 3391084) -- -- Name: money_example Type: TABLE Owner: postgres -- CREATE TABLE "money_example" ( "money_cash" money, "numeric_cash" numeric(6,2) ); -- -- TOC Entry ID 22 (OID 3391184) -- -- Name: shipments Type: TABLE Owner: postgres -- CREATE TABLE "shipments" ( "id" integer DEFAULT nextval('"shipments_ship_id_seq"'::text) NOT NULL, "customer_id" integer, "isbn" text, "ship_date" timestamp with time zone ); -- -- TOC Entry ID 24 (OID 3391454) -- -- Name: customers Type: TABLE Owner: manager -- CREATE TABLE "customers" ( "id" integer NOT NULL, "last_name" text, "first_name" text, Constraint "customers_pkey" Primary Key ("id") ); -- -- TOC Entry ID 6 (OID 3574018) -- -- Name: book_ids Type: SEQUENCE Owner: postgres -- CREATE SEQUENCE "book_ids" start 0 increment 1 maxvalue 2147483647 minvalue 0 cache 1 ; -- -- TOC Entry ID 26 (OID 3574043) -- -- Name: book_queue Type: TABLE Owner: postgres -- CREATE TABLE "book_queue" ( "title" text NOT NULL, "author_id" integer, "subject_id" integer, "approved" boolean ); -- -- TOC Entry ID 78 (OID 3574403) -- -- Name: "title" (integer) Type: FUNCTION Owner: postgres -- CREATE FUNCTION "title" (integer) RETURNS text AS 'SELECT title from books where id = $1' LANGUAGE 'sql'; -- -- TOC Entry ID 27 (OID 3574983) -- -- Name: stock_backup Type: TABLE Owner: postgres -- CREATE TABLE "stock_backup" ( "isbn" text, "cost" numeric(5,2), "retail" numeric(5,2), "stock" integer ); -- -- TOC Entry ID 89 (OID 3625934) -- -- Name: "double_price" (double precision) Type: FUNCTION Owner: postgres -- CREATE FUNCTION "double_price" (double precision) RETURNS double precision AS ' DECLARE BEGIN return $1 * 2; END; ' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 90 (OID 3625935) -- -- Name: "triple_price" (double precision) Type: FUNCTION Owner: postgres -- CREATE FUNCTION "triple_price" (double precision) RETURNS double precision AS ' DECLARE -- Declare input_price as an alias for the -- argument variable normally referenced with -- the $1 identifier. input_price ALIAS FOR $1; BEGIN -- Return the input price multiplied by three. RETURN input_price * 3; END; ' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 87 (OID 3625944) -- -- Name: "stock_amount" (integer,integer) Type: FUNCTION Owner: postgres -- CREATE FUNCTION "stock_amount" (integer,integer) RETURNS integer AS ' DECLARE -- Declare aliases for function arguments. b_id ALIAS FOR $1; b_edition ALIAS FOR $2; -- Declare variable to store the ISBN number. b_isbn TEXT; -- Declare variable to store the stock amount. stock_amount INTEGER; BEGIN -- This SELECT INTO statement retrieves the ISBN -- number of the row in the editions table that had -- both the book ID number and edition number that -- were provided as function arguments. SELECT INTO b_isbn isbn FROM editions WHERE book_id = b_id AND edition = b_edition; -- Check to see if the ISBN number retrieved -- is NULL. This will happen if there is not an -- existing book with both the ID number and edition -- number specified in the function arguments. -- If the ISBN is null, the function returns a -- value of -1 and ends. IF b_isbn IS NULL THEN RETURN -1; END IF; -- Retrieve the amount of books available from the -- stock table and record the number in the -- stock_amount variable. SELECT INTO stock_amount stock FROM stock WHERE isbn = b_isbn; -- Return the amount of books available. RETURN stock_amount; END; ' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 86 (OID 3625946) -- -- Name: "in_stock" (integer,integer) Type: FUNCTION Owner: postgres -- CREATE FUNCTION "in_stock" (integer,integer) RETURNS boolean AS ' DECLARE b_id ALIAS FOR $1; b_edition ALIAS FOR $2; b_isbn TEXT; stock_amount INTEGER; BEGIN -- This SELECT INTO statement retrieves the ISBN -- number of the row in the editions table that had -- both the book ID number and edition number that -- were provided as function arguments. SELECT INTO b_isbn isbn FROM editions WHERE book_id = b_id AND edition = b_edition; -- Check to see if the ISBN number retrieved -- is NULL. This will happen if there is not an -- existing book with both the ID number and edition -- number specified in the function arguments. -- If the ISBN is null, the function returns a -- FALSE value and ends. IF b_isbn IS NULL THEN RETURN FALSE; END IF; -- Retrieve the amount of books available from the -- stock table and record the number in the -- stock_amount variable. SELECT INTO stock_amount stock FROM stock WHERE isbn = b_isbn; -- Use an IF/THEN/ELSE check to see if the amount -- of books available is less than, or equal to 0. -- If so, return FALSE. If not, return TRUE. IF stock_amount <= 0 THEN RETURN FALSE; ELSE RETURN TRUE; END IF; END; ' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 82 (OID 3626013) -- -- Name: "extract_all_titles" () Type: FUNCTION Owner: postgres -- CREATE FUNCTION "extract_all_titles" () RETURNS text AS ' DECLARE sub_id INTEGER; text_output TEXT = '' ''; sub_title TEXT; row_data books%ROWTYPE; BEGIN FOR i IN 0..15 LOOP SELECT INTO sub_title subject FROM subjects WHERE id = i; text_output = text_output || '' '' || sub_title || '': ''; FOR row_data IN SELECT * FROM books WHERE subject_id = i LOOP IF NOT FOUND THEN text_output := text_output || ''None. ''; ELSE text_output := text_output || row_data.title || '' ''; END IF; END LOOP; END LOOP; RETURN text_output; END; ' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 79 (OID 3626052) -- -- Name: "books_by_subject" (text) Type: FUNCTION Owner: postgres -- CREATE FUNCTION "books_by_subject" (text) RETURNS text AS ' DECLARE sub_title ALIAS FOR $1; sub_id INTEGER; found_text TEXT :=''''; BEGIN SELECT INTO sub_id id FROM subjects WHERE subject = sub_title; RAISE NOTICE ''sub_id = %'',sub_id; IF sub_title = ''all'' THEN found_text := extract_all_titles(); RETURN found_text; ELSE IF sub_id >= 0 THEN found_text := extract_title(sub_id); RETURN '' '' || sub_title || '': '' || found_text; END IF; END IF; RETURN ''Subject not found.''; END; ' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 81 (OID 3626590) -- -- Name: "add_two_loop" (integer,integer) Type: FUNCTION Owner: postgres -- CREATE FUNCTION "add_two_loop" (integer,integer) RETURNS integer AS ' DECLARE -- Declare aliases for function arguments. low_number ALIAS FOR $1; high_number ALIAS FOR $2; -- Declare a variable to hold the result. result INTEGER = 0; BEGIN WHILE result != high_number LOOP result := result + 1; END LOOP; RETURN result; END; ' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 92 (OID 3627916) -- -- Name: "extract_all_titles2" () Type: FUNCTION Owner: postgres -- CREATE FUNCTION "extract_all_titles2" () RETURNS text AS ' DECLARE sub_id INTEGER; text_output TEXT = '' ''; sub_title TEXT; row_data books%ROWTYPE; BEGIN FOR i IN 0..15 LOOP SELECT INTO sub_title subject FROM subjects WHERE id = i; text_output = text_output || '' '' || sub_title || '': ''; FOR row_data IN SELECT * FROM books WHERE subject_id = i LOOP text_output := text_output || row_data.title || '' ''; END LOOP; END LOOP; RETURN text_output; END; ' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 94 (OID 3627974) -- -- Name: "extract_title" (integer) Type: FUNCTION Owner: postgres -- CREATE FUNCTION "extract_title" (integer) RETURNS text AS ' DECLARE sub_id ALIAS FOR $1; text_output TEXT :='' ''; row_data RECORD; BEGIN FOR row_data IN SELECT * FROM books WHERE subject_id = sub_id ORDER BY title LOOP text_output := text_output || row_data.title || '' ''; END LOOP; RETURN text_output; END; ' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 95 (OID 3628021) -- -- Name: "raise_test" () Type: FUNCTION Owner: postgres -- CREATE FUNCTION "raise_test" () RETURNS integer AS ' DECLARE -- Declare an integer variable for testing. an_integer INTEGER = 1; BEGIN -- Raise a debug level message. RAISE DEBUG ''The raise_test() function began.''; an_integer = an_integer + 1; -- Raise a notice stating that the an_integer -- variable was changed, then raise another notice -- stating its new value. RAISE NOTICE ''Variable an_integer was changed.''; RAISE NOTICE ''Variable an_integer value is now %.'',an_integer; -- Raise an exception. RAISE EXCEPTION ''Variable % changed. Aborting transaction.'',an_integer; END; ' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 93 (OID 3628069) -- -- Name: "add_shipment" (integer,text) Type: FUNCTION Owner: postgres -- CREATE FUNCTION "add_shipment" (integer,text) RETURNS timestamp with time zone AS ' DECLARE customer_id ALIAS FOR $1; isbn ALIAS FOR $2; shipment_id INTEGER; right_now timestamp; BEGIN right_now := ''now''; SELECT INTO shipment_id id FROM shipments ORDER BY id DESC; shipment_id := shipment_id + 1; INSERT INTO shipments VALUES ( shipment_id, customer_id, isbn, right_now ); RETURN right_now; END; ' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 102 (OID 3628076) -- -- Name: "ship_item" (text,text,text) Type: FUNCTION Owner: postgres -- CREATE FUNCTION "ship_item" (text,text,text) RETURNS integer AS ' DECLARE l_name ALIAS FOR $1; f_name ALIAS FOR $2; book_isbn ALIAS FOR $3; book_id INTEGER; customer_id INTEGER; BEGIN SELECT INTO customer_id get_customer_id(l_name,f_name); IF customer_id = -1 THEN RETURN -1; END IF; SELECT INTO book_id book_id FROM editions WHERE isbn = book_isbn; IF NOT FOUND THEN RETURN -1; END IF; PERFORM add_shipment(customer_id,book_isbn); RETURN 1; END; ' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 103 (OID 3628114) -- -- Name: "check_book_addition" () Type: FUNCTION Owner: postgres -- CREATE FUNCTION "check_book_addition" () RETURNS opaque AS ' DECLARE id_number INTEGER; book_isbn TEXT; BEGIN SELECT INTO id_number id FROM customers WHERE id = NEW.customer_id; IF NOT FOUND THEN RAISE EXCEPTION ''Invalid customer ID number.''; END IF; SELECT INTO book_isbn isbn FROM editions WHERE isbn = NEW.isbn; IF NOT FOUND THEN RAISE EXCEPTION ''Invalid ISBN.''; END IF; UPDATE stock SET stock = stock -1 WHERE isbn = NEW.isbn; RETURN NEW; END; ' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 28 (OID 3628246) -- -- Name: stock_view Type: VIEW Owner: postgres -- CREATE VIEW "stock_view" as SELECT stock.isbn, stock.retail, stock.stock FROM stock; CREATE MATERIALIZED VIEW "m_stock_view" as SELECT stock.isbn, stock.retail, stock.stock FROM stock; -- -- TOC Entry ID 30 (OID 3628247) -- -- Name: favorite_books Type: TABLE Owner: manager -- CREATE TABLE "favorite_books" ( "employee_id" integer, "books" text[] ); -- -- TOC Entry ID 8 (OID 3628626) -- -- Name: shipments_ship_id_seq Type: SEQUENCE Owner: manager -- CREATE SEQUENCE "shipments_ship_id_seq" start 0 increment 1 maxvalue 2147483647 minvalue 0 cache 1 ; -- -- TOC Entry ID 74 (OID 3628648) -- -- Name: "check_shipment_addition" () Type: FUNCTION Owner: postgres -- CREATE FUNCTION "check_shipment_addition" () RETURNS opaque AS ' DECLARE -- Declare a variable to hold the customer ID. id_number INTEGER; -- Declare a variable to hold the ISBN. book_isbn TEXT; BEGIN -- If there is an ID number that matches the customer ID in -- the new table, retrieve it from the customers table. SELECT INTO id_number id FROM customers WHERE id = NEW.customer_id; -- If there was no matching ID number, raise an exception. IF NOT FOUND THEN RAISE EXCEPTION ''Invalid customer ID number.''; END IF; -- If there is an ISBN that matches the ISBN specified in the -- new table, retrieve it from the editions table. SELECT INTO book_isbn isbn FROM editions WHERE isbn = NEW.isbn; -- If there is no matching ISBN, raise an exception. IF NOT FOUND THEN RAISE EXCEPTION ''Invalid ISBN.''; END IF; -- If the previous checks succeeded, update the stock amount -- for INSERT commands. IF TG_OP = ''INSERT'' THEN UPDATE stock SET stock = stock -1 WHERE isbn = NEW.isbn; END IF; RETURN NEW; END; ' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 31 (OID 3628899) -- -- Name: employees Type: TABLE Owner: postgres -- CREATE TABLE "employees" ( "id" integer NOT NULL, "last_name" text NOT NULL, "first_name" text, CONSTRAINT "employees_id" CHECK ((id > 100)), Constraint "employees_pkey" Primary Key ("id") ); -- -- TOC Entry ID 32 (OID 3629174) -- -- Name: editions Type: TABLE Owner: manager -- CREATE TABLE "editions" ( "isbn" text NOT NULL, "book_id" integer, "edition" integer, "publisher_id" integer, "publication" date, "type" character(1), CONSTRAINT "integrity" CHECK (((book_id NOTNULL) AND (edition NOTNULL))), Constraint "pkey" Primary Key ("isbn") ); -- -- TOC Entry ID 10 (OID 3629402) -- -- Name: author_ids Type: SEQUENCE Owner: manager -- CREATE SEQUENCE "author_ids" start 0 increment 1 maxvalue 2147483647 minvalue 0 cache 1 ; -- -- TOC Entry ID 35 (OID 3629424) -- -- Name: distinguished_authors Type: TABLE Owner: manager -- CREATE TABLE "distinguished_authors" ( "award" text ) INHERITS ("authors"); -- -- TOC Entry ID 107 (OID 3726476) -- -- Name: "isbn_to_title" (text) Type: FUNCTION Owner: manager -- CREATE FUNCTION "isbn_to_title" (text) RETURNS text AS 'SELECT title FROM books JOIN editions AS e (isbn, id) USING (id) WHERE isbn = $1' LANGUAGE 'sql'; -- -- TOC Entry ID 36 (OID 3727889) -- -- Name: favorite_authors Type: TABLE Owner: manager -- CREATE TABLE "favorite_authors" ( "employee_id" integer, "authors_and_titles" text[] ); -- -- TOC Entry ID 99 (OID 3728728) -- -- Name: "get_customer_name" (integer) Type: FUNCTION Owner: postgres -- CREATE FUNCTION "get_customer_name" (integer) RETURNS text AS ' DECLARE -- Declare aliases for user input. customer_id ALIAS FOR $1; -- Declare variables to hold the customer name. customer_fname TEXT; customer_lname TEXT; BEGIN -- Retrieve the customer first and last name for the customer whose -- ID matches the value supplied as a function argument. SELECT INTO customer_fname, customer_lname first_name, last_name FROM customers WHERE id = customer_id; -- Return the name. RETURN customer_fname || '' '' || customer_lname; END; ' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 100 (OID 3728729) -- -- Name: "get_customer_id" (text,text) Type: FUNCTION Owner: postgres -- CREATE FUNCTION "get_customer_id" (text,text) RETURNS integer AS ' DECLARE -- Declare aliases for user input. l_name ALIAS FOR $1; f_name ALIAS FOR $2; -- Declare a variable to hold the customer ID number. customer_id INTEGER; BEGIN -- Retrieve the customer ID number of the customer whose first and last -- name match the values supplied as function arguments. SELECT INTO customer_id id FROM customers WHERE last_name = l_name AND first_name = f_name; -- Return the ID number. RETURN customer_id; END; ' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 101 (OID 3728730) -- -- Name: "get_author" (text) Type: FUNCTION Owner: postgres -- CREATE FUNCTION "get_author" (text) RETURNS text AS ' DECLARE -- Declare an alias for the function argument, -- which should be the first name of an author. f_name ALIAS FOR $1; -- Declare a variable with the same type as -- the last_name field of the authors table. l_name authors.last_name%TYPE; BEGIN -- Retrieve the last name of an author from the -- authors table whose first name matches the -- argument received by the function, and -- insert it into the l_name variable. SELECT INTO l_name last_name FROM authors WHERE first_name = f_name; -- Return the first name and last name, separated -- by a space. return f_name || '' '' || l_name; END; ' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 97 (OID 3728759) -- -- Name: "get_author" (integer) Type: FUNCTION Owner: postgres -- CREATE FUNCTION "get_author" (integer) RETURNS text AS ' DECLARE -- Declare an alias for the function argument, -- which should be the id of the author. author_id ALIAS FOR $1; -- Declare a variable that uses the structure of -- the authors table. found_author authors%ROWTYPE; BEGIN -- Retrieve a row of author information for -- the author whose id number matches -- the argument received by the function. SELECT INTO found_author * FROM authors WHERE id = author_id; -- Return the first RETURN found_author.first_name || '' '' || found_author.last_name; END; ' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 70 (OID 3743412) -- -- Name: "html_linebreaks" (text) Type: FUNCTION Owner: postgres -- CREATE FUNCTION "html_linebreaks" (text) RETURNS text AS ' DECLARE formatted_string text := ''''; BEGIN FOR i IN 0 .. length($1) LOOP IF substr($1, i, 1) = '' '' THEN formatted_string := formatted_string || ''
''; ELSE formatted_string := formatted_string || substr($1, i, 1); END IF; END LOOP; RETURN formatted_string; END; ' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 37 (OID 3751599) -- -- Name: text_sorting Type: TABLE Owner: postgres -- CREATE TABLE "text_sorting" ( "letter" character(1) ); -- -- TOC Entry ID 38 (OID 3751882) -- -- Name: subjects Type: TABLE Owner: postgres -- CREATE TABLE "subjects" ( "id" integer NOT NULL, "subject" text, "location" text, Constraint "subjects_pkey" Primary Key ("id") ); -- -- TOC Entry ID 108 (OID 3751924) -- -- Name: sum(text) Type: AGGREGATE Owner: postgres -- CREATE AGGREGATE sum ( BASETYPE = text, SFUNC = textcat, STYPE = text, INITCOND = '' ); -- -- TOC Entry ID 39 (OID 3751975) -- -- Name: alternate_stock Type: TABLE Owner: postgres -- CREATE TABLE "alternate_stock" ( "isbn" text, "cost" numeric(5,2), "retail" numeric(5,2), "stock" integer ); -- -- TOC Entry ID 40 (OID 3752020) -- -- Name: book_backup Type: TABLE Owner: postgres -- CREATE TABLE "book_backup" ( "id" integer, "title" text, "author_id" integer, "subject_id" integer ); -- -- TOC Entry ID 80 (OID 3752102) -- -- Name: "sync_authors_and_books" () Type: FUNCTION Owner: postgres -- CREATE FUNCTION "sync_authors_and_books" () RETURNS opaque AS ' BEGIN IF TG_OP = ''UPDATE'' THEN UPDATE books SET author_id = new.id WHERE author_id = old.id; END IF; RETURN new; END; ' LANGUAGE 'plpgsql'; -- -- TOC Entry ID 41 (OID 4063343) -- -- Name: schedules Type: TABLE Owner: postgres -- CREATE TABLE "schedules" ( "employee_id" integer NOT NULL, "schedule" text, Constraint "schedules_pkey" Primary Key ("employee_id") ); -- -- TOC Entry ID 42 (OID 4063653) -- -- Name: recent_shipments Type: VIEW Owner: postgres -- CREATE VIEW "recent_shipments" as SELECT count(*) AS num_shipped, max(shipments.ship_date) AS max, b.title FROM ((shipments JOIN editions USING (isbn)) NATURAL JOIN books b(book_id)) GROUP BY b.title ORDER BY count(*) DESC; -- -- Data for TOC Entry ID 112 (OID 3117548) -- -- Name: publishers Type: TABLE DATA Owner: postgres -- COPY "publishers" FROM stdin; 150 Kids Can Press Kids Can Press, 29 Birch Ave. Toronto,�ON��M4V 1E2 91 Henry Holt & Company, Inc. Henry Holt & Company, Inc. 115 West 18th Street New York, NY 10011 113 O'Reilly & Associates O'Reilly & Associates, Inc. 101 Morris St, Sebastopol, CA 95472 62 Watson-Guptill Publications 1515 Boradway, New York, NY 10036 105 Noonday Press Farrar Straus & Giroux Inc, 19 Union Square W, New York, NY 10003 99 Ace Books The Berkley Publishing Group, Penguin Putnam Inc, 375 Hudson St, New York, NY 10014 101 Roc Penguin Putnam Inc, 375 Hudson St, New York, NY 10014 163 Mojo Press Mojo Press, PO Box 1215, Dripping Springs, TX 78720 171 Books of Wonder Books of Wonder, 16 W. 18th St. New York, NY, 10011 102 Penguin Penguin Putnam Inc, 375 Hudson St, New York, NY 10014 75 Doubleday Random House, Inc, 1540 Broadway, New York, NY 10036 65 HarperCollins HarperCollins Publishers, 10 E 53rd St, New York, NY 10022 59 Random House Random House, Inc, 1540 Broadway, New York, NY 10036 \. -- -- Data for TOC Entry ID 113 (OID 3389594) -- -- Name: authors Type: TABLE DATA Owner: manager -- COPY "authors" FROM stdin; 1111 Denham Ariel 1212 Worsley John 15990 Bourgeois Paulette 25041 Bianco Margery Williams 16 Alcott Louisa May 4156 King Stephen 1866 Herbert Frank 1644 Hogarth Burne 2031 Brown Margaret Wise 115 Poe Edgar Allen 7805 Lutz Mark 7806 Christiansen Tom 1533 Brautigan Richard 1717 Brite Poppy Z. 2112 Gorey Edward 2001 Clarke Arthur C. 1213 Brookins Andrew \. -- -- Data for TOC Entry ID 114 (OID 3389632) -- -- Name: states Type: TABLE DATA Owner: postgres -- COPY "states" FROM stdin; 42 Washington WA 51 Oregon OR \. -- -- Data for TOC Entry ID 115 (OID 3389702) -- -- Name: my_list Type: TABLE DATA Owner: postgres -- COPY "my_list" FROM stdin; Pick up laundry. Send out bills. Wrap up Grand Unifying Theory for publication. \. -- -- Data for TOC Entry ID 116 (OID 3390348) -- -- Name: stock Type: TABLE DATA Owner: postgres -- COPY "stock" FROM stdin; 0385121679 29.00 36.95 65 039480001X 30.00 32.95 31 0394900014 23.00 23.95 0 044100590X 36.00 45.95 89 0441172717 17.00 21.95 77 0451160916 24.00 28.95 22 0451198492 36.00 46.95 0 0451457994 17.00 22.95 0 0590445065 23.00 23.95 10 0679803335 20.00 24.95 18 0694003611 25.00 28.95 50 0760720002 18.00 23.95 28 0823015505 26.00 28.95 16 0929605942 19.00 21.95 25 1885418035 23.00 24.95 77 0394800753 16.00 16.95 4 \. -- -- Data for TOC Entry ID 117 (OID 3390653) -- -- Name: numeric_values Type: TABLE DATA Owner: postgres -- COPY "numeric_values" FROM stdin; 68719476736.000000 68719476737.000000 6871947673778.000000 999999999999999999999999.999900 999999999999999999999999.999999 -999999999999999999999999.999999 -100000000000000000000000.999999 1.999999 2.000000 2.000000 999999999999999999999999.999999 999999999999999999999999.000000 \. -- -- Data for TOC Entry ID 118 (OID 3390866) -- -- Name: daily_inventory Type: TABLE DATA Owner: postgres -- COPY "daily_inventory" FROM stdin; 039480001X t 044100590X t 0451198492 f 0394900014 f 0441172717 t 0451160916 f 0385121679 \N \. -- -- Data for TOC Entry ID 119 (OID 3391084) -- -- Name: money_example Type: TABLE DATA Owner: postgres -- COPY "money_example" FROM stdin; $12.24 12.24 \. -- -- Data for TOC Entry ID 120 (OID 3391184) -- -- Name: shipments Type: TABLE DATA Owner: postgres -- COPY "shipments" FROM stdin; 375 142 039480001X 2001-08-06 09:29:21-07 323 671 0451160916 2001-08-14 10:36:41-07 998 1045 0590445065 2001-08-12 12:09:47-07 749 172 0694003611 2001-08-11 10:52:34-07 662 655 0679803335 2001-08-09 07:30:07-07 806 1125 0760720002 2001-08-05 09:34:04-07 102 146 0394900014 2001-08-11 13:34:08-07 813 112 0385121679 2001-08-08 09:53:46-07 652 724 1885418035 2001-08-14 13:41:39-07 599 430 0929605942 2001-08-10 08:29:42-07 969 488 0441172717 2001-08-14 08:42:58-07 433 898 044100590X 2001-08-12 08:46:35-07 660 409 0451457994 2001-08-07 11:56:42-07 310 738 0451198492 2001-08-15 14:02:01-07 510 860 0823015505 2001-08-14 07:33:47-07 997 185 039480001X 2001-08-10 13:47:52-07 999 221 0451160916 2001-08-14 13:45:51-07 56 880 0590445065 2001-08-14 13:49:00-07 72 574 0694003611 2001-08-06 07:49:44-07 146 270 039480001X 2001-08-13 09:42:10-07 981 652 0451160916 2001-08-08 08:36:44-07 95 480 0590445065 2001-08-10 07:29:52-07 593 476 0694003611 2001-08-15 11:57:40-07 977 853 0679803335 2001-08-09 09:30:46-07 117 185 0760720002 2001-08-07 13:00:48-07 406 1123 0394900014 2001-08-13 09:47:04-07 340 1149 0385121679 2001-08-12 13:39:22-07 871 388 1885418035 2001-08-07 11:31:57-07 1000 221 039480001X 2001-09-14 16:46:32-07 1001 107 039480001X 2001-09-14 17:42:22-07 754 107 0394800753 2001-08-11 09:55:05-07 458 107 0394800753 2001-08-07 10:58:36-07 189 107 0394800753 2001-08-06 11:46:36-07 720 107 0394800753 2001-08-08 10:46:13-07 1002 107 0394800753 2001-09-22 11:23:28-07 2 107 0394800753 2001-09-22 20:58:56-07 \. -- -- Data for TOC Entry ID 121 (OID 3391454) -- -- Name: customers Type: TABLE DATA Owner: manager -- COPY "customers" FROM stdin; 107 Jackson Annie 112 Gould Ed 142 Allen Chad 146 Williams James 172 Brown Richard 185 Morrill Eric 221 King Jenny 270 Bollman Julie 388 Morrill Royce 409 Holloway Christine 430 Black Jean 476 Clark James 480 Thomas Rich 488 Young Trevor 574 Bennett Laura 652 Anderson Jonathan 655 Olson Dave 671 Brown Chuck 723 Eisele Don 724 Holloway Adam 738 Gould Shirley 830 Robertson Royce 853 Black Wendy 860 Owens Tim 880 Robinson Tammy 898 Gerdes Kate 964 Gould Ramon 1045 Owens Jean 1125 Bollman Owen 1149 Becker Owen 1123 Corner Kathy \. -- -- Data for TOC Entry ID 122 (OID 3574043) -- -- Name: book_queue Type: TABLE DATA Owner: postgres -- COPY "book_queue" FROM stdin; Learning Python 7805 4 t Perl Cookbook 7806 4 t \. -- -- Data for TOC Entry ID 123 (OID 3574983) -- -- Name: stock_backup Type: TABLE DATA Owner: postgres -- COPY "stock_backup" FROM stdin; 0385121679 29.00 36.95 65 039480001X 30.00 32.95 31 0394800753 16.00 16.95 0 0394900014 23.00 23.95 0 044100590X 36.00 45.95 89 0441172717 17.00 21.95 77 0451160916 24.00 28.95 22 0451198492 36.00 46.95 0 0451457994 17.00 22.95 0 0590445065 23.00 23.95 10 0679803335 20.00 24.95 18 0694003611 25.00 28.95 50 0760720002 18.00 23.95 28 0823015505 26.00 28.95 16 0929605942 19.00 21.95 25 1885418035 23.00 24.95 77 \. -- -- Data for TOC Entry ID 124 (OID 3628247) -- -- Name: favorite_books Type: TABLE DATA Owner: manager -- COPY "favorite_books" FROM stdin; 102 {"The Hitchhiker's Guide to the Galaxy","The Restauraunt at the End of the Universe"} 103 {"There and Back Again: A Hobbit's Holiday","Kittens Squared"} \. -- -- Data for TOC Entry ID 125 (OID 3628899) -- -- Name: employees Type: TABLE DATA Owner: postgres -- COPY "employees" FROM stdin; 101 Appel Vincent 102 Holloway Michael 105 Connoly Sarah 104 Noble Ben 103 Joble David 106 Hall Timothy 1008 Williams \N \. -- -- Data for TOC Entry ID 126 (OID 3629174) -- -- Name: editions Type: TABLE DATA Owner: manager -- COPY "editions" FROM stdin; 039480001X 1608 1 59 1957-03-01 h 0451160916 7808 1 75 1981-08-01 p 0394800753 1590 1 59 1949-03-01 p 0590445065 25908 1 150 1987-03-01 p 0694003611 1501 1 65 1947-03-04 p 0679803335 1234 1 102 1922-01-01 p 0760720002 190 1 91 1868-01-01 p 0394900014 1608 1 59 1957-01-01 p 0385121679 7808 2 75 1993-10-01 h 1885418035 156 1 163 1995-03-28 p 0929605942 156 2 171 1998-12-01 p 0441172717 4513 2 99 1998-09-01 p 044100590X 4513 3 99 1999-10-01 h 0451457994 4267 3 101 2000-09-12 p 0451198492 4267 3 101 1999-10-01 h 0823015505 2038 1 62 1958-01-01 p 0596000855 41473 2 113 2001-03-01 p \. -- -- Data for TOC Entry ID 127 (OID 3629264) -- -- Name: books Type: TABLE DATA Owner: manager -- COPY "books" FROM stdin; 7808 The Shining 4156 9 4513 Dune 1866 15 4267 2001: A Space Odyssey 2001 15 1608 The Cat in the Hat 1809 2 1590 Bartholomew and the Oobleck 1809 2 25908 Franklin in the Dark 15990 2 1501 Goodnight Moon 2031 2 190 Little Women 16 6 1234 The Velveteen Rabbit 25041 3 2038 Dynamic Anatomy 1644 0 156 The Tell-Tale Heart 115 9 41473 Programming Python 7805 4 41477 Learning Python 7805 4 41478 Perl Cookbook 7806 4 41472 Practical PostgreSQL 1212 4 \. -- -- Data for TOC Entry ID 128 (OID 3629424) -- -- Name: distinguished_authors Type: TABLE DATA Owner: manager -- COPY "distinguished_authors" FROM stdin; 25043 Simon Neil Pulitzer Prize 1809 Geisel Theodor Seuss Pulitzer Prize \. -- -- Data for TOC Entry ID 129 (OID 3727889) -- -- Name: favorite_authors Type: TABLE DATA Owner: manager -- COPY "favorite_authors" FROM stdin; 102 {{"J.R.R. Tolkien","The Silmarillion"},{"Charles Dickens","Great Expectations"},{"Ariel Denham","Attic Lives"}} \. -- -- Data for TOC Entry ID 130 (OID 3751599) -- -- Name: text_sorting Type: TABLE DATA Owner: postgres -- COPY "text_sorting" FROM stdin; 0 1 2 3 A B C D a b c d \. -- -- Data for TOC Entry ID 131 (OID 3751882) -- -- Name: subjects Type: TABLE DATA Owner: postgres -- COPY "subjects" FROM stdin; 0 Arts Creativity St 1 Business Productivity Ave 2 Children's Books Kids Ct 3 Classics Academic Rd 4 Computers Productivity Ave 5 Cooking Creativity St 6 Drama Main St 7 Entertainment Main St 8 History Academic Rd 9 Horror Black Raven Dr 10 Mystery Black Raven Dr 11 Poetry Sunset Dr 12 Religion \N 13 Romance Main St 14 Science Productivity Ave 15 Science Fiction Main St \. -- -- Data for TOC Entry ID 132 (OID 3751975) -- -- Name: alternate_stock Type: TABLE DATA Owner: postgres -- COPY "alternate_stock" FROM stdin; 0385121679 29.00 36.95 65 039480001X 30.00 32.95 31 0394900014 23.00 23.95 0 044100590X 36.00 45.95 89 0441172717 17.00 21.95 77 0451160916 24.00 28.95 22 0451198492 36.00 46.95 0 0451457994 17.00 22.95 0 0590445065 23.00 23.95 10 0679803335 20.00 24.95 18 0694003611 25.00 28.95 50 0760720002 18.00 23.95 28 0823015505 26.00 28.95 16 0929605942 19.00 21.95 25 1885418035 23.00 24.95 77 0394800753 16.00 16.95 4 \. -- -- Data for TOC Entry ID 133 (OID 3752020) -- -- Name: book_backup Type: TABLE DATA Owner: postgres -- COPY "book_backup" FROM stdin; 7808 The Shining 4156 9 4513 Dune 1866 15 4267 2001: A Space Odyssey 2001 15 1608 The Cat in the Hat 1809 2 1590 Bartholomew and the Oobleck 1809 2 25908 Franklin in the Dark 15990 2 1501 Goodnight Moon 2031 2 190 Little Women 16 6 1234 The Velveteen Rabbit 25041 3 2038 Dynamic Anatomy 1644 0 156 The Tell-Tale Heart 115 9 41472 Practical PostgreSQL 1212 4 41473 Programming Python 7805 4 41477 Learning Python 7805 4 41478 Perl Cookbook 7806 4 7808 The Shining 4156 9 4513 Dune 1866 15 4267 2001: A Space Odyssey 2001 15 1608 The Cat in the Hat 1809 2 1590 Bartholomew and the Oobleck 1809 2 25908 Franklin in the Dark 15990 2 1501 Goodnight Moon 2031 2 190 Little Women 16 6 1234 The Velveteen Rabbit 25041 3 2038 Dynamic Anatomy 1644 0 156 The Tell-Tale Heart 115 9 41473 Programming Python 7805 4 41477 Learning Python 7805 4 41478 Perl Cookbook 7806 4 41472 Practical PostgreSQL 1212 4 \. -- -- Data for TOC Entry ID 134 (OID 4063343) -- -- Name: schedules Type: TABLE DATA Owner: postgres -- COPY "schedules" FROM stdin; 102 Mon - Fri, 9am - 5pm \. -- -- TOC Entry ID 45 (OID 3117548) -- -- Name: "unique_publisher_idx" Type: INDEX Owner: postgres -- CREATE UNIQUE INDEX "unique_publisher_idx" on "publishers" using btree ( "name" "text_ops" ); -- -- TOC Entry ID 43 (OID 3391184) -- -- Name: "shipments_ship_id_key" Type: INDEX Owner: postgres -- CREATE UNIQUE INDEX "shipments_ship_id_key" on "shipments" using btree ( "id" "int4_ops" ); -- -- TOC Entry ID 44 (OID 3629264) -- -- Name: "books_title_idx" Type: INDEX Owner: manager -- CREATE INDEX "books_title_idx" on "books" using btree ( "title" "text_ops" ); -- -- TOC Entry ID 46 (OID 3751599) -- -- Name: "text_idx" Type: INDEX Owner: postgres -- CREATE INDEX "text_idx" on "text_sorting" using btree ( "letter" "bpchar_ops" ); -- -- TOC Entry ID 136 (OID 3628649) -- -- Name: check_shipment Type: TRIGGER Owner: postgres -- CREATE TRIGGER "check_shipment" BEFORE INSERT OR UPDATE ON "shipments" FOR EACH ROW EXECUTE PROCEDURE "check_shipment_addition" (); -- -- TOC Entry ID 135 (OID 3752103) -- -- Name: sync_authors_books Type: TRIGGER Owner: manager -- CREATE TRIGGER "sync_authors_books" BEFORE UPDATE ON "authors" FOR EACH ROW EXECUTE PROCEDURE "sync_authors_and_books" (); -- -- TOC Entry ID 139 (OID 4063374) -- -- Name: "RI_ConstraintTrigger_4063373" Type: TRIGGER Owner: postgres -- CREATE CONSTRAINT TRIGGER "valid_employee" AFTER INSERT OR UPDATE ON "schedules" FROM "employees" NOT DEFERRABLE INITIALLY IMMEDIATE FOR EACH ROW EXECUTE PROCEDURE "RI_FKey_check_ins" ('valid_employee', 'schedules', 'employees', 'FULL', 'employee_id', 'id'); -- -- TOC Entry ID 137 (OID 4063376) -- -- Name: "RI_ConstraintTrigger_4063375" Type: TRIGGER Owner: postgres -- CREATE CONSTRAINT TRIGGER "valid_employee" AFTER DELETE ON "employees" FROM "schedules" NOT DEFERRABLE INITIALLY IMMEDIATE FOR EACH ROW EXECUTE PROCEDURE "RI_FKey_noaction_del" ('valid_employee', 'schedules', 'employees', 'FULL', 'employee_id', 'id'); -- -- TOC Entry ID 138 (OID 4063378) -- -- Name: "RI_ConstraintTrigger_4063377" Type: TRIGGER Owner: postgres -- CREATE CONSTRAINT TRIGGER "valid_employee" AFTER UPDATE ON "employees" FROM "schedules" NOT DEFERRABLE INITIALLY IMMEDIATE FOR EACH ROW EXECUTE PROCEDURE "RI_FKey_noaction_upd" ('valid_employee', 'schedules', 'employees', 'FULL', 'employee_id', 'id'); -- -- TOC Entry ID 140 (OID 3752079) -- -- Name: sync_stock_with_editions Type: RULE Owner: manager -- CREATE RULE sync_stock_with_editions AS ON UPDATE TO editions DO UPDATE stock SET isbn = new.isbn WHERE (stock.isbn = old.isbn); -- -- TOC Entry ID 5 (OID 3390416) -- -- Name: subject_ids Type: SEQUENCE SET Owner: -- SELECT setval ('"subject_ids"', 15, 't'); -- -- TOC Entry ID 7 (OID 3574018) -- -- Name: book_ids Type: SEQUENCE SET Owner: -- SELECT setval ('"book_ids"', 41478, 't'); -- -- TOC Entry ID 9 (OID 3628626) -- -- Name: shipments_ship_id_seq Type: SEQUENCE SET Owner: -- SELECT setval ('"shipments_ship_id_seq"', 1011, 't'); -- -- TOC Entry ID 11 (OID 3629402) -- -- Name: author_ids Type: SEQUENCE SET Owner: -- SELECT setval ('"author_ids"', 25044, 't'); ================================================ FILE: data/invalid.toml ================================================ invalid encoding ================================================ FILE: data/lc_example1.sql ================================================ -- pgweb: host="localhost" select 'foo' ================================================ FILE: data/lc_example2.sql ================================================ -- pgweb: host="localhost" -- some comment -- pgweb: user="foo" select 'foo' ================================================ FILE: data/lc_invalid_meta.sql ================================================ -- pgweb: host="localhost" mode="foo" select 'foo' ================================================ FILE: data/lc_no_meta.sql ================================================ select 'foo' ================================================ FILE: data/passfile ================================================ localhost:5432:dbname:username:password 127.0.0.1:5432:*:*:password2 ================================================ FILE: data/roach.sql ================================================ DROP DATABASE IF EXISTS "roach"; CREATE DATABASE "roach"; USE "roach"; CREATE TABLE product_information ( product_id INT PRIMARY KEY NOT NULL, product_name STRING(50) UNIQUE NOT NULL, product_description STRING(2000), category_id STRING(1) NOT NULL CHECK (category_id IN ('A','B','C')), weight_class INT, warranty_period INT CONSTRAINT valid_warranty CHECK (warranty_period BETWEEN 0 AND 24), supplier_id INT, product_status STRING(20), list_price DECIMAL(8,2), min_price DECIMAL(8,2), catalog_url STRING(50) UNIQUE, date_added DATE DEFAULT CURRENT_DATE(), misc JSONB, CONSTRAINT price_check CHECK (list_price >= min_price), INDEX date_added_idx (date_added), INDEX supp_id_prod_status_idx (supplier_id, product_status), INVERTED INDEX details (misc) ); INSERT INTO product_information VALUES (1, 'Product A', 'Text', 'A', NULL, 1), (2, 'Product B', 'Text', 'B', NULL, 2), (3, 'Product C', 'Text', 'C', NULL, 3); CREATE TABLE customers ( id INT PRIMARY KEY, name STRING ); CREATE TABLE orders ( id INT PRIMARY KEY, customer_id INT REFERENCES customers(id) ON DELETE CASCADE ); INSERT INTO customers VALUES (1, 'Lauren'); INSERT INTO orders VALUES (1,1); DELETE FROM customers WHERE id = 1; SELECT * FROM orders; ================================================ FILE: docker-compose-pg.yml ================================================ --- x-base: &base environment: &env POSTGRES_DB: pgweb POSTGRES_PASSWORD: pgweb POSTGRES_USER: pgweb healthcheck: test: pg_isready -U pgweb -h 127.0.0.1 interval: 5s services: postgres18: <<: *base image: postgres:18 ports: - 5433:5432 postgres17: <<: *base image: postgres:17 ports: - 5433:5432 postgres16: <<: *base image: postgres:16 ports: - 5433:5432 postgres15: <<: *base image: postgres:15 ports: - 5433:5432 postgres14: <<: *base image: postgres:14 ports: - 5434:5432 postgres13: <<: *base image: postgres:13 ports: - 5435:5432 postgres12: <<: *base image: postgres:12 ports: - 5436:5432 postgres11: <<: *base image: postgres:11 ports: - 5437:5432 postgres10: <<: *base image: postgres:10 ports: - 5438:5432 postgres9.6: <<: *base image: postgres:9.6 ports: - 5439:5432 ================================================ FILE: docker-compose.yml ================================================ --- services: postgres: container_name: pgweb-postgres image: postgres:15 ports: - 5433:5432 volumes: - data:/var/lib/postgresql/data environment: POSTGRES_DB: pgweb POSTGRES_PASSWORD: pgweb POSTGRES_USER: pgweb healthcheck: test: pg_isready -U pgweb -h 127.0.0.1 interval: 5s networks: - pgweb pgweb: container_name: pgweb image: sosedoff/pgweb:latest build: . environment: PGWEB_DATABASE_URL: postgres://pgweb:pgweb@pgweb-postgres:5432/pgweb?sslmode=disable ports: - 8081:8081 networks: - pgweb depends_on: postgres: condition: service_healthy healthcheck: test: ["CMD", "nc", "-vz", "127.0.0.1", "8081"] interval: 5s volumes: data: name: pgweb_postgres networks: pgweb: name: pgweb ================================================ FILE: fly.toml ================================================ app = "pgweb-demo" kill_signal = "SIGINT" kill_timeout = 5 [processes] web = "pgweb --sessions --bind=0.0.0.0 --metrics --idle-timeout=30" [[services]] http_checks = [] internal_port = 8081 processes = ["web"] protocol = "tcp" script_checks = [] [services.concurrency] hard_limit = 25 soft_limit = 20 type = "connections" [[services.ports]] force_https = true handlers = ["http"] port = 80 [[services.ports]] handlers = ["tls", "http"] port = 443 [[services.tcp_checks]] grace_period = "1s" interval = "15s" restart_limit = 0 timeout = "2s" [metrics] port = 8081 path = "/metrics" ================================================ FILE: go.mod ================================================ module github.com/sosedoff/pgweb go 1.25 toolchain go1.25.4 require ( github.com/BurntSushi/toml v1.1.0 github.com/ScaleFT/sshkeys v0.0.0-20200327173127-6142f742bca5 github.com/gin-gonic/gin v1.11.0 github.com/jackc/pgpassfile v1.0.0 github.com/jessevdk/go-flags v1.5.0 github.com/jmoiron/sqlx v1.3.5 github.com/lib/pq v1.10.5 github.com/mitchellh/go-homedir v1.1.0 github.com/mr-tron/base58 v1.2.0 github.com/prometheus/client_golang v1.19.1 github.com/sirupsen/logrus v1.9.1 github.com/stretchr/testify v1.11.1 github.com/tuvistavie/securerandom v0.0.0-20140719024926-15512123a948 golang.org/x/crypto v0.46.0 ) require ( github.com/beorn7/perks v1.0.1 // indirect github.com/bytedance/gopkg v0.1.3 // indirect github.com/bytedance/sonic v1.14.2 // indirect github.com/bytedance/sonic/loader v0.4.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/cloudwego/base64x v0.1.6 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a // indirect github.com/gabriel-vasile/mimetype v1.4.12 // indirect github.com/gin-contrib/sse v1.1.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.30.1 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/goccy/go-yaml v1.19.1 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/common v0.48.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect github.com/quic-go/qpack v0.6.0 // indirect github.com/quic-go/quic-go v0.58.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.3.1 // indirect go.uber.org/mock v0.6.0 // indirect golang.org/x/arch v0.23.0 // indirect golang.org/x/mod v0.31.0 // indirect golang.org/x/net v0.48.0 // indirect golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.39.0 // indirect golang.org/x/text v0.32.0 // indirect golang.org/x/tools v0.40.0 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) ================================================ FILE: go.sum ================================================ github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/ScaleFT/sshkeys v0.0.0-20200327173127-6142f742bca5 h1:VauE2GcJNZFun2Och6tIT2zJZK1v6jxALQDA9BIji/E= github.com/ScaleFT/sshkeys v0.0.0-20200327173127-6142f742bca5/go.mod h1:gxOHeajFfvGQh/fxlC8oOKBe23xnnJTif00IFFbiT+o= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE= github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980= github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o= github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a h1:saTgr5tMLFnmy/yg3qDTft4rE5DY2uJ/cCxCe3q0XTU= github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a/go.mod h1:Bw9BbhOJVNR+t0jCqx2GC6zv0TGBsShs56Y3gfSCvl0= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw= github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk= github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w= github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-yaml v1.19.1 h1:3rG3+v8pkhRqoQ/88NYNMHYVGYztCOCIZ7UQhu7H+NE= github.com/goccy/go-yaml v1.19.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.5 h1:J+gdV2cUmX7ZqL2B0lFcW0m+egaHC2V3lpO8nWxyYiQ= github.com/lib/pq v1.10.5/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI= github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y= github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= github.com/quic-go/quic-go v0.58.0 h1:ggY2pvZaVdB9EyojxL1p+5mptkuHyX5MOSv4dgWF4Ug= github.com/quic-go/quic-go v0.58.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/sirupsen/logrus v1.9.1 h1:Ou41VVR3nMWWmTiEUnj0OlsgOSCUFgsPAOl6jRIcVtQ= github.com/sirupsen/logrus v1.9.1/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tuvistavie/securerandom v0.0.0-20140719024926-15512123a948 h1:yL0l/u242MzDP6D0B5vGC+wxm5WRY+alQZy+dJk3bFI= github.com/tuvistavie/securerandom v0.0.0-20140719024926-15512123a948/go.mod h1:a06d/M1pxWi51qiSrfGMHaEydtuXT06nha8N2aNQuXk= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY= github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg= golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA= golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= ================================================ FILE: main.go ================================================ package main import ( "github.com/sosedoff/pgweb/pkg/cli" ) func main() { cli.Run() } ================================================ FILE: pkg/api/api.go ================================================ package api import ( "context" "encoding/base64" "fmt" "net/http" neturl "net/url" "strings" "time" "github.com/gin-gonic/gin" "github.com/tuvistavie/securerandom" "github.com/sosedoff/pgweb/pkg/bookmarks" "github.com/sosedoff/pgweb/pkg/client" "github.com/sosedoff/pgweb/pkg/command" "github.com/sosedoff/pgweb/pkg/connect" "github.com/sosedoff/pgweb/pkg/connection" "github.com/sosedoff/pgweb/pkg/metrics" "github.com/sosedoff/pgweb/pkg/queries" "github.com/sosedoff/pgweb/pkg/shared" "github.com/sosedoff/pgweb/static" ) var ( // DbClient represents the active database connection in a single-session mode DbClient *client.Client // DbSessions represents the mapping for client connections DbSessions *SessionManager // QueryStore reads the SQL queries stores in the home directory QueryStore *queries.Store ) // DB returns a database connection from the client context func DB(c *gin.Context) *client.Client { if command.Opts.Sessions { return DbSessions.Get(getSessionId(c.Request)) } return DbClient } // setClient sets the database client connection for the sessions func setClient(c *gin.Context, newClient *client.Client) error { currentClient := DB(c) if currentClient != nil { currentClient.Close() } if !command.Opts.Sessions { DbClient = newClient return nil } sid := getSessionId(c.Request) if sid == "" { return errSessionRequired } DbSessions.Add(sid, newClient) return nil } // GetHome renders the home page func GetHome(prefix string) http.Handler { if prefix != "" { prefix = "/" + prefix } return http.StripPrefix(prefix, static.GetHandler()) } func GetAssets(prefix string) http.Handler { if prefix != "" { prefix = "/" + prefix + "static/" } else { prefix = "/static/" } return http.StripPrefix(prefix, static.GetHandler()) } // GetSessions renders the number of active sessions func GetSessions(c *gin.Context) { // In debug mode endpoint will return a lot of sensitive information // like full database connection string and all query history. if command.Opts.Debug { successResponse(c, DbSessions.Sessions()) return } successResponse(c, gin.H{"sessions": DbSessions.Len()}) } // ConnectWithBackend creates a new connection based on backend resource func ConnectWithBackend(c *gin.Context) { backend := connect.NewBackend(command.Opts.ConnectBackend, command.Opts.ConnectToken) backend.SetLogger(logger) if command.Opts.ConnectHeaders != "" { backend.SetPassHeaders(strings.Split(command.Opts.ConnectHeaders, ",")) } ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) defer cancel() // Fetch connection credentials cred, err := backend.FetchCredential(ctx, c.Param("resource"), c.Request.Header) if err != nil { badRequest(c, err) return } // Make the new session sid, err := securerandom.Uuid() if err != nil { badRequest(c, err) return } c.Request.Header.Add("x-session-id", sid) // Connect to the database cl, err := client.NewFromUrl(cred.DatabaseURL, nil) if err != nil { badRequest(c, err) return } cl.External = true // Finalize session seetup _, err = cl.Info() if err == nil { err = setClient(c, cl) } if err != nil { cl.Close() badRequest(c, err) return } redirectURI := fmt.Sprintf("/%s?session=%s", command.Opts.Prefix, sid) c.Redirect(302, redirectURI) } // Connect creates a new client connection func Connect(c *gin.Context) { if command.Opts.LockSession { badRequest(c, errSessionLocked) return } var ( cl *client.Client err error ) if bookmarkID := c.Request.FormValue("bookmark_id"); bookmarkID != "" { cl, err = ConnectWithBookmark(bookmarkID) } else if command.Opts.BookmarksOnly { err = errNotPermitted } else { cl, err = ConnectWithURL(c) } if err != nil { badRequest(c, err) return } err = cl.Test() if err != nil { badRequest(c, err) return } info, err := cl.Info() if err == nil { err = setClient(c, cl) } if err != nil { cl.Close() badRequest(c, err) return } successResponse(c, info.Format()[0]) } func ConnectWithURL(c *gin.Context) (*client.Client, error) { url := c.Request.FormValue("url") if url == "" { return nil, errURLRequired } url, err := connection.FormatURL(command.Options{ URL: url, Passfile: command.Opts.Passfile, }) if err != nil { return nil, err } var sshInfo *shared.SSHInfo if c.Request.FormValue("ssh") != "" { sshInfo = parseSshInfo(c) } return client.NewFromUrl(url, sshInfo) } func ConnectWithBookmark(id string) (*client.Client, error) { manager := bookmarks.NewManager(command.Opts.BookmarksDir) bookmark, err := manager.Get(id) if err != nil { return nil, err } return client.NewFromBookmark(bookmark) } // SwitchDb perform database switch for the client connection func SwitchDb(c *gin.Context) { if command.Opts.LockSession { badRequest(c, errSessionLocked) return } name := c.Request.URL.Query().Get("db") if name == "" { name = c.Request.FormValue("db") } if name == "" { badRequest(c, errDatabaseNameRequired) return } conn := DB(c) if conn == nil { badRequest(c, errNotConnected) return } // Do not allow switching databases for connections from third-party backends if conn.External { badRequest(c, errSessionLocked) return } currentURL, err := neturl.Parse(conn.ConnectionString) if err != nil { badRequest(c, errInvalidConnString) return } currentURL.Path = name cl, err := client.NewFromUrl(currentURL.String(), nil) if err != nil { badRequest(c, err) return } err = cl.Test() if err != nil { badRequest(c, err) return } info, err := cl.Info() if err == nil { err = setClient(c, cl) } if err != nil { cl.Close() badRequest(c, err) return } conn.Close() successResponse(c, info.Format()[0]) } // Disconnect closes the current database connection func Disconnect(c *gin.Context) { if command.Opts.LockSession { badRequest(c, errSessionLocked) return } if command.Opts.Sessions { result := DbSessions.Remove(getSessionId(c.Request)) successResponse(c, gin.H{"success": result}) return } conn := DB(c) if conn == nil { badRequest(c, errNotConnected) return } err := conn.Close() if err != nil { badRequest(c, err) return } DbClient = nil successResponse(c, gin.H{"success": true}) } // RunQuery executes the query func RunQuery(c *gin.Context) { query := cleanQuery(c.Request.FormValue("query")) if query == "" { badRequest(c, errQueryRequired) return } HandleQuery(query, c) } // ExplainQuery renders query explain plan func ExplainQuery(c *gin.Context) { query := cleanQuery(c.Request.FormValue("query")) if query == "" { badRequest(c, errQueryRequired) return } HandleQuery(fmt.Sprintf("EXPLAIN %s", query), c) } // AnalyzeQuery renders query explain plan and analyze profile func AnalyzeQuery(c *gin.Context) { query := cleanQuery(c.Request.FormValue("query")) if query == "" { badRequest(c, errQueryRequired) return } HandleQuery(fmt.Sprintf("EXPLAIN ANALYZE %s", query), c) } // GetDatabases renders a list of all databases on the server func GetDatabases(c *gin.Context) { if command.Opts.LockSession { serveResult(c, []string{}, nil) return } conn := DB(c) if conn.External { errorResponse(c, 403, errNotPermitted) return } names, err := DB(c).Databases() serveResult(c, names, err) } // GetObjects renders a list of database objects func GetObjects(c *gin.Context) { result, err := DB(c).Objects() if err != nil { badRequest(c, err) return } successResponse(c, client.ObjectsFromResult(result)) } // GetSchemas renders list of available schemas func GetSchemas(c *gin.Context) { res, err := DB(c).Schemas() serveResult(c, res, err) } // GetTable renders table information func GetTable(c *gin.Context) { var ( res *client.Result err error ) db := DB(c) tableName := c.Params.ByName("table") switch c.Request.FormValue("type") { case client.ObjTypeMaterializedView: res, err = db.MaterializedView(tableName) case client.ObjTypeFunction: res, err = db.Function(tableName) default: res, err = db.Table(tableName) } serveResult(c, res, err) } // GetTableRows renders table rows func GetTableRows(c *gin.Context) { offset, err := parseIntFormValue(c, "offset", 0) if err != nil { badRequest(c, err) return } limit, err := parseIntFormValue(c, "limit", 100) if err != nil { badRequest(c, err) return } opts := client.RowsOptions{ Limit: limit, Offset: offset, SortColumn: c.Request.FormValue("sort_column"), SortOrder: c.Request.FormValue("sort_order"), Where: c.Request.FormValue("where"), } res, err := DB(c).TableRows(c.Params.ByName("table"), opts) if err != nil { badRequest(c, err) return } countRes, err := DB(c).TableRowsCount(c.Params.ByName("table"), opts) if err != nil { badRequest(c, err) return } numFetch := int64(opts.Limit) numOffset := int64(opts.Offset) numRows := countRes.Rows[0][0].(int64) numPages := numRows / numFetch if numPages*numFetch < numRows { numPages++ } res.Pagination = &client.Pagination{ Rows: numRows, Page: (numOffset / numFetch) + 1, Pages: numPages, PerPage: numFetch, } serveResult(c, res, err) } // GetTableInfo renders a selected table information func GetTableInfo(c *gin.Context) { res, err := DB(c).TableInfo(c.Params.ByName("table")) if err == nil { successResponse(c, res.Format()[0]) } else { badRequest(c, err) } } // GetHistory renders a list of recent queries func GetHistory(c *gin.Context) { successResponse(c, DB(c).History) } // GetConnectionInfo renders information about current connection func GetConnectionInfo(c *gin.Context) { conn := DB(c) if err := conn.TestWithTimeout(5 * time.Second); err != nil { badRequest(c, err) return } res, err := conn.Info() if err != nil { badRequest(c, err) return } info := res.Format()[0] info["session_lock"] = command.Opts.LockSession successResponse(c, info) } // GetServerSettings renders a list of all server settings func GetServerSettings(c *gin.Context) { res, err := DB(c).ServerSettings() serveResult(c, res, err) } // GetActivity renders a list of running queries func GetActivity(c *gin.Context) { res, err := DB(c).Activity() serveResult(c, res, err) } // GetTableIndexes renders a list of database table indexes func GetTableIndexes(c *gin.Context) { res, err := DB(c).TableIndexes(c.Params.ByName("table")) serveResult(c, res, err) } // GetTableConstraints renders a list of database constraints func GetTableConstraints(c *gin.Context) { res, err := DB(c).TableConstraints(c.Params.ByName("table")) serveResult(c, res, err) } // GetTablesStats renders data sizes and estimated rows for all tables in the database func GetTablesStats(c *gin.Context) { db := DB(c) connCtx, err := db.GetConnContext() if err != nil { badRequest(c, err) return } res, err := db.TablesStats() if err != nil { badRequest(c, err) return } format := getQueryParam(c, "format") if format == "" { format = "json" } // Save as attachment if exporting parameter is set if getQueryParam(c, "export") == "true" { ts := time.Now().Format(time.DateOnly) filename := fmt.Sprintf("pgweb-dbstats-%s-%s.%s", connCtx.Database, ts, format) c.Writer.Header().Set("Content-disposition", "attachment;filename="+filename) } switch format { case "json": c.JSON(http.StatusOK, res) case "csv": c.Data(http.StatusOK, "text/csv", res.CSV()) case "xml": c.XML(200, res) default: badRequest(c, "invalid format") } } // HandleQuery runs the database query func HandleQuery(query string, c *gin.Context) { metrics.IncrementQueriesCount() rawQuery, err := base64.StdEncoding.DecodeString(desanitize64(query)) if err == nil { query = string(rawQuery) } result, err := DB(c).Query(query) if err != nil { badRequest(c, err) return } format := getQueryParam(c, "format") filename := getQueryParam(c, "filename") if filename == "" { filename = fmt.Sprintf("pgweb-%v.%v", time.Now().Unix(), format) } if format != "" { c.Writer.Header().Set("Content-disposition", "attachment;filename="+filename) } switch format { case "csv": c.Data(200, "text/csv", result.CSV()) case "json": c.Data(200, "application/json", result.JSON()) case "xml": c.XML(200, result) default: c.JSON(200, result) } } // GetBookmarks renders the list of available bookmarks func GetBookmarks(c *gin.Context) { manager := bookmarks.NewManager(command.Opts.BookmarksDir) ids, err := manager.ListIDs() serveResult(c, ids, err) } // GetInfo renders the pgweb system information func GetInfo(c *gin.Context) { successResponse(c, gin.H{ "app": command.Info, "features": gin.H{ "session_lock": command.Opts.LockSession, "query_timeout": command.Opts.QueryTimeout, "local_queries": QueryStore != nil, "bookmarks_only": command.Opts.BookmarksOnly, }, }) } // DataExport performs database table export func DataExport(c *gin.Context) { db := DB(c) info, err := db.Info() if err != nil { badRequest(c, err) return } dump := client.Dump{ Table: strings.TrimSpace(c.Request.FormValue("table")), } // Perform validation of pg_dump command availability and compatibility. // Must be done before the actual command is executed to display errors. if err := dump.Validate(db.ServerVersion()); err != nil { badRequest(c, err) return } formattedInfo := info.Format()[0] filename := formattedInfo["current_database"].(string) if dump.Table != "" { filename = filename + "_" + dump.Table } filename = sanitizeFilename(filename) filename = fmt.Sprintf("%s_%s", filename, time.Now().Format("20060102_150405")) c.Header( "Content-Disposition", fmt.Sprintf(`attachment; filename="%s.sql.gz"`, filename), ) err = dump.Export(c.Request.Context(), db.ConnectionString, c.Writer) if err != nil { logger.WithError(err).Error("pg_dump command failed") badRequest(c, err) } } // GetFunction renders function information func GetFunction(c *gin.Context) { res, err := DB(c).Function(c.Param("id")) serveResult(c, res, err) } func GetLocalQueries(c *gin.Context) { connCtx, err := DB(c).GetConnContext() if err != nil { badRequest(c, err) return } storeQueries, err := QueryStore.ReadAll() if err != nil { badRequest(c, err) return } queries := []localQuery{} for _, q := range storeQueries { if !q.IsPermitted(connCtx.Host, connCtx.User, connCtx.Database, connCtx.Mode) { continue } queries = append(queries, localQuery{ ID: q.ID, Title: q.Meta.Title, Description: q.Meta.Description, Query: cleanQuery(q.Data), }) } successResponse(c, queries) } func RunLocalQuery(c *gin.Context) { query, err := QueryStore.Read(c.Param("id")) if err != nil { if err == queries.ErrQueryFileNotExist { query = nil } else { badRequest(c, err) return } } if query == nil { errorResponse(c, 404, "query not found") return } connCtx, err := DB(c).GetConnContext() if err != nil { badRequest(c, err) return } if !query.IsPermitted(connCtx.Host, connCtx.User, connCtx.Database, connCtx.Mode) { errorResponse(c, 404, "query not found") return } if c.Request.Method == http.MethodGet { successResponse(c, localQuery{ ID: query.ID, Title: query.Meta.Title, Description: query.Meta.Description, Query: query.Data, }) return } statement := cleanQuery(query.Data) if statement == "" { badRequest(c, errQueryRequired) return } HandleQuery(statement, c) } ================================================ FILE: pkg/api/api_test.go ================================================ package api import ( "testing" "github.com/stretchr/testify/assert" ) func Test_assetContentType(t *testing.T) { samples := map[string]string{ "foo.html": "text/html; charset=utf-8", "foo.css": "text/css; charset=utf-8", "foo.js": "application/javascript", "foo.icon": "image-x-icon", "foo.png": "image/png", "foo.jpg": "image/jpeg", "foo.gif": "image/gif", "foo.eot": "application/vnd.ms-fontobject", "foo.svg": "image/svg+xml", "foo.foo": "text/plain; charset=utf-8", "foo": "text/plain; charset=utf-8", } alternatives := map[string]string{ "foo.js": "text/javascript; charset=utf-8", } for name, expected := range samples { if alternatives[name] == "" { assert.Equal(t, expected, assetContentType(name)) continue } actual := assetContentType(name) if actual != expected && actual != alternatives[name] { t.Errorf("expected %v but got %v (alternative value failed)", expected, actual) } } } ================================================ FILE: pkg/api/errors.go ================================================ package api import ( "errors" ) var ( errNotConnected = errors.New("Not connected") errNotPermitted = errors.New("Not permitted") errInvalidConnString = errors.New("Invalid connection string") errSessionRequired = errors.New("Session ID is required") errSessionLocked = errors.New("Session is locked") errURLRequired = errors.New("URL parameter is required") errQueryRequired = errors.New("Query parameter is required") errDatabaseNameRequired = errors.New("Database name is required") ) ================================================ FILE: pkg/api/helpers.go ================================================ package api import ( "fmt" "mime" "net/http" "path/filepath" "regexp" "strconv" "strings" "github.com/gin-gonic/gin" "github.com/sosedoff/pgweb/pkg/shared" ) var ( // Mime types definitions extraMimeTypes = map[string]string{ ".icon": "image-x-icon", ".ttf": "application/x-font-ttf", ".woff": "application/x-font-woff", ".eot": "application/vnd.ms-fontobject", ".svg": "image/svg+xml", ".html": "text/html; charset-utf-8", } // Paths that dont require database connection allowedPaths = map[string]bool{ "/api/sessions": true, "/api/info": true, "/api/connect": true, "/api/bookmarks": true, "/api/history": true, } // List of characters replaced by javascript code to make queries url-safe. base64subs = map[string]string{ "-": "+", "_": "/", ".": "=", } // Regular expression to remove unwanted characters in filenames regexCleanFilename = regexp.MustCompile(`[^\w]+`) ) type Error struct { Message string `json:"error"` } func NewError(err error) Error { return Error{err.Error()} } // Returns a clean query without any comment statements func cleanQuery(query string) string { lines := []string{} for _, line := range strings.Split(query, "\n") { line = strings.TrimSpace(line) if strings.HasPrefix(line, "--") { continue } lines = append(lines, line) } return strings.TrimSpace(strings.Join(lines, "\n")) } func desanitize64(query string) string { // Before feeding the string into decoded, we must "reconstruct" the base64 data. // Javascript replaces a few characters to be url-safe. for olds, news := range base64subs { query = strings.Replace(query, olds, news, -1) } return query } func sanitizeFilename(str string) string { str = strings.ReplaceAll(str, ".", "_") return regexCleanFilename.ReplaceAllString(str, "") } func getSessionId(req *http.Request) string { id := req.Header.Get("x-session-id") if id == "" { id = req.URL.Query().Get("_session_id") } return id } func getQueryParam(c *gin.Context, name string) string { result := "" q := c.Request.URL.Query() if len(q[name]) > 0 { result = q[name][0] } return result } func parseIntFormValue(c *gin.Context, name string, defValue int) (int, error) { val := c.Request.FormValue(name) if val == "" { return defValue, nil } num, err := strconv.Atoi(val) if err != nil { return defValue, fmt.Errorf("%s must be a number", name) } if num < 1 && defValue != 0 { return defValue, fmt.Errorf("%s must be greater than 0", name) } return num, nil } func parseSshInfo(c *gin.Context) *shared.SSHInfo { info := shared.SSHInfo{ Host: c.Request.FormValue("ssh_host"), Port: c.Request.FormValue("ssh_port"), User: c.Request.FormValue("ssh_user"), Password: c.Request.FormValue("ssh_password"), Key: c.Request.FormValue("ssh_key"), KeyPassword: c.Request.FormValue("ssh_key_password"), } if info.Port == "" { info.Port = "22" } return &info } func assetContentType(name string) string { ext := filepath.Ext(name) result := mime.TypeByExtension(ext) if result == "" { result = extraMimeTypes[ext] } if result == "" { result = "text/plain; charset=utf-8" } return result } // Send a query result to client func serveResult(c *gin.Context, result interface{}, err interface{}) { if err != nil { badRequest(c, err) return } successResponse(c, result) } // Send successful response back to client func successResponse(c *gin.Context, data interface{}) { c.JSON(200, data) } // Send an error response back to client func errorResponse(c *gin.Context, status int, err interface{}) { var message interface{} switch v := err.(type) { case error: message = v.Error() case string: message = v default: message = v } c.AbortWithStatusJSON(status, gin.H{"status": status, "error": message}) } // Send a bad request (http 400) back to client func badRequest(c *gin.Context, err interface{}) { errorResponse(c, 400, err) } ================================================ FILE: pkg/api/helpers_test.go ================================================ package api import ( "errors" "net/http" "net/http/httptest" "net/url" "testing" "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" ) func Test_desanitize64(t *testing.T) { examples := map[string]string{ "test": "test", "test+test+": "test-test-", "test/test/": "test_test_", "test=test==": "test.test..", } for expected, example := range examples { assert.Equal(t, expected, desanitize64(example)) } } func Test_cleanQuery(t *testing.T) { assert.Equal(t, "a\nb\nc", cleanQuery("a\nb\nc")) assert.Equal(t, "", cleanQuery("--something")) assert.Equal(t, "test", cleanQuery("--test\ntest\n -- test\n")) } func Test_sanitizeFilename(t *testing.T) { examples := map[string]string{ "foo": "foo", "fooBar": "fooBar", "foo.bar": "foo_bar", `"foo"."bar"`: "foo_bar", "!@#$foo.&&*(&bar": "foo_bar", } for given, expected := range examples { t.Run(given, func(t *testing.T) { assert.Equal(t, expected, sanitizeFilename(given)) }) } } func Test_getSessionId(t *testing.T) { req := &http.Request{Header: http.Header{}} req.Header.Add("x-session-id", "token") assert.Equal(t, "token", getSessionId(req)) req = &http.Request{} req.URL, _ = url.Parse("http://foobar/?_session_id=token") assert.Equal(t, "token", getSessionId(req)) } func Test_serveResult(t *testing.T) { server := gin.Default() server.GET("/good", func(c *gin.Context) { serveResult(c, gin.H{"foo": "bar"}, nil) }) server.GET("/bad", func(c *gin.Context) { serveResult(c, nil, errors.New("message")) }) server.GET("/nodata", func(c *gin.Context) { serveResult(c, nil, nil) }) w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/good", nil) server.ServeHTTP(w, req) assert.Equal(t, 200, w.Code) assert.Equal(t, `{"foo":"bar"}`, w.Body.String()) w = httptest.NewRecorder() req, _ = http.NewRequest("GET", "/bad", nil) server.ServeHTTP(w, req) assert.Equal(t, 400, w.Code) assert.Equal(t, `{"error":"message","status":400}`, w.Body.String()) w = httptest.NewRecorder() req, _ = http.NewRequest("GET", "/nodata", nil) server.ServeHTTP(w, req) assert.Equal(t, 200, w.Code) assert.Equal(t, `null`, w.Body.String()) } ================================================ FILE: pkg/api/logger.go ================================================ package api import ( "net/http" "regexp" "strings" "time" "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" "github.com/sosedoff/pgweb/pkg/command" ) var ( logger *logrus.Logger reConnectToken = regexp.MustCompile("/connect/(.*)") ) func init() { if logger == nil { logger = logrus.New() } } // TODO: Move this into server struct when it's ready func SetLogger(l *logrus.Logger) { logger = l } func RequestLogger(logger *logrus.Logger) gin.HandlerFunc { debug := logger.Level > logrus.InfoLevel logForwardedUser := command.Opts.LogForwardedUser return func(c *gin.Context) { start := time.Now() path := c.Request.URL.Path // Process request c.Next() if !debug { // Skip static assets logging if strings.Contains(path, "/static/") { return } path = sanitizeLogPath(path) } status := c.Writer.Status() end := time.Now() latency := end.Sub(start) fields := logrus.Fields{ "status": status, "method": c.Request.Method, "remote_addr": c.ClientIP(), "duration": latency.String(), "duration_ms": latency.Milliseconds(), "path": path, } if reqID := getRequestID(c); reqID != "" { fields["id"] = reqID } if logForwardedUser { if forwardedUser := c.GetHeader("X-Forwarded-User"); forwardedUser != "" { fields["forwarded_user"] = forwardedUser } if forwardedEmail := c.GetHeader("X-Forwarded-Email"); forwardedEmail != "" { fields["forwarded_email"] = forwardedEmail } } if err := c.Errors.Last(); err != nil { fields["error"] = err.Error() } // Additional fields for debugging if debug { fields["raw_query"] = c.Request.URL.RawQuery if c.Request.Method != http.MethodGet { fields["raw_form"] = c.Request.Form } } entry := logger.WithFields(fields) msg := "http_request" switch { case status >= http.StatusBadRequest && status < http.StatusInternalServerError: entry.Warn(msg) case status >= http.StatusInternalServerError: entry.Error(msg) default: entry.Info(msg) } } } func sanitizeLogPath(str string) string { return reConnectToken.ReplaceAllString(str, "/connect/REDACTED") } func getRequestID(c *gin.Context) string { id := c.GetHeader("x-request-id") if id == "" { id = c.GetHeader("x-amzn-trace-id") } return id } ================================================ FILE: pkg/api/logger_test.go ================================================ package api import ( "net/http" "testing" "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" ) func Test_getRequestID(t *testing.T) { examples := []struct { headers map[string]string result string }{ {map[string]string{}, ""}, {map[string]string{"X-Request-ID": "foo"}, "foo"}, {map[string]string{"x-request-id": "foo"}, "foo"}, {map[string]string{"x-request-id": "foo"}, "foo"}, {map[string]string{"x-request-id": "foo", "x-amzn-trace-id": "amz"}, "foo"}, } for _, ex := range examples { req := &http.Request{Header: http.Header{}} for k, v := range ex.headers { req.Header.Set(k, v) } assert.Equal(t, ex.result, getRequestID(&gin.Context{Request: req})) } } ================================================ FILE: pkg/api/middleware.go ================================================ package api import ( "strings" "github.com/gin-gonic/gin" "github.com/sosedoff/pgweb/pkg/command" ) // Middleware to check database connection status before running queries func dbCheckMiddleware() gin.HandlerFunc { return func(c *gin.Context) { path := strings.Replace(c.Request.URL.Path, command.Opts.Prefix, "", -1) // Allow whitelisted paths if allowedPaths[path] { c.Next() return } // Check if session exists in single-session mode if !command.Opts.Sessions { if DbClient == nil { badRequest(c, errNotConnected) return } c.Next() return } // Determine session ID from the client request sid := getSessionId(c.Request) if sid == "" { badRequest(c, errSessionRequired) return } // Determine the database connection handle for the session conn := DbSessions.Get(sid) if conn == nil { badRequest(c, errNotConnected) return } c.Next() } } // Middleware to inject CORS headers func corsMiddleware() gin.HandlerFunc { return func(c *gin.Context) { c.Header("Access-Control-Allow-Methods", "GET, POST, OPTIONS") c.Header("Access-Control-Expose-Headers", "*") c.Header("Access-Control-Allow-Origin", command.Opts.CorsOrigin) } } func requireLocalQueries() gin.HandlerFunc { return func(c *gin.Context) { if QueryStore == nil { badRequest(c, "local queries are disabled") return } c.Next() } } ================================================ FILE: pkg/api/routes.go ================================================ package api import ( "github.com/gin-gonic/gin" "github.com/sosedoff/pgweb/pkg/command" "github.com/sosedoff/pgweb/pkg/metrics" ) func SetupMiddlewares(group *gin.RouterGroup) { if command.Opts.Cors { group.Use(corsMiddleware()) } group.Use(dbCheckMiddleware()) } func SetupRoutes(router *gin.Engine) { root := router.Group(command.Opts.Prefix) root.GET("/", gin.WrapH(GetHome(command.Opts.Prefix))) root.GET("/static/*path", gin.WrapH(GetAssets(command.Opts.Prefix))) root.GET("/connect/:resource", ConnectWithBackend) api := root.Group("/api") SetupMiddlewares(api) if command.Opts.Sessions { api.GET("/sessions", GetSessions) } api.GET("/info", GetInfo) api.POST("/connect", Connect) api.POST("/disconnect", Disconnect) api.POST("/switchdb", SwitchDb) api.GET("/databases", GetDatabases) api.GET("/connection", GetConnectionInfo) api.GET("/server_settings", GetServerSettings) api.GET("/activity", GetActivity) api.GET("/schemas", GetSchemas) api.GET("/objects", GetObjects) api.GET("/tables/:table", GetTable) api.GET("/tables/:table/rows", GetTableRows) api.GET("/tables/:table/info", GetTableInfo) api.GET("/tables/:table/indexes", GetTableIndexes) api.GET("/tables/:table/constraints", GetTableConstraints) api.GET("/tables_stats", GetTablesStats) api.GET("/functions/:id", GetFunction) api.GET("/query", RunQuery) api.POST("/query", RunQuery) api.GET("/explain", ExplainQuery) api.POST("/explain", ExplainQuery) api.GET("/analyze", AnalyzeQuery) api.POST("/analyze", AnalyzeQuery) api.GET("/history", GetHistory) api.GET("/bookmarks", GetBookmarks) api.GET("/export", DataExport) api.GET("/local_queries", requireLocalQueries(), GetLocalQueries) api.GET("/local_queries/:id", requireLocalQueries(), RunLocalQuery) api.POST("/local_queries/:id", requireLocalQueries(), RunLocalQuery) } func SetupMetrics(engine *gin.Engine) { if command.Opts.MetricsEnabled && command.Opts.MetricsAddr == "" { // NOTE: We're not supporting the MetricsPath CLI option here to avoid the route conflicts. engine.GET("/metrics", gin.WrapH(metrics.NewHandler())) } } ================================================ FILE: pkg/api/session_manager.go ================================================ package api import ( "sync" "time" "github.com/sirupsen/logrus" "github.com/sosedoff/pgweb/pkg/client" "github.com/sosedoff/pgweb/pkg/metrics" ) type SessionManager struct { logger *logrus.Logger sessions map[string]*client.Client mu sync.Mutex idleTimeout time.Duration } func NewSessionManager(logger *logrus.Logger) *SessionManager { return &SessionManager{ logger: logger, sessions: map[string]*client.Client{}, mu: sync.Mutex{}, } } func (m *SessionManager) SetIdleTimeout(timeout time.Duration) { m.idleTimeout = timeout } func (m *SessionManager) IDs() []string { m.mu.Lock() defer m.mu.Unlock() ids := []string{} for k := range m.sessions { ids = append(ids, k) } return ids } func (m *SessionManager) Sessions() map[string]*client.Client { m.mu.Lock() defer m.mu.Unlock() sessions := make(map[string]*client.Client, len(m.sessions)) for k, v := range m.sessions { sessions[k] = v } return sessions } func (m *SessionManager) Get(id string) *client.Client { m.mu.Lock() defer m.mu.Unlock() return m.sessions[id] } func (m *SessionManager) Add(id string, conn *client.Client) { m.mu.Lock() defer m.mu.Unlock() m.sessions[id] = conn metrics.SetSessionsCount(len(m.sessions)) } func (m *SessionManager) Remove(id string) bool { m.mu.Lock() defer m.mu.Unlock() conn, ok := m.sessions[id] if ok { conn.Close() delete(m.sessions, id) } metrics.SetSessionsCount(len(m.sessions)) return ok } func (m *SessionManager) Len() int { m.mu.Lock() defer m.mu.Unlock() return len(m.sessions) } func (m *SessionManager) Cleanup() int { if m.idleTimeout == 0 { return 0 } removed := 0 m.logger.Debug("starting idle sessions cleanup") defer func() { m.logger.Debug("removed idle sessions:", removed) }() for _, id := range m.staleSessions() { m.logger.WithField("id", id).Debug("closing stale session") if m.Remove(id) { removed++ } } return removed } func (m *SessionManager) RunPeriodicCleanup() { m.logger.WithField("timeout", m.idleTimeout).Info("session manager cleanup enabled") for range time.Tick(time.Minute) { m.Cleanup() } } func (m *SessionManager) staleSessions() []string { m.mu.Lock() defer m.mu.Unlock() now := time.Now() ids := []string{} for id, conn := range m.sessions { if now.Sub(conn.LastQueryTime()) > m.idleTimeout { ids = append(ids, id) } } return ids } ================================================ FILE: pkg/api/session_manager_test.go ================================================ package api import ( "sort" "testing" "time" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/sosedoff/pgweb/pkg/client" ) func TestSessionManager(t *testing.T) { t.Run("return ids", func(t *testing.T) { manager := NewSessionManager(nil) assert.Equal(t, []string{}, manager.IDs()) manager.sessions["foo"] = &client.Client{} manager.sessions["bar"] = &client.Client{} ids := manager.IDs() sort.Strings(ids) assert.Equal(t, []string{"bar", "foo"}, ids) }) t.Run("get session", func(t *testing.T) { manager := NewSessionManager(nil) assert.Nil(t, manager.Get("foo")) manager.sessions["foo"] = &client.Client{} assert.NotNil(t, manager.Get("foo")) }) t.Run("set session", func(t *testing.T) { manager := NewSessionManager(nil) assert.Nil(t, manager.Get("foo")) manager.Add("foo", &client.Client{}) assert.NotNil(t, manager.Get("foo")) }) t.Run("remove session", func(t *testing.T) { manager := NewSessionManager(nil) assert.Nil(t, manager.Get("foo")) manager.Add("foo", &client.Client{}) assert.NotNil(t, manager.Get("foo")) assert.True(t, manager.Remove("foo")) assert.False(t, manager.Remove("foo")) assert.Nil(t, manager.Get("foo")) }) t.Run("return len", func(t *testing.T) { manager := NewSessionManager(nil) manager.sessions["foo"] = &client.Client{} manager.sessions["bar"] = &client.Client{} assert.Equal(t, 2, manager.Len()) }) t.Run("clean up stale sessions", func(t *testing.T) { manager := NewSessionManager(logrus.New()) conn := &client.Client{} manager.Add("foo", conn) assert.Equal(t, 1, manager.Len()) assert.Equal(t, 0, manager.Cleanup()) assert.Equal(t, 1, manager.Len()) res, err := conn.Query("select 1") assert.Nil(t, res) assert.Nil(t, err) manager.SetIdleTimeout(time.Minute) assert.Equal(t, 1, manager.Cleanup()) assert.Equal(t, 0, manager.Len()) assert.True(t, conn.IsClosed()) }) } ================================================ FILE: pkg/api/types.go ================================================ package api type localQuery struct { ID string `json:"id"` Title string `json:"title,omitempty"` Description string `json:"description,omitempty"` Query string `json:"query"` } ================================================ FILE: pkg/bookmarks/bookmarks.go ================================================ package bookmarks import ( "os" "github.com/sosedoff/pgweb/pkg/command" "github.com/sosedoff/pgweb/pkg/shared" ) // Bookmark contains information about bookmarked database connection type Bookmark struct { ID string // ID generated from the filename URL string // Postgres connection URL Host string // Server hostname Port int // Server port User string // Database user UserVar string // Database user environment variable Password string // User password PasswordVar string // User password environment variable Database string // Database name SSLMode string // Connection SSL mode SSH *shared.SSHInfo // SSH tunnel config ReadOnly bool // Enable read-only transaction mode } // SSHInfoIsEmpty returns true if ssh configuration is not provided func (b Bookmark) SSHInfoIsEmpty() bool { return b.SSH == nil || (b.SSH.User == "" && b.SSH.Host == "" && b.SSH.Port == "") } // ConvertToOptions returns an options struct from connection details func (b Bookmark) ConvertToOptions() command.Options { user := b.User if b.User == "" { user = os.Getenv(b.UserVar) } pass := b.Password if b.Password == "" { pass = os.Getenv(b.PasswordVar) } return command.Options{ URL: b.URL, Host: b.Host, Port: b.Port, User: user, Pass: pass, DbName: b.Database, SSLMode: b.SSLMode, ReadOnly: b.ReadOnly, } } ================================================ FILE: pkg/bookmarks/bookmarks_test.go ================================================ package bookmarks import ( "testing" "github.com/sosedoff/pgweb/pkg/command" "github.com/sosedoff/pgweb/pkg/shared" "github.com/stretchr/testify/assert" ) func TestBookmarkSSHInfoIsEmpty(t *testing.T) { t.Run("empty", func(t *testing.T) { info := &shared.SSHInfo{ Host: "", Port: "", User: "", } b := Bookmark{SSH: nil} assert.True(t, b.SSHInfoIsEmpty()) b = Bookmark{SSH: info} assert.True(t, b.SSHInfoIsEmpty()) }) t.Run("only host set", func(t *testing.T) { b := Bookmark{SSH: &shared.SSHInfo{Host: "localhost"}} assert.False(t, b.SSHInfoIsEmpty()) }) t.Run("only port set", func(t *testing.T) { b := Bookmark{SSH: &shared.SSHInfo{Port: "8080"}} assert.False(t, b.SSHInfoIsEmpty()) }) t.Run("only user set", func(t *testing.T) { b := Bookmark{SSH: &shared.SSHInfo{User: "postgres"}} assert.False(t, b.SSHInfoIsEmpty()) }) t.Run("populated", func(t *testing.T) { info := &shared.SSHInfo{ Host: "localhost", Port: "8080", User: "postgres", } b := Bookmark{SSH: info} assert.False(t, b.SSHInfoIsEmpty()) }) } func TestBookmarkWithVarsConvertToOptions(t *testing.T) { t.Run("literals set", func(t *testing.T) { b := Bookmark{ User: "user", UserVar: "", Password: "password", PasswordVar: "", } expOpt := command.Options{ User: "user", Pass: "password", } opt := b.ConvertToOptions() assert.Equal(t, expOpt, opt) }) t.Run("all set", func(t *testing.T) { b := Bookmark{ User: "user", UserVar: "DB_USER", Password: "password", PasswordVar: "DB_PASSWORD", } expOpt := command.Options{ User: "user", Pass: "password", } t.Setenv("DB_USER", "user123") t.Setenv("DB_PASSWORD", "password123") opt := b.ConvertToOptions() assert.Equal(t, expOpt, opt) }) t.Run("env vars set", func(t *testing.T) { b := Bookmark{ User: "", UserVar: "DB_USER", Password: "", PasswordVar: "DB_PASSWORD", } expOpt := command.Options{ User: "user123", Pass: "password123", } t.Setenv("DB_USER", "user123") t.Setenv("DB_PASSWORD", "password123") opt := b.ConvertToOptions() assert.Equal(t, expOpt, opt) }) } func TestBookmarkConvertToOptions(t *testing.T) { b := Bookmark{ URL: "postgres://username:password@host:port/database?sslmode=disable", Host: "localhost", Port: 5432, User: "postgres", Password: "password", Database: "mydatabase", SSLMode: "disable", ReadOnly: true, } expOpt := command.Options{ URL: "postgres://username:password@host:port/database?sslmode=disable", Host: "localhost", Port: 5432, User: "postgres", Pass: "password", DbName: "mydatabase", SSLMode: "disable", ReadOnly: true, } opt := b.ConvertToOptions() assert.Equal(t, expOpt, opt) } ================================================ FILE: pkg/bookmarks/manager.go ================================================ package bookmarks import ( "errors" "fmt" "os" "path/filepath" "strings" "github.com/BurntSushi/toml" ) type Manager struct { dir string } func NewManager(dir string) Manager { return Manager{ dir: dir, } } func (m Manager) Get(id string) (*Bookmark, error) { bookmarks, err := m.list() if err != nil { return nil, err } for _, b := range bookmarks { if b.ID == id { return &b, nil } } return nil, fmt.Errorf("bookmark %v not found", id) } func (m Manager) List() ([]Bookmark, error) { return m.list() } func (m Manager) ListIDs() ([]string, error) { bookmarks, err := m.list() if err != nil { return nil, err } ids := make([]string, len(bookmarks)) for i, bookmark := range bookmarks { ids[i] = bookmark.ID } return ids, nil } func (m Manager) list() ([]Bookmark, error) { result := []Bookmark{} if m.dir == "" { return result, nil } info, err := os.Stat(m.dir) if err != nil { // Do not fail if base dir does not exists: it's not created by default if errors.Is(err, os.ErrNotExist) { fmt.Fprintf(os.Stderr, "[WARN] bookmarks dir %s does not exist\n", m.dir) return result, nil } return nil, err } if !info.IsDir() { return nil, fmt.Errorf("path %s is not a directory", m.dir) } dirEntries, err := os.ReadDir(m.dir) if err != nil { return nil, err } for _, entry := range dirEntries { name := entry.Name() if filepath.Ext(name) != ".toml" { continue } bookmark, err := readBookmark(filepath.Join(m.dir, name)) if err != nil { // Do not fail if one of the bookmarks is invalid fmt.Fprintf(os.Stderr, "[WARN] bookmark file %s is invalid: %s\n", name, err) continue } result = append(result, bookmark) } return result, nil } func readBookmark(path string) (Bookmark, error) { bookmark := Bookmark{ ID: fileBasename(path), } _, err := os.Stat(path) if err != nil { if errors.Is(err, os.ErrNotExist) { err = fmt.Errorf("bookmark file %s does not exist", path) } return bookmark, err } buff, err := os.ReadFile(path) if err != nil { return bookmark, err } _, err = toml.Decode(string(buff), &bookmark) if bookmark.Port == 0 { bookmark.Port = 5432 } // List of all supported postgres modes modes := []string{"disable", "allow", "prefer", "require", "verify-ca", "verify-full"} valid := false for _, mode := range modes { if bookmark.SSLMode == mode { valid = true break } } // Fall back to a default mode if mode is not set or invalid // Typical typo: ssl mode set to "disabled" if bookmark.SSLMode == "" || !valid { bookmark.SSLMode = "disable" } // Set default SSH port if it's not provided by user if bookmark.SSH != nil && bookmark.SSH.Port == "" { bookmark.SSH.Port = "22" } return bookmark, err } func fileBasename(path string) string { filename := filepath.Base(path) return strings.Replace(filename, filepath.Ext(path), "", 1) } ================================================ FILE: pkg/bookmarks/manager_test.go ================================================ package bookmarks import ( "testing" "github.com/stretchr/testify/assert" ) func TestManagerList(t *testing.T) { examples := []struct { dir string num int err string }{ {"../../data", 4, ""}, {"../../data/bookmark.toml", 0, "is not a directory"}, {"../../data2", 0, ""}, {"", 0, ""}, } for _, ex := range examples { t.Run(ex.dir, func(t *testing.T) { bookmarks, err := NewManager(ex.dir).List() if ex.err != "" { assert.Contains(t, err.Error(), ex.err) } assert.Len(t, bookmarks, ex.num) }) } } func TestManagerListIDs(t *testing.T) { ids, err := NewManager("../../data").ListIDs() assert.NoError(t, err) assert.Equal(t, []string{ "bookmark", "bookmark_invalid_ssl", "bookmark_url", "bookmark_with_ssh", }, ids) } func TestManagerGet(t *testing.T) { manager := NewManager("../../data") b, err := manager.Get("bookmark") assert.NoError(t, err) assert.Equal(t, "bookmark", b.ID) b, err = manager.Get("foo") assert.Equal(t, "bookmark foo not found", err.Error()) assert.Nil(t, b) } func Test_fileBasename(t *testing.T) { assert.Equal(t, "filename", fileBasename("filename.toml")) assert.Equal(t, "filename", fileBasename("path/filename.toml")) assert.Equal(t, "filename", fileBasename("~/long/path/filename.toml")) assert.Equal(t, "filename", fileBasename("filename")) } func Test_readBookmark(t *testing.T) { t.Run("good", func(t *testing.T) { b, err := readBookmark("../../data/bookmark.toml") assert.NoError(t, err) assert.Equal(t, "bookmark", b.ID) assert.Equal(t, "localhost", b.Host) assert.Equal(t, 5432, b.Port) assert.Equal(t, "postgres", b.User) assert.Equal(t, "mydatabase", b.Database) assert.Equal(t, "disable", b.SSLMode) assert.Equal(t, "", b.Password) assert.Equal(t, "", b.URL) }) t.Run("with url", func(t *testing.T) { b, err := readBookmark("../../data/bookmark_url.toml") assert.NoError(t, err) assert.Equal(t, "postgres://username:password@host:port/database?sslmode=disable", b.URL) assert.Equal(t, "", b.Host) assert.Equal(t, 5432, b.Port) assert.Equal(t, "", b.User) assert.Equal(t, "", b.Database) assert.Equal(t, "disable", b.SSLMode) assert.Equal(t, "", b.Password) }) t.Run("with ssh options", func(t *testing.T) { b, err := readBookmark("../../data/bookmark_with_ssh.toml") assert.NoError(t, err) assert.NotNil(t, b.SSH) sshc := b.SSH assert.Equal(t, "ssh-host", sshc.Host) assert.Equal(t, "ssh-user", sshc.User) assert.Equal(t, "ssh-password", sshc.Password) assert.Equal(t, "/path/to/key-file", sshc.Key) assert.Equal(t, "key-file-password", sshc.KeyPassword) }) t.Run("invalid ssl", func(t *testing.T) { b, err := readBookmark("../../data/bookmark_invalid_ssl.toml") assert.NoError(t, err) assert.Equal(t, "disable", b.SSLMode) }) t.Run("invalid file", func(t *testing.T) { _, err := readBookmark("foobar") assert.Equal(t, "bookmark file foobar does not exist", err.Error()) }) t.Run("invalid syntax", func(t *testing.T) { _, err := readBookmark("../../data/invalid.toml") assert.Equal(t, "toml: line 1: expected '.' or '=', but got 'e' instead", err.Error()) }) } ================================================ FILE: pkg/cli/cli.go ================================================ package cli import ( "errors" "fmt" "os" "os/exec" "os/signal" "strings" "syscall" "time" "github.com/gin-gonic/gin" "github.com/jessevdk/go-flags" "github.com/sirupsen/logrus" "github.com/sosedoff/pgweb/pkg/api" "github.com/sosedoff/pgweb/pkg/bookmarks" "github.com/sosedoff/pgweb/pkg/client" "github.com/sosedoff/pgweb/pkg/command" "github.com/sosedoff/pgweb/pkg/connection" "github.com/sosedoff/pgweb/pkg/metrics" "github.com/sosedoff/pgweb/pkg/queries" "github.com/sosedoff/pgweb/pkg/util" ) var ( logger *logrus.Logger options command.Options readonlyWarning = ` -------------------------------------------------------------------------------- SECURITY WARNING: You are running Pgweb in read-only mode. This mode is designed for environments where users could potentially delete or change data. For proper read-only access please follow PostgreSQL role management documentation. --------------------------------------------------------------------------------` ) func init() { logger = logrus.New() } func exitWithMessage(message string) { fmt.Println("Error:", message) os.Exit(1) } func initClientUsingBookmark(baseDir, bookmarkName string) (*client.Client, error) { manager := bookmarks.NewManager(baseDir) bookmark, err := manager.Get(bookmarkName) if err != nil { return nil, err } return client.NewFromBookmark(bookmark) } func initClient() { if connection.IsBlank(command.Opts) && options.Bookmark == "" { return } var cl *client.Client var err error if options.Bookmark != "" { cl, err = initClientUsingBookmark(options.BookmarksDir, options.Bookmark) } else { cl, err = client.New() } if err != nil { exitWithMessage(err.Error()) } if command.Opts.Debug { fmt.Println("Opening database connection using string:", cl.ConnectionString) } retryCount := command.Opts.RetryCount retryDelay := time.Second * time.Duration(command.Opts.RetryDelay) fmt.Println("Connecting to server...") abort, err := testClient(cl, int(retryCount), retryDelay) if err != nil { if abort { exitWithMessage(err.Error()) } else { return } } if !command.Opts.Sessions { fmt.Printf("Connected to %s\n", cl.ServerVersionInfo()) } fmt.Println("Checking database objects...") _, err = cl.Objects() if err != nil { exitWithMessage(err.Error()) } api.DbClient = cl } func initOptions() { opts, err := command.ParseOptions(os.Args) if err != nil { switch errVal := err.(type) { case *flags.Error: if errVal.Type == flags.ErrHelp { fmt.Println("Available environment variables:") fmt.Println(command.AvailableEnvVars()) } // no need to print error, flags package already does that default: fmt.Println(err.Error()) } os.Exit(1) } command.Opts = opts options = opts if options.Version { printVersion() os.Exit(0) } if err := configureLogger(opts); err != nil { exitWithMessage(err.Error()) return } if options.ReadOnly { fmt.Println(readonlyWarning) } if options.BinaryCodec != "" { if err := client.SetBinaryCodec(options.BinaryCodec); err != nil { exitWithMessage(err.Error()) } } configureLocalQueryStore() printVersion() } func configureLocalQueryStore() { if options.Sessions || options.QueriesDir == "" { return } stat, err := os.Stat(options.QueriesDir) if err != nil { if errors.Is(err, os.ErrNotExist) { logger.Debugf("local queries directory %q does not exist, disabling feature", options.QueriesDir) } else { logger.Debugf("local queries feature disabled due to error: %v", err) } return } if !stat.IsDir() { logger.Debugf("local queries path %q is not a directory", options.QueriesDir) return } api.QueryStore = queries.NewStore(options.QueriesDir) } func configureLogger(opts command.Options) error { if options.Debug { logger.SetLevel(logrus.DebugLevel) } else { lvl, err := logrus.ParseLevel(opts.LogLevel) if err != nil { return err } logger.SetLevel(lvl) } switch options.LogFormat { case "text": logger.SetFormatter(&logrus.TextFormatter{}) case "json": logger.SetFormatter(&logrus.JSONFormatter{}) default: return fmt.Errorf("invalid logger format: %v", options.LogFormat) } return nil } func printVersion() { fmt.Println(command.VersionString()) } func startServer() { router := gin.New() router.Use(api.RequestLogger(logger)) router.Use(gin.Recovery()) // Enable HTTP basic authentication only if both user and password are set if options.AuthUser != "" && options.AuthPass != "" { auth := map[string]string{options.AuthUser: options.AuthPass} router.Use(gin.BasicAuth(auth)) } api.SetLogger(logger) api.SetupRoutes(router) api.SetupMetrics(router) fmt.Println("Starting server...") go func() { metrics.SetHealthy(true) err := router.Run(fmt.Sprintf("%v:%v", options.HTTPHost, options.HTTPPort)) if err != nil { fmt.Println("Can't start server:", err) if strings.Contains(err.Error(), "address already in use") { openPage() } os.Exit(1) } }() } func startMetricsServer() { serverAddr := fmt.Sprintf("%v:%v", command.Opts.HTTPHost, command.Opts.HTTPPort) if options.MetricsAddr == serverAddr { return } err := metrics.StartServer(logger, options.MetricsPath, options.MetricsAddr) if err != nil { logger.WithError(err).Fatal("unable to start prometheus metrics server") } } func handleSignals() { c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) <-c } func openPage() { url := fmt.Sprintf("http://%v:%v/%s", options.HTTPHost, options.HTTPPort, options.Prefix) fmt.Println("To view database open", url, "in browser") if options.SkipOpen { return } _, err := exec.Command("which", "open").Output() if err != nil { return } _, err = exec.Command("open", url).Output() if err != nil { fmt.Println("Unable to auto-open pgweb URL:", err) } } // testClient attempts to establish a database connection until it succeeds or // give up after certain number of retries. Retries only available when database // name or a connection string is provided. func testClient(cl *client.Client, retryCount int, retryDelay time.Duration) (abort bool, err error) { usingDefaultDB := command.Opts.DbName == "" && command.Opts.URL == "" for { err = cl.Test() if err == nil { return false, nil } // Continue normal start up if can't connect locally without database details. if usingDefaultDB { if errors.Is(err, client.ErrConnectionRefused) || errors.Is(err, client.ErrAuthFailed) || errors.Is(err, client.ErrDatabaseNotExist) { return false, err } } // Only retry if can't establish connection to the server. if errors.Is(err, client.ErrConnectionRefused) && retryCount > 0 { fmt.Printf("Connection error: %v, retrying in %v (%d remaining)\n", err, retryDelay, retryCount) retryCount-- <-time.After(retryDelay) continue } return true, err } } func Run() { initOptions() initClient() if api.DbClient != nil { defer api.DbClient.Close() } if !options.Debug { gin.SetMode("release") } // Print memory usage every 30 seconds with debug flag if options.Debug { util.StartProfiler() } // Start session cleanup worker if options.Sessions { api.DbSessions = api.NewSessionManager(logger) if !command.Opts.DisableConnectionIdleTimeout { api.DbSessions.SetIdleTimeout(time.Minute * time.Duration(command.Opts.ConnectionIdleTimeout)) go api.DbSessions.RunPeriodicCleanup() } } // Start a separate metrics http server. If metrics addr is not provided, we // add the metrics endpoint in the existing application server (see api.go). if options.MetricsEnabled && options.MetricsAddr != "" { go startMetricsServer() } startServer() openPage() handleSignals() } ================================================ FILE: pkg/client/client.go ================================================ package client import ( "context" "errors" "fmt" "log" neturl "net/url" "reflect" "regexp" "strings" "time" "github.com/jmoiron/sqlx" _ "github.com/lib/pq" "github.com/sosedoff/pgweb/pkg/bookmarks" "github.com/sosedoff/pgweb/pkg/command" "github.com/sosedoff/pgweb/pkg/connection" "github.com/sosedoff/pgweb/pkg/history" "github.com/sosedoff/pgweb/pkg/shared" "github.com/sosedoff/pgweb/pkg/statements" ) var ( regexErrAuthFailed = regexp.MustCompile(`(authentication failed|role "(.*)" does not exist)`) regexErrConnectionRefused = regexp.MustCompile(`(connection|actively) refused`) regexErrDatabaseNotExist = regexp.MustCompile(`database "(.*)" does not exist`) ) var ( ErrAuthFailed = errors.New("authentication failed") ErrConnectionRefused = errors.New("connection refused") ErrDatabaseNotExist = errors.New("database does not exist") ) type Client struct { db *sqlx.DB tunnel *Tunnel serverVersion string serverType string lastQueryTime time.Time queryTimeout time.Duration readonly bool closed bool External bool `json:"external"` History []history.Record `json:"history"` ConnectionString string `json:"connection_string"` } func getSchemaAndTable(str string) (string, string) { chunks := strings.Split(str, ".") if len(chunks) == 1 { return "public", chunks[0] } return chunks[0], chunks[1] } func New() (*Client, error) { str, err := connection.BuildStringFromOptions(command.Opts) if command.Opts.Debug && str != "" { fmt.Println("Creating a new client for:", str) } if err != nil { return nil, err } db, err := sqlx.Open("postgres", str) if err != nil { return nil, err } client := Client{ db: db, ConnectionString: str, History: history.New(), } client.init() return &client, nil } func NewFromUrl(url string, sshInfo *shared.SSHInfo) (*Client, error) { var ( tunnel *Tunnel err error ) if sshInfo != nil { if command.Opts.DisableSSH { return nil, fmt.Errorf("ssh connections are disabled") } if command.Opts.Debug { fmt.Println("Opening SSH tunnel for:", sshInfo) } tunnel, err = NewTunnel(sshInfo, url) if err != nil { tunnel.Close() return nil, err } err = tunnel.Configure() if err != nil { tunnel.Close() return nil, err } go tunnel.Start() uri, err := neturl.Parse(url) if err != nil { tunnel.Close() return nil, err } // Override remote postgres port with local proxy port url = strings.Replace(url, uri.Host, fmt.Sprintf("127.0.0.1:%v", tunnel.Port), 1) } if command.Opts.Debug { fmt.Println("Creating a new client for:", url) } uri, err := neturl.Parse(url) if err == nil && uri.Path == "" { return nil, fmt.Errorf("Database name is not provided") } db, err := sqlx.Open("postgres", url) if err != nil { return nil, err } client := Client{ db: db, tunnel: tunnel, serverType: postgresType, ConnectionString: url, History: history.New(), } client.init() return &client, nil } func NewFromBookmark(bookmark *bookmarks.Bookmark) (*Client, error) { var ( connStr string err error ) options := bookmark.ConvertToOptions() if options.URL != "" { connStr = options.URL } else { connStr, err = connection.BuildStringFromOptions(options) if err != nil { return nil, err } } var sshInfo *shared.SSHInfo if !bookmark.SSHInfoIsEmpty() { sshInfo = bookmark.SSH } client, err := NewFromUrl(connStr, sshInfo) if err != nil { return nil, err } if bookmark.ReadOnly { client.readonly = true } return client, nil } func (client *Client) init() { if command.Opts.QueryTimeout > 0 { client.queryTimeout = time.Second * time.Duration(command.Opts.QueryTimeout) } client.setServerVersion() } func (client *Client) setServerVersion() { res, err := client.query("SELECT version()") if err != nil || len(res.Rows) < 1 { return } version := res.Rows[0][0].(string) match, serverType, serverVersion := detectServerTypeAndVersion(version) if match { client.serverType = serverType client.serverVersion = serverVersion } } func (client *Client) Test() error { // NOTE: This is a different timeout defined in CLI OpenTimeout ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() err := client.db.PingContext(ctx) if err == nil { return nil } errMsg := err.Error() if regexErrConnectionRefused.MatchString(errMsg) { return ErrConnectionRefused } if regexErrAuthFailed.MatchString(errMsg) { return ErrAuthFailed } if regexErrDatabaseNotExist.MatchString(errMsg) { return ErrDatabaseNotExist } return err } func (client *Client) TestWithTimeout(timeout time.Duration) (result error) { ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() // Check connection status right away without waiting for the ticker to kick in. // We're expecting to get "connection refused" here for the most part. if err := client.db.PingContext(ctx); err == nil { return nil } ticker := time.NewTicker(250 * time.Millisecond) defer ticker.Stop() for { select { case <-ticker.C: result = client.db.PingContext(ctx) if result == nil { return } case <-ctx.Done(): return } } } func (client *Client) Info() (*Result, error) { result, err := client.query(statements.Info) if err != nil { msg := err.Error() if strings.Contains(msg, "inet_") && (strings.Contains(msg, "not supported") || strings.Contains(msg, "permission denied")) { // Fetch client information without inet_ function calls result, err = client.query(statements.InfoSimple) } } return result, err } func (client *Client) Databases() ([]string, error) { return client.fetchRows(statements.Databases) } func (client *Client) Schemas() ([]string, error) { return client.fetchRows(statements.Schemas) } func (client *Client) Objects() (*Result, error) { return client.query(statements.Objects) } func (client *Client) Table(table string) (*Result, error) { schema, table := getSchemaAndTable(table) return client.query(statements.TableSchema, schema, table) } func (client *Client) MaterializedView(name string) (*Result, error) { return client.query(statements.MaterializedView, name) } func (client *Client) Function(id string) (*Result, error) { return client.query(statements.Function, id) } func (client *Client) TableRows(table string, opts RowsOptions) (*Result, error) { schema, table := getSchemaAndTable(table) sql := fmt.Sprintf(`SELECT * FROM "%s"."%s"`, schema, table) if opts.Where != "" { sql += fmt.Sprintf(" WHERE %s", opts.Where) } if opts.SortColumn != "" { if opts.SortOrder == "" { opts.SortOrder = "ASC" } sql += fmt.Sprintf(` ORDER BY "%s" %s`, opts.SortColumn, opts.SortOrder) } if opts.Limit > 0 { sql += fmt.Sprintf(" LIMIT %d", opts.Limit) } if opts.Offset > 0 { sql += fmt.Sprintf(" OFFSET %d", opts.Offset) } return client.query(sql) } func (client *Client) EstimatedTableRowsCount(table string, opts RowsOptions) (*Result, error) { schema, table := getSchemaAndTable(table) result, err := client.query(statements.EstimatedTableRowCount, schema, table) if err != nil { return nil, err } // float64 to int64 conversion estimatedRowsCount := result.Rows[0][0].(float64) result.Rows[0] = Row{int64(estimatedRowsCount)} return result, nil } func (client *Client) TableRowsCount(table string, opts RowsOptions) (*Result, error) { // Return postgres estimated rows count on empty filter if opts.Where == "" && client.serverType == postgresType { res, err := client.EstimatedTableRowsCount(table, opts) if err != nil { return nil, err } n := res.Rows[0][0].(int64) if n >= 100000 { return res, nil } } schema, tableName := getSchemaAndTable(table) sql := fmt.Sprintf(`SELECT COUNT(1) FROM "%s"."%s"`, schema, tableName) if opts.Where != "" { sql += fmt.Sprintf(" WHERE %s", opts.Where) } return client.query(sql) } func (client *Client) TableInfo(table string) (*Result, error) { if client.serverType == cockroachType { return client.query(statements.TableInfoCockroach) } schema, table := getSchemaAndTable(table) return client.query(statements.TableInfo, fmt.Sprintf(`"%s"."%s"`, schema, table)) } func (client *Client) TableIndexes(table string) (*Result, error) { schema, table := getSchemaAndTable(table) res, err := client.query(statements.TableIndexes, schema, table) if err != nil { return nil, err } return res, err } func (client *Client) TableConstraints(table string) (*Result, error) { schema, table := getSchemaAndTable(table) res, err := client.query(statements.TableConstraints, schema, table) if err != nil { return nil, err } return res, err } func (client *Client) TablesStats() (*Result, error) { return client.query(statements.TablesStats) } func (client *Client) ServerSettings() (*Result, error) { return client.query(statements.Settings) } // Returns all active queriers on the server func (client *Client) Activity() (*Result, error) { if client.serverType == cockroachType { return client.query("SHOW QUERIES") } version := getMajorMinorVersionString(client.serverVersion) query := statements.Activity[version] if query == "" { query = statements.Activity["default"] } return client.query(query) } func (client *Client) Query(query string) (*Result, error) { res, err := client.query(query) // Save history records only if query did not fail if err == nil && !client.hasHistoryRecord(query) { client.History = append(client.History, history.NewRecord(query)) } return res, err } func (client *Client) SetReadOnlyMode() error { var value string if err := client.db.Get(&value, "SHOW default_transaction_read_only;"); err != nil { return err } if value == "off" { _, err := client.db.Exec("SET default_transaction_read_only=on;") return err } return nil } func (client *Client) ServerVersionInfo() string { return fmt.Sprintf("%s %s", client.serverType, client.serverVersion) } func (client *Client) ServerVersion() string { return client.serverVersion } func (client *Client) context() (context.Context, context.CancelFunc) { if client.queryTimeout > 0 { return context.WithTimeout(context.Background(), client.queryTimeout) } return context.Background(), func() {} } func (client *Client) exec(query string, args ...interface{}) (*Result, error) { ctx, cancel := client.context() defer cancel() queryStart := time.Now() res, err := client.db.ExecContext(ctx, query, args...) queryFinish := time.Now() if err != nil { return nil, err } affected, err := res.RowsAffected() if err != nil { return nil, err } result := Result{ Columns: []string{"Rows Affected"}, Rows: []Row{ {affected}, }, Stats: &ResultStats{ ColumnsCount: 1, RowsCount: 1, QueryStartTime: queryStart.UTC(), QueryFinishTime: queryFinish.UTC(), QueryDuration: queryFinish.Sub(queryStart).Milliseconds(), }, } return &result, nil } func (client *Client) query(query string, args ...interface{}) (*Result, error) { if client.db == nil { return nil, nil } // Update the last usage time defer func() { client.lastQueryTime = time.Now().UTC() }() // We're going to force-set transaction mode on every query. // This is needed so that default mode could not be changed by user. if command.Opts.ReadOnly || client.readonly { if err := client.SetReadOnlyMode(); err != nil { return nil, err } if containsRestrictedKeywords(query) { return nil, errors.New("query contains keywords not allowed in read-only mode") } } action := strings.ToLower(strings.Split(query, " ")[0]) hasReturnValues := strings.Contains(strings.ToLower(query), " returning ") if (action == "update" || action == "delete") && !hasReturnValues { return client.exec(query, args...) } ctx, cancel := client.context() defer cancel() queryStart := time.Now() rows, err := client.db.QueryxContext(ctx, query, args...) queryFinish := time.Now() if err != nil { if command.Opts.Debug { log.Println("Failed query:", query, "\nArgs:", args) } return nil, err } defer rows.Close() cols, err := rows.Columns() if err != nil { return nil, err } // Make sure to never return null columns if cols == nil { cols = []string{} } result := Result{ Columns: cols, Rows: []Row{}, } for rows.Next() { obj, err := rows.SliceScan() for i, item := range obj { if item == nil { obj[i] = nil } else { t := reflect.TypeOf(item).Kind().String() if t == "slice" { obj[i] = string(item.([]byte)) } } } if err == nil { result.Rows = append(result.Rows, obj) } } result.Stats = &ResultStats{ ColumnsCount: len(cols), RowsCount: len(result.Rows), QueryStartTime: queryStart.UTC(), QueryFinishTime: queryFinish.UTC(), QueryDuration: queryFinish.Sub(queryStart).Milliseconds(), } result.PostProcess() return &result, nil } // Close database connection func (client *Client) Close() error { if client.closed { return nil } defer func() { client.closed = true client.tunnel = nil }() if client.tunnel != nil { client.tunnel.Close() } if client.db != nil { return client.db.Close() } return nil } func (c *Client) IsClosed() bool { return c.closed } func (c *Client) LastQueryTime() time.Time { return c.lastQueryTime } func (client *Client) IsIdle() bool { mins := int(time.Since(client.lastQueryTime).Minutes()) if command.Opts.ConnectionIdleTimeout > 0 { return mins >= command.Opts.ConnectionIdleTimeout } return false } // Fetch all rows as strings for a single column func (client *Client) fetchRows(q string) ([]string, error) { res, err := client.query(q) if err != nil { return nil, err } // Init empty slice so json.Marshal will encode it to "[]" instead of "null" results := make([]string, 0) for _, row := range res.Rows { results = append(results, row[0].(string)) } return results, nil } func (client *Client) hasHistoryRecord(query string) bool { result := false for _, record := range client.History { if record.Query == query { result = true break } } return result } type ConnContext struct { Host string User string Database string Mode string } func (c ConnContext) String() string { return fmt.Sprintf( "host=%q user=%q database=%q mode=%q", c.Host, c.User, c.Database, c.Mode, ) } // ConnContext returns information about current database connection func (client *Client) GetConnContext() (*ConnContext, error) { url, err := neturl.Parse(client.ConnectionString) if err != nil { return nil, err } ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) defer cancel() connCtx := ConnContext{ Host: url.Hostname(), Mode: "default", } if command.Opts.ReadOnly { connCtx.Mode = "readonly" } row := client.db.QueryRowContext(ctx, "SELECT current_user, current_database()") if err := row.Scan(&connCtx.User, &connCtx.Database); err != nil { return nil, err } return &connCtx, nil } ================================================ FILE: pkg/client/client_test.go ================================================ package client import ( "fmt" "log" "os" "os/exec" "runtime" "sort" "strings" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/sosedoff/pgweb/pkg/command" ) var ( testClient *Client testCommands map[string]string serverHost string serverPort string serverUser string serverPassword string serverDatabase string ) func mapKeys(data map[string]*Objects) []string { result := []string{} for k := range data { result = append(result, k) } return result } func objectNames(data []Object) []string { names := make([]string, len(data)) for i, obj := range data { names[i] = obj.Name } sort.Strings(names) return names } // assertMatches is a helper method to check if src slice contains any elements of expected slice func assertMatches(t *testing.T, expected, src []string) { assert.NotEqual(t, 0, len(expected)) assert.NotEqual(t, 0, len(src)) for _, val := range expected { assert.Contains(t, src, val) } } func pgVersion() (int, int) { var major, minor int if _, err := fmt.Sscanf(os.Getenv("PGVERSION"), "%d.%d", &major, &minor); err != nil { log.Println("[warn] unable to read value of PGVERSION env var") } return major, minor } func getVar(name, def string) string { val := os.Getenv(name) if val == "" { return def } return val } func initVars() { // We need to load default options to make sure all stuff works if err := command.SetDefaultOptions(); err != nil { log.Fatal(err) } serverHost = getVar("PGHOST", "localhost") serverPort = getVar("PGPORT", "5432") serverUser = getVar("PGUSER", "postgres") serverPassword = getVar("PGPASSWORD", "postgres") serverDatabase = getVar("PGDATABASE", "booktown") } func setupCommands() { testCommands = map[string]string{ "createdb": "createdb", "psql": "psql", "dropdb": "dropdb", } if onWindows() { for k, v := range testCommands { testCommands[k] = v + ".exe" } } } func onWindows() bool { return runtime.GOOS == "windows" } func setup() { // No pretty JSON for tests command.Opts.DisablePrettyJSON = true out, err := exec.Command( testCommands["createdb"], "-U", serverUser, "-h", serverHost, "-p", serverPort, serverDatabase, ).CombinedOutput() if err != nil { fmt.Println("Database creation failed:", string(out)) fmt.Println("Error:", err) os.Exit(1) } out, err = exec.Command( testCommands["psql"], "-U", serverUser, "-h", serverHost, "-p", serverPort, "-f", "../../data/booktown.sql", serverDatabase, ).CombinedOutput() if err != nil { fmt.Println("Database import failed:", string(out)) fmt.Println("Error:", err) os.Exit(1) } } func setupClient() { url := fmt.Sprintf("postgres://%s@%s:%s/%s?sslmode=disable", serverUser, serverHost, serverPort, serverDatabase) testClient, _ = NewFromUrl(url, nil) } func teardownClient() { if testClient != nil { testClient.Close() } } func teardown(t *testing.T, allowFail bool) { output, err := exec.Command( testCommands["dropdb"], "-U", serverUser, "-h", serverHost, "-p", serverPort, serverDatabase, ).CombinedOutput() if err != nil && strings.Contains(err.Error(), "does not exist") { t.Log("Teardown error:", err) t.Logf("%s\n", output) if !allowFail { assert.NoError(t, err) } } } func testNewClientFromURL(t *testing.T) { t.Run("postgres prefix", func(t *testing.T) { url := fmt.Sprintf("postgres://%s@%s:%s/%s?sslmode=disable", serverUser, serverHost, serverPort, serverDatabase) client, err := NewFromUrl(url, nil) assert.Equal(t, nil, err) assert.Equal(t, url, client.ConnectionString) assert.NoError(t, client.Close()) }) t.Run("postgresql prefix", func(t *testing.T) { url := fmt.Sprintf("postgresql://%s@%s:%s/%s?sslmode=disable", serverUser, serverHost, serverPort, serverDatabase) client, err := NewFromUrl(url, nil) assert.Equal(t, nil, err) assert.Equal(t, url, client.ConnectionString) assert.NoError(t, client.Close()) }) } func testClientIdleTime(t *testing.T) { examples := map[time.Time]bool{ time.Now(): false, // Current time time.Now().Add(time.Minute * -30): false, // 30 minutes ago time.Now().Add(time.Minute * -240): true, // 240 minutes ago time.Now().Add(time.Minute * 30): false, // 30 minutes in future time.Now().Add(time.Minute * 128): false, // 128 minutes in future } for ts, expected := range examples { testClient.lastQueryTime = ts assert.Equal(t, expected, testClient.IsIdle()) } } func testTest(t *testing.T) { examples := []struct { name string input string err error }{ { name: "success", input: fmt.Sprintf("postgres://%s@%s:%s/%s?sslmode=disable", serverUser, serverHost, serverPort, serverDatabase), err: nil, }, { name: "connection refused", input: "postgresql://localhost:5433/dbname", err: ErrConnectionRefused, }, { name: "invalid user", input: fmt.Sprintf("postgres://%s:%s@%s:%s/%s?sslmode=disable", "foo", serverPassword, serverHost, serverPort, serverDatabase), err: ErrAuthFailed, }, { name: "invalid password", input: fmt.Sprintf("postgres://%s:%s@%s:%s/%s?sslmode=disable", serverUser, "foo", serverHost, serverPort, serverDatabase), err: ErrAuthFailed, }, { name: "invalid database", input: fmt.Sprintf("postgres://%s@%s:%s/%s?sslmode=disable", serverUser, serverHost, serverPort, "foo"), err: ErrDatabaseNotExist, }, } for _, ex := range examples { t.Run(ex.name, func(t *testing.T) { conn, err := NewFromUrl(ex.input, nil) require.NoError(t, err) require.Equal(t, ex.err, conn.Test()) }) } } func testInfo(t *testing.T) { t.Run("normal", func(t *testing.T) { expected := []string{ "session_user", "current_user", "current_database", "current_schemas", "inet_client_addr", "inet_client_port", "inet_server_addr", "inet_server_port", "version", } res, err := testClient.Info() assert.NoError(t, err) assert.Equal(t, expected, res.Columns) }) t.Run("with restrictions", func(t *testing.T) { expected := []string{ "session_user", "current_user", "current_database", "current_schemas", "version", } // Prepare a new user and database testClient.db.MustExec("DROP DATABASE IF EXISTS testdb") testClient.db.Exec("DROP OWNED BY IF EXISTS testuser") //nolint:all testClient.db.MustExec("DROP ROLE IF EXISTS testuser") testClient.db.MustExec("CREATE ROLE testuser WITH PASSWORD 'secret' LOGIN NOSUPERUSER NOINHERIT") testClient.db.MustExec("CREATE DATABASE testdb OWNER testuser") // Disable access to inet_ calls for new user url := fmt.Sprintf("postgres://%s:@%s:%s/testdb?sslmode=disable", serverUser, serverHost, serverPort) client, err := NewFromUrl(url, nil) assert.NoError(t, err) client.db.MustExec("REVOKE EXECUTE ON FUNCTION inet_client_addr() FROM PUBLIC") assert.NoError(t, client.Close()) // Connect using new user url = fmt.Sprintf("postgres://testuser:secret@%s:%s/testdb?sslmode=disable", serverHost, serverPort) client, err = NewFromUrl(url, nil) assert.NoError(t, err) defer client.Close() res, err := client.Info() assert.NoError(t, err) assert.Equal(t, expected, res.Columns) }) } func testActivity(t *testing.T) { expected := []string{"datid", "pid", "query", "query_start", "state", "client_addr"} res, err := testClient.Activity() assert.NoError(t, err) assertMatches(t, expected, res.Columns) } func testDatabases(t *testing.T) { res, err := testClient.Databases() assert.NoError(t, err) assertMatches(t, []string{"booktown", "postgres"}, res) } func testSchemas(t *testing.T) { res, err := testClient.Schemas() assert.NoError(t, err) assert.Equal(t, []string{"public"}, res) } func testObjects(t *testing.T) { res, err := testClient.Objects() objects := ObjectsFromResult(res) tables := []string{ "alternate_stock", "authors", "book_backup", "book_queue", "books", "customers", "daily_inventory", "distinguished_authors", "dummies", "editions", "employees", "favorite_authors", "favorite_books", "money_example", "my_list", "numeric_values", "publishers", "schedules", "shipments", "states", "stock", "stock_backup", "subjects", "text_sorting", } functions := []string{ "add_shipment", "add_two_loop", "books_by_subject", "compound_word", "count_by_two", "double_price", "extract_all_titles", "extract_all_titles2", "extract_title", "first", "get_author", "get_author", "get_customer_id", "get_customer_name", "html_linebreaks", "in_stock", "isbn_to_title", "mixed", "raise_test", "ship_item", "stock_amount", "test", "title", "triple_price", } assert.NoError(t, err) assert.Equal(t, []string{"oid", "schema", "name", "type", "owner", "comment"}, res.Columns) assert.Equal(t, []string{"public"}, mapKeys(objects)) assert.Equal(t, tables, objectNames(objects["public"].Tables)) assertMatches(t, functions, objectNames(objects["public"].Functions)) assert.Equal(t, []string{"recent_shipments", "stock_view"}, objectNames(objects["public"].Views)) assert.Equal(t, []string{"author_ids", "book_ids", "shipments_ship_id_seq", "subject_ids"}, objectNames(objects["public"].Sequences)) major, minor := pgVersion() if minor == 0 || minor >= 3 { assert.Equal(t, []string{"m_stock_view"}, objectNames(objects["public"].MaterializedViews)) } else { t.Logf("Skipping materialized view on %d.%d\n", major, minor) } } func testTable(t *testing.T) { columns := []string{ "column_name", "data_type", "is_nullable", "character_maximum_length", "character_set_catalog", "column_default", "comment", } res, err := testClient.Table("books") assert.NoError(t, err) assert.Equal(t, columns, res.Columns) assert.Equal(t, 4, len(res.Rows)) } func testTableRows(t *testing.T) { res, err := testClient.TableRows("books", RowsOptions{}) assert.NoError(t, err) assert.Equal(t, 4, len(res.Columns)) assert.Equal(t, 15, len(res.Rows)) } func testTableInfo(t *testing.T) { res, err := testClient.TableInfo("books") assert.NoError(t, err) assert.Equal(t, 4, len(res.Columns)) assert.Equal(t, 1, len(res.Rows)) } func testEstimatedTableRowsCount(t *testing.T) { res, err := testClient.EstimatedTableRowsCount("books", RowsOptions{}) assert.NoError(t, err) assert.Equal(t, []string{"reltuples"}, res.Columns) assert.Equal(t, []Row{{int64(15)}}, res.Rows) } func testTableRowsCount(t *testing.T) { res, err := testClient.TableRowsCount("books", RowsOptions{}) assert.NoError(t, err) assert.Equal(t, []string{"count"}, res.Columns) assert.Equal(t, []Row{{int64(15)}}, res.Rows) } func testTableRowsCountWithLargeTable(t *testing.T) { testClient.db.MustExec(`CREATE TABLE large_table AS SELECT s FROM generate_series(1,1000000) s;`) testClient.db.MustExec(`VACUUM large_table;`) res, err := testClient.TableRowsCount("large_table", RowsOptions{}) assert.Equal(t, nil, err) assert.Equal(t, []string{"reltuples"}, res.Columns) assert.Equal(t, []Row{{int64(1000000)}}, res.Rows) } func testTableIndexes(t *testing.T) { res, err := testClient.TableIndexes("books") assert.NoError(t, err) assert.Equal(t, []string{"index_name", "index_size", "index_definition"}, res.Columns) assert.Equal(t, 2, len(res.Rows)) } func testTableConstraints(t *testing.T) { res, err := testClient.TableConstraints("editions") assert.NoError(t, err) assert.Equal(t, []string{"name", "definition"}, res.Columns) assert.Equal(t, Row{"pkey", "PRIMARY KEY (isbn)"}, res.Rows[0]) assert.Equal(t, Row{"integrity", "CHECK (book_id IS NOT NULL AND edition IS NOT NULL)"}, res.Rows[1]) } func testTableNameWithCamelCase(t *testing.T) { testClient.db.MustExec(`CREATE TABLE "exampleTable" (id int, name varchar);`) testClient.db.MustExec(`INSERT INTO "exampleTable" (id, name) VALUES (1, 'foo'), (2, 'bar');`) _, err := testClient.Table("exampleTable") assert.NoError(t, err) _, err = testClient.TableInfo("exampleTable") assert.NoError(t, err) _, err = testClient.TableConstraints("exampleTable") assert.NoError(t, err) _, err = testClient.TableIndexes("exampleTable") assert.NoError(t, err) _, err = testClient.TableRowsCount("exampleTable", RowsOptions{}) assert.NoError(t, err) _, err = testClient.EstimatedTableRowsCount("exampleTable", RowsOptions{}) assert.NoError(t, err) } func testQuery(t *testing.T) { t.Run("basic query", func(t *testing.T) { res, err := testClient.Query("SELECT * FROM books") assert.NoError(t, err) assert.Equal(t, 4, len(res.Columns)) assert.Equal(t, 15, len(res.Rows)) }) t.Run("error", func(t *testing.T) { res, err := testClient.Query("SELCT * FROM books") assert.NotNil(t, err) assert.Equal(t, "pq: syntax error at or near \"SELCT\"", err.Error()) assert.Nil(t, res) }) t.Run("invalid table", func(t *testing.T) { res, err := testClient.Query("SELECT * FROM books2") assert.NotNil(t, err) assert.Equal(t, "pq: relation \"books2\" does not exist", err.Error()) assert.Nil(t, res) }) t.Run("timeout", func(t *testing.T) { testClient.queryTimeout = time.Millisecond * 100 defer func() { testClient.queryTimeout = 0 }() res, err := testClient.query("SELECT pg_sleep(1);") assert.Equal(t, "pq: canceling statement due to user request", err.Error()) assert.Nil(t, res) }) } func testUpdateQuery(t *testing.T) { t.Run("updating data", func(t *testing.T) { // Add new row testClient.db.MustExec("INSERT INTO books (id, title) VALUES (8888, 'Test Book'), (8889, 'Test Book 2')") // Update without return values res, err := testClient.Query("UPDATE books SET title = 'Foo' WHERE id >= 8888 AND id <= 8889") assert.NoError(t, err) assert.Equal(t, "Rows Affected", res.Columns[0]) assert.Equal(t, int64(2), res.Rows[0][0]) // Update with return values res, err = testClient.Query("UPDATE books SET title = 'Foo2' WHERE id >= 8888 AND id <= 8889 RETURNING id, title") assert.NoError(t, err) assert.Equal(t, []string{"id", "title"}, res.Columns) assert.Equal(t, Row{int64(8888), "Foo2"}, res.Rows[0]) assert.Equal(t, Row{int64(8889), "Foo2"}, res.Rows[1]) }) t.Run("deleting data", func(t *testing.T) { // Add new row testClient.db.MustExec("INSERT INTO books (id, title) VALUES (9999, 'Test Book')") // Delete the existing row res, err := testClient.Query("DELETE FROM books WHERE id = 9999") assert.NoError(t, err) assert.Equal(t, "Rows Affected", res.Columns[0]) assert.Equal(t, int64(1), res.Rows[0][0]) // Deleting already deleted row res, err = testClient.Query("DELETE FROM books WHERE id = 9999") assert.NoError(t, err) assert.Equal(t, int64(0), res.Rows[0][0]) // Delete with returning value testClient.db.MustExec("INSERT INTO books (id, title) VALUES (9999, 'Test Book')") res, err = testClient.Query("DELETE FROM books WHERE id = 9999 RETURNING id") assert.NoError(t, err) assert.Equal(t, int64(9999), res.Rows[0][0]) }) } func testTableRowsOrderEscape(t *testing.T) { rows, err := testClient.TableRows("dummies", RowsOptions{SortColumn: "isDummy"}) assert.NoError(t, err) assert.Equal(t, 2, len(rows.Rows)) rows, err = testClient.TableRows("dummies", RowsOptions{SortColumn: "isdummy"}) assert.NotNil(t, err) assert.Equal(t, `pq: column "isdummy" does not exist`, err.Error()) assert.Nil(t, rows) } func testFunctions(t *testing.T) { funcName := "get_customer_name" funcID := "" res, err := testClient.Objects() assert.NoError(t, err) for _, row := range res.Rows { if row[2] == funcName { funcID = row[0].(string) break } } res, err = testClient.Function("12345") assert.NoError(t, err) assertMatches(t, []string{"oid", "proname", "functiondef"}, res.Columns) assert.Equal(t, 0, len(res.Rows)) res, err = testClient.Function(funcID) assert.NoError(t, err) assertMatches(t, []string{"oid", "proname", "functiondef"}, res.Columns) assert.Equal(t, 1, len(res.Rows)) assert.Equal(t, funcName, res.Rows[0][1]) assert.Contains(t, res.Rows[0][len(res.Columns)-1], "SELECT INTO customer_fname, customer_lname") } func testResult(t *testing.T) { t.Run("json", func(t *testing.T) { result, err := testClient.Query("SELECT * FROM books LIMIT 1") assert.NoError(t, err) assert.Equal(t, `[{"author_id":4156,"id":7808,"subject_id":9,"title":"The Shining"}]`, string(result.JSON())) result, err = testClient.Query("SELECT 'NaN'::float AS value;") assert.NoError(t, err) assert.Equal(t, `[{"value":null}]`, string(result.JSON())) }) t.Run("csv", func(t *testing.T) { expected := "id,title,author_id,subject_id\n156,The Tell-Tale Heart,115,9\n" res, err := testClient.Query("SELECT * FROM books ORDER BY id ASC LIMIT 1") assert.NoError(t, err) assert.Equal(t, expected, string(res.CSV())) }) } func testHistory(t *testing.T) { t.Run("success", func(t *testing.T) { _, err := testClient.Query("SELECT * FROM books WHERE id = 12345") query := testClient.History[len(testClient.History)-1].Query assert.NoError(t, err) assert.Equal(t, "SELECT * FROM books WHERE id = 12345", query) }) t.Run("failed query", func(t *testing.T) { _, err := testClient.Query("SELECT * FROM books123") query := testClient.History[len(testClient.History)-1].Query assert.NotNil(t, err) assert.NotEqual(t, "SELECT * FROM books123", query) }) t.Run("unique queries", func(t *testing.T) { url := fmt.Sprintf("postgres://%s@%s:%s/%s?sslmode=disable", serverUser, serverHost, serverPort, serverDatabase) client, _ := NewFromUrl(url, nil) defer client.Close() for i := 0; i < 3; i++ { _, err := client.Query("SELECT * FROM books WHERE id = 1") assert.NoError(t, err) } assert.Equal(t, 1, len(client.History)) assert.Equal(t, "SELECT * FROM books WHERE id = 1", client.History[0].Query) }) } func testReadOnlyMode(t *testing.T) { command.Opts.ReadOnly = true defer func() { command.Opts.ReadOnly = false }() url := fmt.Sprintf("postgres://%s@%s:%s/%s?sslmode=disable", serverUser, serverHost, serverPort, serverDatabase) client, _ := NewFromUrl(url, nil) defer client.Close() err := client.SetReadOnlyMode() assert.NoError(t, err) _, err = client.Query("\nCREATE TABLE foobar(id integer);\n") assert.NotNil(t, err) assert.Error(t, err, "query contains keywords not allowed in read-only mode") // Turn off guard _, err = client.db.Exec("SET default_transaction_read_only=off;") assert.NoError(t, err) _, err = client.Query("\nCREATE TABLE foobar(id integer);\n") assert.NotNil(t, err) assert.Contains(t, err.Error(), "query contains keywords not allowed in read-only mode") _, err = client.Query("-- CREATE TABLE foobar(id integer);\nSELECT 'foo';") assert.NoError(t, err) _, err = client.Query("/* CREATE TABLE foobar(id integer); */ SELECT 'foo';") assert.NoError(t, err) t.Run("with local readonly flag", func(t *testing.T) { command.Opts.ReadOnly = false client.readonly = true _, err := client.Query("INSERT INTO foobar(id) VALUES(1)") assert.Error(t, err, "query contains keywords not allowed in read-only mode") }) } func testTablesStats(t *testing.T) { columns := []string{ "schema_name", "table_name", "total_size", "data_size", "index_size", "estimated_rows_count", "estimated_rows", "index_to_data_ratio", "indexes_count", "columns_count", } result, err := testClient.TablesStats() assert.NoError(t, err) assert.Equal(t, columns, result.Columns) } func testConnContext(t *testing.T) { result, err := testClient.GetConnContext() assert.NoError(t, err) assert.Equal(t, "localhost", result.Host) assert.Equal(t, "postgres", result.User) assert.Equal(t, "booktown", result.Database) assert.Equal(t, "default", result.Mode) } func testServerSettings(t *testing.T) { expectedColumns := []string{ "name", "setting", "unit", "category", "short_desc", "extra_desc", "context", "vartype", "source", "min_val", "max_val", "enumvals", "boot_val", "reset_val", "sourcefile", "sourceline", "pending_restart", } result, err := testClient.ServerSettings() assert.NoError(t, err) assert.Equal(t, expectedColumns, result.Columns) } func TestAll(t *testing.T) { if onWindows() { t.Log("Unit testing on Windows platform is not supported.") return } initVars() setupCommands() teardown(t, false) setup() setupClient() testNewClientFromURL(t) testClientIdleTime(t) testTest(t) testInfo(t) testActivity(t) testDatabases(t) testSchemas(t) testObjects(t) testTable(t) testTableRows(t) testTableInfo(t) testEstimatedTableRowsCount(t) testTableRowsCount(t) testTableRowsCountWithLargeTable(t) testTableIndexes(t) testTableConstraints(t) testTableNameWithCamelCase(t) testQuery(t) testUpdateQuery(t) testTableRowsOrderEscape(t) testFunctions(t) testResult(t) testHistory(t) testReadOnlyMode(t) testDumpExport(t) testTablesStats(t) testConnContext(t) testServerSettings(t) teardownClient() teardown(t, true) } ================================================ FILE: pkg/client/codec.go ================================================ package client import ( "encoding/base64" "encoding/hex" "fmt" "github.com/mr-tron/base58" ) const ( CodecNone = "none" CodecHex = "hex" CodecBase58 = "base58" CodecBase64 = "base64" ) var ( // BinaryCodec sets the serialization format of binary data BinaryCodec = CodecBase64 ) func SetBinaryCodec(codec string) error { switch codec { case CodecNone, CodecHex, CodecBase58, CodecBase64: BinaryCodec = codec default: return fmt.Errorf("invalid binary codec: %v", codec) } return nil } func encodeBinaryData(data []byte, codec string) string { switch codec { case CodecHex: return hex.EncodeToString(data) case CodecBase58: return base58.Encode(data) case CodecBase64: return base64.StdEncoding.EncodeToString(data) default: return string(data) } } ================================================ FILE: pkg/client/codec_test.go ================================================ package client import ( "errors" "testing" "github.com/stretchr/testify/assert" ) func TestSetBinaryCodec(t *testing.T) { examples := []struct { input string err error }{ {input: CodecNone, err: nil}, {input: CodecBase58, err: nil}, {input: CodecBase64, err: nil}, {input: CodecHex, err: nil}, {input: "foobar", err: errors.New("invalid binary codec: foobar")}, } for _, ex := range examples { t.Run(ex.input, func(t *testing.T) { val := BinaryCodec defer func() { BinaryCodec = val }() assert.Equal(t, ex.err, SetBinaryCodec(ex.input)) }) } } func Test_encodeBinaryData(t *testing.T) { examples := []struct { input string expected string encoding string }{ {input: "hello world", expected: "hello world", encoding: CodecNone}, {input: "hello world", expected: "StV1DL6CwTryKyV", encoding: CodecBase58}, {input: "hello world", expected: "aGVsbG8gd29ybGQ=", encoding: CodecBase64}, {input: "hello world", expected: "68656c6c6f20776f726c64", encoding: CodecHex}, } for _, ex := range examples { t.Run(ex.input, func(t *testing.T) { assert.Equal(t, ex.expected, encodeBinaryData([]byte(ex.input), ex.encoding)) }) } } ================================================ FILE: pkg/client/dump.go ================================================ package client import ( "bytes" "context" "fmt" "io" "net/url" "os/exec" "strings" ) var ( unsupportedDumpOptions = []string{ "search_path", } ) // Dump represents a database dump type Dump struct { Table string } // Validate checks availability and version of pg_dump CLI func (d *Dump) Validate(serverVersion string) error { out := bytes.NewBuffer(nil) cmd := exec.Command("pg_dump", "--version") cmd.Stdout = out cmd.Stderr = out if err := cmd.Run(); err != nil { return fmt.Errorf("pg_dump command failed: %s", out.Bytes()) } detected, dumpVersion := detectDumpVersion(out.String()) if detected && serverVersion != "" { satisfied := checkVersionRequirement(dumpVersion, serverVersion) if !satisfied { return fmt.Errorf("pg_dump version %v not compatible with server version %v", dumpVersion, serverVersion) } } return nil } // Export streams the database dump to the specified writer func (d *Dump) Export(ctx context.Context, connstr string, writer io.Writer) error { if str, err := removeUnsupportedOptions(connstr); err != nil { return err } else { connstr = str } opts := []string{ "--no-owner", // skip restoration of object ownership in plain-text format "--clean", // clean (drop) database objects before recreating "--compress", "6", // compression level for compressed formats } if d.Table != "" { opts = append(opts, []string{"--table", d.Table}...) } opts = append(opts, connstr) errOutput := bytes.NewBuffer(nil) cmd := exec.CommandContext(ctx, "pg_dump", opts...) cmd.Stdout = writer cmd.Stderr = errOutput if err := cmd.Run(); err != nil { return fmt.Errorf("error: %s. output: %s", err.Error(), errOutput.Bytes()) } return nil } // removeUnsupportedOptions removes any options unsupported for making a db dump func removeUnsupportedOptions(input string) (string, error) { uri, err := url.Parse(input) if err != nil { return "", err } q := uri.Query() for _, opt := range unsupportedDumpOptions { q.Del(opt) q.Del(strings.ToUpper(opt)) } uri.RawQuery = q.Encode() return uri.String(), nil } ================================================ FILE: pkg/client/dump_test.go ================================================ package client import ( "context" "fmt" "os" "testing" "github.com/stretchr/testify/assert" ) func testDumpExport(t *testing.T) { url := fmt.Sprintf("postgres://%s@%s:%s/%s?sslmode=disable", serverUser, serverHost, serverPort, serverDatabase) savePath := "/tmp/dump.sql.gz" os.Remove(savePath) saveFile, err := os.Create(savePath) if err != nil { t.Fatal(err.Error()) } defer func() { saveFile.Close() os.Remove(savePath) }() dump := Dump{} // Test for pg_dump presence assert.NoError(t, dump.Validate("10.0")) assert.NoError(t, dump.Validate("")) assert.Contains(t, dump.Validate("20").Error(), "not compatible with server version 20") // Test full db dump err = dump.Export(context.Background(), url, saveFile) assert.NoError(t, err) // Test nonexistent database invalidURL := fmt.Sprintf("postgres://%s@%s:%s/%s?sslmode=disable", serverUser, serverHost, serverPort, "foobar") err = dump.Export(context.Background(), invalidURL, saveFile) assert.Contains(t, err.Error(), `database "foobar" does not exist`) // Test dump of non existent db dump = Dump{Table: "foobar"} err = dump.Export(context.Background(), url, saveFile) assert.NotNil(t, err) assert.Contains(t, err.Error(), "no matching tables were found") // Should drop "search_path" param from URI dump = Dump{} searchPathURL := fmt.Sprintf("postgres://%s@%s:%s/%s?sslmode=disable&search_path=private", serverUser, serverHost, serverPort, serverDatabase) err = dump.Export(context.Background(), searchPathURL, saveFile) assert.NoError(t, err) } ================================================ FILE: pkg/client/result.go ================================================ package client import ( "bytes" "encoding/csv" "encoding/json" "fmt" "log" "math" "strconv" "time" "github.com/sosedoff/pgweb/pkg/command" ) const ( ObjTypeTable = "table" ObjTypeView = "view" ObjTypeMaterializedView = "materialized_view" ObjTypeSequence = "sequence" ObjTypeFunction = "function" ) type ( // Row represents a single row of data Row []interface{} // RowsOptions contains a list of parameters for table browsing requests RowsOptions struct { Where string // Custom filter Offset int // Number of rows to skip Limit int // Number of rows to fetch SortColumn string // Column to sort by SortOrder string // Sort direction (ASC, DESC) } Pagination struct { Rows int64 `json:"rows_count"` Page int64 `json:"page"` Pages int64 `json:"pages_count"` PerPage int64 `json:"per_page"` } Result struct { Pagination *Pagination `json:"pagination,omitempty"` Columns []string `json:"columns"` Rows []Row `json:"rows"` Stats *ResultStats `json:"stats,omitempty"` } ResultStats struct { ColumnsCount int `json:"columns_count"` RowsCount int `json:"rows_count"` RowsAffected int64 `json:"rows_affected"` QueryStartTime time.Time `json:"query_start_time"` QueryFinishTime time.Time `json:"query_finish_time"` QueryDuration int64 `json:"query_duration_ms"` } Object struct { OID string `json:"oid"` Name string `json:"name"` } Objects struct { Tables []Object `json:"table"` Views []Object `json:"view"` MaterializedViews []Object `json:"materialized_view"` Functions []Object `json:"function"` Sequences []Object `json:"sequence"` } ) // Due to big int number limitations in javascript, numbers should be encoded // as strings so they could be properly loaded on the frontend. func (res *Result) PostProcess() { for i, row := range res.Rows { for j, col := range row { if col == nil { continue } switch val := col.(type) { case int64: if val < -9007199254740991 || val > 9007199254740991 { res.Rows[i][j] = strconv.FormatInt(col.(int64), 10) } case float64: // json.Marshal panics when dealing with NaN/Inf values // issue: https://github.com/golang/go/issues/25721 if math.IsNaN(val) { res.Rows[i][j] = nil break } if val < -999999999999999 || val > 999999999999999 { res.Rows[i][j] = strconv.FormatFloat(val, 'e', -1, 64) } case string: if hasBinary(val, 8) && BinaryCodec != CodecNone { res.Rows[i][j] = encodeBinaryData([]byte(val), BinaryCodec) } case time.Time: // RFC 3339 is clear that years are 4 digits exactly. // See golang.org/issue/4556#c15 for more discussion. if val.Year() < 0 || val.Year() >= 10000 { res.Rows[i][j] = "ERR: INVALID_DATE" } else { res.Rows[i][j] = val } } } } } func (res *Result) Format() []map[string]interface{} { items := make([]map[string]interface{}, len(res.Rows)) for rowIdx, row := range res.Rows { item := make(map[string]interface{}) for i, c := range res.Columns { item[c] = row[i] } items[rowIdx] = item } return items } func (res *Result) CSV() []byte { buff := &bytes.Buffer{} writer := csv.NewWriter(buff) if err := writer.Write(res.Columns); err != nil { log.Printf("result csv write error: %v\n", err) } for _, row := range res.Rows { record := make([]string, len(res.Columns)) for i, item := range row { switch v := item.(type) { case time.Time: record[i] = v.Format("2006-01-02 15:04:05") case nil: record[i] = "" default: record[i] = fmt.Sprintf("%v", item) } } err := writer.Write(record) if err != nil { fmt.Println(err) break } } writer.Flush() return buff.Bytes() } func (res *Result) JSON() []byte { var data []byte if command.Opts.DisablePrettyJSON { data, _ = json.Marshal(res.Format()) } else { data, _ = json.MarshalIndent(res.Format(), "", " ") } return data } func ObjectsFromResult(res *Result) map[string]*Objects { objects := map[string]*Objects{} for _, row := range res.Rows { oid := row[0].(string) schema := row[1].(string) name := row[2].(string) objectType := row[3].(string) if objects[schema] == nil { objects[schema] = &Objects{ Tables: []Object{}, Views: []Object{}, MaterializedViews: []Object{}, Functions: []Object{}, Sequences: []Object{}, } } obj := Object{OID: oid, Name: name} switch objectType { case ObjTypeTable: objects[schema].Tables = append(objects[schema].Tables, obj) case ObjTypeView: objects[schema].Views = append(objects[schema].Views, obj) case ObjTypeMaterializedView: objects[schema].MaterializedViews = append(objects[schema].MaterializedViews, obj) case ObjTypeFunction: objects[schema].Functions = append(objects[schema].Functions, obj) case ObjTypeSequence: objects[schema].Sequences = append(objects[schema].Sequences, obj) } } return objects } ================================================ FILE: pkg/client/result_test.go ================================================ package client import ( "encoding/json" "strings" "testing" "time" "github.com/sosedoff/pgweb/pkg/command" "github.com/stretchr/testify/assert" ) func TestPostProcess(t *testing.T) { t.Run("large numbers", func(t *testing.T) { result := Result{ Columns: []string{"value"}, Rows: []Row{ {int(1234)}, {int64(9223372036854775807)}, {int64(-9223372036854775808)}, {float64(9223372036854775808.9223372036854775808)}, {float64(999999999999999.9)}, }, } result.PostProcess() assert.Equal(t, 1234, result.Rows[0][0]) assert.Equal(t, "9223372036854775807", result.Rows[1][0]) assert.Equal(t, "-9223372036854775808", result.Rows[2][0]) assert.Equal(t, "9.223372036854776e+18", result.Rows[3][0]) assert.Equal(t, "9.999999999999999e+14", result.Rows[4][0]) }) t.Run("binary encoding", func(t *testing.T) { result := Result{ Columns: []string{"data"}, Rows: []Row{ {"text value"}, {"text with symbols !@#$%"}, {string([]byte{10, 11, 12, 13})}, }, } result.PostProcess() assert.Equal(t, "text value", result.Rows[0][0]) assert.Equal(t, "text with symbols !@#$%", result.Rows[1][0]) assert.Equal(t, "CgsMDQ==", result.Rows[2][0]) }) } func TestCSV(t *testing.T) { result := Result{ Columns: []string{"id", "name", "email", "extra"}, Rows: []Row{ {1, "John", "john@example.com", "data"}, {2, "Bob", "bob@example.com", nil}, }, } expected := strings.Join([]string{ "id,name,email,extra", "1,John,john@example.com,data", "2,Bob,bob@example.com,", }, "\n") + "\n" assert.Equal(t, expected, string(result.CSV())) } func TestJSON(t *testing.T) { result := Result{ Columns: []string{"id", "name", "email"}, Rows: []Row{ {1, "John", "john@example.com"}, {2, "Bob", "bob@example.com"}, }, } obj := []struct { Id int Name string Email string }{} expected := []struct { Id int Name string Email string }{ {1, "John", "john@example.com"}, {2, "Bob", "bob@example.com"}, } assert.NoError(t, json.Unmarshal(result.JSON(), &obj)) assert.Equal(t, 2, len(obj)) assert.Equal(t, expected, obj) t.Run("invalid time", func(t *testing.T) { loc, err := time.LoadLocation("UTC") if err != nil { panic(err) } command.Opts.DisablePrettyJSON = true defer func() { command.Opts.DisablePrettyJSON = false }() result := Result{ Columns: []string{"value"}, Rows: []Row{ {time.Unix(1640995200, 0).In(loc)}, {time.Unix(222539616000, 0).In(loc)}, {time.Unix(254096611200, 0).In(loc)}, }, } result.PostProcess() assert.Equal(t, `[{"value":"2022-01-01T00:00:00Z"},{"value":"9022-01-01T00:00:00Z"},{"value":"ERR: INVALID_DATE"}]`, string(result.JSON())) }) } func TestResultFormat(t *testing.T) { result := Result{ Columns: []string{"col1", "col2", "col3", "col4"}, Rows: []Row{ {"1", "2", "3", nil}, {"4", "5", "6", nil}, }, } expected := []map[string]interface{}{ {"col1": "1", "col2": "2", "col3": "3", "col4": nil}, {"col1": "4", "col2": "5", "col3": "6", "col4": nil}, } assert.Equal(t, expected, result.Format()) } ================================================ FILE: pkg/client/tunnel.go ================================================ package client import ( "errors" "fmt" "io" "log" "net" "net/url" "os" "path/filepath" "strings" "sync" "time" "github.com/ScaleFT/sshkeys" "golang.org/x/crypto/ssh" "github.com/sosedoff/pgweb/pkg/connection" "github.com/sosedoff/pgweb/pkg/shared" ) const ( portStart = 29168 portLimit = 500 ) // Tunnel represents the connection between local and remote server type Tunnel struct { TargetHost string TargetPort string Port int SSHInfo *shared.SSHInfo Config *ssh.ClientConfig Client *ssh.Client Listener *net.TCPListener } func defaultKeyPath() string { return filepath.Join(os.Getenv("HOME"), ".ssh/id_rsa") } func expandKeyPath(path string) string { home := os.Getenv("HOME") if home == "" { return path } return strings.Replace(path, "~", home, 1) } func fileExists(path string) bool { _, err := os.Stat(path) return err == nil } func parsePrivateKey(keyPath string, keyPass string) (ssh.Signer, error) { buff, err := os.ReadFile(keyPath) if err != nil { return nil, err } signer, err := ssh.ParsePrivateKey(buff) if _, ok := err.(*ssh.PassphraseMissingError); ok { if keyPass == "" { return nil, errors.New("ssh key password is not provided") } return sshkeys.ParseEncryptedPrivateKey(buff, []byte(keyPass)) } return signer, err } func makeConfig(info *shared.SSHInfo) (*ssh.ClientConfig, error) { methods := []ssh.AuthMethod{} // Try to use user-provided key, fallback to system default key keyPath := info.Key if keyPath == "" { keyPath = defaultKeyPath() } else { keyPath = expandKeyPath(keyPath) } if !fileExists(keyPath) { return nil, fmt.Errorf("ssh public key not found at path %q", keyPath) } // Append public key authentication method key, err := parsePrivateKey(keyPath, info.KeyPassword) if err != nil { return nil, err } methods = append(methods, ssh.PublicKeys(key)) // Append password authentication method if info.Password != "" { methods = append(methods, ssh.Password(info.Password)) } cfg := &ssh.ClientConfig{ User: info.User, Auth: methods, Timeout: time.Second * 10, HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error { return nil }, } return cfg, nil } func (tunnel *Tunnel) sshEndpoint() string { return fmt.Sprintf("%s:%v", tunnel.SSHInfo.Host, tunnel.SSHInfo.Port) } func (tunnel *Tunnel) targetEndpoint() string { return fmt.Sprintf("%v:%v", tunnel.TargetHost, tunnel.TargetPort) } func (tunnel *Tunnel) copy(wg *sync.WaitGroup, writer, reader net.Conn) { defer wg.Done() if _, err := io.Copy(writer, reader); err != nil { log.Println("Tunnel copy error:", err) } } func (tunnel *Tunnel) handleConnection(local net.Conn) { remote, err := tunnel.Client.Dial("tcp", tunnel.targetEndpoint()) if err != nil { return } wg := &sync.WaitGroup{} wg.Add(2) go tunnel.copy(wg, local, remote) go tunnel.copy(wg, remote, local) wg.Wait() local.Close() } // Close closes the tunnel connection func (tunnel *Tunnel) Close() { if tunnel.Client != nil { tunnel.Client.Close() } if tunnel.Listener != nil { tunnel.Listener.Close() } } // Configure establishes the tunnel between localhost and remote machine func (tunnel *Tunnel) Configure() error { config, err := makeConfig(tunnel.SSHInfo) if err != nil { return err } tunnel.Config = config client, err := ssh.Dial("tcp", tunnel.sshEndpoint(), config) if err != nil { return err } tunnel.Client = client listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%v", tunnel.Port)) if err != nil { return err } tunnel.Listener = listener.(*net.TCPListener) return nil } // Start starts the connection handler loop func (tunnel *Tunnel) Start() { defer tunnel.Close() for { conn, err := tunnel.Listener.Accept() if err != nil { return } go tunnel.handleConnection(conn) } } // NewTunnel instantiates a new tunnel struct from given ssh info func NewTunnel(sshInfo *shared.SSHInfo, dbUrl string) (*Tunnel, error) { uri, err := url.Parse(dbUrl) if err != nil { return nil, err } listenPort, err := connection.FindAvailablePort(portStart, portLimit) if err != nil { return nil, err } chunks := strings.Split(uri.Host, ":") host := chunks[0] port := "5432" if len(chunks) == 2 { port = chunks[1] } tunnel := &Tunnel{ Port: listenPort, SSHInfo: sshInfo, TargetHost: host, TargetPort: port, } return tunnel, nil } ================================================ FILE: pkg/client/util.go ================================================ package client import ( "fmt" "regexp" "strings" ) var ( // List of keywords that are not allowed in read-only mode reRestrictedKeywords = regexp.MustCompile(`(?mi)\s?(CREATE|INSERT|UPDATE|DROP|DELETE|TRUNCATE|GRANT|OPEN|IMPORT|COPY)\s`) // Comment regular expressions reSlashComment = regexp.MustCompile(`(?m)/\*.+\*/`) reDashComment = regexp.MustCompile(`(?m)--.+`) // Postgres version signature postgresSignature = regexp.MustCompile(`(?i)postgresql ([\d\.]+)\s?`) postgresDumpSignature = regexp.MustCompile(`\s([\d\.]+)\s?`) postgresType = "PostgreSQL" // Cockroach version signature cockroachSignature = regexp.MustCompile(`(?i)cockroachdb ccl v([\d\.]+)\s?`) cockroachType = "CockroachDB" ) // Get major and minor version components // Example: 10.2.3.1 -> 10.2 func getMajorMinorVersion(str string) (major int, minor int) { chunks := strings.Split(str, ".") fmt.Sscanf(chunks[0], "%d", &major) //nolint if len(chunks) > 1 { fmt.Sscanf(chunks[1], "%d", &minor) //nolint } return } // Get short version from the string // Example: 10.2.3.1 -> 10.2 func getMajorMinorVersionString(str string) string { major, minor := getMajorMinorVersion(str) return fmt.Sprintf("%d.%d", major, minor) } func detectServerTypeAndVersion(version string) (bool, string, string) { version = strings.TrimSpace(version) // Detect postgresql matches := postgresSignature.FindAllStringSubmatch(version, 1) if len(matches) > 0 { return true, postgresType, matches[0][1] } // Detect cockroachdb matches = cockroachSignature.FindAllStringSubmatch(version, 1) if len(matches) > 0 { return true, cockroachType, matches[0][1] } return false, "", "" } // detectDumpVersion parses out version from `pg_dump -V` command. func detectDumpVersion(version string) (bool, string) { matches := postgresDumpSignature.FindAllStringSubmatch(version, 1) if len(matches) > 0 { return true, matches[0][1] } return false, "" } func checkVersionRequirement(client, server string) bool { clientMajor, clientMinor := getMajorMinorVersion(client) serverMajor, serverMinor := getMajorMinorVersion(server) if serverMajor < 10 { return clientMajor >= serverMajor && clientMinor >= serverMinor } return clientMajor >= serverMajor } // containsRestrictedKeywords returns true if given keyword is not allowed in read-only mode func containsRestrictedKeywords(str string) bool { str = reSlashComment.ReplaceAllString(str, "") str = reDashComment.ReplaceAllString(str, "") return reRestrictedKeywords.MatchString(str) } func hasBinary(data string, checkLen int) bool { for idx, chr := range data { if int(chr) < 32 || int(chr) > 126 { return true } if idx >= checkLen { break } } return false } ================================================ FILE: pkg/client/util_test.go ================================================ package client import ( "testing" "github.com/stretchr/testify/assert" ) func TestDetectServerType(t *testing.T) { examples := []struct { input string match bool serverType string version string }{ {input: "", match: false, serverType: "", version: "", }, { input: " postgresql 15 ", match: true, serverType: postgresType, version: "15", }, { input: "PostgreSQL 14.5 (Homebrew) on aarch64-apple-darwin21.6.0", match: true, serverType: postgresType, version: "14.5", }, { input: "PostgreSQL 11.16, compiled by Visual C++ build 1800, 64-bit", match: true, serverType: postgresType, version: "11.16", }, } for _, ex := range examples { t.Run("input:"+ex.input, func(t *testing.T) { match, stype, version := detectServerTypeAndVersion(ex.input) assert.Equal(t, ex.match, match) assert.Equal(t, ex.serverType, stype) assert.Equal(t, ex.version, version) }) } } func TestDetectDumpVersion(t *testing.T) { examples := []struct { input string match bool version string }{ {"", false, ""}, {"pg_dump (PostgreSQL) 9.6", true, "9.6"}, {"pg_dump 10", true, "10"}, {"pg_dump (PostgreSQL) 14.5 (Homebrew)", true, "14.5"}, } for _, ex := range examples { t.Run("input:"+ex.input, func(t *testing.T) { match, version := detectDumpVersion(ex.input) assert.Equal(t, ex.match, match) assert.Equal(t, ex.version, version) }) } } func TestGetMajorMinorVersion(t *testing.T) { examples := []struct { input string major int minor int }{ {"", 0, 0}, {" ", 0, 0}, {"0", 0, 0}, {"9.6", 9, 6}, {"9.6.1.1", 9, 6}, {"10", 10, 0}, {"10.1 ", 10, 1}, } for _, ex := range examples { t.Run(ex.input, func(t *testing.T) { major, minor := getMajorMinorVersion(ex.input) assert.Equal(t, ex.major, major) assert.Equal(t, ex.minor, minor) }) } } func TestCheckVersionRequirement(t *testing.T) { examples := []struct { client string server string result bool }{ {"", "", true}, {"0", "0", true}, {"9.6", "9.7", false}, {"9.6.10", "9.6.25", true}, {"10.0", "10.1", true}, {"10.5", "10.1", true}, {"14.5", "15.1", false}, } for _, ex := range examples { assert.Equal(t, ex.result, checkVersionRequirement(ex.client, ex.server)) } } ================================================ FILE: pkg/command/options.go ================================================ package command import ( "errors" "fmt" "os" "os/user" "path/filepath" "strings" "github.com/jackc/pgpassfile" "github.com/jessevdk/go-flags" "github.com/mitchellh/go-homedir" "github.com/sirupsen/logrus" ) const ( // Prefix to use for all pgweb env vars, ie PGWEB_HOST, PGWEB_PORT, etc envVarPrefix = "PGWEB_" ) type Options struct { Version bool `short:"v" long:"version" description:"Print version"` Debug bool `short:"d" long:"debug" description:"Enable debugging mode"` LogLevel string `long:"log-level" description:"Logging level" default:"info"` LogFormat string `long:"log-format" description:"Logging output format" default:"text"` LogForwardedUser bool `long:"log-forwarded-user" description:"Log user information available in X-Forwarded-User/Email headers"` URL string `long:"url" description:"Database connection string"` Host string `long:"host" description:"Server hostname or IP" default:"localhost"` Port int `long:"port" description:"Server port" default:"5432"` User string `long:"user" description:"Database user"` Pass string `long:"pass" description:"Password for user"` Passfile string `long:"passfile" description:"Local passwords file location"` DbName string `long:"db" description:"Database name"` SSLMode string `long:"ssl" description:"SSL mode"` SSLRootCert string `long:"ssl-rootcert" description:"SSL certificate authority file"` SSLCert string `long:"ssl-cert" description:"SSL client certificate file"` SSLKey string `long:"ssl-key" description:"SSL client certificate key file"` OpenTimeout int `long:"open-timeout" description:"Maximum wait time for connection, in seconds" default:"30"` RetryDelay uint `long:"open-retry-delay" description:"Number of seconds to wait before retrying the connection" default:"3"` RetryCount uint `long:"open-retry" description:"Number of times to retry establishing connection" default:"0"` HTTPHost string `long:"bind" description:"HTTP server host" default:"localhost"` HTTPPort uint `long:"listen" description:"HTTP server listen port" default:"8081"` AuthUser string `long:"auth-user" description:"HTTP basic auth user"` AuthPass string `long:"auth-pass" description:"HTTP basic auth password"` SkipOpen bool `short:"s" long:"skip-open" description:"Skip browser open on start"` Sessions bool `long:"sessions" description:"Enable multiple database sessions"` Prefix string `long:"prefix" description:"Add a url prefix"` ReadOnly bool `long:"readonly" description:"Run database connection in readonly mode"` LockSession bool `long:"lock-session" description:"Lock session to a single database connection"` Bookmark string `short:"b" long:"bookmark" description:"Bookmark to use for connection. Bookmark files are stored under $HOME/.pgweb/bookmarks/*.toml" default:""` BookmarksDir string `long:"bookmarks-dir" description:"Overrides default directory for bookmark files to search" default:""` BookmarksOnly bool `long:"bookmarks-only" description:"Allow only connections from bookmarks"` QueriesDir string `long:"queries-dir" description:"Overrides default directory for local queries"` DisablePrettyJSON bool `long:"no-pretty-json" description:"Disable JSON formatting feature for result export"` DisableSSH bool `long:"no-ssh" description:"Disable database connections via SSH"` ConnectBackend string `long:"connect-backend" description:"Enable database authentication through a third party backend"` ConnectToken string `long:"connect-token" description:"Authentication token for the third-party connect backend"` ConnectHeaders string `long:"connect-headers" description:"List of headers to pass to the connect backend"` DisableConnectionIdleTimeout bool `long:"no-idle-timeout" description:"Disable connection idle timeout"` ConnectionIdleTimeout int `long:"idle-timeout" description:"Set connection idle timeout in minutes" default:"180"` QueryTimeout uint `long:"query-timeout" description:"Set global query execution timeout in seconds" default:"300"` Cors bool `long:"cors" description:"Enable Cross-Origin Resource Sharing (CORS)"` CorsOrigin string `long:"cors-origin" description:"Allowed CORS origins" default:"*"` BinaryCodec string `long:"binary-codec" description:"Codec for binary data serialization, one of 'none', 'hex', 'base58', 'base64'" default:"none"` MetricsEnabled bool `long:"metrics" description:"Enable Prometheus metrics endpoint"` MetricsPath string `long:"metrics-path" description:"Path prefix for Prometheus metrics endpoint" default:"/metrics"` MetricsAddr string `long:"metrics-addr" description:"Listen host and port for Prometheus metrics server"` } var Opts Options // ParseOptions returns a new options struct from the input arguments func ParseOptions(args []string) (Options, error) { var opts = Options{} _, err := flags.ParseArgs(&opts, args) if err != nil { return opts, err } _, err = logrus.ParseLevel(opts.LogLevel) if err != nil { return opts, err } if opts.URL == "" { opts.URL = getPrefixedEnvVar("DATABASE_URL") } if opts.Prefix == "" { opts.Prefix = getPrefixedEnvVar("URL_PREFIX") } if opts.Passfile == "" { passfile := os.Getenv("PGPASSFILE") if passfile == "" { passfile = filepath.Join(os.Getenv("HOME"), ".pgpass") } _, err := os.Stat(passfile) if err == nil { _, err = pgpassfile.ReadPassfile(passfile) if err == nil { opts.Passfile = passfile } else { fmt.Printf("[WARN] Pgpass file unreadable: %s\n", err) } } } // Handle edge case where pgweb is started with a default host `localhost` and no user. // When user is not set the `lib/pq` connection will fail and cause pgweb's termination. if (opts.Host == "localhost" || opts.Host == "127.0.0.1") && opts.User == "" { if username := getCurrentUser(); username != "" { opts.User = username } else { opts.Host = "" } } if getPrefixedEnvVar("BOOKMARKS_ONLY") != "" { opts.BookmarksOnly = true } if getPrefixedEnvVar("SESSIONS") != "" { opts.Sessions = true } if getPrefixedEnvVar("LOCK_SESSION") != "" { opts.LockSession = true opts.Sessions = false } if opts.Sessions || opts.ConnectBackend != "" { opts.Bookmark = "" opts.URL = "" opts.Host = "" opts.User = "" opts.Pass = "" opts.DbName = "" opts.SSLMode = "" } if opts.Prefix != "" && !strings.HasSuffix(opts.Prefix, "/") { opts.Prefix = opts.Prefix + "/" } if opts.AuthUser == "" { opts.AuthUser = getPrefixedEnvVar("AUTH_USER") } if opts.AuthPass == "" { opts.AuthPass = getPrefixedEnvVar("AUTH_PASS") } if opts.ConnectBackend != "" { if !opts.Sessions { return opts, errors.New("--sessions flag must be set") } if opts.ConnectToken == "" { return opts, errors.New("--connect-token flag must be set") } } else { if opts.ConnectToken != "" || opts.ConnectHeaders != "" { return opts, errors.New("--connect-backend flag must be set") } } if opts.BookmarksOnly { if opts.URL != "" { return opts, errors.New("--url not supported in bookmarks-only mode") } if opts.Host != "" && opts.Host != "localhost" { return opts, errors.New("--host not supported in bookmarks-only mode") } if opts.ConnectBackend != "" { return opts, errors.New("--connect-backend not supported in bookmarks-only mode") } } if opts.BookmarksDir == "" { opts.BookmarksDir = getPrefixedEnvVar("BOOKMARKS_DIR") } homePath, err := homedir.Dir() if err != nil { fmt.Fprintf(os.Stderr, "[WARN] can't detect home dir: %v", err) homePath = os.Getenv("HOME") } if homePath != "" { if opts.BookmarksDir == "" { opts.BookmarksDir = filepath.Join(homePath, ".pgweb/bookmarks") } if opts.QueriesDir == "" { opts.QueriesDir = filepath.Join(homePath, ".pgweb/queries") } } return opts, nil } // SetDefaultOptions parses and assigns the options func SetDefaultOptions() error { opts, err := ParseOptions([]string{}) if err != nil { return err } Opts = opts return nil } // getCurrentUser returns a current user name func getCurrentUser() string { u, _ := user.Current() if u != nil { return u.Username } return os.Getenv("USER") } // getPrefixedEnvVar returns env var with prefix, or falls back to unprefixed one func getPrefixedEnvVar(name string) string { val := os.Getenv(envVarPrefix + name) if val == "" { val = os.Getenv(name) if val != "" { fmt.Printf("[DEPRECATION] Usage of %s env var is deprecated, please use PGWEB_%s variable instead\n", name, name) } } return val } // AvailableEnvVars returns list of supported env vars. // // TODO: These should probably be embedded into flag parsing logic so we dont have // to maintain the list manually. func AvailableEnvVars() string { return strings.Join([]string{ " " + envVarPrefix + "DATABASE_URL Database connection string", " " + envVarPrefix + "URL_PREFIX HTTP server path prefix", " " + envVarPrefix + "SESSIONS Enable multiple database sessions", " " + envVarPrefix + "LOCK_SESSION Lock session to a single database connection", " " + envVarPrefix + "AUTH_USER HTTP basic auth username", " " + envVarPrefix + "AUTH_PASS HTTP basic auth password", " " + envVarPrefix + "BOOKMARKS_DIR Overrides default directory for bookmark files", }, "\n") } ================================================ FILE: pkg/command/options_test.go ================================================ package command import ( "os" "path/filepath" "testing" "github.com/mitchellh/go-homedir" "github.com/stretchr/testify/assert" ) func TestParseOptions(t *testing.T) { var hdir string if d, err := homedir.Dir(); err == nil { hdir = d } t.Run("defaults", func(t *testing.T) { opts, err := ParseOptions([]string{}) assert.NoError(t, err) assert.Equal(t, false, opts.Sessions) assert.Equal(t, "", opts.Prefix) assert.Equal(t, "", opts.ConnectToken) assert.Equal(t, "", opts.ConnectHeaders) assert.Equal(t, false, opts.DisableSSH) assert.Equal(t, false, opts.DisablePrettyJSON) assert.Equal(t, false, opts.DisableConnectionIdleTimeout) assert.Equal(t, 180, opts.ConnectionIdleTimeout) assert.Equal(t, false, opts.Cors) assert.Equal(t, "*", opts.CorsOrigin) assert.Equal(t, "", opts.Passfile) assert.Equal(t, filepath.Join(hdir, ".pgweb/bookmarks"), opts.BookmarksDir) }) t.Run("sessions", func(t *testing.T) { opts, err := ParseOptions([]string{"--sessions", "1"}) assert.NoError(t, err) assert.Equal(t, true, opts.Sessions) }) t.Run("url prefix", func(t *testing.T) { opts, err := ParseOptions([]string{"--prefix", "pgweb"}) assert.NoError(t, err) assert.Equal(t, "pgweb/", opts.Prefix) opts, err = ParseOptions([]string{"--prefix", "pgweb/"}) assert.NoError(t, err) assert.Equal(t, "pgweb/", opts.Prefix) }) t.Run("connect backend", func(t *testing.T) { _, err := ParseOptions([]string{"--connect-backend", "test"}) assert.EqualError(t, err, "--sessions flag must be set") _, err = ParseOptions([]string{"--connect-backend", "test", "--sessions"}) assert.EqualError(t, err, "--connect-token flag must be set") _, err = ParseOptions([]string{"--connect-backend", "test", "--sessions", "--connect-token", "token"}) assert.NoError(t, err) }) t.Run("passfile", func(t *testing.T) { defer os.Unsetenv("PGPASSFILE") // File does not exist os.Setenv("PGPASSFILE", "/tmp/foo") opts, err := ParseOptions([]string{}) assert.NoError(t, err) assert.Equal(t, "", opts.Passfile) // File exists and valid os.Setenv("PGPASSFILE", "../../data/passfile") opts, err = ParseOptions([]string{}) assert.NoError(t, err) assert.Equal(t, "../../data/passfile", opts.Passfile) // Set via flag os.Unsetenv("PGPASSFILE") opts, err = ParseOptions([]string{"--passfile", "../../data/passfile"}) assert.NoError(t, err) assert.Equal(t, "../../data/passfile", opts.Passfile) }) t.Run("bookmarks dir from env var", func(t *testing.T) { os.Setenv("PGWEB_BOOKMARKS_DIR", "/tmp/my-bookmarks") defer os.Unsetenv("PGWEB_BOOKMARKS_DIR") opts, err := ParseOptions([]string{}) assert.NoError(t, err) assert.Equal(t, "/tmp/my-bookmarks", opts.BookmarksDir) }) t.Run("bookmarks dir flag takes precedence over env var", func(t *testing.T) { os.Setenv("PGWEB_BOOKMARKS_DIR", "/tmp/my-bookmarks") defer os.Unsetenv("PGWEB_BOOKMARKS_DIR") flagDir := t.TempDir() opts, err := ParseOptions([]string{"--bookmarks-dir", flagDir}) assert.NoError(t, err) assert.Equal(t, flagDir, opts.BookmarksDir) }) t.Run("bookmarks only mode", func(t *testing.T) { _, err := ParseOptions([]string{"--bookmarks-only"}) assert.NoError(t, err) _, err = ParseOptions([]string{"--bookmarks-only", "--url", "test"}) assert.EqualError(t, err, "--url not supported in bookmarks-only mode") _, err = ParseOptions([]string{"--bookmarks-only", "--host", "test", "--port", "5432"}) assert.EqualError(t, err, "--host not supported in bookmarks-only mode") _, err = ParseOptions([]string{"--bookmarks-only", "--connect-backend", "test", "--sessions", "--connect-token", "token", "--url", "127.0.0.2"}) assert.EqualError(t, err, "--connect-backend not supported in bookmarks-only mode") }) } ================================================ FILE: pkg/command/version.go ================================================ package command import ( "fmt" "runtime" "strings" ) const ( // Version is the current Pgweb application version Version = "0.17.0" ) var ( // GitCommit contains the Git commit SHA for the binary GitCommit string // BuildTime contains the binary build time BuildTime string // BuildArch contains the OS architecture of the binary BuildArch string = fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH) // GoVersion contains the build time Go version GoVersion string // Info contains all version information Info VersionInfo ) type VersionInfo struct { Version string `json:"version"` GitCommit string `json:"git_sha"` BuildTime string `json:"build_time"` BuildArch string `json:"build_arch"` GoVersion string `json:"go_version"` } func init() { Info.Version = Version Info.GitCommit = GitCommit Info.BuildTime = BuildTime Info.BuildArch = BuildArch Info.GoVersion = GoVersion } func VersionString() string { chunks := []string{fmt.Sprintf("Pgweb v%s", Version)} if GitCommit != "" { chunks = append(chunks, fmt.Sprintf("(git: %s)", GitCommit)) } if GoVersion != "" { chunks = append(chunks, fmt.Sprintf("(go: %s)", GoVersion)) } if BuildTime != "" { chunks = append(chunks, fmt.Sprintf("(build time: %s)", BuildTime)) } if BuildArch != "" { chunks = append(chunks, fmt.Sprintf("(arch: %s)", BuildArch)) } return strings.Join(chunks, " ") } ================================================ FILE: pkg/connect/backend.go ================================================ package connect import ( "bytes" "context" "encoding/json" "fmt" "net/http" "strings" "github.com/sirupsen/logrus" ) type Backend struct { Endpoint string Token string PassHeaders []string logger *logrus.Logger } func NewBackend(endpoint string, token string) Backend { return Backend{ Endpoint: endpoint, Token: token, logger: logrus.StandardLogger(), } } func (be *Backend) SetLogger(logger *logrus.Logger) { be.logger = logger } func (be *Backend) SetPassHeaders(headers []string) { be.PassHeaders = headers } func (be *Backend) FetchCredential(ctx context.Context, resource string, headers http.Header) (*Credential, error) { be.logger.WithField("resource", resource).Debug("fetching database credential") request := Request{ Resource: resource, Token: be.Token, Headers: map[string]string{}, } // Pass allow-listed client headers to the backend request for _, name := range be.PassHeaders { request.Headers[strings.ToLower(name)] = headers.Get(name) } body, err := json.Marshal(request) if err != nil { be.logger.WithField("resource", resource).Error("backend request serialization error:", err) return nil, err } req, err := http.NewRequestWithContext(ctx, http.MethodPost, be.Endpoint, bytes.NewReader(body)) if err != nil { return nil, err } req.Header.Set("content-type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { be.logger.WithField("resource", resource).Error("backend credential fetch failed:", err) return nil, errBackendConnectError } defer resp.Body.Close() if resp.StatusCode != 200 { err = fmt.Errorf("backend credential fetch received HTTP status code %v", resp.StatusCode) be.logger. WithField("resource", request.Resource). WithField("status", resp.StatusCode). Error(err) return nil, err } cred := &Credential{} if err := json.NewDecoder(resp.Body).Decode(cred); err != nil { return nil, err } if cred.DatabaseURL == "" { return nil, errConnStringRequired } return cred, nil } ================================================ FILE: pkg/connect/backend_test.go ================================================ package connect import ( "context" "errors" "net" "net/http" "testing" "time" "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" ) func TestBackendFetchCredential(t *testing.T) { examples := []struct { name string backend Backend resourceName string cred *Credential headers http.Header ctx func() (context.Context, context.CancelFunc) err error }{ { name: "Bad auth token", backend: Backend{Endpoint: "http://localhost:5555/unauthorized"}, err: errors.New("backend credential fetch received HTTP status code 401"), }, { name: "Backend timeout", backend: Backend{Endpoint: "http://localhost:5555/timeout"}, ctx: func() (context.Context, context.CancelFunc) { return context.WithTimeout(context.Background(), time.Millisecond*100) }, err: errors.New("unable to connect to the auth backend"), }, { name: "Empty response", backend: Backend{Endpoint: "http://localhost:5555/empty-response"}, err: errors.New("connection string is required"), }, { name: "Missing header", backend: Backend{Endpoint: "http://localhost:5555/pass-header"}, err: errors.New("backend credential fetch received HTTP status code 400"), }, { name: "Require header", backend: Backend{ Endpoint: "http://localhost:5555/pass-header", PassHeaders: []string{"x-foo"}, }, headers: http.Header{ "X-Foo": []string{"bar"}, }, cred: &Credential{DatabaseURL: "postgres://hostname/bar"}, }, { name: "Success", backend: Backend{Endpoint: "http://localhost:5555/success"}, cred: &Credential{DatabaseURL: "postgres://hostname/dbname"}, }, } srvCtx, srvCancel := context.WithTimeout(context.Background(), time.Minute) defer srvCancel() startTestBackend(srvCtx, "localhost:5555") for _, ex := range examples { ex.backend.logger = logrus.StandardLogger() t.Run(ex.name, func(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) if ex.ctx != nil { ctx, cancel = ex.ctx() } defer cancel() cred, err := ex.backend.FetchCredential(ctx, ex.resourceName, ex.headers) assert.Equal(t, ex.err, err) assert.Equal(t, ex.cred, cred) }) } } func startTestBackend(ctx context.Context, listenAddr string) { router := gin.New() router.Use(func(c *gin.Context) { if c.GetHeader("content-type") != "application/json" { c.AbortWithStatus(http.StatusBadRequest) } }) router.POST("/unauthorized", func(c *gin.Context) { c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"}) }) router.POST("/timeout", func(c *gin.Context) { time.Sleep(time.Second) c.JSON(http.StatusOK, gin.H{}) }) router.POST("/empty-response", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{}) }) router.POST("/pass-header", func(c *gin.Context) { req := Request{} if err := c.BindJSON(&req); err != nil { panic(err) } header := req.Headers["x-foo"] if header == "" { c.AbortWithStatus(http.StatusBadRequest) return } c.JSON(http.StatusOK, gin.H{ "database_url": "postgres://hostname/" + header, }) }) router.POST("/success", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "database_url": "postgres://hostname/dbname", }) }) server := &http.Server{Addr: listenAddr, Handler: router} mustStartServer(server) go func() { <-ctx.Done() if err := server.Shutdown(context.Background()); err != nil && err != http.ErrServerClosed { panic(err) } }() } func mustStartServer(server *http.Server) { go func() { err := server.ListenAndServe() if err != nil && err != http.ErrServerClosed { panic(err) } }() if err := waitForServer(server.Addr, 5); err != nil { panic(err) } } func waitForServer(addr string, n int) error { var lastErr error for i := 0; i < n; i++ { conn, err := net.Dial("tcp", addr) if err == nil { conn.Close() return nil } lastErr = err time.Sleep(time.Millisecond * 100) } return lastErr } ================================================ FILE: pkg/connect/types.go ================================================ package connect import "errors" var ( errBackendConnectError = errors.New("unable to connect to the auth backend") errConnStringRequired = errors.New("connection string is required") ) // Request holds the resource request details type Request struct { Resource string `json:"resource"` Token string `json:"token"` Headers map[string]string `json:"headers,omitempty"` } // Credential holds the database connection string type Credential struct { DatabaseURL string `json:"database_url"` } ================================================ FILE: pkg/connection/connection_string.go ================================================ package connection import ( "errors" "fmt" neturl "net/url" "os" "os/user" "strconv" "strings" "github.com/jackc/pgpassfile" "github.com/sosedoff/pgweb/pkg/command" ) // Common errors var ( errCantDetectUser = errors.New("Could not detect default username") errInvalidURLFormat = errors.New("Invalid URL. Valid format: postgres://user:password@host:port/db?sslmode=mode") ) // currentUser returns a current user name func currentUser() (string, error) { u, err := user.Current() if err == nil { return u.Username, nil } name := os.Getenv("USER") if name != "" { return name, nil } return "", errCantDetectUser } // Check if connection url has a correct postgres prefix func hasValidPrefix(str string) bool { return strings.HasPrefix(str, "postgres://") || strings.HasPrefix(str, "postgresql://") } // Extract all query vals and return as a map func valsFromQuery(vals neturl.Values) map[string]string { result := map[string]string{} for k, v := range vals { result[strings.ToLower(k)] = v[0] } return result } // FormatURL reformats the existing connection string func FormatURL(opts command.Options) (string, error) { url := opts.URL // Validate connection string prefix if !hasValidPrefix(url) { return "", errInvalidURLFormat } // Validate the URL uri, err := neturl.Parse(url) if err != nil { return "", errInvalidURLFormat } // Get query params params := valsFromQuery(uri.Query()) // Determine if we need to specify sslmode if it's missing if params["sslmode"] == "" { if opts.SSLMode == "" { // Only modify sslmode for local connections if strings.Contains(uri.Host, "localhost") || strings.Contains(uri.Host, "127.0.0.1") { params["sslmode"] = "disable" } } else { params["sslmode"] = opts.SSLMode } } // When password is not provided, look it up from a .pgpass file if uri.User != nil { pass, _ := uri.User.Password() if pass == "" && opts.Passfile != "" { pass = lookupPassword(opts, uri) if pass != "" { uri.User = neturl.UserPassword(uri.User.Username(), pass) } } } // Configure default connect timeout if opts.OpenTimeout > 0 { params["connect_timeout"] = strconv.Itoa(opts.OpenTimeout) } // Rebuild query params query := neturl.Values{} for k, v := range params { query.Add(k, v) } uri.RawQuery = query.Encode() return uri.String(), nil } // IsBlank returns true if command options do not contain connection details func IsBlank(opts command.Options) bool { return opts.Host == "" && opts.User == "" && opts.DbName == "" && opts.URL == "" } // BuildStringFromOptions returns a new connection string built from options func BuildStringFromOptions(opts command.Options) (string, error) { query := neturl.Values{} // If connection string is provided we just use that if opts.URL != "" { return FormatURL(opts) } // Try to detect user from current OS user if opts.User == "" { u, err := currentUser() if err == nil { opts.User = u } } if opts.SSLMode != "" { query.Add("sslmode", opts.SSLMode) } else { if opts.Host == "localhost" || opts.Host == "127.0.0.1" { query.Add("sslmode", "disable") } } if opts.SSLCert != "" { query.Add("sslcert", opts.SSLCert) } if opts.SSLKey != "" { query.Add("sslkey", opts.SSLKey) } if opts.SSLRootCert != "" { query.Add("sslrootcert", opts.SSLRootCert) } // Grab password from .pgpass file if it's available if opts.Pass == "" && opts.Passfile != "" { opts.Pass = lookupPassword(opts, nil) } // Configure default connect timeout if opts.OpenTimeout > 0 { query.Add("connect_timeout", strconv.Itoa(opts.OpenTimeout)) } url := neturl.URL{ Scheme: "postgres", Host: fmt.Sprintf("%v:%v", opts.Host, opts.Port), User: neturl.UserPassword(opts.User, opts.Pass), Path: fmt.Sprintf("/%s", opts.DbName), RawQuery: query.Encode(), } return url.String(), nil } func lookupPassword(opts command.Options, url *neturl.URL) string { if opts.Passfile == "" { return "" } passfile, err := pgpassfile.ReadPassfile(opts.Passfile) if err != nil { fmt.Println("[WARN] .pgpassfile", opts.Passfile, "is not readable") return "" } if url != nil { var dbName string fmt.Sscanf(url.Path, "/%s", &dbName) //nolint return passfile.FindPassword( url.Hostname(), url.Port(), dbName, url.User.Username(), ) } return passfile.FindPassword( opts.Host, fmt.Sprintf("%d", opts.Port), opts.DbName, opts.User, ) } ================================================ FILE: pkg/connection/connection_string_test.go ================================================ package connection import ( "fmt" "net/url" "os/user" "testing" "github.com/sosedoff/pgweb/pkg/command" "github.com/stretchr/testify/assert" ) func TestBuildStringFromOptions(t *testing.T) { t.Run("valid url", func(t *testing.T) { url := "postgres://myhost/database" str, err := BuildStringFromOptions(command.Options{URL: url}) assert.NoError(t, err) assert.Equal(t, url, str) }) t.Run("with sslmode param", func(t *testing.T) { str, err := BuildStringFromOptions(command.Options{ URL: "postgres://myhost/database", SSLMode: "disable", }) assert.NoError(t, err) assert.Equal(t, "postgres://myhost/database?sslmode=disable", str) }) t.Run("sets sslmode param if not set", func(t *testing.T) { str, err := BuildStringFromOptions(command.Options{ URL: "postgres://localhost/database", }) assert.NoError(t, err) assert.Equal(t, "postgres://localhost/database?sslmode=disable", str) str, err = BuildStringFromOptions(command.Options{ URL: "postgres://127.0.0.1/database", }) assert.NoError(t, err) assert.Equal(t, "postgres://127.0.0.1/database?sslmode=disable", str) }) t.Run("sslmode as an option", func(t *testing.T) { str, err := BuildStringFromOptions(command.Options{ URL: "postgres://localhost/database", SSLMode: "require", }) assert.NoError(t, err) assert.Equal(t, "postgres://localhost/database?sslmode=require", str) str, err = BuildStringFromOptions(command.Options{ URL: "postgres://127.0.0.1/database", SSLMode: "require", }) assert.NoError(t, err) assert.Equal(t, "postgres://127.0.0.1/database?sslmode=require", str) }) t.Run("localhost and sslmode flag", func(t *testing.T) { str, err := BuildStringFromOptions(command.Options{ URL: "postgres://localhost/database?sslmode=require", }) assert.NoError(t, err) assert.Equal(t, "postgres://localhost/database?sslmode=require", str) str, err = BuildStringFromOptions(command.Options{ URL: "postgres://127.0.0.1/database?sslmode=require", }) assert.NoError(t, err) assert.Equal(t, "postgres://127.0.0.1/database?sslmode=require", str) }) t.Run("extended options", func(t *testing.T) { str, err := BuildStringFromOptions(command.Options{ URL: "postgres://localhost/database?sslmode=require&sslcert=cert&sslkey=key&sslrootcert=ca", }) assert.NoError(t, err) assert.Equal(t, "postgres://localhost/database?sslcert=cert&sslkey=key&sslmode=require&sslrootcert=ca", str) }) t.Run("from flags", func(t *testing.T) { str, err := BuildStringFromOptions(command.Options{ Host: "host", Port: 5432, User: "user", Pass: "password", DbName: "db", }) assert.NoError(t, err) assert.Equal(t, "postgres://user:password@host:5432/db", str) }) t.Run("localhost", func(t *testing.T) { opts := command.Options{ Host: "localhost", Port: 5432, User: "user", Pass: "password", DbName: "db", } str, err := BuildStringFromOptions(opts) assert.NoError(t, err) assert.Equal(t, "postgres://user:password@localhost:5432/db?sslmode=disable", str) opts.Host = "127.0.0.1" str, err = BuildStringFromOptions(opts) assert.NoError(t, err) assert.Equal(t, "postgres://user:password@127.0.0.1:5432/db?sslmode=disable", str) }) t.Run("localhost and ssl", func(t *testing.T) { opts := command.Options{ Host: "localhost", Port: 5432, User: "user", Pass: "password", DbName: "db", SSLMode: "require", SSLKey: "keyPath", SSLCert: "certPath", SSLRootCert: "caPath", } str, err := BuildStringFromOptions(opts) assert.NoError(t, err) assert.Equal(t, "postgres://user:password@localhost:5432/db?sslcert=certPath&sslkey=keyPath&sslmode=require&sslrootcert=caPath", str) }) t.Run("no user", func(t *testing.T) { opts := command.Options{Host: "host", Port: 5432, DbName: "db"} u, _ := user.Current() str, err := BuildStringFromOptions(opts) userAndPass := url.UserPassword(u.Username, "").String() assert.NoError(t, err) assert.Equal(t, fmt.Sprintf("postgres://%s@host:5432/db", userAndPass), str) }) t.Run("port", func(t *testing.T) { opts := command.Options{Host: "host", User: "user", Port: 5000, DbName: "db"} str, err := BuildStringFromOptions(opts) assert.NoError(t, err) assert.Equal(t, "postgres://user:@host:5000/db", str) }) t.Run("with pgpass", func(t *testing.T) { opts := command.Options{ Host: "localhost", Port: 5432, User: "username", DbName: "dbname", Passfile: "../../data/passfile", } str, err := BuildStringFromOptions(opts) assert.NoError(t, err) assert.Equal(t, "postgres://username:password@localhost:5432/dbname?sslmode=disable", str) opts.User = "foobar" str, err = BuildStringFromOptions(opts) assert.NoError(t, err) assert.Equal(t, "postgres://foobar:@localhost:5432/dbname?sslmode=disable", str) opts.Host = "127.0.0.1" opts.DbName = "foobar2" str, err = BuildStringFromOptions(opts) assert.NoError(t, err) assert.Equal(t, "postgres://foobar:password2@127.0.0.1:5432/foobar2?sslmode=disable", str) }) t.Run("with connection timeout", func(t *testing.T) { opts := command.Options{ URL: "postgres://user:pass@localhost:5432/dbname", OpenTimeout: 30, } str, err := BuildStringFromOptions(opts) assert.NoError(t, err) assert.Equal(t, "postgres://user:pass@localhost:5432/dbname?connect_timeout=30&sslmode=disable", str) opts = command.Options{ Host: "localhost", Port: 5432, User: "username", DbName: "dbname", OpenTimeout: 30, } str, err = BuildStringFromOptions(opts) assert.NoError(t, err) assert.Equal(t, "postgres://username:@localhost:5432/dbname?connect_timeout=30&sslmode=disable", str) }) t.Run("invalid url", func(t *testing.T) { opts := command.Options{} examples := []string{ "postgre://foobar", "tcp://blah", "foobar", } for _, val := range examples { opts.URL = val str, err := BuildStringFromOptions(opts) assert.Equal(t, "", str) assert.Error(t, err) assert.Equal(t, "Invalid URL. Valid format: postgres://user:password@host:port/db?sslmode=mode", err.Error()) } }) } func TestFormatURL(t *testing.T) { examples := []struct { name string input command.Options result string err string }{ { name: "empty opts", input: command.Options{}, }, { name: "invalid url", input: command.Options{URL: "barurl"}, err: "Invalid URL", }, { name: "good", input: command.Options{ URL: "postgres://user:pass@localhost:5432/dbname", }, result: "postgres://user:pass@localhost:5432/dbname?sslmode=disable", }, { name: "password lookup, password set", input: command.Options{ URL: "postgres://username:@localhost:5432/dbname", Passfile: "../../data/passfile", }, result: "postgres://username:password@localhost:5432/dbname?sslmode=disable", }, { name: "password lookup, password not set", input: command.Options{ URL: "postgres://username@localhost:5432/dbname", Passfile: "../../data/passfile", }, result: "postgres://username:password@localhost:5432/dbname?sslmode=disable", }, { name: "with timeout setting", input: command.Options{ URL: "postgres://username@localhost:5432/dbname", OpenTimeout: 30, }, result: "postgres://username@localhost:5432/dbname?connect_timeout=30&sslmode=disable", }, } for _, ex := range examples { t.Run(ex.name, func(t *testing.T) { str, err := FormatURL(ex.input) if ex.err != "" { assert.Error(t, err) assert.Contains(t, err.Error(), ex.err) } assert.Equal(t, ex.result, str) }) } } func TestIsBlank(t *testing.T) { assert.Equal(t, true, IsBlank(command.Options{})) assert.Equal(t, false, IsBlank(command.Options{Host: "host", User: "user"})) assert.Equal(t, false, IsBlank(command.Options{Host: "host", User: "user", DbName: "db"})) assert.Equal(t, false, IsBlank(command.Options{URL: "url"})) } ================================================ FILE: pkg/connection/port.go ================================================ package connection import ( "errors" "fmt" "net" "strings" ) // IsPortAvailable returns true if there's no listeners on a given port func IsPortAvailable(port int) bool { conn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%v", port)) if err != nil { return strings.Index(err.Error(), "connection refused") > 0 } conn.Close() return false } // FindAvailablePort returns the first available TCP port in the range func FindAvailablePort(start int, limit int) (int, error) { for i := start; i <= (start + limit); i++ { if IsPortAvailable(i) { return i, nil } } return -1, errors.New("No available port") } ================================================ FILE: pkg/connection/port_test.go ================================================ package connection import ( "fmt" "net" "os" "runtime" "testing" "github.com/stretchr/testify/assert" ) func TestIsPortAvailable(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("FIXME") } assert.Equal(t, true, IsPortAvailable(30000)) serv, err := net.Listen("tcp", "127.0.0.1:30000") if err != nil { fmt.Fprintln(os.Stderr, "Unable to start test tcp listener:", err) t.Fail() return } defer serv.Close() go func() { for { conn, err := serv.Accept() if err == nil { conn.Close() } serv.Close() } }() assert.Equal(t, false, IsPortAvailable(30000)) assert.Equal(t, true, IsPortAvailable(30001)) } func TestFindAvailablePort(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("FIXME") } port, err := FindAvailablePort(30000, 1) assert.Equal(t, nil, err) assert.Equal(t, 30000, port) serv, err := net.Listen("tcp", "127.0.0.1:30000") if err != nil { fmt.Fprintln(os.Stderr, "Unable to start test tcp listener:", err) t.Fail() return } defer serv.Close() go func() { for { conn, err := serv.Accept() if err == nil { conn.Close() } } }() port, err = FindAvailablePort(30000, 0) assert.EqualError(t, err, "No available port") assert.Equal(t, -1, port) port, err = FindAvailablePort(30000, 1) assert.Equal(t, nil, err) assert.Equal(t, 30001, port) } ================================================ FILE: pkg/history/history.go ================================================ package history import ( "time" ) type Record struct { Query string `json:"query"` Timestamp string `json:"timestamp"` } func New() []Record { return make([]Record, 0) } func NewRecord(query string) Record { return Record{ Query: query, Timestamp: time.Now().String(), } } ================================================ FILE: pkg/metrics/handler.go ================================================ package metrics import ( "net/http" "time" "github.com/prometheus/client_golang/prometheus/promhttp" ) type Handler struct { startTime time.Time promHandler http.Handler } func (h Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { uptimeGauge.Set(time.Since(h.startTime).Seconds()) h.promHandler.ServeHTTP(rw, req) } func NewHandler() http.Handler { return Handler{ startTime: time.Now(), promHandler: promhttp.Handler(), } } ================================================ FILE: pkg/metrics/metrics.go ================================================ package metrics import ( "time" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" ) var ( sessionsGauge = promauto.NewGauge(prometheus.GaugeOpts{ Name: "pgweb_sessions_count", Help: "Total number of database sessions", }) queriesCounter = promauto.NewCounter(prometheus.CounterOpts{ Name: "pgweb_queries_count", Help: "Total number of custom queries executed", }) healthyGauge = promauto.NewGauge(prometheus.GaugeOpts{ Name: "pgweb_healthy", Help: "Server health status", }) startTimeGauge = promauto.NewGauge(prometheus.GaugeOpts{ Name: "pgweb_process_start_time", Help: "Server start time, seconds since unix epoch", }) uptimeGauge = promauto.NewGauge(prometheus.GaugeOpts{ Name: "pgweb_uptime", Help: "Server application uptime in seconds", }) ) func init() { startTimeGauge.Set(float64(time.Now().Unix())) } func IncrementQueriesCount() { queriesCounter.Inc() } func SetSessionsCount(val int) { sessionsGauge.Set(float64(val)) } func SetHealthy(val bool) { healthy := 0.0 if val { healthy = 1.0 } healthyGauge.Set(float64(healthy)) } ================================================ FILE: pkg/metrics/server.go ================================================ package metrics import ( "net/http" "github.com/sirupsen/logrus" ) func StartServer(logger *logrus.Logger, path string, addr string) error { logger.WithField("addr", addr).WithField("path", path).Info("starting prometheus metrics server") http.Handle(path, NewHandler()) return http.ListenAndServe(addr, nil) } ================================================ FILE: pkg/queries/field.go ================================================ package queries import ( "fmt" "regexp" "strings" ) type field struct { value string re *regexp.Regexp } func (f field) String() string { return f.value } func (f field) matches(input string) bool { if f.re != nil { return f.re.MatchString(input) } return f.value == input } func newField(value string) (field, error) { f := field{value: value} if value == "*" { // match everything f.re = reMatchAll } else if reExpression.MatchString(value) { // match by given expression // Make writing expressions easier for values like "foo_*" if strings.Count(value, "*") == 1 { value = strings.Replace(value, "*", "(.+)", 1) } re, err := regexp.Compile(fmt.Sprintf("^%s$", value)) if err != nil { return f, err } f.re = re } return f, nil } ================================================ FILE: pkg/queries/field_test.go ================================================ package queries import ( "testing" "github.com/stretchr/testify/assert" ) func Test_field(t *testing.T) { field, err := newField("val") assert.NoError(t, err) assert.Equal(t, "val", field.value) assert.Equal(t, true, field.matches("val")) assert.Equal(t, false, field.matches("value")) field, err = newField("*") assert.NoError(t, err) assert.Equal(t, "*", field.value) assert.NotNil(t, field.re) assert.Equal(t, true, field.matches("val")) assert.Equal(t, true, field.matches("value")) field, err = newField("(.+") assert.EqualError(t, err, "error parsing regexp: missing closing ): `^(.+$`") assert.NotNil(t, field) field, err = newField("foo_*") assert.NoError(t, err) assert.Equal(t, "foo_*", field.value) assert.NotNil(t, field.re) assert.Equal(t, false, field.matches("foo")) assert.Equal(t, true, field.matches("foo_bar")) assert.Equal(t, true, field.matches("foo_bar_widget")) } func Test_fieldString(t *testing.T) { field, err := newField("val") assert.NoError(t, err) assert.Equal(t, "val", field.String()) } ================================================ FILE: pkg/queries/metadata.go ================================================ package queries import ( "fmt" "regexp" "strconv" "strings" "time" ) var ( reMetaPrefix = regexp.MustCompile(`(?m)^\s*--\s*pgweb:\s*(.+)`) reMetaContent = regexp.MustCompile(`([\w]+)\s*=\s*"([^"]+)"`) reMatchAll = regexp.MustCompile(`^(.+)$`) reExpression = regexp.MustCompile(`[\[\]\(\)\+\*]+`) allowedKeys = []string{"title", "description", "host", "user", "database", "mode", "timeout"} allowedModes = map[string]bool{"readonly": true, "*": true} ) type Metadata struct { Title string Description string Host field User field Database field Mode field Timeout *time.Duration } func parseMetadata(input string) (*Metadata, error) { fields, err := parseFields(input) if err != nil { return nil, err } if fields == nil { return nil, nil } // Host must be set to limit queries availability if fields["host"] == "" { return nil, fmt.Errorf("host field must be set") } // Allow matching for any user, database and mode by default if fields["user"] == "" { fields["user"] = "*" } if fields["database"] == "" { fields["database"] = "*" } if fields["mode"] == "" { fields["mode"] = "*" } hostField, err := newField(fields["host"]) if err != nil { return nil, fmt.Errorf(`error initializing "host" field: %w`, err) } userField, err := newField(fields["user"]) if err != nil { return nil, fmt.Errorf(`error initializing "user" field: %w`, err) } dbField, err := newField(fields["database"]) if err != nil { return nil, fmt.Errorf(`error initializing "database" field: %w`, err) } if !allowedModes[fields["mode"]] { return nil, fmt.Errorf(`invalid "mode" field value: %q`, fields["mode"]) } modeField, err := newField(fields["mode"]) if err != nil { return nil, fmt.Errorf(`error initializing "mode" field: %w`, err) } var timeout *time.Duration if fields["timeout"] != "" { timeoutSec, err := strconv.Atoi(fields["timeout"]) if err != nil { return nil, fmt.Errorf(`error initializing "timeout" field: %w`, err) } timeoutVal := time.Duration(timeoutSec) * time.Second timeout = &timeoutVal } return &Metadata{ Title: fields["title"], Description: fields["description"], Host: hostField, User: userField, Database: dbField, Mode: modeField, Timeout: timeout, }, nil } func parseFields(input string) (map[string]string, error) { result := map[string]string{} seenKeys := map[string]bool{} allowed := map[string]bool{} for _, key := range allowedKeys { allowed[key] = true } matches := reMetaPrefix.FindAllStringSubmatch(input, -1) if len(matches) == 0 { return nil, nil } for _, match := range matches { content := reMetaContent.FindAllStringSubmatch(match[1], -1) if len(content) == 0 { continue } for _, field := range content { key := field[1] value := field[2] if !allowed[key] { return result, fmt.Errorf("unknown key: %q", key) } if seenKeys[key] { return result, fmt.Errorf("duplicate key: %q", key) } seenKeys[key] = true result[key] = value } } return result, nil } func sanitizeMetadata(input string) string { lines := []string{} for _, line := range strings.Split(input, "\n") { line = reMetaPrefix.ReplaceAllString(line, "") if len(line) > 0 { lines = append(lines, line) } } return strings.Join(lines, "\n") } ================================================ FILE: pkg/queries/metadata_test.go ================================================ package queries import ( "testing" "github.com/stretchr/testify/assert" ) func Test_parseFields(t *testing.T) { examples := []struct { input string err error vals map[string]string }{ {input: "", err: nil, vals: nil}, {input: "foobar", err: nil, vals: nil}, {input: "-- no pgweb meta", err: nil, vals: nil}, { input: `--pgweb: foo=bar`, err: nil, vals: map[string]string{}, }, { input: `--pgweb: host="localhost"`, err: nil, vals: map[string]string{"host": "localhost"}, }, { input: `--pgweb: host="*" user="admin" database ="mydb"; mode = "readonly"`, err: nil, vals: map[string]string{ "host": "*", "database": "mydb", "user": "admin", "mode": "readonly", }, }, } for _, ex := range examples { t.Run(ex.input, func(t *testing.T) { fields, err := parseFields(ex.input) assert.Equal(t, ex.err, err) assert.Equal(t, ex.vals, fields) }) } } func Test_parseMetadata(t *testing.T) { examples := []struct { input string err string check func(meta *Metadata) bool }{ { input: `--pgweb: `, err: `host field must be set`, }, { input: `--pgweb: hello="world"`, err: `unknown key: "hello"`, }, { input: `--pgweb: host="localhost" user="anyuser" database="anydb" mode="foo"`, err: `invalid "mode" field value: "foo"`, }, { input: "--pgweb2:", check: func(m *Metadata) bool { return m == nil }, }, { input: `--pgweb: host="localhost"`, check: func(m *Metadata) bool { return m.Host.value == "localhost" && m.User.value == "*" && m.Database.value == "*" && m.Mode.value == "*" && m.Timeout == nil }, }, { input: `--pgweb: host="localhost" user="anyuser" database="anydb" mode="*"`, check: func(m *Metadata) bool { return m.Host.value == "localhost" && m.Host.re == nil && m.User.value == "anyuser" && m.Database.value == "anydb" && m.Mode.value == "*" && m.Timeout == nil }, }, { input: `--pgweb: host="localhost" timeout="foo"`, err: `error initializing "timeout" field: strconv.Atoi: parsing "foo": invalid syntax`, }, { input: `-- pgweb: host="local(host|dev)"`, check: func(m *Metadata) bool { return m.Host.value == "local(host|dev)" && m.Host.re != nil && m.Host.matches("localhost") && m.Host.matches("localdev") && !m.Host.matches("localfoo") && !m.Host.matches("superlocaldev") }, }, } for _, ex := range examples { t.Run(ex.input, func(t *testing.T) { meta, err := parseMetadata(ex.input) if ex.err != "" { assert.Contains(t, err.Error(), ex.err) } if ex.check != nil { assert.Equal(t, true, ex.check(meta)) } }) } } func Test_sanitizeMetadata(t *testing.T) { examples := []struct { input string output string }{ {input: "", output: ""}, {input: "foo", output: "foo"}, { input: ` -- pgweb: metadata query1 -- pgweb: more metadata query2 `, output: "query1\nquery2", }, } for _, ex := range examples { t.Run(ex.input, func(t *testing.T) { assert.Equal(t, ex.output, sanitizeMetadata(ex.input)) }) } } ================================================ FILE: pkg/queries/query.go ================================================ package queries type Query struct { ID string Path string Meta *Metadata Data string } // IsPermitted returns true if a query is allowed to execute for a given db context func (q Query) IsPermitted(host, user, database, mode string) bool { // All fields must be provided for matching if q.Meta == nil || host == "" || user == "" || database == "" || mode == "" { return false } meta := q.Meta return meta.Host.matches(host) && meta.User.matches(user) && meta.Database.matches(database) && meta.Mode.matches(mode) } ================================================ FILE: pkg/queries/query_test.go ================================================ package queries import ( "testing" "github.com/stretchr/testify/assert" ) func TestQueryIsPermitted(t *testing.T) { examples := []struct { name string query Query args []string expected bool }{ { name: "no input provided", query: makeQuery("localhost", "someuser", "somedb", "default"), args: makeArgs("", "", "", ""), expected: false, }, { name: "match on host", query: makeQuery("localhost", "*", "*", "*"), args: makeArgs("localhost", "user", "db", "default"), expected: true, }, { name: "match on full set", query: makeQuery("localhost", "user", "database", "mode"), args: makeArgs("localhost", "someuser", "somedb", "default"), expected: false, }, { name: "match on partial database", query: makeQuery("localhost", "*", "myapp_*", "*"), args: makeArgs("localhost", "user", "myapp_development", "default"), expected: true, }, { name: "match on full set but not mode", query: makeQuery("localhost", "*", "*", "readonly"), args: makeArgs("localhost", "user", "db", "default"), expected: false, }, } for _, ex := range examples { t.Run(ex.name, func(t *testing.T) { result := ex.query.IsPermitted(ex.args[0], ex.args[1], ex.args[2], ex.args[3]) assert.Equal(t, ex.expected, result) }) } } func makeArgs(vals ...string) []string { return vals } func makeQuery(host, user, database, mode string) Query { mustfield := func(input string) field { f, err := newField(input) if err != nil { panic(err) } return f } return Query{ Meta: &Metadata{ Host: mustfield(host), User: mustfield(user), Database: mustfield(database), Mode: mustfield(mode), }, } } ================================================ FILE: pkg/queries/store.go ================================================ package queries import ( "errors" "fmt" "os" "path/filepath" "strings" ) var ( ErrQueryDirNotExist = errors.New("queries directory does not exist") ErrQueryFileNotExist = errors.New("query file does not exist") ) type Store struct { dir string } func NewStore(dir string) *Store { return &Store{ dir: dir, } } func (s Store) Read(id string) (*Query, error) { path := filepath.Join(s.dir, fmt.Sprintf("%s.sql", id)) return readQuery(path) } func (s Store) ReadAll() ([]Query, error) { entries, err := os.ReadDir(s.dir) if err != nil { if errors.Is(err, os.ErrNotExist) { err = ErrQueryDirNotExist } return nil, err } queries := []Query{} for _, entry := range entries { name := entry.Name() if filepath.Ext(name) != ".sql" { continue } path := filepath.Join(s.dir, name) query, err := readQuery(path) if err != nil { fmt.Fprintf(os.Stderr, "[WARN] skipping %q query file due to error: %v\n", name, err) continue } if query == nil { continue } queries = append(queries, *query) } return queries, nil } func readQuery(path string) (*Query, error) { data, err := os.ReadFile(path) if err != nil { if errors.Is(err, os.ErrNotExist) { return nil, ErrQueryFileNotExist } return nil, err } dataStr := string(data) meta, err := parseMetadata(dataStr) if err != nil { return nil, err } if meta == nil { return nil, nil } return &Query{ ID: strings.Replace(filepath.Base(path), ".sql", "", 1), Path: path, Meta: meta, Data: sanitizeMetadata(dataStr), }, nil } ================================================ FILE: pkg/queries/store_test.go ================================================ //go:build !windows package queries import ( "testing" "github.com/stretchr/testify/assert" ) func TestStoreReadAll(t *testing.T) { t.Run("valid dir", func(t *testing.T) { queries, err := NewStore("../../data").ReadAll() assert.NoError(t, err) assert.Equal(t, 2, len(queries)) }) t.Run("invalid dir", func(t *testing.T) { queries, err := NewStore("../../data2").ReadAll() assert.Equal(t, err.Error(), "queries directory does not exist") assert.Equal(t, 0, len(queries)) }) } func TestStoreRead(t *testing.T) { examples := []struct { id string err string check func(*testing.T, *Query) }{ {id: "foo", err: "query file does not exist"}, {id: "lc_no_meta"}, {id: "lc_invalid_meta", err: `invalid "mode" field value: "foo"`}, { id: "lc_example1", check: func(t *testing.T, q *Query) { assert.Equal(t, "lc_example1", q.ID) assert.Equal(t, "../../data/lc_example1.sql", q.Path) assert.Equal(t, "select 'foo'", q.Data) assert.Equal(t, "localhost", q.Meta.Host.String()) assert.Equal(t, "*", q.Meta.User.String()) assert.Equal(t, "*", q.Meta.Database.String()) }, }, { id: "lc_example2", check: func(t *testing.T, q *Query) { assert.Equal(t, "lc_example2", q.ID) assert.Equal(t, "../../data/lc_example2.sql", q.Path) assert.Equal(t, "-- some comment\nselect 'foo'", q.Data) assert.Equal(t, "localhost", q.Meta.Host.String()) assert.Equal(t, "foo", q.Meta.User.String()) assert.Equal(t, "*", q.Meta.Database.String()) }, }, } store := NewStore("../../data") for _, ex := range examples { t.Run(ex.id, func(t *testing.T) { query, err := store.Read(ex.id) if ex.err != "" || err != nil { assert.Equal(t, ex.err, err.Error()) } if ex.check != nil { ex.check(t, query) } }) } } ================================================ FILE: pkg/shared/ssh_info.go ================================================ package shared import ( "fmt" ) // SSHInfo contains ssh server configuration type SSHInfo struct { Host string `json:"host,omitempty"` Port string `json:"port,omitempty"` User string `json:"user,omitempty"` Password string `json:"password,omitempty"` Key string `json:"key,omitempty"` KeyPassword string `json:"keypassword,omitempty"` } func (info SSHInfo) String() string { return fmt.Sprintf("%s@%s:%s", info.User, info.Host, info.Port) } ================================================ FILE: pkg/statements/sql/databases.sql ================================================ SELECT datname FROM pg_database WHERE NOT datistemplate ORDER BY datname ASC ================================================ FILE: pkg/statements/sql/estimated_row_count.sql ================================================ SELECT reltuples FROM pg_class WHERE oid = ('"' || $1::text || '"."' || $2::text || '"')::regclass ================================================ FILE: pkg/statements/sql/function.sql ================================================ SELECT p.oid, p.proname, p.pronamespace, p.proowner, pg_get_functiondef(oid) AS functiondef FROM pg_catalog.pg_proc p WHERE oid = $1::oid ================================================ FILE: pkg/statements/sql/info.sql ================================================ SELECT session_user, current_user, current_database(), current_schemas(false), inet_client_addr(), inet_client_port(), inet_server_addr(), inet_server_port(), version() ================================================ FILE: pkg/statements/sql/info_simple.sql ================================================ SELECT session_user, current_user, current_database(), current_schemas(false), version() ================================================ FILE: pkg/statements/sql/materialized_view.sql ================================================ SELECT attname AS column_name, atttypid::regtype AS data_type, (CASE WHEN attnotnull IS TRUE THEN 'NO' ELSE 'YES' END) AS is_nullable, NULL AS character_maximum_length, NULL AS character_set_catalog, NULL AS column_default FROM pg_attribute WHERE attrelid = $1::regclass AND attnum > 0 AND NOT attisdropped ================================================ FILE: pkg/statements/sql/objects.sql ================================================ WITH all_objects AS ( SELECT c.oid, n.nspname AS schema, c.relname AS name, CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' WHEN 'm' THEN 'materialized_view' WHEN 'i' THEN 'index' WHEN 'S' THEN 'sequence' WHEN 's' THEN 'special' WHEN 'f' THEN 'foreign_table' END AS type, pg_catalog.pg_get_userbyid(c.relowner) AS owner, pg_catalog.obj_description(c.oid) AS comment FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind IN ('r','v','m','S','s','') AND n.nspname !~ '^pg_(toast|temp)' AND n.nspname NOT IN ('information_schema', 'pg_catalog') AND has_schema_privilege(n.nspname, 'USAGE') UNION SELECT p.oid, n.nspname AS schema, p.proname AS name, 'function' AS function, pg_catalog.pg_get_userbyid(p.proowner) AS owner, NULL AS comment FROM pg_catalog.pg_namespace n JOIN pg_catalog.pg_proc p ON p.pronamespace = n.oid WHERE n.nspname !~ '^pg_(toast|temp)' AND n.nspname NOT IN ('information_schema', 'pg_catalog') ) SELECT * FROM all_objects ORDER BY 2, 3 ================================================ FILE: pkg/statements/sql/schemas.sql ================================================ SELECT schema_name FROM information_schema.schemata WHERE schema_name NOT IN ('information_schema', 'pg_catalog') AND schema_name !~ '^pg_(toast|temp)' ORDER BY schema_name ASC ================================================ FILE: pkg/statements/sql/settings.sql ================================================ SELECT * FROM pg_settings ================================================ FILE: pkg/statements/sql/table_constraints.sql ================================================ SELECT conname AS name, pg_get_constraintdef(c.oid, true) AS definition FROM pg_constraint c JOIN pg_namespace n ON n.oid = c.connamespace JOIN pg_class cl ON cl.oid = c.conrelid WHERE n.nspname = $1 AND relname = $2 AND contype != 'n' ORDER BY contype DESC ================================================ FILE: pkg/statements/sql/table_indexes.sql ================================================ SELECT indexname AS index_name, pg_size_pretty(pg_table_size(('"' || schemaname || '"."' || indexname || '"')::regclass)) AS index_size, indexdef AS index_definition FROM pg_indexes WHERE schemaname = $1 AND tablename = $2 ================================================ FILE: pkg/statements/sql/table_info.sql ================================================ SELECT pg_size_pretty(pg_table_size($1)) AS data_size, pg_size_pretty(pg_indexes_size($1)) AS index_size, pg_size_pretty(pg_total_relation_size($1)) AS total_size, (SELECT reltuples FROM pg_class WHERE oid = $1::regclass) AS rows_count ================================================ FILE: pkg/statements/sql/table_info_cockroach.sql ================================================ SELECT 'n/a' AS data_size, 'n/a' AS index_size, 'n/a' AS total_size, 'n/a' AS rows_count ================================================ FILE: pkg/statements/sql/table_schema.sql ================================================ SELECT column_name, data_type, is_nullable, character_maximum_length, character_set_catalog, column_default, pg_catalog.col_description(('"' || $1::text || '"."' || $2::text || '"')::regclass::oid, ordinal_position) as comment FROM information_schema.columns WHERE table_schema = $1 AND table_name = $2 ================================================ FILE: pkg/statements/sql/tables_stats.sql ================================================ WITH columns_counts AS ( SELECT table_schema, table_name, COUNT(1) AS num FROM information_schema.columns GROUP BY table_schema, table_name ), indexes_counts AS ( SELECT schemaname, tablename, COUNT(1) AS num FROM pg_indexes GROUP BY schemaname, tablename ) SELECT tables.schemaname AS schema_name, tables.relname AS table_name, pg_size_pretty(pg_total_relation_size(tables.relid)) AS total_size, pg_size_pretty(pg_table_size(tables.relid)) AS data_size, pg_size_pretty(pg_indexes_size(tables.relid)) AS index_size, pg_class.reltuples AS estimated_rows_count, CASE WHEN pg_class.reltuples >= 0 AND pg_class.reltuples < 1000 THEN pg_class.reltuples::text WHEN pg_class.reltuples >= 1000 AND pg_class.reltuples < 1000000 THEN ROUND((pg_class.reltuples / 1000))::text || 'K' WHEN pg_class.reltuples >= 1000000 THEN ROUND(pg_class.reltuples / 1000000)::text || 'M' END AS estimated_rows, CASE WHEN pg_class.reltuples > 1000 THEN ROUND(pg_indexes_size(tables.relid)::numeric / pg_table_size(tables.relid), 2) END AS index_to_data_ratio, indexes_counts.num AS indexes_count, columns_counts.num AS columns_count FROM pg_catalog.pg_statio_user_tables AS tables LEFT JOIN pg_class ON pg_class.oid = tables.relid LEFT JOIN indexes_counts ON indexes_counts.schemaname = tables.schemaname AND indexes_counts.tablename = tables.relname LEFT JOIN columns_counts ON columns_counts.table_schema = tables.schemaname AND columns_counts.table_name = tables.relname ORDER BY pg_total_relation_size(tables.relid) DESC, pg_table_size(tables.relid) DESC ================================================ FILE: pkg/statements/sql.go ================================================ package statements import ( _ "embed" ) var ( //go:embed sql/databases.sql Databases string //go:embed sql/schemas.sql Schemas string //go:embed sql/info.sql Info string //go:embed sql/info_simple.sql InfoSimple string //go:embed sql/estimated_row_count.sql EstimatedTableRowCount string //go:embed sql/table_indexes.sql TableIndexes string //go:embed sql/table_constraints.sql TableConstraints string //go:embed sql/table_info.sql TableInfo string //go:embed sql/table_info_cockroach.sql TableInfoCockroach string //go:embed sql/table_schema.sql TableSchema string //go:embed sql/materialized_view.sql MaterializedView string //go:embed sql/objects.sql Objects string //go:embed sql/tables_stats.sql TablesStats string //go:embed sql/function.sql Function string //go:embed sql/settings.sql Settings string // Activity queries for specific PG versions Activity = map[string]string{ "default": "SELECT * FROM pg_stat_activity WHERE datname = current_database()", "9.1": "SELECT datname, current_query, waiting, query_start, procpid as pid, datid, application_name, client_addr FROM pg_stat_activity WHERE datname = current_database()", "9.2": "SELECT datname, query, state, waiting, query_start, state_change, pid, datid, application_name, client_addr FROM pg_stat_activity WHERE datname = current_database()", "9.3": "SELECT datname, query, state, waiting, query_start, state_change, pid, datid, application_name, client_addr FROM pg_stat_activity WHERE datname = current_database()", "9.4": "SELECT datname, query, state, waiting, query_start, state_change, pid, datid, application_name, client_addr FROM pg_stat_activity WHERE datname = current_database()", "9.5": "SELECT datname, query, state, waiting, query_start, state_change, pid, datid, application_name, client_addr FROM pg_stat_activity WHERE datname = current_database()", "9.6": "SELECT datname, query, state, wait_event, wait_event_type, query_start, state_change, pid, datid, application_name, client_addr FROM pg_stat_activity WHERE datname = current_database()", } ) ================================================ FILE: pkg/util/profiler.go ================================================ package util import ( "log" "os" "runtime" "time" ) const MEGABYTE = 1024 * 1024 func runProfiler() { logger := log.New(os.Stdout, "", 0) m := &runtime.MemStats{} for { runtime.ReadMemStats(m) logger.Printf( "[DEBUG] Goroutines: %v, Mem used: %v (%v mb), Mem acquired: %v (%v mb)\n", runtime.NumGoroutine(), m.Alloc, m.Alloc/MEGABYTE, m.Sys, m.Sys/MEGABYTE, ) time.Sleep(time.Second * 30) } } func StartProfiler() { go runProfiler() } ================================================ FILE: script/build_all.sh ================================================ #!/usr/bin/env bash TARGETS="darwin/amd64 darwin/arm64 linux/amd64 linux/arm64 windows/amd64" ARM_TARGETS="arm/v5 arm64/v7" for target in $TARGETS; do echo "-> target: $target" parts=(${target//\// }) os=${parts[0]} arch=${parts[1]} GOOS=$os GOARCH=$arch go build -ldflags "$LDFLAGS" -o "./bin/pgweb_${os}_${arch}" done for target in $ARM_TARGETS; do echo "-> target: $target" parts=(${target//\// }) arch=${parts[0]} arm=$(echo ${parts[1]} | sed s/v//g) GOOS=linux GOARCH=$arch GOARM=$arm go build -ldflags "$LDFLAGS" -o "./bin/pgweb_linux_${arch}_v${arm}" done ================================================ FILE: script/check_formatting.sh ================================================ #!/bin/bash # Get list of offending files files="$(go fmt ./pkg/...)" if [ -n "$files" ]; then echo "Go code is not formatted:" for file in $files; do echo "----> $file" done exit 1 fi ================================================ FILE: script/package.sh ================================================ #!/bin/bash set -e DIR="./bin" rm -f $DIR/*.zip for file in $(ls $DIR) do fin=$DIR/$file fout=$DIR/$file.zip shasum -a 256 $fin zip -9 -q -j $fout $fin shasum -a 256 $fout done ================================================ FILE: script/test_all.sh ================================================ #!/bin/bash # # Integartion testing with dockerized Postgres servers # # Requires Docker for Mac to run on OSX. # Install: https://docs.docker.com/engine/installation/mac/ # set -e export PGHOST=${PGHOST:-localhost} export PGUSER="postgres" export PGPASSWORD="ci" export PGDATABASE="booktown" export PGPORT="15432" # TODO: Enable the 10.x branch when it's supported on Travis. # Local 10.x version is required so that pg_dump can properly work with older versions. # 10.x branch is normally supported. versions="9.1 9.2 9.3 9.4 9.5 9.6" for i in $versions do export PGVERSION="$i" echo "------------------------------- BEGIN TEST -------------------------------" echo "Running tests against PostgreSQL v$PGVERSION" docker rm -f postgres || true docker run -p $PGPORT:5432 --name postgres -e POSTGRES_PASSWORD=$PGPASSWORD -d postgres:$PGVERSION sleep 5 make test echo "-------------------------------- END TEST --------------------------------" done ================================================ FILE: script/test_cockroach.sh ================================================ #!/bin/bash set -e function killproc() { if [[ $(lsof -i tcp:8888) ]]; then lsof -i tcp:8888 | grep pgweb | awk '{print $2}' | xargs kill fi } # Nuke the old container if exists. docker rm -f cockroach || true # Start cockroach on 26258 so we dont mess with local server. docker run \ --name=cockroach \ -d \ -t \ -p 26258:26257 \ cockroachdb/cockroach:v20.2.5 \ start-single-node --insecure sleep 3 # Load the demo database. docker exec -i cockroach ./cockroach sql --insecure < ./data/roach.sql # Find and destroy the existing pgweb process. # Would be great if pgweb had --pid option. killproc # Start pgweb and connect to cockroach. make build ./pgweb \ --url=postgres://root@localhost:26258/roach?sslmode=disable \ --listen=8888 \ --skip-open & sleep 1 # Run smoke tests base="-w \"\n\" -f http://localhost:8888/api" table="product_information" curl $base/info curl $base/connection curl $base/schemas curl $base/objects curl $base/query -F query='select * from product_information;' curl $base/tables/$table curl $base/tables/$table/rows curl $base/tables/$table/info curl $base/tables/$table/indexes curl $base/tables/$table/constraints # Cleanup killproc ================================================ FILE: script/update_homebrew.sh ================================================ #!/bin/bash RELEASE_FILE="./tmp/release.json" HOMEBREW_ROOT="/usr/local/Homebrew/Library/Taps/homebrew/homebrew-core" export HOMEBREW_GITHUB_API_TOKEN=$(awk '/api.github.com/{getline;getline;print $2}' ~/.netrc) # Setup directory mkdir -p ./tmp rm -rf ./tmp/* # Fetch the latest published version curl -s https://api.github.com/repos/sosedoff/pgweb/releases/latest > $RELEASE_FILE VERSION="$(jq -r .tag_name < $RELEASE_FILE)" URL="https://github.com/sosedoff/pgweb/archive/$VERSION.tar.gz" URL_SHA256=$(wget -qO- $URL | shasum -a 256 | cut -d ' ' -f 1) # Reset any changes git -C $HOMEBREW_ROOT reset --hard # Update formula brew bump-formula-pr \ --url=$URL \ --sha256=$URL_SHA256 \ pgweb ================================================ FILE: static/css/app.css ================================================ #main { display: none; } #nav { position: fixed; top: 0; left: 250px; right: 0; height: 50px; background: #79589f; } #nav ul { margin: 0px; padding: 0px; height: 50px; display: block; } #nav ul li { margin: 0px; padding: 0px; float: left; height: 49px; line-height: 49px; font-size: 13px; padding: 0px 14px; color: #d6cce2; font-weight: 500; margin: 0 1px; cursor: pointer; display: block; list-style: none; list-style-type: none; } #nav ul li:first-child { margin-left: 0px; } #nav ul li:hover { color: #fff; } #nav ul li.selected { position: relative; background: #fff; color: #000; margin: 0; } #nav ul li.selected:before { content: ' '; position: absolute; bottom: -1px; left: 0; right: 0; height: 1px; widows: 100%; background: #fff; } #nav ul li.selected:first-child { border-left: none; } #sidebar { position: fixed; width: 250px; left: 0px; top: 0px; bottom: 0px; box-sizing: border-box; background: #f9f9f9; } #sidebar .current-database { position: fixed; left: 0px; top: 0px; width: 250px; height: 50px; line-height: 50px; font-weight: bold; font-size: 14px; color: #fff; background: #79589f; z-index: 900; } #sidebar .current-database .wrap { position: relative; height: 50px; } #sidebar .current-database span.current-database-name { margin-left: 30px; display: inline-block; width: 200px; height: 50px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: pointer; } #sidebar .current-database i.fa-database { position: absolute; left: 8px; top: 18px; } #sidebar .current-database input.typeahead { margin-left: 30px; display: inline-block; width: 200px; height: 50px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } #sidebar .current-database input.typeahead { display: none; background: #79589f; width: 200px; height: 22px; color: #fff; font-weight: normal; outline: none; border: 0px none; } #sidebar .current-database span.refresh { position: absolute; top: 0px; right: 6px; cursor: pointer; color: #fff; display: none; } #sidebar .current-database:hover span.refresh { display: inline-block; } #sidebar .current-database input.typeahead::-webkit-input-placeholder { color: #fff; } #sidebar ul.typeahead { position: absolute; margin-left: 30px; overflow-y: auto; max-height: 300px; z-index: 999; } #sidebar ul.typeahead a { font-size: 13px; padding: 3px 10px; } #sidebar .objects-search { position: fixed; top: 50px; left: 0px; width: 250px; height: 30px; overflow: hidden; } #sidebar .objects-search .wrap { position: relative; background: transparent; height: 30px; padding: 4px; box-sizing: border-box; border-bottom: 1px solid #eee; } #sidebar .objects-search i.fa-search { position: absolute; left: 8px; top: 7px; color: #a1a1a1; } #sidebar .objects-search i.fa-times-circle { position: absolute; right: 8px; top: 7px; color: #a1a1a1; display: none; cursor: pointer; } #sidebar .objects-search i.fa-times-circle:hover { color: #888; } #sidebar .objects-search input[type="text"] { background: transparent; border: 0px none; font-size: 12px; color: #000; height: 22px; line-height: 22px; padding: 0px; margin-left: 25px; width: 200px; outline: none; } #sidebar div.tables-list { position: fixed; width: 250px; top: 80px; left: 0px; bottom: 130px; overflow: auto; } #sidebar div.tables-list .wrap { height: 100%; overflow: auto; } #sidebar div.tables-list #tables, #sequences, #objects { font-size: 12px; } #sidebar div.table-information { position: fixed; width: 250px; left: 0px; height: 130px; bottom: 0px; box-sizing: border-box; cursor: default; } #sidebar div.table-information .wrap { height: 100%; overflow-y: auto; } #sidebar div.table-information .title { font-size: 12px; text-transform: uppercase; color: #aaa; line-height: 30px; padding: 0px 8px; border-top: 1px solid #eee; } #sidebar div.table-information .lines { display: none; } #sidebar div.table-information .lines .line { line-height: 24px; height: 24px; padding: 0 8px 0 8px; font-size: 12px; color: #7F7E7F !important; } #sidebar div.table-information .lines .line span { color: #4F4F4F; min-width: 0; padding-left: 0px; } #body { position: fixed; top: 50px; left: 250px; bottom: 0px; right: 0px; overflow: auto; } #input { width: 100%; height: 300px; position: relative; } #input .input-wrapper { height: 250px; padding-top: 12px; } #input_resize_handler { width: 100%; height: 1px; background: #ddd; cursor: row-resize; position: absolute; bottom: 0px; } #input_resize_handler:hover, #input_resize_handler.dragging { height: 3px; } #input .actions { background: #fff; padding: 10px; height: 50px; bottom: 0px; } #input .actions #result-rows-count { font-size: 12px; display: inline-block; height: 30px; line-height: 30px; float: left; margin-right: 10px; color: #999; } #input .actions .btn { line-height: 30px; height: 30px; padding: 0px 13px; margin: 0px; font-size: 13px; color: #555; border: none; box-shadow: none; background: transparent; border: 1px solid #999; float: left; margin-right: 10px; } #input .actions .btn-primary { border-color: #7eb54e; color: #7eb54e; } #input .actions .btn:hover { background: transparent !important; border-color: #444; color: #000; } #input .actions .btn-primary:hover { color: #64903e; border-color: #64903e; } #input .actions #query_progress { display: none; float: left; font-size: 12px; line-height: 30px; height: 30px; color: #aaa; } #input .actions .btn:focus { outline: 0 none; box-shadow: 0; } #input .actions .btn:hover { background: #7eb154; } #input .actions .btn-default:hover { background: #bbb; } #input .actions .btn:last-child { margin-right: 0px; } #output { position: absolute; left: 0px; top: 300px; bottom: 0px; right: 0px; margin: 0px; padding: 0px; overflow: auto; } #pagination { display: none; position: absolute; width: 100%; height: 50px; padding: 10px; top: 0px; left: 0px; background: #fff; border-bottom: 1px solid #eee; box-shadow: 0 1px 3px 0px #f1f1f1; } #pagination .pager-container { float: right; } #pagination .filters { float: left; font-size: 12px; } #pagination .filters span { display: inline-block; float: left; font-weight: bold; line-height: 32px; height: 32px; margin: 0px 8px; color: #999; } #pagination .filters select { font-size: 12px; width: 100px; float: left; line-height: 30px; height: 30px; margin-right: 8px; outline: none; } #pagination .filters select.column { width: 150px; } #pagination .filters select.filter { width: 100px; } #pagination .filters input { float: left; width: 200px; height: 30px; line-height: 30px; margin-right: 8px; font-size: 12px; } #pagination .filters .btn-primary { border-color: #7eb54e; color: #7eb54e; background: #fff; outline: none; float: left; margin-right: 8px; } #pagination .filters .btn-default { float: left; outline: none; } #pagination .btn-group { float: right; } #pagination .current-page { float: right; font-size: 12px; margin-right: 12px; color: #999; line-height: 32px; height: 32px; } #results { font-size: 12px; margin: 0px; padding: 0px; } #results.empty td { border: 0px none; } #results tr:nth-child(even) > td { border: none; background: #ffffff; } #results tr:nth-child(odd) > td { border: none; background: #f9f9f9; } #results th { border-top: none; border-bottom: 1px solid #eae9e9; padding: 3px 9px; line-height: 24px; color: #333; font-weight: bold; cursor: pointer; -webkit-font-smoothing: antialiased; -moz-user-select: none; -khtml-user-select: none; -webkit-user-select: none; user-select: none; } #results th:hover { background: #f8f8f8; } #results th.active { background: #f3faff; } #results[data-mode="browse"] th:hover { cursor: pointer; background: #fafafa; } #results tbody tr:hover td { background: #ffe; } #results tbody tr { border-bottom: 1px solid #f2f2f2; } #results tr.selected td { background: #3874d7 !important; color: #fff !important; } #results td { color: #3a3633; vertical-align: middle; border: 0px none; } #results td div { max-width: 350px; max-height: 51px; overflow: hidden; text-overflow: ellipsis; white-space: pre; } #results td div span.null { color: #bbb; text-transform: uppercase; } #results td textarea { color: #000 !important; font-size: 12px; } #results th:first-child, #results td:first-child { padding-left: 15px; } #results.no-crop td div { max-width: none; } #results_view { display: none; padding: 12px; font-size: 14px; } #results_view .title { margin-bottom: 8px; } #results_view pre { border: 0px none; position: relative; } #results_view .copy { position: absolute; display: none; text-align: center; line-height: 30px; right: 4px; top: 4px; width: 30px; height: 30px; background: #fff; border: 1px solid #ddd; border-radius: 3px; cursor: pointer; } #results_view .copy:hover { border-color: #999; } #results_view pre:hover .copy { display: block; } .full #output { top: 0px !important; } .with-pagination #output { top: 50px !important; } .with-pagination #pagination { display: block; } .left { float: left; } #content_modal { display: none; width: 60%; height: 400px; position: fixed; top: 20%; left: 20%; background: #fff; border: 1px solid #ccc; box-shadow: #ddd 0 0 20px; } #content_modal .content { border: 0px none; position: relative; padding: 8px; white-space: break-spaces; background: #fff; overflow-y: scroll; height: 366px; box-sizing: border-box; } #content_modal .title { font-weight: bold; line-height: 24px; background: #f5f5f5; padding: 4px 4px 4px 8px; } #content_modal .actions { float: right; } #content_modal .actions .fa { float: right; width: 24px; height: 24px; line-height: 24px; font-size: 13px; text-align: center; background: #fff; border: 1px solid #ddd; border-radius: 3px; cursor: pointer; margin-left: 4px; } #content_modal .actions .fa:hover { border-color: #999; box-shadow: #eee 0 0 5px; } #error_banner { line-height: 30px; text-align: center; background-color: #be2740; color: #fff; display: none; position: fixed; bottom: 0px; left: 0px; right: 0px; height: 30px; } /* -------------------------------------------------------------------------- */ #custom_query { height: 238px; } #connection_window { z-index: 3; position: fixed; background: #fff; top: 0px; left: 0px; bottom: 0px; right: 0px; display: none; overflow-y: auto; } .connection-actions { position: fixed; right: 8px; top: 10px; display: none; } #edit_connection, #close_connection { background: #79589f; color: #fff; border-color: #fff; } #edit_connection i { margin-right: 4px; } #close_connection_window { display: none; } #connection_error { display: none; } .bookmarks { display: none; } .connection-settings { width: 600px; margin: 0px auto; margin-top: 50px; } .connection-settings .header { margin-bottom: 25px; } .connection-settings .header h1 { text-align: center; text-shadow: 0px 1px 0px #fff; color: #999; font-weight: normal; } .connection-settings .header .version { font-size: 12px; color: #aaa; text-align: center; display: block; } .connection-settings .header .update { font-size: 12px; text-align: center; padding: 4px; margin: 12px 0px; display: none; } .connection-settings form { background: #f6f6f6; padding: 25px; border-radius: 4px; -moz-border-radius: 4px; -webkit-border-radius: 4px; } .connection-settings form p.help-block { font-size: 12px; } .connection-settings form .no-left-padding { padding-left: 0; } .connection-scheme-group { display: none; } .connection-ssh-group { display: none; z-index: 1000; } .connection-ssh-group h3 { font-size: 18px; margin: 0px 0px 20px 0px; color: #aaa; } /* -------------------------------------------------------------------------- */ /* Sidebar Schema Objects */ /* -------------------------------------------------------------------------- */ .schema {} .schema i { display: inline-block; margin-right: 4px; } .schema i.fa-folder-o { display: inline-block; } .schema i.fa-folder-open-o { display: none; } .schema.expanded i.fa-folder-open-o { display: inline-block; } .schema.expanded i.fa-folder-o { display: none; } .schema .schema-name { font-weight: bold; font-size: 13px; display: block; line-height: 30px; height: 30px; padding: 0px 8px; cursor: pointer; white-space: nowrap; } .schema .schema-container { display: none; } .schema.expanded .schema-container { display: block; } .schema .schema-container .schema-group .fa-chevron-down { display: none; } .schema .schema-container .schema-group .schema-group-title { display: block; cursor: pointer; line-height: 30px; height: 30px; padding: 0px 8px; overflow: hidden; } .schema .schema-container .schema-group .schema-group-count { color: #999; display: inline-block; float: right; margin-right: 8px; } .schema .schema-container .schema-group ul { padding: 0px; margin: 0px; display: none; } .schema .schema-container .schema-group ul li { list-style: none; list-style-type: none; margin: 0px; line-height: 30px; height: 30px; cursor: pointer; padding: 0px 8px; padding-left: 16px; white-space: nowrap; overflow: x-scroll; } .schema .schema-container .schema-group ul li i { color: #999; } .schema .schema-container .schema-group ul li.active { background: #f1f1f1; color: #333; font-weight: bold; } .schema .schema-container .schema-group ul li:hover { background: #f1f1f1; } .schema .schema-container .schema-group.expanded .fa-chevron-down { display: inline-block; } .schema .schema-container .schema-group.expanded .fa-chevron-right { display: none; } .schema .schema-container .schema-group.expanded ul { display: block; } /* -------------------------------------------------------------------------- */ /* Ace Customizations */ /* -------------------------------------------------------------------------- */ .ace_gutter, .ace_gutter-cell { background: #fff !important; color: #bbb; font-size: 12px; } .ace_content .ace_active-line { background: none !important; } .ace_autocomplete .ace_active-line { background: #eee !important; } ================================================ FILE: static/css/bootstrap.css ================================================ /*! * Bootstrap v3.2.0 (http://getbootstrap.com) * Copyright 2011-2014 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) *//*! normalize.css v3.0.1 | MIT License | git.io/normalize */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background:0 0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}@media print{*{color:#000!important;text-shadow:none!important;background:transparent!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}select{background:#fff!important}.navbar{display:none}.table td,.table th{background-color:#fff!important}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table-bordered th,.table-bordered td{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:before,:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#428bca;text-decoration:none}a:hover,a:focus{color:#2a6496;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;width:100% \9;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;width:100% \9;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#777}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}cite{font-style:normal}mark,.mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#428bca}a.text-primary:hover{color:#3071a9}.text-success{color:#3c763d}a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#428bca}a.bg-primary:hover{background-color:#3071a9}.bg-success{background-color:#dff0d8}a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote footer:before,blockquote small:before,blockquote .small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:''}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:'\00A0 \2014'}blockquote:before,blockquote:after{content:""}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-child(odd)>td,.table-striped>tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover>tbody>tr:hover>td,.table-hover>tbody>tr:hover>th{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=radio],input[type=checkbox]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=radio]:focus,input[type=checkbox]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#777;opacity:1}.form-control:-ms-input-placeholder{color:#777}.form-control::-webkit-input-placeholder{color:#777}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{cursor:not-allowed;background-color:#eee;opacity:1}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}input[type=date],input[type=time],input[type=datetime-local],input[type=month]{line-height:34px;line-height:1.42857143 \0}input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;min-height:20px;margin-top:10px;margin-bottom:10px}.radio label,.checkbox label{padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.radio input[type=radio],.radio-inline input[type=radio],.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox]{position:absolute;margin-top:4px \9;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}input[type=radio][disabled],input[type=checkbox][disabled],input[type=radio].disabled,input[type=checkbox].disabled,fieldset[disabled] input[type=radio],fieldset[disabled] input[type=checkbox]{cursor:not-allowed}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.form-control-static{padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm,.form-horizontal .form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}textarea.input-sm,select[multiple].input-sm{height:auto}.input-lg,.form-horizontal .form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-lg{height:46px;line-height:46px}textarea.input-lg,select[multiple].input-lg{height:auto}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:25px;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center}.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type=radio],.form-inline .checkbox input[type=checkbox]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .radio,.form-horizontal .checkbox{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{top:0;right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14.3px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn:focus,.btn:active:focus,.btn.active:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus{color:#333;text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{pointer-events:none;cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:hover,.btn-default:focus,.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default[disabled],fieldset[disabled] .btn-default,.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled:active,.btn-default[disabled]:active,fieldset[disabled] .btn-default:active,.btn-default.disabled.active,.btn-default[disabled].active,fieldset[disabled] .btn-default.active{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#428bca;border-color:#357ebd}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#3071a9;border-color:#285e8e}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary[disabled],fieldset[disabled] .btn-primary,.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled:active,.btn-primary[disabled]:active,fieldset[disabled] .btn-primary:active,.btn-primary.disabled.active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary.active{background-color:#428bca;border-color:#357ebd}.btn-primary .badge{color:#428bca;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success[disabled],fieldset[disabled] .btn-success,.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled:active,.btn-success[disabled]:active,fieldset[disabled] .btn-success:active,.btn-success.disabled.active,.btn-success[disabled].active,fieldset[disabled] .btn-success.active{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info[disabled],fieldset[disabled] .btn-info,.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled:active,.btn-info[disabled]:active,fieldset[disabled] .btn-info:active,.btn-info.disabled.active,.btn-info[disabled].active,fieldset[disabled] .btn-info.active{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-warning,.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled:active,.btn-warning[disabled]:active,fieldset[disabled] .btn-warning:active,.btn-warning.disabled.active,.btn-warning[disabled].active,fieldset[disabled] .btn-warning.active{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger[disabled],fieldset[disabled] .btn-danger,.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled:active,.btn-danger[disabled]:active,fieldset[disabled] .btn-danger:active,.btn-danger.disabled.active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger.active{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#428bca;cursor:pointer;border-radius:0}.btn-link,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#2a6496;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#777;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=submit].btn-block,input[type=reset].btn-block,input[type=button].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;-o-transition:height .35s ease;transition:height .35s ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px solid;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;background-color:#428bca;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#777}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px solid}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group>.btn:focus,.btn-group-vertical>.btn:focus{outline:0}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child>.btn:last-child,.btn-group>.btn-group:first-child>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn>input[type=radio],[data-toggle=buttons]>.btn>input[type=checkbox]{position:absolute;z-index:-1;filter:alpha(opacity=0);opacity:0}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=radio],.input-group-addon input[type=checkbox]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eee;border-color:#428bca}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#fff;background-color:#428bca}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;-webkit-transform:translate3d(0,0,0);-o-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}.navbar-nav.navbar-right:last-child{margin-right:-15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type=radio],.navbar-form .checkbox input[type=checkbox]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-form.navbar-right:last-child{margin-right:-15px}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}.navbar-text.navbar-right:last-child{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#333}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#777}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#777}.navbar-inverse .navbar-nav>li>a{color:#777}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#777}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#777}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#fff}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#428bca;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{color:#2a6496;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:2;color:#fff;cursor:default;background-color:#428bca;border-color:#428bca}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:hover,.label-default[href]:focus{background-color:#5e5e5e}.label-primary{background-color:#428bca}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#3071a9}.label-success{background-color:#5cb85c}.label-success[href]:hover,.label-success[href]:focus{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}a.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#428bca;background-color:#fff}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron{padding-right:60px;padding-left:60px}.jumbotron h1,.jumbotron .h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-right:auto;margin-left:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#428bca}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#428bca;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar[aria-valuenow="1"],.progress-bar[aria-valuenow="2"]{min-width:30px}.progress-bar[aria-valuenow="0"]{min-width:30px;color:#777;background-color:transparent;background-image:none;-webkit-box-shadow:none;box-shadow:none}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media,.media-body{overflow:hidden;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media>.pull-left{margin-right:10px}.media>.pull-right{margin-left:10px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}a.list-group-item{color:#555}a.list-group-item .list-group-item-heading{color:#333}a.list-group-item:hover,a.list-group-item:focus{color:#555;text-decoration:none;background-color:#f5f5f5}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{color:#777;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#fff;background-color:#428bca;border-color:#428bca}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#e1edf7}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,a.list-group-item-success:focus{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:hover,a.list-group-item-success.active:focus{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,a.list-group-item-info:focus{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:hover,a.list-group-item-info.active:focus{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,a.list-group-item-warning:focus{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,a.list-group-item-danger:focus{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group{margin-bottom:0}.panel>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#428bca}.panel-primary>.panel-heading{color:#fff;background-color:#428bca;border-color:#428bca}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#428bca}.panel-primary>.panel-heading .badge{color:#428bca;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#428bca}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate3d(0,-25%,0);-o-transform:translate3d(0,-25%,0);transform:translate3d(0,-25%,0)}.modal.in .modal-dialog{-webkit-transform:translate3d(0,0,0);-o-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{min-height:16.43px;padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-size:12px;line-height:1.4;visibility:visible;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{bottom:0;left:5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{right:5px;bottom:0;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;left:5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;right:5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;text-align:left;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;font-weight:400;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:hover,.carousel-control:focus{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;margin-top:-10px;font-family:serif}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-footer:before,.modal-footer:after{display:table;content:" "}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-footer:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important;visibility:hidden!important}.affix{position:fixed;-webkit-transform:translate3d(0,0,0);-o-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none!important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table}tr.visible-xs{display:table-row!important}th.visible-xs,td.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table}tr.visible-sm{display:table-row!important}th.visible-sm,td.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table}tr.visible-md{display:table-row!important}th.visible-md,td.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table}tr.visible-lg{display:table-row!important}th.visible-lg,td.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table}tr.visible-print{display:table-row!important}th.visible-print,td.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} ================================================ FILE: static/css/font-awesome.css ================================================ /*! * Font Awesome 4.2.0 by @davegandy - http://fontawesome.io - @fontawesome * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.2.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.2.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff?v=4.2.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.2.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.2.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"} ================================================ FILE: static/data.go ================================================ package static import ( "embed" "net/http" "os" ) //go:embed img/* js/* css/* fonts/* //go:embed index.html var assets embed.FS func GetFilesystem() http.FileSystem { if os.Getenv("PGWEB_ASSETS_DEVMODE") == "1" { return http.Dir("./static") } return http.FS(assets) } func GetHandler() http.Handler { return http.FileServer(GetFilesystem()) } ================================================ FILE: static/index.html ================================================ pgweb
Please wait, query is executing...
Cell Content

  

pgweb


URL format: postgres://user:password@host:port/db?sslmode=mode
Read more on PostgreSQL connection string format.


SSH Connection


================================================ FILE: static/js/ace-pgsql.js ================================================ ace.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}),ace.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}),ace.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|async|await|nonlocal",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|bin|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|apply|delattr|help|next|setattr|set|buffer|dict|hex|object|slice|coerce|dir|id|oct|sorted|intern|ascii|breakpoint|bytes",r=this.createKeywordMapper({"invalid.deprecated":"debugger","support.function":n,"variable.language":"self|cls","constant.language":t,keyword:e},"identifier"),i="[uU]?",s="[rR]",o="[fF]",u="(?:[rR][fF]|[fF][rR])",a="(?:(?:[1-9]\\d*)|(?:0))",f="(?:0[oO]?[0-7]+)",l="(?:0[xX][\\dA-Fa-f]+)",c="(?:0[bB][01]+)",h="(?:"+a+"|"+f+"|"+l+"|"+c+")",p="(?:[eE][+-]?\\d+)",d="(?:\\.\\d+)",v="(?:\\d+)",m="(?:(?:"+v+"?"+d+")|(?:"+v+"\\.))",g="(?:(?:"+m+"|"+v+")"+p+")",y="(?:"+g+"|"+m+")",b="\\\\(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:"string",regex:s+'"{3}',next:"rawqqstring3"},{token:"string",regex:s+'"(?=.)',next:"rawqqstring"},{token:"string",regex:s+"'{3}",next:"rawqstring3"},{token:"string",regex:s+"'(?=.)",next:"rawqstring"},{token:"string",regex:o+'"{3}',next:"fqqstring3"},{token:"string",regex:o+'"(?=.)',next:"fqqstring"},{token:"string",regex:o+"'{3}",next:"fqstring3"},{token:"string",regex:o+"'(?=.)",next:"fqstring"},{token:"string",regex:u+'"{3}',next:"rfqqstring3"},{token:"string",regex:u+'"(?=.)',next:"rfqqstring"},{token:"string",regex:u+"'{3}",next:"rfqstring3"},{token:"string",regex:u+"'(?=.)",next:"rfqstring"},{token:"keyword.operator",regex:"\\+|\\-|\\*|\\*\\*|\\/|\\/\\/|%|@|<<|>>|&|\\||\\^|~|<|>|<=|=>|==|!=|<>|="},{token:"punctuation",regex:",|:|;|\\->|\\+=|\\-=|\\*=|\\/=|\\/\\/=|%=|@=|&=|\\|=|^=|>>=|<<=|\\*\\*="},{token:"paren.lparen",regex:"[\\[\\(\\{]"},{token:"paren.rparen",regex:"[\\]\\)\\}]"},{token:"text",regex:"\\s+"},{include:"constants"}],qqstring3:[{token:"constant.language.escape",regex:b},{token:"string",regex:'"{3}',next:"start"},{defaultToken:"string"}],qstring3:[{token:"constant.language.escape",regex:b},{token:"string",regex:"'{3}",next:"start"},{defaultToken:"string"}],qqstring:[{token:"constant.language.escape",regex:b},{token:"string",regex:"\\\\$",next:"qqstring"},{token:"string",regex:'"|$',next:"start"},{defaultToken:"string"}],qstring:[{token:"constant.language.escape",regex:b},{token:"string",regex:"\\\\$",next:"qstring"},{token:"string",regex:"'|$",next:"start"},{defaultToken:"string"}],rawqqstring3:[{token:"string",regex:'"{3}',next:"start"},{defaultToken:"string"}],rawqstring3:[{token:"string",regex:"'{3}",next:"start"},{defaultToken:"string"}],rawqqstring:[{token:"string",regex:"\\\\$",next:"rawqqstring"},{token:"string",regex:'"|$',next:"start"},{defaultToken:"string"}],rawqstring:[{token:"string",regex:"\\\\$",next:"rawqstring"},{token:"string",regex:"'|$",next:"start"},{defaultToken:"string"}],fqqstring3:[{token:"constant.language.escape",regex:b},{token:"string",regex:'"{3}',next:"start"},{token:"paren.lparen",regex:"{",push:"fqstringParRules"},{defaultToken:"string"}],fqstring3:[{token:"constant.language.escape",regex:b},{token:"string",regex:"'{3}",next:"start"},{token:"paren.lparen",regex:"{",push:"fqstringParRules"},{defaultToken:"string"}],fqqstring:[{token:"constant.language.escape",regex:b},{token:"string",regex:"\\\\$",next:"fqqstring"},{token:"string",regex:'"|$',next:"start"},{token:"paren.lparen",regex:"{",push:"fqstringParRules"},{defaultToken:"string"}],fqstring:[{token:"constant.language.escape",regex:b},{token:"string",regex:"'|$",next:"start"},{token:"paren.lparen",regex:"{",push:"fqstringParRules"},{defaultToken:"string"}],rfqqstring3:[{token:"string",regex:'"{3}',next:"start"},{token:"paren.lparen",regex:"{",push:"fqstringParRules"},{defaultToken:"string"}],rfqstring3:[{token:"string",regex:"'{3}",next:"start"},{token:"paren.lparen",regex:"{",push:"fqstringParRules"},{defaultToken:"string"}],rfqqstring:[{token:"string",regex:"\\\\$",next:"rfqqstring"},{token:"string",regex:'"|$',next:"start"},{token:"paren.lparen",regex:"{",push:"fqstringParRules"},{defaultToken:"string"}],rfqstring:[{token:"string",regex:"'|$",next:"start"},{token:"paren.lparen",regex:"{",push:"fqstringParRules"},{defaultToken:"string"}],fqstringParRules:[{token:"paren.lparen",regex:"[\\[\\(]"},{token:"paren.rparen",regex:"[\\]\\)]"},{token:"string",regex:"\\s+"},{token:"string",regex:"'(.)*'"},{token:"string",regex:'"(.)*"'},{token:"function.support",regex:"(!s|!r|!a)"},{include:"constants"},{token:"paren.rparen",regex:"}",next:"pop"},{token:"paren.lparen",regex:"{",push:"fqstringParRules"}],constants:[{token:"constant.numeric",regex:"(?:"+y+"|\\d+)[jJ]\\b"},{token:"constant.numeric",regex:y},{token:"constant.numeric",regex:h+"[lL]\\b"},{token:"constant.numeric",regex:h+"\\b"},{token:["punctuation","function.support"],regex:"(\\.)([a-zA-Z_]+)\\b"},{token:r,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"}]},this.normalizeRules()};r.inherits(s,i),t.PythonHighlightRules=s}),ace.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:"text",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"comment",regex:"\\/\\/.*$"},{token:"comment.start",regex:"\\/\\*",next:"comment"},{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:'"|$',next:"start"},{defaultToken:"string"}],comment:[{token:"comment.end",regex:"\\*\\/",next:"start"},{defaultToken:"comment"}]}};r.inherits(s,i),t.JsonHighlightRules=s}),ace.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]+|[oO][0-7]+|[bB][01]+)\b/},{token:"constant.numeric",regex:/(?:\d\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:"from(?=\\s*('|\"))"},{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:"storage.type",regex:/=>/,next:"start"},{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:"\\\\$",consumeLineEnd:!0},{token:"string",regex:'"|$',next:"no_regex"},{defaultToken:"string"}],qstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",consumeLineEnd:!0},{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}),ace.define("ace/mode/pgsql_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules","ace/mode/perl_highlight_rules","ace/mode/python_highlight_rules","ace/mode/json_highlight_rules","ace/mode/javascript_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=e("./perl_highlight_rules").PerlHighlightRules,a=e("./python_highlight_rules").PythonHighlightRules,f=e("./json_highlight_rules").JsonHighlightRules,l=e("./javascript_highlight_rules").JavaScriptHighlightRules,c=function(){var e="abort|absolute|abstime|access|aclitem|action|add|admin|after|aggregate|all|also|alter|always|analyse|analyze|and|any|anyarray|anyelement|anyenum|anynonarray|anyrange|array|as|asc|assertion|assignment|asymmetric|at|attribute|authorization|backward|before|begin|between|bigint|binary|bit|bool|boolean|both|box|bpchar|by|bytea|cache|called|cascade|cascaded|case|cast|catalog|chain|char|character|characteristics|check|checkpoint|cid|cidr|circle|class|close|cluster|coalesce|collate|collation|column|comment|comments|commit|committed|concurrently|configuration|connection|constraint|constraints|content|continue|conversion|copy|cost|create|cross|cstring|csv|current|current_catalog|current_date|current_role|current_schema|current_time|current_timestamp|current_user|cursor|cycle|data|database|date|daterange|day|deallocate|dec|decimal|declare|default|defaults|deferrable|deferred|definer|delete|delimiter|delimiters|desc|dictionary|disable|discard|distinct|do|document|domain|double|drop|each|else|enable|encoding|encrypted|end|enum|escape|event|event_trigger|except|exclude|excluding|exclusive|execute|exists|explain|extension|external|extract|false|family|fdw_handler|fetch|first|float|float4|float8|following|for|force|foreign|forward|freeze|from|full|function|functions|global|grant|granted|greatest|group|gtsvector|handler|having|header|hold|hour|identity|if|ilike|immediate|immutable|implicit|in|including|increment|index|indexes|inet|inherit|inherits|initially|inline|inner|inout|input|insensitive|insert|instead|int|int2|int2vector|int4|int4range|int8|int8range|integer|internal|intersect|interval|into|invoker|is|isnull|isolation|join|json|key|label|language|language_handler|large|last|lateral|lc_collate|lc_ctype|leading|leakproof|least|left|level|like|limit|line|listen|load|local|localtime|localtimestamp|location|lock|lseg|macaddr|mapping|match|materialized|maxvalue|minute|minvalue|mode|money|month|move|name|names|national|natural|nchar|next|no|none|not|nothing|notify|notnull|nowait|null|nullif|nulls|numeric|numrange|object|of|off|offset|oid|oids|oidvector|on|only|opaque|operator|option|options|or|order|out|outer|over|overlaps|overlay|owned|owner|parser|partial|partition|passing|password|path|pg_attribute|pg_auth_members|pg_authid|pg_class|pg_database|pg_node_tree|pg_proc|pg_type|placing|plans|point|polygon|position|preceding|precision|prepare|prepared|preserve|primary|prior|privileges|procedural|procedure|program|quote|range|read|real|reassign|recheck|record|recursive|ref|refcursor|references|refresh|regclass|regconfig|regdictionary|regoper|regoperator|regproc|regprocedure|regtype|reindex|relative|release|reltime|rename|repeatable|replace|replica|reset|restart|restrict|returning|returns|revoke|right|role|rollback|row|rows|rule|savepoint|schema|scroll|search|second|security|select|sequence|sequences|serializable|server|session|session_user|set|setof|share|show|similar|simple|smallint|smgr|snapshot|some|stable|standalone|start|statement|statistics|stdin|stdout|storage|strict|strip|substring|symmetric|sysid|system|table|tables|tablespace|temp|template|temporary|text|then|tid|time|timestamp|timestamptz|timetz|tinterval|to|trailing|transaction|treat|trigger|trim|true|truncate|trusted|tsquery|tsrange|tstzrange|tsvector|txid_snapshot|type|types|unbounded|uncommitted|unencrypted|union|unique|unknown|unlisten|unlogged|until|update|user|using|uuid|vacuum|valid|validate|validator|value|values|varbit|varchar|variadic|varying|verbose|version|view|void|volatile|when|where|whitespace|window|with|without|work|wrapper|write|xid|xml|xmlattributes|xmlconcat|xmlelement|xmlexists|xmlforest|xmlparse|xmlpi|xmlroot|xmlserialize|year|yes|zone",t="RI_FKey_cascade_del|RI_FKey_cascade_upd|RI_FKey_check_ins|RI_FKey_check_upd|RI_FKey_noaction_del|RI_FKey_noaction_upd|RI_FKey_restrict_del|RI_FKey_restrict_upd|RI_FKey_setdefault_del|RI_FKey_setdefault_upd|RI_FKey_setnull_del|RI_FKey_setnull_upd|abbrev|abs|abstime|abstimeeq|abstimege|abstimegt|abstimein|abstimele|abstimelt|abstimene|abstimeout|abstimerecv|abstimesend|aclcontains|acldefault|aclexplode|aclinsert|aclitemeq|aclitemin|aclitemout|aclremove|acos|age|any_in|any_out|anyarray_in|anyarray_out|anyarray_recv|anyarray_send|anyelement_in|anyelement_out|anyenum_in|anyenum_out|anynonarray_in|anynonarray_out|anyrange_in|anyrange_out|anytextcat|area|areajoinsel|areasel|array_agg|array_agg_finalfn|array_agg_transfn|array_append|array_cat|array_dims|array_eq|array_fill|array_ge|array_gt|array_in|array_larger|array_le|array_length|array_lower|array_lt|array_ndims|array_ne|array_out|array_prepend|array_recv|array_remove|array_replace|array_send|array_smaller|array_to_json|array_to_string|array_typanalyze|array_upper|arraycontained|arraycontains|arraycontjoinsel|arraycontsel|arrayoverlap|ascii|ascii_to_mic|ascii_to_utf8|asin|atan|atan2|avg|big5_to_euc_tw|big5_to_mic|big5_to_utf8|bit_and|bit_in|bit_length|bit_or|bit_out|bit_recv|bit_send|bitand|bitcat|bitcmp|biteq|bitge|bitgt|bitle|bitlt|bitne|bitnot|bitor|bitshiftleft|bitshiftright|bittypmodin|bittypmodout|bitxor|bool|bool_and|bool_or|booland_statefunc|booleq|boolge|boolgt|boolin|boolle|boollt|boolne|boolor_statefunc|boolout|boolrecv|boolsend|box|box_above|box_above_eq|box_add|box_below|box_below_eq|box_center|box_contain|box_contain_pt|box_contained|box_distance|box_div|box_eq|box_ge|box_gt|box_in|box_intersect|box_le|box_left|box_lt|box_mul|box_out|box_overabove|box_overbelow|box_overlap|box_overleft|box_overright|box_recv|box_right|box_same|box_send|box_sub|bpchar_larger|bpchar_pattern_ge|bpchar_pattern_gt|bpchar_pattern_le|bpchar_pattern_lt|bpchar_smaller|bpcharcmp|bpchareq|bpcharge|bpchargt|bpchariclike|bpcharicnlike|bpcharicregexeq|bpcharicregexne|bpcharin|bpcharle|bpcharlike|bpcharlt|bpcharne|bpcharnlike|bpcharout|bpcharrecv|bpcharregexeq|bpcharregexne|bpcharsend|bpchartypmodin|bpchartypmodout|broadcast|btabstimecmp|btarraycmp|btbeginscan|btboolcmp|btbpchar_pattern_cmp|btbuild|btbuildempty|btbulkdelete|btcanreturn|btcharcmp|btcostestimate|btendscan|btfloat48cmp|btfloat4cmp|btfloat4sortsupport|btfloat84cmp|btfloat8cmp|btfloat8sortsupport|btgetbitmap|btgettuple|btinsert|btint24cmp|btint28cmp|btint2cmp|btint2sortsupport|btint42cmp|btint48cmp|btint4cmp|btint4sortsupport|btint82cmp|btint84cmp|btint8cmp|btint8sortsupport|btmarkpos|btnamecmp|btnamesortsupport|btoidcmp|btoidsortsupport|btoidvectorcmp|btoptions|btrecordcmp|btreltimecmp|btrescan|btrestrpos|btrim|bttext_pattern_cmp|bttextcmp|bttidcmp|bttintervalcmp|btvacuumcleanup|bytea_string_agg_finalfn|bytea_string_agg_transfn|byteacat|byteacmp|byteaeq|byteage|byteagt|byteain|byteale|bytealike|bytealt|byteane|byteanlike|byteaout|bytearecv|byteasend|cash_cmp|cash_div_cash|cash_div_flt4|cash_div_flt8|cash_div_int2|cash_div_int4|cash_eq|cash_ge|cash_gt|cash_in|cash_le|cash_lt|cash_mi|cash_mul_flt4|cash_mul_flt8|cash_mul_int2|cash_mul_int4|cash_ne|cash_out|cash_pl|cash_recv|cash_send|cash_words|cashlarger|cashsmaller|cbrt|ceil|ceiling|center|char|char_length|character_length|chareq|charge|chargt|charin|charle|charlt|charne|charout|charrecv|charsend|chr|cideq|cidin|cidout|cidr|cidr_in|cidr_out|cidr_recv|cidr_send|cidrecv|cidsend|circle|circle_above|circle_add_pt|circle_below|circle_center|circle_contain|circle_contain_pt|circle_contained|circle_distance|circle_div_pt|circle_eq|circle_ge|circle_gt|circle_in|circle_le|circle_left|circle_lt|circle_mul_pt|circle_ne|circle_out|circle_overabove|circle_overbelow|circle_overlap|circle_overleft|circle_overright|circle_recv|circle_right|circle_same|circle_send|circle_sub_pt|clock_timestamp|close_lb|close_ls|close_lseg|close_pb|close_pl|close_ps|close_sb|close_sl|col_description|concat|concat_ws|contjoinsel|contsel|convert|convert_from|convert_to|corr|cos|cot|count|covar_pop|covar_samp|cstring_in|cstring_out|cstring_recv|cstring_send|cume_dist|current_database|current_query|current_schema|current_schemas|current_setting|current_user|currtid|currtid2|currval|cursor_to_xml|cursor_to_xmlschema|database_to_xml|database_to_xml_and_xmlschema|database_to_xmlschema|date|date_cmp|date_cmp_timestamp|date_cmp_timestamptz|date_eq|date_eq_timestamp|date_eq_timestamptz|date_ge|date_ge_timestamp|date_ge_timestamptz|date_gt|date_gt_timestamp|date_gt_timestamptz|date_in|date_larger|date_le|date_le_timestamp|date_le_timestamptz|date_lt|date_lt_timestamp|date_lt_timestamptz|date_mi|date_mi_interval|date_mii|date_ne|date_ne_timestamp|date_ne_timestamptz|date_out|date_part|date_pl_interval|date_pli|date_recv|date_send|date_smaller|date_sortsupport|date_trunc|daterange|daterange_canonical|daterange_subdiff|datetime_pl|datetimetz_pl|dcbrt|decode|degrees|dense_rank|dexp|diagonal|diameter|dispell_init|dispell_lexize|dist_cpoly|dist_lb|dist_pb|dist_pc|dist_pl|dist_ppath|dist_ps|dist_sb|dist_sl|div|dlog1|dlog10|domain_in|domain_recv|dpow|dround|dsimple_init|dsimple_lexize|dsnowball_init|dsnowball_lexize|dsqrt|dsynonym_init|dsynonym_lexize|dtrunc|elem_contained_by_range|encode|enum_cmp|enum_eq|enum_first|enum_ge|enum_gt|enum_in|enum_larger|enum_last|enum_le|enum_lt|enum_ne|enum_out|enum_range|enum_recv|enum_send|enum_smaller|eqjoinsel|eqsel|euc_cn_to_mic|euc_cn_to_utf8|euc_jis_2004_to_shift_jis_2004|euc_jis_2004_to_utf8|euc_jp_to_mic|euc_jp_to_sjis|euc_jp_to_utf8|euc_kr_to_mic|euc_kr_to_utf8|euc_tw_to_big5|euc_tw_to_mic|euc_tw_to_utf8|event_trigger_in|event_trigger_out|every|exp|factorial|family|fdw_handler_in|fdw_handler_out|first_value|float4|float48div|float48eq|float48ge|float48gt|float48le|float48lt|float48mi|float48mul|float48ne|float48pl|float4_accum|float4abs|float4div|float4eq|float4ge|float4gt|float4in|float4larger|float4le|float4lt|float4mi|float4mul|float4ne|float4out|float4pl|float4recv|float4send|float4smaller|float4um|float4up|float8|float84div|float84eq|float84ge|float84gt|float84le|float84lt|float84mi|float84mul|float84ne|float84pl|float8_accum|float8_avg|float8_corr|float8_covar_pop|float8_covar_samp|float8_regr_accum|float8_regr_avgx|float8_regr_avgy|float8_regr_intercept|float8_regr_r2|float8_regr_slope|float8_regr_sxx|float8_regr_sxy|float8_regr_syy|float8_stddev_pop|float8_stddev_samp|float8_var_pop|float8_var_samp|float8abs|float8div|float8eq|float8ge|float8gt|float8in|float8larger|float8le|float8lt|float8mi|float8mul|float8ne|float8out|float8pl|float8recv|float8send|float8smaller|float8um|float8up|floor|flt4_mul_cash|flt8_mul_cash|fmgr_c_validator|fmgr_internal_validator|fmgr_sql_validator|format|format_type|gb18030_to_utf8|gbk_to_utf8|generate_series|generate_subscripts|get_bit|get_byte|get_current_ts_config|getdatabaseencoding|getpgusername|gin_cmp_prefix|gin_cmp_tslexeme|gin_extract_tsquery|gin_extract_tsvector|gin_tsquery_consistent|ginarrayconsistent|ginarrayextract|ginbeginscan|ginbuild|ginbuildempty|ginbulkdelete|gincostestimate|ginendscan|gingetbitmap|gininsert|ginmarkpos|ginoptions|ginqueryarrayextract|ginrescan|ginrestrpos|ginvacuumcleanup|gist_box_compress|gist_box_consistent|gist_box_decompress|gist_box_penalty|gist_box_picksplit|gist_box_same|gist_box_union|gist_circle_compress|gist_circle_consistent|gist_point_compress|gist_point_consistent|gist_point_distance|gist_poly_compress|gist_poly_consistent|gistbeginscan|gistbuild|gistbuildempty|gistbulkdelete|gistcostestimate|gistendscan|gistgetbitmap|gistgettuple|gistinsert|gistmarkpos|gistoptions|gistrescan|gistrestrpos|gistvacuumcleanup|gtsquery_compress|gtsquery_consistent|gtsquery_decompress|gtsquery_penalty|gtsquery_picksplit|gtsquery_same|gtsquery_union|gtsvector_compress|gtsvector_consistent|gtsvector_decompress|gtsvector_penalty|gtsvector_picksplit|gtsvector_same|gtsvector_union|gtsvectorin|gtsvectorout|has_any_column_privilege|has_column_privilege|has_database_privilege|has_foreign_data_wrapper_privilege|has_function_privilege|has_language_privilege|has_schema_privilege|has_sequence_privilege|has_server_privilege|has_table_privilege|has_tablespace_privilege|has_type_privilege|hash_aclitem|hash_array|hash_numeric|hash_range|hashbeginscan|hashbpchar|hashbuild|hashbuildempty|hashbulkdelete|hashchar|hashcostestimate|hashendscan|hashenum|hashfloat4|hashfloat8|hashgetbitmap|hashgettuple|hashinet|hashinsert|hashint2|hashint2vector|hashint4|hashint8|hashmacaddr|hashmarkpos|hashname|hashoid|hashoidvector|hashoptions|hashrescan|hashrestrpos|hashtext|hashvacuumcleanup|hashvarlena|height|host|hostmask|iclikejoinsel|iclikesel|icnlikejoinsel|icnlikesel|icregexeqjoinsel|icregexeqsel|icregexnejoinsel|icregexnesel|inet_client_addr|inet_client_port|inet_in|inet_out|inet_recv|inet_send|inet_server_addr|inet_server_port|inetand|inetmi|inetmi_int8|inetnot|inetor|inetpl|initcap|int2|int24div|int24eq|int24ge|int24gt|int24le|int24lt|int24mi|int24mul|int24ne|int24pl|int28div|int28eq|int28ge|int28gt|int28le|int28lt|int28mi|int28mul|int28ne|int28pl|int2_accum|int2_avg_accum|int2_mul_cash|int2_sum|int2abs|int2and|int2div|int2eq|int2ge|int2gt|int2in|int2larger|int2le|int2lt|int2mi|int2mod|int2mul|int2ne|int2not|int2or|int2out|int2pl|int2recv|int2send|int2shl|int2shr|int2smaller|int2um|int2up|int2vectoreq|int2vectorin|int2vectorout|int2vectorrecv|int2vectorsend|int2xor|int4|int42div|int42eq|int42ge|int42gt|int42le|int42lt|int42mi|int42mul|int42ne|int42pl|int48div|int48eq|int48ge|int48gt|int48le|int48lt|int48mi|int48mul|int48ne|int48pl|int4_accum|int4_avg_accum|int4_mul_cash|int4_sum|int4abs|int4and|int4div|int4eq|int4ge|int4gt|int4in|int4inc|int4larger|int4le|int4lt|int4mi|int4mod|int4mul|int4ne|int4not|int4or|int4out|int4pl|int4range|int4range_canonical|int4range_subdiff|int4recv|int4send|int4shl|int4shr|int4smaller|int4um|int4up|int4xor|int8|int82div|int82eq|int82ge|int82gt|int82le|int82lt|int82mi|int82mul|int82ne|int82pl|int84div|int84eq|int84ge|int84gt|int84le|int84lt|int84mi|int84mul|int84ne|int84pl|int8_accum|int8_avg|int8_avg_accum|int8_sum|int8abs|int8and|int8div|int8eq|int8ge|int8gt|int8in|int8inc|int8inc_any|int8inc_float8_float8|int8larger|int8le|int8lt|int8mi|int8mod|int8mul|int8ne|int8not|int8or|int8out|int8pl|int8pl_inet|int8range|int8range_canonical|int8range_subdiff|int8recv|int8send|int8shl|int8shr|int8smaller|int8um|int8up|int8xor|integer_pl_date|inter_lb|inter_sb|inter_sl|internal_in|internal_out|interval_accum|interval_avg|interval_cmp|interval_div|interval_eq|interval_ge|interval_gt|interval_hash|interval_in|interval_larger|interval_le|interval_lt|interval_mi|interval_mul|interval_ne|interval_out|interval_pl|interval_pl_date|interval_pl_time|interval_pl_timestamp|interval_pl_timestamptz|interval_pl_timetz|interval_recv|interval_send|interval_smaller|interval_transform|interval_um|intervaltypmodin|intervaltypmodout|intinterval|isclosed|isempty|isfinite|ishorizontal|iso8859_1_to_utf8|iso8859_to_utf8|iso_to_koi8r|iso_to_mic|iso_to_win1251|iso_to_win866|isopen|isparallel|isperp|isvertical|johab_to_utf8|json_agg|json_agg_finalfn|json_agg_transfn|json_array_element|json_array_element_text|json_array_elements|json_array_length|json_each|json_each_text|json_extract_path|json_extract_path_op|json_extract_path_text|json_extract_path_text_op|json_in|json_object_field|json_object_field_text|json_object_keys|json_out|json_populate_record|json_populate_recordset|json_recv|json_send|justify_days|justify_hours|justify_interval|koi8r_to_iso|koi8r_to_mic|koi8r_to_utf8|koi8r_to_win1251|koi8r_to_win866|koi8u_to_utf8|lag|language_handler_in|language_handler_out|last_value|lastval|latin1_to_mic|latin2_to_mic|latin2_to_win1250|latin3_to_mic|latin4_to_mic|lead|left|length|like|like_escape|likejoinsel|likesel|line|line_distance|line_eq|line_horizontal|line_in|line_interpt|line_intersect|line_out|line_parallel|line_perp|line_recv|line_send|line_vertical|ln|lo_close|lo_creat|lo_create|lo_export|lo_import|lo_lseek|lo_lseek64|lo_open|lo_tell|lo_tell64|lo_truncate|lo_truncate64|lo_unlink|log|loread|lower|lower_inc|lower_inf|lowrite|lpad|lseg|lseg_center|lseg_distance|lseg_eq|lseg_ge|lseg_gt|lseg_horizontal|lseg_in|lseg_interpt|lseg_intersect|lseg_le|lseg_length|lseg_lt|lseg_ne|lseg_out|lseg_parallel|lseg_perp|lseg_recv|lseg_send|lseg_vertical|ltrim|macaddr_and|macaddr_cmp|macaddr_eq|macaddr_ge|macaddr_gt|macaddr_in|macaddr_le|macaddr_lt|macaddr_ne|macaddr_not|macaddr_or|macaddr_out|macaddr_recv|macaddr_send|makeaclitem|masklen|max|md5|mic_to_ascii|mic_to_big5|mic_to_euc_cn|mic_to_euc_jp|mic_to_euc_kr|mic_to_euc_tw|mic_to_iso|mic_to_koi8r|mic_to_latin1|mic_to_latin2|mic_to_latin3|mic_to_latin4|mic_to_sjis|mic_to_win1250|mic_to_win1251|mic_to_win866|min|mktinterval|mod|money|mul_d_interval|name|nameeq|namege|namegt|nameiclike|nameicnlike|nameicregexeq|nameicregexne|namein|namele|namelike|namelt|namene|namenlike|nameout|namerecv|nameregexeq|nameregexne|namesend|neqjoinsel|neqsel|netmask|network|network_cmp|network_eq|network_ge|network_gt|network_le|network_lt|network_ne|network_sub|network_subeq|network_sup|network_supeq|nextval|nlikejoinsel|nlikesel|notlike|now|npoints|nth_value|ntile|numeric_abs|numeric_accum|numeric_add|numeric_avg|numeric_avg_accum|numeric_cmp|numeric_div|numeric_div_trunc|numeric_eq|numeric_exp|numeric_fac|numeric_ge|numeric_gt|numeric_in|numeric_inc|numeric_larger|numeric_le|numeric_ln|numeric_log|numeric_lt|numeric_mod|numeric_mul|numeric_ne|numeric_out|numeric_power|numeric_recv|numeric_send|numeric_smaller|numeric_sqrt|numeric_stddev_pop|numeric_stddev_samp|numeric_sub|numeric_transform|numeric_uminus|numeric_uplus|numeric_var_pop|numeric_var_samp|numerictypmodin|numerictypmodout|numnode|numrange|numrange_subdiff|obj_description|octet_length|oid|oideq|oidge|oidgt|oidin|oidlarger|oidle|oidlt|oidne|oidout|oidrecv|oidsend|oidsmaller|oidvectoreq|oidvectorge|oidvectorgt|oidvectorin|oidvectorle|oidvectorlt|oidvectorne|oidvectorout|oidvectorrecv|oidvectorsend|oidvectortypes|on_pb|on_pl|on_ppath|on_ps|on_sb|on_sl|opaque_in|opaque_out|overlaps|overlay|path|path_add|path_add_pt|path_center|path_contain_pt|path_distance|path_div_pt|path_in|path_inter|path_length|path_mul_pt|path_n_eq|path_n_ge|path_n_gt|path_n_le|path_n_lt|path_npoints|path_out|path_recv|path_send|path_sub_pt|pclose|percent_rank|pg_advisory_lock|pg_advisory_lock_shared|pg_advisory_unlock|pg_advisory_unlock_all|pg_advisory_unlock_shared|pg_advisory_xact_lock|pg_advisory_xact_lock_shared|pg_available_extension_versions|pg_available_extensions|pg_backend_pid|pg_backup_start_time|pg_cancel_backend|pg_char_to_encoding|pg_client_encoding|pg_collation_for|pg_collation_is_visible|pg_column_is_updatable|pg_column_size|pg_conf_load_time|pg_conversion_is_visible|pg_create_restore_point|pg_current_xlog_insert_location|pg_current_xlog_location|pg_cursor|pg_database_size|pg_describe_object|pg_encoding_max_length|pg_encoding_to_char|pg_event_trigger_dropped_objects|pg_export_snapshot|pg_extension_config_dump|pg_extension_update_paths|pg_function_is_visible|pg_get_constraintdef|pg_get_expr|pg_get_function_arguments|pg_get_function_identity_arguments|pg_get_function_result|pg_get_functiondef|pg_get_indexdef|pg_get_keywords|pg_get_multixact_members|pg_get_ruledef|pg_get_serial_sequence|pg_get_triggerdef|pg_get_userbyid|pg_get_viewdef|pg_has_role|pg_identify_object|pg_indexes_size|pg_is_in_backup|pg_is_in_recovery|pg_is_other_temp_schema|pg_is_xlog_replay_paused|pg_last_xact_replay_timestamp|pg_last_xlog_receive_location|pg_last_xlog_replay_location|pg_listening_channels|pg_lock_status|pg_ls_dir|pg_my_temp_schema|pg_node_tree_in|pg_node_tree_out|pg_node_tree_recv|pg_node_tree_send|pg_notify|pg_opclass_is_visible|pg_operator_is_visible|pg_opfamily_is_visible|pg_options_to_table|pg_postmaster_start_time|pg_prepared_statement|pg_prepared_xact|pg_read_binary_file|pg_read_file|pg_relation_filenode|pg_relation_filepath|pg_relation_is_updatable|pg_relation_size|pg_reload_conf|pg_rotate_logfile|pg_sequence_parameters|pg_show_all_settings|pg_size_pretty|pg_sleep|pg_start_backup|pg_stat_clear_snapshot|pg_stat_file|pg_stat_get_activity|pg_stat_get_analyze_count|pg_stat_get_autoanalyze_count|pg_stat_get_autovacuum_count|pg_stat_get_backend_activity|pg_stat_get_backend_activity_start|pg_stat_get_backend_client_addr|pg_stat_get_backend_client_port|pg_stat_get_backend_dbid|pg_stat_get_backend_idset|pg_stat_get_backend_pid|pg_stat_get_backend_start|pg_stat_get_backend_userid|pg_stat_get_backend_waiting|pg_stat_get_backend_xact_start|pg_stat_get_bgwriter_buf_written_checkpoints|pg_stat_get_bgwriter_buf_written_clean|pg_stat_get_bgwriter_maxwritten_clean|pg_stat_get_bgwriter_requested_checkpoints|pg_stat_get_bgwriter_stat_reset_time|pg_stat_get_bgwriter_timed_checkpoints|pg_stat_get_blocks_fetched|pg_stat_get_blocks_hit|pg_stat_get_buf_alloc|pg_stat_get_buf_fsync_backend|pg_stat_get_buf_written_backend|pg_stat_get_checkpoint_sync_time|pg_stat_get_checkpoint_write_time|pg_stat_get_db_blk_read_time|pg_stat_get_db_blk_write_time|pg_stat_get_db_blocks_fetched|pg_stat_get_db_blocks_hit|pg_stat_get_db_conflict_all|pg_stat_get_db_conflict_bufferpin|pg_stat_get_db_conflict_lock|pg_stat_get_db_conflict_snapshot|pg_stat_get_db_conflict_startup_deadlock|pg_stat_get_db_conflict_tablespace|pg_stat_get_db_deadlocks|pg_stat_get_db_numbackends|pg_stat_get_db_stat_reset_time|pg_stat_get_db_temp_bytes|pg_stat_get_db_temp_files|pg_stat_get_db_tuples_deleted|pg_stat_get_db_tuples_fetched|pg_stat_get_db_tuples_inserted|pg_stat_get_db_tuples_returned|pg_stat_get_db_tuples_updated|pg_stat_get_db_xact_commit|pg_stat_get_db_xact_rollback|pg_stat_get_dead_tuples|pg_stat_get_function_calls|pg_stat_get_function_self_time|pg_stat_get_function_total_time|pg_stat_get_last_analyze_time|pg_stat_get_last_autoanalyze_time|pg_stat_get_last_autovacuum_time|pg_stat_get_last_vacuum_time|pg_stat_get_live_tuples|pg_stat_get_numscans|pg_stat_get_tuples_deleted|pg_stat_get_tuples_fetched|pg_stat_get_tuples_hot_updated|pg_stat_get_tuples_inserted|pg_stat_get_tuples_returned|pg_stat_get_tuples_updated|pg_stat_get_vacuum_count|pg_stat_get_wal_senders|pg_stat_get_xact_blocks_fetched|pg_stat_get_xact_blocks_hit|pg_stat_get_xact_function_calls|pg_stat_get_xact_function_self_time|pg_stat_get_xact_function_total_time|pg_stat_get_xact_numscans|pg_stat_get_xact_tuples_deleted|pg_stat_get_xact_tuples_fetched|pg_stat_get_xact_tuples_hot_updated|pg_stat_get_xact_tuples_inserted|pg_stat_get_xact_tuples_returned|pg_stat_get_xact_tuples_updated|pg_stat_reset|pg_stat_reset_shared|pg_stat_reset_single_function_counters|pg_stat_reset_single_table_counters|pg_stop_backup|pg_switch_xlog|pg_table_is_visible|pg_table_size|pg_tablespace_databases|pg_tablespace_location|pg_tablespace_size|pg_terminate_backend|pg_timezone_abbrevs|pg_timezone_names|pg_total_relation_size|pg_trigger_depth|pg_try_advisory_lock|pg_try_advisory_lock_shared|pg_try_advisory_xact_lock|pg_try_advisory_xact_lock_shared|pg_ts_config_is_visible|pg_ts_dict_is_visible|pg_ts_parser_is_visible|pg_ts_template_is_visible|pg_type_is_visible|pg_typeof|pg_xlog_location_diff|pg_xlog_replay_pause|pg_xlog_replay_resume|pg_xlogfile_name|pg_xlogfile_name_offset|pi|plainto_tsquery|plpgsql_call_handler|plpgsql_inline_handler|plpgsql_validator|point|point_above|point_add|point_below|point_distance|point_div|point_eq|point_horiz|point_in|point_left|point_mul|point_ne|point_out|point_recv|point_right|point_send|point_sub|point_vert|poly_above|poly_below|poly_center|poly_contain|poly_contain_pt|poly_contained|poly_distance|poly_in|poly_left|poly_npoints|poly_out|poly_overabove|poly_overbelow|poly_overlap|poly_overleft|poly_overright|poly_recv|poly_right|poly_same|poly_send|polygon|popen|position|positionjoinsel|positionsel|postgresql_fdw_validator|pow|power|prsd_end|prsd_headline|prsd_lextype|prsd_nexttoken|prsd_start|pt_contained_circle|pt_contained_poly|query_to_xml|query_to_xml_and_xmlschema|query_to_xmlschema|querytree|quote_ident|quote_literal|quote_nullable|radians|radius|random|range_adjacent|range_after|range_before|range_cmp|range_contained_by|range_contains|range_contains_elem|range_eq|range_ge|range_gist_compress|range_gist_consistent|range_gist_decompress|range_gist_penalty|range_gist_picksplit|range_gist_same|range_gist_union|range_gt|range_in|range_intersect|range_le|range_lt|range_minus|range_ne|range_out|range_overlaps|range_overleft|range_overright|range_recv|range_send|range_typanalyze|range_union|rangesel|rank|record_eq|record_ge|record_gt|record_in|record_le|record_lt|record_ne|record_out|record_recv|record_send|regclass|regclassin|regclassout|regclassrecv|regclasssend|regconfigin|regconfigout|regconfigrecv|regconfigsend|regdictionaryin|regdictionaryout|regdictionaryrecv|regdictionarysend|regexeqjoinsel|regexeqsel|regexnejoinsel|regexnesel|regexp_matches|regexp_replace|regexp_split_to_array|regexp_split_to_table|regoperatorin|regoperatorout|regoperatorrecv|regoperatorsend|regoperin|regoperout|regoperrecv|regopersend|regprocedurein|regprocedureout|regprocedurerecv|regproceduresend|regprocin|regprocout|regprocrecv|regprocsend|regr_avgx|regr_avgy|regr_count|regr_intercept|regr_r2|regr_slope|regr_sxx|regr_sxy|regr_syy|regtypein|regtypeout|regtyperecv|regtypesend|reltime|reltimeeq|reltimege|reltimegt|reltimein|reltimele|reltimelt|reltimene|reltimeout|reltimerecv|reltimesend|repeat|replace|reverse|right|round|row_number|row_to_json|rpad|rtrim|scalargtjoinsel|scalargtsel|scalarltjoinsel|scalarltsel|schema_to_xml|schema_to_xml_and_xmlschema|schema_to_xmlschema|session_user|set_bit|set_byte|set_config|set_masklen|setseed|setval|setweight|shell_in|shell_out|shift_jis_2004_to_euc_jis_2004|shift_jis_2004_to_utf8|shobj_description|sign|similar_escape|sin|sjis_to_euc_jp|sjis_to_mic|sjis_to_utf8|slope|smgreq|smgrin|smgrne|smgrout|spg_kd_choose|spg_kd_config|spg_kd_inner_consistent|spg_kd_picksplit|spg_quad_choose|spg_quad_config|spg_quad_inner_consistent|spg_quad_leaf_consistent|spg_quad_picksplit|spg_range_quad_choose|spg_range_quad_config|spg_range_quad_inner_consistent|spg_range_quad_leaf_consistent|spg_range_quad_picksplit|spg_text_choose|spg_text_config|spg_text_inner_consistent|spg_text_leaf_consistent|spg_text_picksplit|spgbeginscan|spgbuild|spgbuildempty|spgbulkdelete|spgcanreturn|spgcostestimate|spgendscan|spggetbitmap|spggettuple|spginsert|spgmarkpos|spgoptions|spgrescan|spgrestrpos|spgvacuumcleanup|split_part|sqrt|statement_timestamp|stddev|stddev_pop|stddev_samp|string_agg|string_agg_finalfn|string_agg_transfn|string_to_array|strip|strpos|substr|substring|sum|suppress_redundant_updates_trigger|table_to_xml|table_to_xml_and_xmlschema|table_to_xmlschema|tan|text|text_ge|text_gt|text_larger|text_le|text_lt|text_pattern_ge|text_pattern_gt|text_pattern_le|text_pattern_lt|text_smaller|textanycat|textcat|texteq|texticlike|texticnlike|texticregexeq|texticregexne|textin|textlen|textlike|textne|textnlike|textout|textrecv|textregexeq|textregexne|textsend|thesaurus_init|thesaurus_lexize|tideq|tidge|tidgt|tidin|tidlarger|tidle|tidlt|tidne|tidout|tidrecv|tidsend|tidsmaller|time_cmp|time_eq|time_ge|time_gt|time_hash|time_in|time_larger|time_le|time_lt|time_mi_interval|time_mi_time|time_ne|time_out|time_pl_interval|time_recv|time_send|time_smaller|time_transform|timedate_pl|timemi|timenow|timeofday|timepl|timestamp_cmp|timestamp_cmp_date|timestamp_cmp_timestamptz|timestamp_eq|timestamp_eq_date|timestamp_eq_timestamptz|timestamp_ge|timestamp_ge_date|timestamp_ge_timestamptz|timestamp_gt|timestamp_gt_date|timestamp_gt_timestamptz|timestamp_hash|timestamp_in|timestamp_larger|timestamp_le|timestamp_le_date|timestamp_le_timestamptz|timestamp_lt|timestamp_lt_date|timestamp_lt_timestamptz|timestamp_mi|timestamp_mi_interval|timestamp_ne|timestamp_ne_date|timestamp_ne_timestamptz|timestamp_out|timestamp_pl_interval|timestamp_recv|timestamp_send|timestamp_smaller|timestamp_sortsupport|timestamp_transform|timestamptypmodin|timestamptypmodout|timestamptz_cmp|timestamptz_cmp_date|timestamptz_cmp_timestamp|timestamptz_eq|timestamptz_eq_date|timestamptz_eq_timestamp|timestamptz_ge|timestamptz_ge_date|timestamptz_ge_timestamp|timestamptz_gt|timestamptz_gt_date|timestamptz_gt_timestamp|timestamptz_in|timestamptz_larger|timestamptz_le|timestamptz_le_date|timestamptz_le_timestamp|timestamptz_lt|timestamptz_lt_date|timestamptz_lt_timestamp|timestamptz_mi|timestamptz_mi_interval|timestamptz_ne|timestamptz_ne_date|timestamptz_ne_timestamp|timestamptz_out|timestamptz_pl_interval|timestamptz_recv|timestamptz_send|timestamptz_smaller|timestamptztypmodin|timestamptztypmodout|timetypmodin|timetypmodout|timetz_cmp|timetz_eq|timetz_ge|timetz_gt|timetz_hash|timetz_in|timetz_larger|timetz_le|timetz_lt|timetz_mi_interval|timetz_ne|timetz_out|timetz_pl_interval|timetz_recv|timetz_send|timetz_smaller|timetzdate_pl|timetztypmodin|timetztypmodout|timezone|tinterval|tintervalct|tintervalend|tintervaleq|tintervalge|tintervalgt|tintervalin|tintervalle|tintervalleneq|tintervallenge|tintervallengt|tintervallenle|tintervallenlt|tintervallenne|tintervallt|tintervalne|tintervalout|tintervalov|tintervalrecv|tintervalrel|tintervalsame|tintervalsend|tintervalstart|to_ascii|to_char|to_date|to_hex|to_json|to_number|to_timestamp|to_tsquery|to_tsvector|transaction_timestamp|translate|trigger_in|trigger_out|trunc|ts_debug|ts_headline|ts_lexize|ts_match_qv|ts_match_tq|ts_match_tt|ts_match_vq|ts_parse|ts_rank|ts_rank_cd|ts_rewrite|ts_stat|ts_token_type|ts_typanalyze|tsmatchjoinsel|tsmatchsel|tsq_mcontained|tsq_mcontains|tsquery_and|tsquery_cmp|tsquery_eq|tsquery_ge|tsquery_gt|tsquery_le|tsquery_lt|tsquery_ne|tsquery_not|tsquery_or|tsqueryin|tsqueryout|tsqueryrecv|tsquerysend|tsrange|tsrange_subdiff|tstzrange|tstzrange_subdiff|tsvector_cmp|tsvector_concat|tsvector_eq|tsvector_ge|tsvector_gt|tsvector_le|tsvector_lt|tsvector_ne|tsvector_update_trigger|tsvector_update_trigger_column|tsvectorin|tsvectorout|tsvectorrecv|tsvectorsend|txid_current|txid_current_snapshot|txid_snapshot_in|txid_snapshot_out|txid_snapshot_recv|txid_snapshot_send|txid_snapshot_xip|txid_snapshot_xmax|txid_snapshot_xmin|txid_visible_in_snapshot|uhc_to_utf8|unique_key_recheck|unknownin|unknownout|unknownrecv|unknownsend|unnest|upper|upper_inc|upper_inf|utf8_to_ascii|utf8_to_big5|utf8_to_euc_cn|utf8_to_euc_jis_2004|utf8_to_euc_jp|utf8_to_euc_kr|utf8_to_euc_tw|utf8_to_gb18030|utf8_to_gbk|utf8_to_iso8859|utf8_to_iso8859_1|utf8_to_johab|utf8_to_koi8r|utf8_to_koi8u|utf8_to_shift_jis_2004|utf8_to_sjis|utf8_to_uhc|utf8_to_win|uuid_cmp|uuid_eq|uuid_ge|uuid_gt|uuid_hash|uuid_in|uuid_le|uuid_lt|uuid_ne|uuid_out|uuid_recv|uuid_send|var_pop|var_samp|varbit_in|varbit_out|varbit_recv|varbit_send|varbit_transform|varbitcmp|varbiteq|varbitge|varbitgt|varbitle|varbitlt|varbitne|varbittypmodin|varbittypmodout|varchar_transform|varcharin|varcharout|varcharrecv|varcharsend|varchartypmodin|varchartypmodout|variance|version|void_in|void_out|void_recv|void_send|width|width_bucket|win1250_to_latin2|win1250_to_mic|win1251_to_iso|win1251_to_koi8r|win1251_to_mic|win1251_to_win866|win866_to_iso|win866_to_koi8r|win866_to_mic|win866_to_win1251|win_to_utf8|xideq|xideqint4|xidin|xidout|xidrecv|xidsend|xml|xml_in|xml_is_well_formed|xml_is_well_formed_content|xml_is_well_formed_document|xml_out|xml_recv|xml_send|xmlagg|xmlcomment|xmlconcat2|xmlexists|xmlvalidate|xpath|xpath_exists",n=this.createKeywordMapper({"support.function":t,keyword:e},"identifier",!0),r=[{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"variable.language",regex:'".*?"'},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:n,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.$rules={start:[{token:"comment",regex:"--.*$"},s.getStartRule("doc-start"),{token:"comment",regex:"\\/\\*",next:"comment"},{token:"keyword.statementBegin",regex:"[a-zA-Z]+",next:"statement"},{token:"support.buildin",regex:"^\\\\[\\S]+.*$"}],statement:[{token:"comment",regex:"--.*$"},{token:"comment",regex:"\\/\\*",next:"commentStatement"},{token:"statementEnd",regex:";",next:"start"},{token:"string",regex:"\\$perl\\$",next:"perl-start"},{token:"string",regex:"\\$python\\$",next:"python-start"},{token:"string",regex:"\\$json\\$",next:"json-start"},{token:"string",regex:"\\$(js|javascript)\\$",next:"javascript-start"},{token:"string",regex:"\\$[\\w_0-9]*\\$$",next:"dollarSql"},{token:"string",regex:"\\$[\\w_0-9]*\\$",next:"dollarStatementString"}].concat(r),dollarSql:[{token:"comment",regex:"--.*$"},{token:"comment",regex:"\\/\\*",next:"commentDollarSql"},{token:"string",regex:"^\\$[\\w_0-9]*\\$",next:"statement"},{token:"string",regex:"\\$[\\w_0-9]*\\$",next:"dollarSqlString"}].concat(r),comment:[{token:"comment",regex:"\\*\\/",next:"start"},{defaultToken:"comment"}],commentStatement:[{token:"comment",regex:"\\*\\/",next:"statement"},{defaultToken:"comment"}],commentDollarSql:[{token:"comment",regex:"\\*\\/",next:"dollarSql"},{defaultToken:"comment"}],dollarStatementString:[{token:"string",regex:".*?\\$[\\w_0-9]*\\$",next:"statement"},{token:"string",regex:".+"}],dollarSqlString:[{token:"string",regex:".*?\\$[\\w_0-9]*\\$",next:"dollarSql"},{token:"string",regex:".+"}]},this.embedRules(s,"doc-",[s.getEndRule("start")]),this.embedRules(u,"perl-",[{token:"string",regex:"\\$perl\\$",next:"statement"}]),this.embedRules(a,"python-",[{token:"string",regex:"\\$python\\$",next:"statement"}]),this.embedRules(f,"json-",[{token:"string",regex:"\\$json\\$",next:"statement"}]),this.embedRules(l,"javascript-",[{token:"string",regex:"\\$(js|javascript)\\$",next:"statement"}])};r.inherits(c,o),t.PgsqlHighlightRules=c}),ace.define("ace/mode/pgsql",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/pgsql_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("../mode/text").Mode,s=e("./pgsql_highlight_rules").PgsqlHighlightRules,o=function(){this.HighlightRules=s,this.$behaviour=this.$defaultBehaviour};r.inherits(o,i),function(){this.lineCommentStart="--",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){return e=="start"||e=="keyword.statementEnd"?"":this.$getIndent(t)},this.$id="ace/mode/pgsql"}.call(o.prototype),t.Mode=o}); (function() { ace.require(["ace/mode/pgsql"], function(m) { if (typeof module == "object" && typeof exports == "object" && module) { module.exports = m; } }); })(); ================================================ FILE: static/js/ace.js ================================================ (function(){function o(n){var i=e;n&&(e[n]||(e[n]={}),i=e[n]);if(!i.define||!i.define.packaged)t.original=i.define,i.define=t,i.define.packaged=!0;if(!i.require||!i.require.packaged)r.original=i.require,i.require=r,i.require.packaged=!0}var ACE_NAMESPACE = "ace",e=function(){return this}();!e&&typeof window!="undefined"&&(e=window);if(!ACE_NAMESPACE&&typeof requirejs!="undefined")return;var t=function(e,n,r){if(typeof e!="string"){t.original?t.original.apply(this,arguments):(console.error("dropping module because define wasn't a string."),console.trace());return}arguments.length==2&&(r=n),t.modules[e]||(t.payloads[e]=r,t.modules[e]=null)};t.modules={},t.payloads={};var n=function(e,t,n){if(typeof t=="string"){var i=s(e,t);if(i!=undefined)return n&&n(),i}else if(Object.prototype.toString.call(t)==="[object Array]"){var o=[];for(var u=0,a=t.length;u1&&u(t,"")>-1&&(a=RegExp(this.source,r.replace.call(o(this),"g","")),r.replace.call(e.slice(t.index),a,function(){for(var e=1;et.index&&this.lastIndex--}return t},s||(RegExp.prototype.test=function(e){var t=r.exec.call(this,e);return t&&this.global&&!t[0].length&&this.lastIndex>t.index&&this.lastIndex--,!!t})}),ace.define("ace/lib/es5-shim",["require","exports","module"],function(e,t,n){function r(){}function w(e){try{return Object.defineProperty(e,"sentinel",{}),"sentinel"in e}catch(t){}}function H(e){return e=+e,e!==e?e=0:e!==0&&e!==1/0&&e!==-1/0&&(e=(e>0||-1)*Math.floor(Math.abs(e))),e}function B(e){var t=typeof e;return e===null||t==="undefined"||t==="boolean"||t==="number"||t==="string"}function j(e){var t,n,r;if(B(e))return e;n=e.valueOf;if(typeof n=="function"){t=n.call(e);if(B(t))return t}r=e.toString;if(typeof r=="function"){t=r.call(e);if(B(t))return t}throw new TypeError}Function.prototype.bind||(Function.prototype.bind=function(t){var n=this;if(typeof n!="function")throw new TypeError("Function.prototype.bind called on incompatible "+n);var i=u.call(arguments,1),s=function(){if(this instanceof s){var e=n.apply(this,i.concat(u.call(arguments)));return Object(e)===e?e:this}return n.apply(t,i.concat(u.call(arguments)))};return n.prototype&&(r.prototype=n.prototype,s.prototype=new r,r.prototype=null),s});var i=Function.prototype.call,s=Array.prototype,o=Object.prototype,u=s.slice,a=i.bind(o.toString),f=i.bind(o.hasOwnProperty),l,c,h,p,d;if(d=f(o,"__defineGetter__"))l=i.bind(o.__defineGetter__),c=i.bind(o.__defineSetter__),h=i.bind(o.__lookupGetter__),p=i.bind(o.__lookupSetter__);if([1,2].splice(0).length!=2)if(!function(){function e(e){var t=new Array(e+2);return t[0]=t[1]=0,t}var t=[],n;t.splice.apply(t,e(20)),t.splice.apply(t,e(26)),n=t.length,t.splice(5,0,"XXX"),n+1==t.length;if(n+1==t.length)return!0}())Array.prototype.splice=function(e,t){var n=this.length;e>0?e>n&&(e=n):e==void 0?e=0:e<0&&(e=Math.max(n+e,0)),e+ta)for(h=l;h--;)this[f+h]=this[a+h];if(s&&e===c)this.length=c,this.push.apply(this,i);else{this.length=c+s;for(h=0;h>>0;if(a(t)!="[object Function]")throw new TypeError;while(++s>>0,s=Array(i),o=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var u=0;u>>0,s=[],o,u=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var f=0;f>>0,s=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var o=0;o>>0,s=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var o=0;o>>0;if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");if(!i&&arguments.length==1)throw new TypeError("reduce of empty array with no initial value");var s=0,o;if(arguments.length>=2)o=arguments[1];else do{if(s in r){o=r[s++];break}if(++s>=i)throw new TypeError("reduce of empty array with no initial value")}while(!0);for(;s>>0;if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");if(!i&&arguments.length==1)throw new TypeError("reduceRight of empty array with no initial value");var s,o=i-1;if(arguments.length>=2)s=arguments[1];else do{if(o in r){s=r[o--];break}if(--o<0)throw new TypeError("reduceRight of empty array with no initial value")}while(!0);do o in this&&(s=t.call(void 0,s,r[o],o,n));while(o--);return s});if(!Array.prototype.indexOf||[0,1].indexOf(1,2)!=-1)Array.prototype.indexOf=function(t){var n=g&&a(this)=="[object String]"?this.split(""):F(this),r=n.length>>>0;if(!r)return-1;var i=0;arguments.length>1&&(i=H(arguments[1])),i=i>=0?i:Math.max(0,r+i);for(;i>>0;if(!r)return-1;var i=r-1;arguments.length>1&&(i=Math.min(i,H(arguments[1]))),i=i>=0?i:r-Math.abs(i);for(;i>=0;i--)if(i in n&&t===n[i])return i;return-1};Object.getPrototypeOf||(Object.getPrototypeOf=function(t){return t.__proto__||(t.constructor?t.constructor.prototype:o)});if(!Object.getOwnPropertyDescriptor){var y="Object.getOwnPropertyDescriptor called on a non-object: ";Object.getOwnPropertyDescriptor=function(t,n){if(typeof t!="object"&&typeof t!="function"||t===null)throw new TypeError(y+t);if(!f(t,n))return;var r,i,s;r={enumerable:!0,configurable:!0};if(d){var u=t.__proto__;t.__proto__=o;var i=h(t,n),s=p(t,n);t.__proto__=u;if(i||s)return i&&(r.get=i),s&&(r.set=s),r}return r.value=t[n],r}}Object.getOwnPropertyNames||(Object.getOwnPropertyNames=function(t){return Object.keys(t)});if(!Object.create){var b;Object.prototype.__proto__===null?b=function(){return{__proto__:null}}:b=function(){var e={};for(var t in e)e[t]=null;return e.constructor=e.hasOwnProperty=e.propertyIsEnumerable=e.isPrototypeOf=e.toLocaleString=e.toString=e.valueOf=e.__proto__=null,e},Object.create=function(t,n){var r;if(t===null)r=b();else{if(typeof t!="object")throw new TypeError("typeof prototype["+typeof t+"] != 'object'");var i=function(){};i.prototype=t,r=new i,r.__proto__=t}return n!==void 0&&Object.defineProperties(r,n),r}}if(Object.defineProperty){var E=w({}),S=typeof document=="undefined"||w(document.createElement("div"));if(!E||!S)var x=Object.defineProperty}if(!Object.defineProperty||x){var T="Property description must be an object: ",N="Object.defineProperty called on non-object: ",C="getters & setters can not be defined on this javascript engine";Object.defineProperty=function(t,n,r){if(typeof t!="object"&&typeof t!="function"||t===null)throw new TypeError(N+t);if(typeof r!="object"&&typeof r!="function"||r===null)throw new TypeError(T+r);if(x)try{return x.call(Object,t,n,r)}catch(i){}if(f(r,"value"))if(d&&(h(t,n)||p(t,n))){var s=t.__proto__;t.__proto__=o,delete t[n],t[n]=r.value,t.__proto__=s}else t[n]=r.value;else{if(!d)throw new TypeError(C);f(r,"get")&&l(t,n,r.get),f(r,"set")&&c(t,n,r.set)}return t}}Object.defineProperties||(Object.defineProperties=function(t,n){for(var r in n)f(n,r)&&Object.defineProperty(t,r,n[r]);return t}),Object.seal||(Object.seal=function(t){return t}),Object.freeze||(Object.freeze=function(t){return t});try{Object.freeze(function(){})}catch(k){Object.freeze=function(t){return function(n){return typeof n=="function"?n:t(n)}}(Object.freeze)}Object.preventExtensions||(Object.preventExtensions=function(t){return t}),Object.isSealed||(Object.isSealed=function(t){return!1}),Object.isFrozen||(Object.isFrozen=function(t){return!1}),Object.isExtensible||(Object.isExtensible=function(t){if(Object(t)===t)throw new TypeError;var n="";while(f(t,n))n+="?";t[n]=!0;var r=f(t,n);return delete t[n],r});if(!Object.keys){var L=!0,A=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],O=A.length;for(var M in{toString:null})L=!1;Object.keys=function I(e){if(typeof e!="object"&&typeof e!="function"||e===null)throw new TypeError("Object.keys called on a non-object");var I=[];for(var t in e)f(e,t)&&I.push(t);if(L)for(var n=0,r=O;n=0?parseFloat((s.match(/(?:MSIE |Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]):parseFloat((s.match(/(?:Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]),t.isOldIE=t.isIE&&t.isIE<9,t.isGecko=t.isMozilla=s.match(/ Gecko\/\d+/),t.isOpera=typeof opera=="object"&&Object.prototype.toString.call(window.opera)=="[object Opera]",t.isWebKit=parseFloat(s.split("WebKit/")[1])||undefined,t.isChrome=parseFloat(s.split(" Chrome/")[1])||undefined,t.isEdge=parseFloat(s.split(" Edge/")[1])||undefined,t.isAIR=s.indexOf("AdobeAIR")>=0,t.isAndroid=s.indexOf("Android")>=0,t.isChromeOS=s.indexOf(" CrOS ")>=0,t.isIOS=/iPad|iPhone|iPod/.test(s)&&!window.MSStream,t.isIOS&&(t.isMac=!0),t.isMobile=t.isIOS||t.isAndroid}),ace.define("ace/lib/dom",["require","exports","module","ace/lib/useragent"],function(e,t,n){"use strict";var r=e("./useragent"),i="http://www.w3.org/1999/xhtml";t.buildDom=function o(e,t,n){if(typeof e=="string"&&e){var r=document.createTextNode(e);return t&&t.appendChild(r),r}if(!Array.isArray(e))return e;if(typeof e[0]!="string"||!e[0]){var i=[];for(var s=0;s=1.5:!0;if(typeof document!="undefined"){var s=document.createElement("div");t.HI_DPI&&s.style.transform!==undefined&&(t.HAS_CSS_TRANSFORMS=!0),!r.isEdge&&typeof s.style.animationName!="undefined"&&(t.HAS_CSS_ANIMATION=!0),s=null}t.HAS_CSS_TRANSFORMS?t.translate=function(e,t,n){e.style.transform="translate("+Math.round(t)+"px, "+Math.round(n)+"px)"}:t.translate=function(e,t,n){e.style.top=Math.round(n)+"px",e.style.left=Math.round(t)+"px"}}),ace.define("ace/lib/oop",["require","exports","module"],function(e,t,n){"use strict";t.inherits=function(e,t){e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}})},t.mixin=function(e,t){for(var n in t)e[n]=t[n];return e},t.implement=function(e,n){t.mixin(e,n)}}),ace.define("ace/lib/keys",["require","exports","module","ace/lib/oop"],function(e,t,n){"use strict";var r=e("./oop"),i=function(){var e={MODIFIER_KEYS:{16:"Shift",17:"Ctrl",18:"Alt",224:"Meta",91:"MetaLeft",92:"MetaRight",93:"ContextMenu"},KEY_MODS:{ctrl:1,alt:2,option:2,shift:4,"super":8,meta:8,command:8,cmd:8},FUNCTION_KEYS:{8:"Backspace",9:"Tab",13:"Return",19:"Pause",27:"Esc",32:"Space",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"Left",38:"Up",39:"Right",40:"Down",44:"Print",45:"Insert",46:"Delete",96:"Numpad0",97:"Numpad1",98:"Numpad2",99:"Numpad3",100:"Numpad4",101:"Numpad5",102:"Numpad6",103:"Numpad7",104:"Numpad8",105:"Numpad9","-13":"NumpadEnter",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"Numlock",145:"Scrolllock"},PRINTABLE_KEYS:{32:" ",48:"0",49:"1",50:"2",51:"3",52:"4",53:"5",54:"6",55:"7",56:"8",57:"9",59:";",61:"=",65:"a",66:"b",67:"c",68:"d",69:"e",70:"f",71:"g",72:"h",73:"i",74:"j",75:"k",76:"l",77:"m",78:"n",79:"o",80:"p",81:"q",82:"r",83:"s",84:"t",85:"u",86:"v",87:"w",88:"x",89:"y",90:"z",107:"+",109:"-",110:".",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'",111:"/",106:"*"}},t,n;for(n in e.FUNCTION_KEYS)t=e.FUNCTION_KEYS[n].toLowerCase(),e[t]=parseInt(n,10);for(n in e.PRINTABLE_KEYS)t=e.PRINTABLE_KEYS[n].toLowerCase(),e[t]=parseInt(n,10);return r.mixin(e,e.MODIFIER_KEYS),r.mixin(e,e.PRINTABLE_KEYS),r.mixin(e,e.FUNCTION_KEYS),e.enter=e["return"],e.escape=e.esc,e.del=e["delete"],e[173]="-",function(){var t=["cmd","ctrl","alt","shift"];for(var n=Math.pow(2,t.length);n--;)e.KEY_MODS[n]=t.filter(function(t){return n&e.KEY_MODS[t]}).join("-")+"-"}(),e.KEY_MODS[0]="",e.KEY_MODS[-1]="input-",e}();r.mixin(t,i),t.keyCodeToString=function(e){var t=i[e];return typeof t!="string"&&(t=String.fromCharCode(e)),t.toLowerCase()}}),ace.define("ace/lib/event",["require","exports","module","ace/lib/keys","ace/lib/useragent"],function(e,t,n){"use strict";function a(e,t,n){var a=u(t);if(!i.isMac&&s){t.getModifierState&&(t.getModifierState("OS")||t.getModifierState("Win"))&&(a|=8);if(s.altGr){if((3&a)==3)return;s.altGr=0}if(n===18||n===17){var f="location"in t?t.location:t.keyLocation;if(n===17&&f===1)s[n]==1&&(o=t.timeStamp);else if(n===18&&a===3&&f===2){var l=t.timeStamp-o;l<50&&(s.altGr=!0)}}}n in r.MODIFIER_KEYS&&(n=-1);if(!a&&n===13){var f="location"in t?t.location:t.keyLocation;if(f===3){e(t,a,-n);if(t.defaultPrevented)return}}if(i.isChromeOS&&a&8){e(t,a,n);if(t.defaultPrevented)return;a&=-9}return!!a||n in r.FUNCTION_KEYS||n in r.PRINTABLE_KEYS?e(t,a,n):!1}function f(){s=Object.create(null)}var r=e("./keys"),i=e("./useragent"),s=null,o=0;t.addListener=function(e,t,n){if(e.addEventListener)return e.addEventListener(t,n,!1);if(e.attachEvent){var r=function(){n.call(e,window.event)};n._wrapper=r,e.attachEvent("on"+t,r)}},t.removeListener=function(e,t,n){if(e.removeEventListener)return e.removeEventListener(t,n,!1);e.detachEvent&&e.detachEvent("on"+t,n._wrapper||n)},t.stopEvent=function(e){return t.stopPropagation(e),t.preventDefault(e),!1},t.stopPropagation=function(e){e.stopPropagation?e.stopPropagation():e.cancelBubble=!0},t.preventDefault=function(e){e.preventDefault?e.preventDefault():e.returnValue=!1},t.getButton=function(e){return e.type=="dblclick"?0:e.type=="contextmenu"||i.isMac&&e.ctrlKey&&!e.altKey&&!e.shiftKey?2:e.preventDefault?e.button:{1:0,2:2,4:1}[e.button]},t.capture=function(e,n,r){function i(e){n&&n(e),r&&r(e),t.removeListener(document,"mousemove",n,!0),t.removeListener(document,"mouseup",i,!0),t.removeListener(document,"dragstart",i,!0)}return t.addListener(document,"mousemove",n,!0),t.addListener(document,"mouseup",i,!0),t.addListener(document,"dragstart",i,!0),i},t.addMouseWheelListener=function(e,n){"onmousewheel"in e?t.addListener(e,"mousewheel",function(e){var t=8;e.wheelDeltaX!==undefined?(e.wheelX=-e.wheelDeltaX/t,e.wheelY=-e.wheelDeltaY/t):(e.wheelX=0,e.wheelY=-e.wheelDelta/t),n(e)}):"onwheel"in e?t.addListener(e,"wheel",function(e){var t=.35;switch(e.deltaMode){case e.DOM_DELTA_PIXEL:e.wheelX=e.deltaX*t||0,e.wheelY=e.deltaY*t||0;break;case e.DOM_DELTA_LINE:case e.DOM_DELTA_PAGE:e.wheelX=(e.deltaX||0)*5,e.wheelY=(e.deltaY||0)*5}n(e)}):t.addListener(e,"DOMMouseScroll",function(e){e.axis&&e.axis==e.HORIZONTAL_AXIS?(e.wheelX=(e.detail||0)*5,e.wheelY=0):(e.wheelX=0,e.wheelY=(e.detail||0)*5),n(e)})},t.addMultiMouseDownListener=function(e,n,r,s){function c(e){t.getButton(e)!==0?o=0:e.detail>1?(o++,o>4&&(o=1)):o=1;if(i.isIE){var c=Math.abs(e.clientX-u)>5||Math.abs(e.clientY-a)>5;if(!f||c)o=1;f&&clearTimeout(f),f=setTimeout(function(){f=null},n[o-1]||600),o==1&&(u=e.clientX,a=e.clientY)}e._clicks=o,r[s]("mousedown",e);if(o>4)o=0;else if(o>1)return r[s](l[o],e)}function h(e){o=2,f&&clearTimeout(f),f=setTimeout(function(){f=null},n[o-1]||600),r[s]("mousedown",e),r[s](l[o],e)}var o=0,u,a,f,l={2:"dblclick",3:"tripleclick",4:"quadclick"};Array.isArray(e)||(e=[e]),e.forEach(function(e){t.addListener(e,"mousedown",c),i.isOldIE&&t.addListener(e,"dblclick",h)})};var u=!i.isMac||!i.isOpera||"KeyboardEvent"in window?function(e){return 0|(e.ctrlKey?1:0)|(e.altKey?2:0)|(e.shiftKey?4:0)|(e.metaKey?8:0)}:function(e){return 0|(e.metaKey?1:0)|(e.altKey?2:0)|(e.shiftKey?4:0)|(e.ctrlKey?8:0)};t.getModifierString=function(e){return r.KEY_MODS[u(e)]},t.addCommandKeyListener=function(e,n){var r=t.addListener;if(i.isOldGecko||i.isOpera&&!("KeyboardEvent"in window)){var o=null;r(e,"keydown",function(e){o=e.keyCode}),r(e,"keypress",function(e){return a(n,e,o)})}else{var u=null;r(e,"keydown",function(e){s[e.keyCode]=(s[e.keyCode]||0)+1;var t=a(n,e,e.keyCode);return u=e.defaultPrevented,t}),r(e,"keypress",function(e){u&&(e.ctrlKey||e.altKey||e.shiftKey||e.metaKey)&&(t.stopEvent(e),u=null)}),r(e,"keyup",function(e){s[e.keyCode]=null}),s||(f(),r(window,"focus",f))}};if(typeof window=="object"&&window.postMessage&&!i.isOldIE){var l=1;t.nextTick=function(e,n){n=n||window;var r="zero-timeout-message-"+l++,i=function(s){s.data==r&&(t.stopPropagation(s),t.removeListener(n,"message",i),e())};t.addListener(n,"message",i),n.postMessage(r,"*")}}t.$idleBlocked=!1,t.onIdle=function(e,n){return setTimeout(function r(){t.$idleBlocked?setTimeout(r,100):e()},n)},t.$idleBlockId=null,t.blockIdle=function(e){t.$idleBlockId&&clearTimeout(t.$idleBlockId),t.$idleBlocked=!0,t.$idleBlockId=setTimeout(function(){t.$idleBlocked=!1},e||100)},t.nextFrame=typeof window=="object"&&(window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame||window.oRequestAnimationFrame),t.nextFrame?t.nextFrame=t.nextFrame.bind(window):t.nextFrame=function(e){setTimeout(e,17)}}),ace.define("ace/range",["require","exports","module"],function(e,t,n){"use strict";var r=function(e,t){return e.row-t.row||e.column-t.column},i=function(e,t,n,r){this.start={row:e,column:t},this.end={row:n,column:r}};(function(){this.isEqual=function(e){return this.start.row===e.start.row&&this.end.row===e.end.row&&this.start.column===e.start.column&&this.end.column===e.end.column},this.toString=function(){return"Range: ["+this.start.row+"/"+this.start.column+"] -> ["+this.end.row+"/"+this.end.column+"]"},this.contains=function(e,t){return this.compare(e,t)==0},this.compareRange=function(e){var t,n=e.end,r=e.start;return t=this.compare(n.row,n.column),t==1?(t=this.compare(r.row,r.column),t==1?2:t==0?1:0):t==-1?-2:(t=this.compare(r.row,r.column),t==-1?-1:t==1?42:0)},this.comparePoint=function(e){return this.compare(e.row,e.column)},this.containsRange=function(e){return this.comparePoint(e.start)==0&&this.comparePoint(e.end)==0},this.intersects=function(e){var t=this.compareRange(e);return t==-1||t==0||t==1},this.isEnd=function(e,t){return this.end.row==e&&this.end.column==t},this.isStart=function(e,t){return this.start.row==e&&this.start.column==t},this.setStart=function(e,t){typeof e=="object"?(this.start.column=e.column,this.start.row=e.row):(this.start.row=e,this.start.column=t)},this.setEnd=function(e,t){typeof e=="object"?(this.end.column=e.column,this.end.row=e.row):(this.end.row=e,this.end.column=t)},this.inside=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)||this.isStart(e,t)?!1:!0:!1},this.insideStart=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)?!1:!0:!1},this.insideEnd=function(e,t){return this.compare(e,t)==0?this.isStart(e,t)?!1:!0:!1},this.compare=function(e,t){return!this.isMultiLine()&&e===this.start.row?tthis.end.column?1:0:ethis.end.row?1:this.start.row===e?t>=this.start.column?0:-1:this.end.row===e?t<=this.end.column?0:1:0},this.compareStart=function(e,t){return this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.compareEnd=function(e,t){return this.end.row==e&&this.end.column==t?1:this.compare(e,t)},this.compareInside=function(e,t){return this.end.row==e&&this.end.column==t?1:this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.clipRows=function(e,t){if(this.end.row>t)var n={row:t+1,column:0};else if(this.end.rowt)var r={row:t+1,column:0};else if(this.start.row0){t&1&&(n+=e);if(t>>=1)e+=e}return n};var r=/^\s\s*/,i=/\s\s*$/;t.stringTrimLeft=function(e){return e.replace(r,"")},t.stringTrimRight=function(e){return e.replace(i,"")},t.copyObject=function(e){var t={};for(var n in e)t[n]=e[n];return t},t.copyArray=function(e){var t=[];for(var n=0,r=e.length;nDate.now()-50?!0:r=!1},cancel:function(){r=Date.now()}}}),ace.define("ace/keyboard/textinput",["require","exports","module","ace/lib/event","ace/lib/useragent","ace/lib/dom","ace/lib/lang","ace/clipboard","ace/lib/keys"],function(e,t,n){"use strict";var r=e("../lib/event"),i=e("../lib/useragent"),s=e("../lib/dom"),o=e("../lib/lang"),u=e("../clipboard"),a=i.isChrome<18,f=i.isIE,l=i.isChrome>63,c=400,h=e("../lib/keys"),p=h.KEY_MODS,d=i.isIOS,v=d?/\s/:/\n/,m=function(e,t){function z(){S=!0,n.blur(),n.focus(),S=!1}function X(e){e.keyCode==27&&n.value.lengthN&&x[s]=="\n")o=h.end;else if(rN&&x.slice(0,s).split("\n").length>2)o=h.down;else if(s>N&&x[s-1]==" ")o=h.right,u=p.option;else if(s>N||s==N&&N!=T&&r==s)o=h.right;r!==s&&(u|=p.shift);if(o){var a=t.onCommandKey({},u,o);if(!a&&t.commands){o=h.keyCodeToString(o);var f=t.commands.findKeyCommand(u,o);f&&t.execCommand(f)}T=r,N=s,L("")}};document.addEventListener("selectionchange",s),t.on("destroy",function(){document.removeEventListener("selectionchange",s)})}var n=s.createElement("textarea");n.className="ace_text-input",n.setAttribute("wrap","off"),n.setAttribute("autocorrect","off"),n.setAttribute("autocapitalize","off"),n.setAttribute("spellcheck",!1),n.style.opacity="0",e.insertBefore(n,e.firstChild);var m=!1,g=!1,y=!1,b=!1,w="";i.isMobile||(n.style.fontSize="1px");var E=!1,S=!1,x="",T=0,N=0;try{var C=document.activeElement===n}catch(k){}r.addListener(n,"blur",function(e){if(S)return;t.onBlur(e),C=!1}),r.addListener(n,"focus",function(e){if(S)return;C=!0;if(i.isEdge)try{if(!document.hasFocus())return}catch(e){}t.onFocus(e),i.isEdge?setTimeout(L):L()}),this.$focusScroll=!1,this.focus=function(){if(w||l||this.$focusScroll=="browser")return n.focus({preventScroll:!0});var e=n.style.top;n.style.position="fixed",n.style.top="0px";try{var t=n.getBoundingClientRect().top!=0}catch(r){return}var i=[];if(t){var s=n.parentElement;while(s&&s.nodeType==1)i.push(s),s.setAttribute("ace_nocontext",!0),!s.parentElement&&s.getRootNode?s=s.getRootNode().host:s=s.parentElement}n.focus({preventScroll:!0}),t&&i.forEach(function(e){e.removeAttribute("ace_nocontext")}),setTimeout(function(){n.style.position="",n.style.top=="0px"&&(n.style.top=e)},0)},this.blur=function(){n.blur()},this.isFocused=function(){return C},t.on("beforeEndOperation",function(){if(t.curOp&&t.curOp.command.name=="insertstring")return;y&&(x=n.value="",U()),L()});var L=d?function(e){if(!C||m&&!e||b)return;e||(e="");var r="\n ab"+e+"cde fg\n";r!=n.value&&(n.value=x=r);var i=4,s=4+(e.length||(t.selection.isEmpty()?0:1));(T!=i||N!=s)&&n.setSelectionRange(i,s),T=i,N=s}:function(){if(y||b)return;if(!C&&!_)return;y=!0;var e=t.selection,r=e.getRange(),i=e.cursor.row,s=r.start.column,o=r.end.column,u=t.session.getLine(i);if(r.start.row!=i){var a=t.session.getLine(i-1);s=r.start.rowi+1?f.length:o,o+=u.length+1,u=u+"\n"+f}u.length>c&&(s=x.length&&e.value===x&&x&&e.selectionEnd!==N},O=function(e){if(y)return;m?m=!1:A(n)&&(t.selectAll(),L())},M=null;this.setInputHandler=function(e){M=e},this.getInputHandler=function(){return M};var _=!1,D=function(e,r){_&&(_=!1);if(g)return L(),e&&t.onPaste(e),g=!1,"";var i=n.selectionStart,s=n.selectionEnd,o=T,u=x.length-N,a=e,f=e.length-i,l=e.length-s,c=0;while(o>0&&x[c]==e[c])c++,o--;a=a.slice(c),c=1;while(u>0&&x.length-c>T-1&&x[x.length-c]==e[e.length-c])c++,u--;f-=c-1,l-=c-1;var h=a.length-c+1;return h<0&&(o=-h,h=0),a=a.slice(0,h),!r&&f==a.length&&!o&&!u&&!l?"":(b=!0,a&&!o&&!u&&!f&&!l||E?t.onTextInput(a):t.onTextInput(a,{extendLeft:o,extendRight:u,restoreStart:f,restoreEnd:l}),b=!1,x=e,T=i,N=s,a)},P=function(e){if(y)return R();if(e&&e.inputType){if(e.inputType=="historyUndo")return t.execCommand("undo");if(e.inputType=="historyRedo")return t.execCommand("redo")}var r=n.value,i=D(r,!0);(r.length>c+100||v.test(i))&&L()},H=function(e,t,n){var r=e.clipboardData||window.clipboardData;if(!r||a)return;var i=f||n?"Text":"text/plain";try{return t?r.setData(i,t)!==!1:r.getData(i)}catch(e){if(!n)return H(e,t,!0)}},B=function(e,i){var s=t.getCopyText();if(!s)return r.preventDefault(e);H(e,s)?(d&&(L(s),m=s,setTimeout(function(){m=!1},10)),i?t.onCut():t.onCopy(),r.preventDefault(e)):(m=!0,n.value=s,n.select(),setTimeout(function(){m=!1,L(),i?t.onCut():t.onCopy()}))},j=function(e){B(e,!0)},F=function(e){B(e,!1)},I=function(e){var s=H(e);if(u.pasteCancelled())return;typeof s=="string"?(s&&t.onPaste(s,e),i.isIE&&setTimeout(L),r.preventDefault(e)):(n.value="",g=!0)};r.addCommandKeyListener(n,t.onCommandKey.bind(t)),r.addListener(n,"select",O),r.addListener(n,"input",P),r.addListener(n,"cut",j),r.addListener(n,"copy",F),r.addListener(n,"paste",I),(!("oncut"in n)||!("oncopy"in n)||!("onpaste"in n))&&r.addListener(e,"keydown",function(e){if(i.isMac&&!e.metaKey||!e.ctrlKey)return;switch(e.keyCode){case 67:F(e);break;case 86:I(e);break;case 88:j(e)}});var q=function(e){if(y||!t.onCompositionStart||t.$readOnly)return;y={};if(E)return;setTimeout(R,0),t.on("mousedown",z);var r=t.getSelectionRange();r.end.row=r.start.row,r.end.column=r.start.column,y.markerRange=r,y.selectionStart=T,t.onCompositionStart(y),y.useTextareaForIME?(n.value="",x="",T=0,N=0):(n.msGetInputContext&&(y.context=n.msGetInputContext()),n.getInputContext&&(y.context=n.getInputContext()))},R=function(){if(!y||!t.onCompositionUpdate||t.$readOnly)return;if(E)return z();if(y.useTextareaForIME)t.onCompositionUpdate(n.value);else{var e=n.value;D(e),y.markerRange&&(y.context&&(y.markerRange.start.column=y.selectionStart=y.context.compositionStartOffset),y.markerRange.end.column=y.markerRange.start.column+N-y.selectionStart)}},U=function(e){if(!t.onCompositionEnd||t.$readOnly)return;y=!1,t.onCompositionEnd(),t.off("mousedown",z),e&&P()},W=o.delayedCall(R,50).schedule.bind(null,null);r.addListener(n,"compositionstart",q),r.addListener(n,"compositionupdate",R),r.addListener(n,"keyup",X),r.addListener(n,"keydown",W),r.addListener(n,"compositionend",U),this.getElement=function(){return n},this.setCommandMode=function(e){E=e,n.readOnly=!1},this.setReadOnly=function(e){E||(n.readOnly=e)},this.setCopyWithEmptySelection=function(e){},this.onContextMenu=function(e){_=!0,L(),t._emit("nativecontextmenu",{target:t,domEvent:e}),this.moveToMouse(e,!0)},this.moveToMouse=function(e,o){w||(w=n.style.cssText),n.style.cssText=(o?"z-index:100000;":"")+(i.isIE?"opacity:0.1;":"")+"text-indent: -"+(T+N)*t.renderer.characterWidth*.5+"px;";var u=t.container.getBoundingClientRect(),a=s.computedStyle(t.container),f=u.top+(parseInt(a.borderTopWidth)||0),l=u.left+(parseInt(u.borderLeftWidth)||0),c=u.bottom-f-n.clientHeight-2,h=function(e){s.translate(n,e.clientX-l-2,Math.min(e.clientY-f-2,c))};h(e);if(e.type!="mousedown")return;t.renderer.$isMousePressed=!0,clearTimeout(V),i.isWin&&r.capture(t.container,h,$)},this.onContextMenuClose=$;var V,J=function(e){t.textInput.onContextMenu(e),$()};r.addListener(n,"mouseup",J),r.addListener(n,"mousedown",function(e){e.preventDefault(),$()}),r.addListener(t.renderer.scroller,"contextmenu",J),r.addListener(n,"contextmenu",J),d&&K(e,t,n)};t.TextInput=m}),ace.define("ace/mouse/default_handlers",["require","exports","module","ace/lib/useragent"],function(e,t,n){"use strict";function o(e){e.$clickSelection=null;var t=e.editor;t.setDefaultHandler("mousedown",this.onMouseDown.bind(e)),t.setDefaultHandler("dblclick",this.onDoubleClick.bind(e)),t.setDefaultHandler("tripleclick",this.onTripleClick.bind(e)),t.setDefaultHandler("quadclick",this.onQuadClick.bind(e)),t.setDefaultHandler("mousewheel",this.onMouseWheel.bind(e));var n=["select","startSelect","selectEnd","selectAllEnd","selectByWordsEnd","selectByLinesEnd","dragWait","dragWaitEnd","focusWait"];n.forEach(function(t){e[t]=this[t]},this),e.selectByLines=this.extendSelectionBy.bind(e,"getLineRange"),e.selectByWords=this.extendSelectionBy.bind(e,"getWordRange")}function u(e,t,n,r){return Math.sqrt(Math.pow(n-e,2)+Math.pow(r-t,2))}function a(e,t){if(e.start.row==e.end.row)var n=2*t.column-e.start.column-e.end.column;else if(e.start.row==e.end.row-1&&!e.start.column&&!e.end.column)var n=t.column-4;else var n=2*t.row-e.start.row-e.end.row;return n<0?{cursor:e.start,anchor:e.end}:{cursor:e.end,anchor:e.start}}var r=e("../lib/useragent"),i=0,s=550;(function(){this.onMouseDown=function(e){var t=e.inSelection(),n=e.getDocumentPosition();this.mousedownEvent=e;var i=this.editor,s=e.getButton();if(s!==0){var o=i.getSelectionRange(),u=o.isEmpty();(u||s==1)&&i.selection.moveToPosition(n),s==2&&(i.textInput.onContextMenu(e.domEvent),r.isMozilla||e.preventDefault());return}this.mousedownEvent.time=Date.now();if(t&&!i.isFocused()){i.focus();if(this.$focusTimeout&&!this.$clickSelection&&!i.inMultiSelectMode){this.setState("focusWait"),this.captureMouse(e);return}}return this.captureMouse(e),this.startSelect(n,e.domEvent._clicks>1),e.preventDefault()},this.startSelect=function(e,t){e=e||this.editor.renderer.screenToTextCoordinates(this.x,this.y);var n=this.editor;if(!this.mousedownEvent)return;this.mousedownEvent.getShiftKey()?n.selection.selectToPosition(e):t||n.selection.moveToPosition(e),t||this.select(),n.renderer.scroller.setCapture&&n.renderer.scroller.setCapture(),n.setStyle("ace_selecting"),this.setState("select")},this.select=function(){var e,t=this.editor,n=t.renderer.screenToTextCoordinates(this.x,this.y);if(this.$clickSelection){var r=this.$clickSelection.comparePoint(n);if(r==-1)e=this.$clickSelection.end;else if(r==1)e=this.$clickSelection.start;else{var i=a(this.$clickSelection,n);n=i.cursor,e=i.anchor}t.selection.setSelectionAnchor(e.row,e.column)}t.selection.selectToPosition(n),t.renderer.scrollCursorIntoView()},this.extendSelectionBy=function(e){var t,n=this.editor,r=n.renderer.screenToTextCoordinates(this.x,this.y),i=n.selection[e](r.row,r.column);if(this.$clickSelection){var s=this.$clickSelection.comparePoint(i.start),o=this.$clickSelection.comparePoint(i.end);if(s==-1&&o<=0){t=this.$clickSelection.end;if(i.end.row!=r.row||i.end.column!=r.column)r=i.start}else if(o==1&&s>=0){t=this.$clickSelection.start;if(i.start.row!=r.row||i.start.column!=r.column)r=i.end}else if(s==-1&&o==1)r=i.end,t=i.start;else{var u=a(this.$clickSelection,r);r=u.cursor,t=u.anchor}n.selection.setSelectionAnchor(t.row,t.column)}n.selection.selectToPosition(r),n.renderer.scrollCursorIntoView()},this.selectEnd=this.selectAllEnd=this.selectByWordsEnd=this.selectByLinesEnd=function(){this.$clickSelection=null,this.editor.unsetStyle("ace_selecting"),this.editor.renderer.scroller.releaseCapture&&this.editor.renderer.scroller.releaseCapture()},this.focusWait=function(){var e=u(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y),t=Date.now();(e>i||t-this.mousedownEvent.time>this.$focusTimeout)&&this.startSelect(this.mousedownEvent.getDocumentPosition())},this.onDoubleClick=function(e){var t=e.getDocumentPosition(),n=this.editor,r=n.session,i=r.getBracketRange(t);i?(i.isEmpty()&&(i.start.column--,i.end.column++),this.setState("select")):(i=n.selection.getWordRange(t.row,t.column),this.setState("selectByWords")),this.$clickSelection=i,this.select()},this.onTripleClick=function(e){var t=e.getDocumentPosition(),n=this.editor;this.setState("selectByLines");var r=n.getSelectionRange();r.isMultiLine()&&r.contains(t.row,t.column)?(this.$clickSelection=n.selection.getLineRange(r.start.row),this.$clickSelection.end=n.selection.getLineRange(r.end.row).end):this.$clickSelection=n.selection.getLineRange(t.row),this.select()},this.onQuadClick=function(e){var t=this.editor;t.selectAll(),this.$clickSelection=t.getSelectionRange(),this.setState("selectAll")},this.onMouseWheel=function(e){if(e.getAccelKey())return;e.getShiftKey()&&e.wheelY&&!e.wheelX&&(e.wheelX=e.wheelY,e.wheelY=0);var t=this.editor;this.$lastScroll||(this.$lastScroll={t:0,vx:0,vy:0,allowed:0});var n=this.$lastScroll,r=e.domEvent.timeStamp,i=r-n.t,o=i?e.wheelX/i:n.vx,u=i?e.wheelY/i:n.vy;i=1&&t.renderer.isScrollableBy(e.wheelX*e.speed,0)&&(f=!0),a<=1&&t.renderer.isScrollableBy(0,e.wheelY*e.speed)&&(f=!0);if(f)n.allowed=r;else if(r-n.allowedt.session.documentToScreenRow(l.row,l.column))return c()}if(f==s)return;f=s.text.join("
"),i.setHtml(f),i.show(),t._signal("showGutterTooltip",i),t.on("mousewheel",c);if(e.$tooltipFollowsMouse)h(u);else{var p=u.domEvent.target,d=p.getBoundingClientRect(),v=i.getElement().style;v.left=d.right+"px",v.top=d.bottom+"px"}}function c(){o&&(o=clearTimeout(o)),f&&(i.hide(),f=null,t._signal("hideGutterTooltip",i),t.removeEventListener("mousewheel",c))}function h(e){i.setPosition(e.x,e.y)}var t=e.editor,n=t.renderer.$gutterLayer,i=new a(t.container);e.editor.setDefaultHandler("guttermousedown",function(r){if(!t.isFocused()||r.getButton()!=0)return;var i=n.getRegion(r);if(i=="foldWidgets")return;var s=r.getDocumentPosition().row,o=t.session.selection;if(r.getShiftKey())o.selectTo(s,0);else{if(r.domEvent.detail==2)return t.selectAll(),r.preventDefault();e.$clickSelection=t.selection.getLineRange(s)}return e.setState("selectByLines"),e.captureMouse(r),r.preventDefault()});var o,u,f;e.editor.setDefaultHandler("guttermousemove",function(t){var n=t.domEvent.target||t.domEvent.srcElement;if(r.hasCssClass(n,"ace_fold-widget"))return c();f&&e.$tooltipFollowsMouse&&h(t),u=t;if(o)return;o=setTimeout(function(){o=null,u&&!e.isMousePressed?l():c()},50)}),s.addListener(t.renderer.$gutter,"mouseout",function(e){u=null;if(!f||o)return;o=setTimeout(function(){o=null,c()},50)}),t.on("changeSession",c)}function a(e){o.call(this,e)}var r=e("../lib/dom"),i=e("../lib/oop"),s=e("../lib/event"),o=e("../tooltip").Tooltip;i.inherits(a,o),function(){this.setPosition=function(e,t){var n=window.innerWidth||document.documentElement.clientWidth,r=window.innerHeight||document.documentElement.clientHeight,i=this.getWidth(),s=this.getHeight();e+=15,t+=15,e+i>n&&(e-=e+i-n),t+s>r&&(t-=20+s),o.prototype.setPosition.call(this,e,t)}}.call(a.prototype),t.GutterHandler=u}),ace.define("ace/mouse/mouse_event",["require","exports","module","ace/lib/event","ace/lib/useragent"],function(e,t,n){"use strict";var r=e("../lib/event"),i=e("../lib/useragent"),s=t.MouseEvent=function(e,t){this.domEvent=e,this.editor=t,this.x=this.clientX=e.clientX,this.y=this.clientY=e.clientY,this.$pos=null,this.$inSelection=null,this.propagationStopped=!1,this.defaultPrevented=!1};(function(){this.stopPropagation=function(){r.stopPropagation(this.domEvent),this.propagationStopped=!0},this.preventDefault=function(){r.preventDefault(this.domEvent),this.defaultPrevented=!0},this.stop=function(){this.stopPropagation(),this.preventDefault()},this.getDocumentPosition=function(){return this.$pos?this.$pos:(this.$pos=this.editor.renderer.screenToTextCoordinates(this.clientX,this.clientY),this.$pos)},this.inSelection=function(){if(this.$inSelection!==null)return this.$inSelection;var e=this.editor,t=e.getSelectionRange();if(t.isEmpty())this.$inSelection=!1;else{var n=this.getDocumentPosition();this.$inSelection=t.contains(n.row,n.column)}return this.$inSelection},this.getButton=function(){return r.getButton(this.domEvent)},this.getShiftKey=function(){return this.domEvent.shiftKey},this.getAccelKey=i.isMac?function(){return this.domEvent.metaKey}:function(){return this.domEvent.ctrlKey}}).call(s.prototype)}),ace.define("ace/mouse/dragdrop_handler",["require","exports","module","ace/lib/dom","ace/lib/event","ace/lib/useragent"],function(e,t,n){"use strict";function f(e){function T(e,n){var r=Date.now(),i=!n||e.row!=n.row,s=!n||e.column!=n.column;if(!S||i||s)t.moveCursorToPosition(e),S=r,x={x:p,y:d};else{var o=l(x.x,x.y,p,d);o>a?S=null:r-S>=u&&(t.renderer.scrollCursorIntoView(),S=null)}}function N(e,n){var r=Date.now(),i=t.renderer.layerConfig.lineHeight,s=t.renderer.layerConfig.characterWidth,u=t.renderer.scroller.getBoundingClientRect(),a={x:{left:p-u.left,right:u.right-p},y:{top:d-u.top,bottom:u.bottom-d}},f=Math.min(a.x.left,a.x.right),l=Math.min(a.y.top,a.y.bottom),c={row:e.row,column:e.column};f/s<=2&&(c.column+=a.x.left=o&&t.renderer.scrollCursorIntoView(c):E=r:E=null}function C(){var e=g;g=t.renderer.screenToTextCoordinates(p,d),T(g,e),N(g,e)}function k(){m=t.selection.toOrientedRange(),h=t.session.addMarker(m,"ace_selection",t.getSelectionStyle()),t.clearSelection(),t.isFocused()&&t.renderer.$cursorLayer.setBlinking(!1),clearInterval(v),C(),v=setInterval(C,20),y=0,i.addListener(document,"mousemove",O)}function L(){clearInterval(v),t.session.removeMarker(h),h=null,t.selection.fromOrientedRange(m),t.isFocused()&&!w&&t.$resetCursorStyle(),m=null,g=null,y=0,E=null,S=null,i.removeListener(document,"mousemove",O)}function O(){A==null&&(A=setTimeout(function(){A!=null&&h&&L()},20))}function M(e){var t=e.types;return!t||Array.prototype.some.call(t,function(e){return e=="text/plain"||e=="Text"})}function _(e){var t=["copy","copymove","all","uninitialized"],n=["move","copymove","linkmove","all","uninitialized"],r=s.isMac?e.altKey:e.ctrlKey,i="uninitialized";try{i=e.dataTransfer.effectAllowed.toLowerCase()}catch(e){}var o="none";return r&&t.indexOf(i)>=0?o="copy":n.indexOf(i)>=0?o="move":t.indexOf(i)>=0&&(o="copy"),o}var t=e.editor,n=r.createElement("img");n.src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==",s.isOpera&&(n.style.cssText="width:1px;height:1px;position:fixed;top:0;left:0;z-index:2147483647;opacity:0;");var f=["dragWait","dragWaitEnd","startDrag","dragReadyEnd","onMouseDrag"];f.forEach(function(t){e[t]=this[t]},this),t.addEventListener("mousedown",this.onMouseDown.bind(e));var c=t.container,h,p,d,v,m,g,y=0,b,w,E,S,x;this.onDragStart=function(e){if(this.cancelDrag||!c.draggable){var r=this;return setTimeout(function(){r.startSelect(),r.captureMouse(e)},0),e.preventDefault()}m=t.getSelectionRange();var i=e.dataTransfer;i.effectAllowed=t.getReadOnly()?"copy":"copyMove",s.isOpera&&(t.container.appendChild(n),n.scrollTop=0),i.setDragImage&&i.setDragImage(n,0,0),s.isOpera&&t.container.removeChild(n),i.clearData(),i.setData("Text",t.session.getTextRange()),w=!0,this.setState("drag")},this.onDragEnd=function(e){c.draggable=!1,w=!1,this.setState(null);if(!t.getReadOnly()){var n=e.dataTransfer.dropEffect;!b&&n=="move"&&t.session.remove(t.getSelectionRange()),t.$resetCursorStyle()}this.editor.unsetStyle("ace_dragging"),this.editor.renderer.setCursorStyle("")},this.onDragEnter=function(e){if(t.getReadOnly()||!M(e.dataTransfer))return;return p=e.clientX,d=e.clientY,h||k(),y++,e.dataTransfer.dropEffect=b=_(e),i.preventDefault(e)},this.onDragOver=function(e){if(t.getReadOnly()||!M(e.dataTransfer))return;return p=e.clientX,d=e.clientY,h||(k(),y++),A!==null&&(A=null),e.dataTransfer.dropEffect=b=_(e),i.preventDefault(e)},this.onDragLeave=function(e){y--;if(y<=0&&h)return L(),b=null,i.preventDefault(e)},this.onDrop=function(e){if(!g)return;var n=e.dataTransfer;if(w)switch(b){case"move":m.contains(g.row,g.column)?m={start:g,end:g}:m=t.moveText(m,g);break;case"copy":m=t.moveText(m,g,!0)}else{var r=n.getData("Text");m={start:g,end:t.session.insert(g,r)},t.focus(),b=null}return L(),i.preventDefault(e)},i.addListener(c,"dragstart",this.onDragStart.bind(e)),i.addListener(c,"dragend",this.onDragEnd.bind(e)),i.addListener(c,"dragenter",this.onDragEnter.bind(e)),i.addListener(c,"dragover",this.onDragOver.bind(e)),i.addListener(c,"dragleave",this.onDragLeave.bind(e)),i.addListener(c,"drop",this.onDrop.bind(e));var A=null}function l(e,t,n,r){return Math.sqrt(Math.pow(n-e,2)+Math.pow(r-t,2))}var r=e("../lib/dom"),i=e("../lib/event"),s=e("../lib/useragent"),o=200,u=200,a=5;(function(){this.dragWait=function(){var e=Date.now()-this.mousedownEvent.time;e>this.editor.getDragDelay()&&this.startDrag()},this.dragWaitEnd=function(){var e=this.editor.container;e.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()),this.selectEnd()},this.dragReadyEnd=function(e){this.editor.$resetCursorStyle(),this.editor.unsetStyle("ace_dragging"),this.editor.renderer.setCursorStyle(""),this.dragWaitEnd()},this.startDrag=function(){this.cancelDrag=!1;var e=this.editor,t=e.container;t.draggable=!0,e.renderer.$cursorLayer.setBlinking(!1),e.setStyle("ace_dragging");var n=s.isWin?"default":"move";e.renderer.setCursorStyle(n),this.setState("dragReady")},this.onMouseDrag=function(e){var t=this.editor.container;if(s.isIE&&this.state=="dragReady"){var n=l(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y);n>3&&t.dragDrop()}if(this.state==="dragWait"){var n=l(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y);n>0&&(t.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()))}},this.onMouseDown=function(e){if(!this.$dragEnabled)return;this.mousedownEvent=e;var t=this.editor,n=e.inSelection(),r=e.getButton(),i=e.domEvent.detail||1;if(i===1&&r===0&&n){if(e.editor.inMultiSelectMode&&(e.getAccelKey()||e.getShiftKey()))return;this.mousedownEvent.time=Date.now();var o=e.domEvent.target||e.domEvent.srcElement;"unselectable"in o&&(o.unselectable="on");if(t.getDragDelay()){if(s.isWebKit){this.cancelDrag=!0;var u=t.container;u.draggable=!0}this.setState("dragWait")}else this.startDrag();this.captureMouse(e,this.onMouseDrag.bind(this)),e.defaultPrevented=!0}}}).call(f.prototype),t.DragdropHandler=f}),ace.define("ace/mouse/touch_handler",["require","exports","module","ace/mouse/mouse_event","ace/lib/dom"],function(e,t,n){"use strict";var r=e("./mouse_event").MouseEvent,i=e("../lib/dom");t.addTouchListeners=function(e,t){function y(){var e=window.navigator&&window.navigator.clipboard,r=!1,s=function(){var n=t.getCopyText(),s=t.session.getUndoManager().hasUndo();g.replaceChild(i.buildDom(r?["span",!n&&["span",{"class":"ace_mobile-button",action:"selectall"},"Select All"],n&&["span",{"class":"ace_mobile-button",action:"copy"},"Copy"],n&&["span",{"class":"ace_mobile-button",action:"cut"},"Cut"],e&&["span",{"class":"ace_mobile-button",action:"paste"},"Paste"],s&&["span",{"class":"ace_mobile-button",action:"undo"},"Undo"],["span",{"class":"ace_mobile-button",action:"find"},"Find"],["span",{"class":"ace_mobile-button",action:"openCommandPallete"},"Pallete"]]:["span"]),g.firstChild)},o=function(n){var i=n.target.getAttribute("action");if(i=="more"||!r)return r=!r,s();if(i=="paste")e.readText().then(function(e){t.execCommand(i,e)});else if(i){if(i=="cut"||i=="copy")e?e.writeText(t.getCopyText()):document.execCommand("copy");t.execCommand(i)}g.firstChild.style.display="none",r=!1,i!="openCommandPallete"&&t.focus()};g=i.buildDom(["div",{"class":"ace_mobile-menu",ontouchstart:function(e){n="menu",e.stopPropagation(),e.preventDefault(),t.textInput.focus()},ontouchend:function(e){e.stopPropagation(),e.preventDefault(),o(e)},onclick:o},["span"],["span",{"class":"ace_mobile-button",action:"more"},"..."]],t.container)}function b(){g||y();var e=t.selection.cursor,n=t.renderer.textToScreenCoordinates(e.row,e.column),r=t.container.getBoundingClientRect();g.style.top=n.pageY-r.top-3+"px",g.style.right="10px",g.style.display="",g.firstChild.style.display="none",t.on("input",w)}function w(e){g&&(g.style.display="none"),t.off("input",w)}function E(){f=null,clearTimeout(f);var e=t.selection.getRange(),r=e.contains(h.row,h.column);if(e.isEmpty()||!r)t.selection.moveToPosition(h),t.selection.selectWord();n="wait",b()}function S(){f=null,clearTimeout(f),t.selection.moveToPosition(h);var e=p>=2?t.selection.getLineRange(h.row):t.session.getBracketRange(h);e&&!e.isEmpty()?t.selection.setRange(e):t.selection.selectWord(),n="wait"}function x(){c+=60,l=setInterval(function(){c--<=0&&(clearInterval(l),l=null),Math.abs(d)<.01&&(d=0),Math.abs(v)<.01&&(v=0),c<20&&(d=.9*d),c<20&&(v=.9*v);var e=t.session.getScrollTop();t.renderer.scrollBy(10*d,10*v),e==t.session.getScrollTop()&&(c=0)},10)}var n="scroll",s,o,u,a,f,l,c=0,h,p=0,d=0,v=0,m,g;e.addEventListener("contextmenu",function(e){if(!m)return;var n=t.textInput.getElement();n.focus()}),e.addEventListener("touchstart",function(e){var i=e.touches;if(f||i.length>1){clearTimeout(f),f=null,u=-1,n="zoom";return}m=t.$mouseHandler.isMousePressed=!0;var l=t.renderer.layerConfig.lineHeight,g=t.renderer.layerConfig.lineHeight,y=e.timeStamp;a=y;var b=i[0],w=b.clientX,x=b.clientY;Math.abs(s-w)+Math.abs(o-x)>l&&(u=-1),s=e.clientX=w,o=e.clientY=x,d=v=0;var T=new r(e,t);h=T.getDocumentPosition();if(y-u<500&&i.length==1&&!c)p++,e.preventDefault(),e.button=0,S();else{p=0;var N=t.selection.cursor,C=t.selection.isEmpty()?N:t.selection.anchor,k=t.renderer.$cursorLayer.getPixelPosition(N,!0),L=t.renderer.$cursorLayer.getPixelPosition(C,!0),A=t.renderer.scroller.getBoundingClientRect(),O=function(e,t){return e/=g,t=t/l-.75,e*e+t*t};if(e.clientX_?"cursor":"anchor"),_<3.5?n="anchor":M<3.5?n="cursor":n="scroll",f=setTimeout(E,450)}u=y}),e.addEventListener("touchend",function(e){m=t.$mouseHandler.isMousePressed=!1,l&&clearInterval(l),n=="zoom"?(n="",c=0):f?(t.selection.moveToPosition(h),c=0,b()):n=="scroll"?(x(),e.preventDefault(),w()):b(),clearTimeout(f),f=null}),e.addEventListener("touchmove",function(e){f&&(clearTimeout(f),f=null);var i=e.touches;if(i.length>1||n=="zoom")return;var u=i[0],l=s-u.clientX,c=o-u.clientY;if(n=="wait"){if(!(l*l+c*c>4))return e.preventDefault();n="cursor"}s=u.clientX,o=u.clientY,e.clientX=u.clientX,e.clientY=u.clientY;var h=e.timeStamp,p=h-a;a=h;if(n=="scroll"){var m=new r(e,t);m.speed=1,m.wheelX=l,m.wheelY=c,10*Math.abs(l)1&&(i=n[n.length-2]);var o=a[t+"Path"];return o==null?o=a.basePath:r=="/"&&(t=r=""),o&&o.slice(-1)!="/"&&(o+="/"),o+t+r+i+this.get("suffix")},t.setModuleUrl=function(e,t){return a.$moduleUrls[e]=t},t.$loading={},t.loadModule=function(n,r){var i,o;Array.isArray(n)&&(o=n[0],n=n[1]);try{i=e(n)}catch(u){}if(i&&!t.$loading[n])return r&&r(i);t.$loading[n]||(t.$loading[n]=[]),t.$loading[n].push(r);if(t.$loading[n].length>1)return;var a=function(){e([n],function(e){t._emit("load.module",{name:n,module:e});var r=t.$loading[n];t.$loading[n]=null,r.forEach(function(t){t&&t(e)})})};if(!t.get("packaged"))return a();s.loadScript(t.moduleUrl(n,o),a),f()};var f=function(){!a.basePath&&!a.workerPath&&!a.modePath&&!a.themePath&&!Object.keys(a.$moduleUrls).length&&(console.error("Unable to infer path to ace from script src,","use ace.config.set('basePath', 'path') to enable dynamic loading of modes and themes","or with webpack use ace/webpack-resolver"),f=function(){})};t.init=l,t.version="1.4.6"}),ace.define("ace/mouse/mouse_handler",["require","exports","module","ace/lib/event","ace/lib/useragent","ace/mouse/default_handlers","ace/mouse/default_gutter_handler","ace/mouse/mouse_event","ace/mouse/dragdrop_handler","ace/mouse/touch_handler","ace/config"],function(e,t,n){"use strict";var r=e("../lib/event"),i=e("../lib/useragent"),s=e("./default_handlers").DefaultHandlers,o=e("./default_gutter_handler").GutterHandler,u=e("./mouse_event").MouseEvent,a=e("./dragdrop_handler").DragdropHandler,f=e("./touch_handler").addTouchListeners,l=e("../config"),c=function(e){var t=this;this.editor=e,new s(this),new o(this),new a(this);var n=function(t){var n=!document.hasFocus||!document.hasFocus()||!e.isFocused()&&document.activeElement==(e.textInput&&e.textInput.getElement());n&&window.focus(),e.focus()},u=e.renderer.getMouseEventTarget();r.addListener(u,"click",this.onMouseEvent.bind(this,"click")),r.addListener(u,"mousemove",this.onMouseMove.bind(this,"mousemove")),r.addMultiMouseDownListener([u,e.renderer.scrollBarV&&e.renderer.scrollBarV.inner,e.renderer.scrollBarH&&e.renderer.scrollBarH.inner,e.textInput&&e.textInput.getElement()].filter(Boolean),[400,300,250],this,"onMouseEvent"),r.addMouseWheelListener(e.container,this.onMouseWheel.bind(this,"mousewheel")),f(e.container,e);var l=e.renderer.$gutter;r.addListener(l,"mousedown",this.onMouseEvent.bind(this,"guttermousedown")),r.addListener(l,"click",this.onMouseEvent.bind(this,"gutterclick")),r.addListener(l,"dblclick",this.onMouseEvent.bind(this,"gutterdblclick")),r.addListener(l,"mousemove",this.onMouseEvent.bind(this,"guttermousemove")),r.addListener(u,"mousedown",n),r.addListener(l,"mousedown",n),i.isIE&&e.renderer.scrollBarV&&(r.addListener(e.renderer.scrollBarV.element,"mousedown",n),r.addListener(e.renderer.scrollBarH.element,"mousedown",n)),e.on("mousemove",function(n){if(t.state||t.$dragDelay||!t.$dragEnabled)return;var r=e.renderer.screenToTextCoordinates(n.x,n.y),i=e.session.selection.getRange(),s=e.renderer;!i.isEmpty()&&i.insideStart(r.row,r.column)?s.setCursorStyle("default"):s.setCursorStyle("")})};(function(){this.onMouseEvent=function(e,t){this.editor._emit(e,new u(t,this.editor))},this.onMouseMove=function(e,t){var n=this.editor._eventRegistry&&this.editor._eventRegistry.mousemove;if(!n||!n.length)return;this.editor._emit(e,new u(t,this.editor))},this.onMouseWheel=function(e,t){var n=new u(t,this.editor);n.speed=this.$scrollSpeed*2,n.wheelX=t.wheelX,n.wheelY=t.wheelY,this.editor._emit(e,n)},this.setState=function(e){this.state=e},this.captureMouse=function(e,t){this.x=e.x,this.y=e.y,this.isMousePressed=!0;var n=this.editor,s=this.editor.renderer;s.$isMousePressed=!0;var o=this,a=function(e){if(!e)return;if(i.isWebKit&&!e.which&&o.releaseMouse)return o.releaseMouse();o.x=e.clientX,o.y=e.clientY,t&&t(e),o.mouseEvent=new u(e,o.editor),o.$mouseMoved=!0},f=function(e){n.off("beforeEndOperation",c),clearInterval(h),l(),o[o.state+"End"]&&o[o.state+"End"](e),o.state="",o.isMousePressed=s.$isMousePressed=!1,s.$keepTextAreaAtCursor&&s.$moveTextAreaToCursor(),o.$onCaptureMouseMove=o.releaseMouse=null,e&&o.onMouseEvent("mouseup",e),n.endOperation()},l=function(){o[o.state]&&o[o.state](),o.$mouseMoved=!1};if(i.isOldIE&&e.domEvent.type=="dblclick")return setTimeout(function(){f(e)});var c=function(e){if(!o.releaseMouse)return;n.curOp.command.name&&n.curOp.selectionChanged&&(o[o.state+"End"]&&o[o.state+"End"](),o.state="",o.releaseMouse())};n.on("beforeEndOperation",c),n.startOperation({command:{name:"mouse"}}),o.$onCaptureMouseMove=a,o.releaseMouse=r.capture(this.editor.container,a,f);var h=setInterval(l,20)},this.releaseMouse=null,this.cancelContextMenu=function(){var e=function(t){if(t&&t.domEvent&&t.domEvent.type!="contextmenu")return;this.editor.off("nativecontextmenu",e),t&&t.domEvent&&r.stopEvent(t.domEvent)}.bind(this);setTimeout(e,10),this.editor.on("nativecontextmenu",e)}}).call(c.prototype),l.defineOptions(c.prototype,"mouseHandler",{scrollSpeed:{initialValue:2},dragDelay:{initialValue:i.isMac?150:0},dragEnabled:{initialValue:!0},focusTimeout:{initialValue:0},tooltipFollowsMouse:{initialValue:!0}}),t.MouseHandler=c}),ace.define("ace/mouse/fold_handler",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";function i(e){e.on("click",function(t){var n=t.getDocumentPosition(),i=e.session,s=i.getFoldAt(n.row,n.column,1);s&&(t.getAccelKey()?i.removeFold(s):i.expandFold(s),t.stop());var o=t.domEvent&&t.domEvent.target;o&&r.hasCssClass(o,"ace_inline_button")&&r.hasCssClass(o,"ace_toggle_wrap")&&(i.setOption("wrap",!i.getUseWrapMode()),e.renderer.scrollCursorIntoView())}),e.on("gutterclick",function(t){var n=e.renderer.$gutterLayer.getRegion(t);if(n=="foldWidgets"){var r=t.getDocumentPosition().row,i=e.session;i.foldWidgets&&i.foldWidgets[r]&&e.session.onFoldWidgetClick(r,t),e.isFocused()||e.focus(),t.stop()}}),e.on("gutterdblclick",function(t){var n=e.renderer.$gutterLayer.getRegion(t);if(n=="foldWidgets"){var r=t.getDocumentPosition().row,i=e.session,s=i.getParentFoldRangeData(r,!0),o=s.range||s.firstRange;if(o){r=o.start.row;var u=i.getFoldAt(r,i.getLine(r).length,1);u?i.removeFold(u):(i.addFold("...",o),e.renderer.scrollCursorIntoView({row:o.start.row,column:0}))}t.stop()}})}var r=e("../lib/dom");t.FoldHandler=i}),ace.define("ace/keyboard/keybinding",["require","exports","module","ace/lib/keys","ace/lib/event"],function(e,t,n){"use strict";var r=e("../lib/keys"),i=e("../lib/event"),s=function(e){this.$editor=e,this.$data={editor:e},this.$handlers=[],this.setDefaultHandler(e.commands)};(function(){this.setDefaultHandler=function(e){this.removeKeyboardHandler(this.$defaultHandler),this.$defaultHandler=e,this.addKeyboardHandler(e,0)},this.setKeyboardHandler=function(e){var t=this.$handlers;if(t[t.length-1]==e)return;while(t[t.length-1]&&t[t.length-1]!=this.$defaultHandler)this.removeKeyboardHandler(t[t.length-1]);this.addKeyboardHandler(e,1)},this.addKeyboardHandler=function(e,t){if(!e)return;typeof e=="function"&&!e.handleKeyboard&&(e.handleKeyboard=e);var n=this.$handlers.indexOf(e);n!=-1&&this.$handlers.splice(n,1),t==undefined?this.$handlers.push(e):this.$handlers.splice(t,0,e),n==-1&&e.attach&&e.attach(this.$editor)},this.removeKeyboardHandler=function(e){var t=this.$handlers.indexOf(e);return t==-1?!1:(this.$handlers.splice(t,1),e.detach&&e.detach(this.$editor),!0)},this.getKeyboardHandler=function(){return this.$handlers[this.$handlers.length-1]},this.getStatusText=function(){var e=this.$data,t=e.editor;return this.$handlers.map(function(n){return n.getStatusText&&n.getStatusText(t,e)||""}).filter(Boolean).join(" ")},this.$callKeyboardHandlers=function(e,t,n,r){var s,o=!1,u=this.$editor.commands;for(var a=this.$handlers.length;a--;){s=this.$handlers[a].handleKeyboard(this.$data,e,t,n,r);if(!s||!s.command)continue;s.command=="null"?o=!0:o=u.exec(s.command,this.$editor,s.args,r),o&&r&&e!=-1&&s.passEvent!=1&&s.command.passEvent!=1&&i.stopEvent(r);if(o)break}return!o&&e==-1&&(s={command:"insertstring"},o=u.exec("insertstring",this.$editor,t)),o&&this.$editor._signal&&this.$editor._signal("keyboardActivity",s),o},this.onCommandKey=function(e,t,n){var i=r.keyCodeToString(n);return this.$callKeyboardHandlers(t,i,n,e)},this.onTextInput=function(e){return this.$callKeyboardHandlers(-1,e)}}).call(s.prototype),t.KeyBinding=s}),ace.define("ace/lib/bidiutil",["require","exports","module"],function(e,t,n){"use strict";function F(e,t,n,r){var i=s?d:p,c=null,h=null,v=null,m=0,g=null,y=null,b=-1,w=null,E=null,T=[];if(!r)for(w=0,r=[];w0)if(g==16){for(w=b;w-1){for(w=b;w=0;C--){if(r[C]!=N)break;t[C]=s}}}function I(e,t,n){if(o=e){u=i+1;while(u=e)u++;for(a=i,l=u-1;a=t.length||(o=n[r-1])!=b&&o!=w||(c=t[r+1])!=b&&c!=w)return E;return u&&(c=w),c==o?c:E;case k:o=r>0?n[r-1]:S;if(o==b&&r+10&&n[r-1]==b)return b;if(u)return E;p=r+1,h=t.length;while(p=1425&&d<=2303||d==64286;o=t[p];if(v&&(o==y||o==T))return y}if(r<1||(o=t[r-1])==S)return E;return n[r-1];case S:return u=!1,f=!0,s;case x:return l=!0,E;case O:case M:case D:case P:case _:u=!1;case H:return E}}function R(e){var t=e.charCodeAt(0),n=t>>8;return n==0?t>191?g:B[t]:n==5?/[\u0591-\u05f4]/.test(e)?y:g:n==6?/[\u0610-\u061a\u064b-\u065f\u06d6-\u06e4\u06e7-\u06ed]/.test(e)?A:/[\u0660-\u0669\u066b-\u066c]/.test(e)?w:t==1642?L:/[\u06f0-\u06f9]/.test(e)?b:T:n==32&&t<=8287?j[t&255]:n==254?t>=65136?T:E:E}function U(e){return e>="\u064b"&&e<="\u0655"}var r=["\u0621","\u0641"],i=["\u063a","\u064a"],s=0,o=0,u=!1,a=!1,f=!1,l=!1,c=!1,h=!1,p=[[0,3,0,1,0,0,0],[0,3,0,1,2,2,0],[0,3,0,17,2,0,1],[0,3,5,5,4,1,0],[0,3,21,21,4,0,1],[0,3,5,5,4,2,0]],d=[[2,0,1,1,0,1,0],[2,0,1,1,0,2,0],[2,0,2,1,3,2,0],[2,0,2,33,3,1,1]],v=0,m=1,g=0,y=1,b=2,w=3,E=4,S=5,x=6,T=7,N=8,C=9,k=10,L=11,A=12,O=13,M=14,_=15,D=16,P=17,H=18,B=[H,H,H,H,H,H,H,H,H,x,S,x,N,S,H,H,H,H,H,H,H,H,H,H,H,H,H,H,S,S,S,x,N,E,E,L,L,L,E,E,E,E,E,k,C,k,C,C,b,b,b,b,b,b,b,b,b,b,C,E,E,E,E,E,E,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,E,E,E,E,E,E,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,E,E,E,E,H,H,H,H,H,H,S,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,C,E,L,L,L,L,E,E,E,E,g,E,E,H,E,E,L,L,b,b,E,g,E,E,E,b,g,E,E,E,E,E],j=[N,N,N,N,N,N,N,N,N,N,N,H,H,H,g,y,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,N,S,O,M,_,D,P,C,L,L,L,L,L,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,C,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,N];t.L=g,t.R=y,t.EN=b,t.ON_R=3,t.AN=4,t.R_H=5,t.B=6,t.RLE=7,t.DOT="\u00b7",t.doBidiReorder=function(e,n,r){if(e.length<2)return{};var i=e.split(""),o=new Array(i.length),u=new Array(i.length),a=[];s=r?m:v,F(i,a,i.length,n);for(var f=0;fT&&n[f]0&&i[f-1]==="\u0644"&&/\u0622|\u0623|\u0625|\u0627/.test(i[f])&&(a[f-1]=a[f]=t.R_H,f++);i[i.length-1]===t.DOT&&(a[i.length-1]=t.B),i[0]==="\u202b"&&(a[0]=t.RLE);for(var f=0;f=0&&(e=this.session.$docRowCache[n])}return e},this.getSplitIndex=function(){var e=0,t=this.session.$screenRowCache;if(t.length){var n,r=this.session.$getRowCacheIndex(t,this.currentRow);while(this.currentRow-e>0){n=this.session.$getRowCacheIndex(t,this.currentRow-e-1);if(n!==r)break;r=n,e++}}else e=this.currentRow;return e},this.updateRowLine=function(e,t){e===undefined&&(e=this.getDocumentRow());var n=e===this.session.getLength()-1,s=n?this.EOF:this.EOL;this.wrapIndent=0,this.line=this.session.getLine(e),this.isRtlDir=this.$isRtl||this.line.charAt(0)===this.RLE;if(this.session.$useWrapMode){var o=this.session.$wrapData[e];o&&(t===undefined&&(t=this.getSplitIndex()),t>0&&o.length?(this.wrapIndent=o.indent,this.wrapOffset=this.wrapIndent*this.charWidths[r.L],this.line=tt?this.session.getOverwrite()?e:e-1:t,i=r.getVisualFromLogicalIdx(n,this.bidiMap),s=this.bidiMap.bidiLevels,o=0;!this.session.getOverwrite()&&e<=t&&s[i]%2!==0&&i++;for(var u=0;ut&&s[i]%2===0&&(o+=this.charWidths[s[i]]),this.wrapIndent&&(o+=this.isRtlDir?-1*this.wrapOffset:this.wrapOffset),this.isRtlDir&&(o+=this.rtlLineOffset),o},this.getSelections=function(e,t){var n=this.bidiMap,r=n.bidiLevels,i,s=[],o=0,u=Math.min(e,t)-this.wrapIndent,a=Math.max(e,t)-this.wrapIndent,f=!1,l=!1,c=0;this.wrapIndent&&(o+=this.isRtlDir?-1*this.wrapOffset:this.wrapOffset);for(var h,p=0;p=u&&hn+s/2){n+=s;if(r===i.length-1){s=0;break}s=this.charWidths[i[++r]]}return r>0&&i[r-1]%2!==0&&i[r]%2===0?(e0&&i[r-1]%2===0&&i[r]%2!==0?t=1+(e>n?this.bidiMap.logicalFromVisual[r]:this.bidiMap.logicalFromVisual[r-1]):this.isRtlDir&&r===i.length-1&&s===0&&i[r-1]%2===0||!this.isRtlDir&&r===0&&i[r]%2!==0?t=1+this.bidiMap.logicalFromVisual[r]:(r>0&&i[r-1]%2!==0&&s!==0&&r--,t=this.bidiMap.logicalFromVisual[r]),t===0&&this.isRtlDir&&t++,t+this.wrapIndent}}).call(o.prototype),t.BidiHandler=o}),ace.define("ace/selection",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/lib/event_emitter","ace/range"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/lang"),s=e("./lib/event_emitter").EventEmitter,o=e("./range").Range,u=function(e){this.session=e,this.doc=e.getDocument(),this.clearSelection(),this.cursor=this.lead=this.doc.createAnchor(0,0),this.anchor=this.doc.createAnchor(0,0),this.$silent=!1;var t=this;this.cursor.on("change",function(e){t.$cursorChanged=!0,t.$silent||t._emit("changeCursor"),!t.$isEmpty&&!t.$silent&&t._emit("changeSelection"),!t.$keepDesiredColumnOnChange&&e.old.column!=e.value.column&&(t.$desiredColumn=null)}),this.anchor.on("change",function(){t.$anchorChanged=!0,!t.$isEmpty&&!t.$silent&&t._emit("changeSelection")})};(function(){r.implement(this,s),this.isEmpty=function(){return this.$isEmpty||this.anchor.row==this.lead.row&&this.anchor.column==this.lead.column},this.isMultiLine=function(){return!this.$isEmpty&&this.anchor.row!=this.cursor.row},this.getCursor=function(){return this.lead.getPosition()},this.setSelectionAnchor=function(e,t){this.$isEmpty=!1,this.anchor.setPosition(e,t)},this.getAnchor=this.getSelectionAnchor=function(){return this.$isEmpty?this.getSelectionLead():this.anchor.getPosition()},this.getSelectionLead=function(){return this.lead.getPosition()},this.isBackwards=function(){var e=this.anchor,t=this.lead;return e.row>t.row||e.row==t.row&&e.column>t.column},this.getRange=function(){var e=this.anchor,t=this.lead;return this.$isEmpty?o.fromPoints(t,t):this.isBackwards()?o.fromPoints(t,e):o.fromPoints(e,t)},this.clearSelection=function(){this.$isEmpty||(this.$isEmpty=!0,this._emit("changeSelection"))},this.selectAll=function(){this.$setSelection(0,0,Number.MAX_VALUE,Number.MAX_VALUE)},this.setRange=this.setSelectionRange=function(e,t){var n=t?e.end:e.start,r=t?e.start:e.end;this.$setSelection(n.row,n.column,r.row,r.column)},this.$setSelection=function(e,t,n,r){var i=this.$isEmpty,s=this.inMultiSelectMode;this.$silent=!0,this.$cursorChanged=this.$anchorChanged=!1,this.anchor.setPosition(e,t),this.cursor.setPosition(n,r),this.$isEmpty=!o.comparePoints(this.anchor,this.cursor),this.$silent=!1,this.$cursorChanged&&this._emit("changeCursor"),(this.$cursorChanged||this.$anchorChanged||i!=this.$isEmpty||s)&&this._emit("changeSelection")},this.$moveSelection=function(e){var t=this.lead;this.$isEmpty&&this.setSelectionAnchor(t.row,t.column),e.call(this)},this.selectTo=function(e,t){this.$moveSelection(function(){this.moveCursorTo(e,t)})},this.selectToPosition=function(e){this.$moveSelection(function(){this.moveCursorToPosition(e)})},this.moveTo=function(e,t){this.clearSelection(),this.moveCursorTo(e,t)},this.moveToPosition=function(e){this.clearSelection(),this.moveCursorToPosition(e)},this.selectUp=function(){this.$moveSelection(this.moveCursorUp)},this.selectDown=function(){this.$moveSelection(this.moveCursorDown)},this.selectRight=function(){this.$moveSelection(this.moveCursorRight)},this.selectLeft=function(){this.$moveSelection(this.moveCursorLeft)},this.selectLineStart=function(){this.$moveSelection(this.moveCursorLineStart)},this.selectLineEnd=function(){this.$moveSelection(this.moveCursorLineEnd)},this.selectFileEnd=function(){this.$moveSelection(this.moveCursorFileEnd)},this.selectFileStart=function(){this.$moveSelection(this.moveCursorFileStart)},this.selectWordRight=function(){this.$moveSelection(this.moveCursorWordRight)},this.selectWordLeft=function(){this.$moveSelection(this.moveCursorWordLeft)},this.getWordRange=function(e,t){if(typeof t=="undefined"){var n=e||this.lead;e=n.row,t=n.column}return this.session.getWordRange(e,t)},this.selectWord=function(){this.setSelectionRange(this.getWordRange())},this.selectAWord=function(){var e=this.getCursor(),t=this.session.getAWordRange(e.row,e.column);this.setSelectionRange(t)},this.getLineRange=function(e,t){var n=typeof e=="number"?e:this.lead.row,r,i=this.session.getFoldLine(n);return i?(n=i.start.row,r=i.end.row):r=n,t===!0?new o(n,0,r,this.session.getLine(r).length):new o(n,0,r+1,0)},this.selectLine=function(){this.setSelectionRange(this.getLineRange())},this.moveCursorUp=function(){this.moveCursorBy(-1,0)},this.moveCursorDown=function(){this.moveCursorBy(1,0)},this.wouldMoveIntoSoftTab=function(e,t,n){var r=e.column,i=e.column+t;return n<0&&(r=e.column-t,i=e.column),this.session.isTabStop(e)&&this.doc.getLine(e.row).slice(r,i).split(" ").length-1==t},this.moveCursorLeft=function(){var e=this.lead.getPosition(),t;if(t=this.session.getFoldAt(e.row,e.column,-1))this.moveCursorTo(t.start.row,t.start.column);else if(e.column===0)e.row>0&&this.moveCursorTo(e.row-1,this.doc.getLine(e.row-1).length);else{var n=this.session.getTabSize();this.wouldMoveIntoSoftTab(e,n,-1)&&!this.session.getNavigateWithinSoftTabs()?this.moveCursorBy(0,-n):this.moveCursorBy(0,-1)}},this.moveCursorRight=function(){var e=this.lead.getPosition(),t;if(t=this.session.getFoldAt(e.row,e.column,1))this.moveCursorTo(t.end.row,t.end.column);else if(this.lead.column==this.doc.getLine(this.lead.row).length)this.lead.row0&&(t.column=r)}}this.moveCursorTo(t.row,t.column)},this.moveCursorFileEnd=function(){var e=this.doc.getLength()-1,t=this.doc.getLine(e).length;this.moveCursorTo(e,t)},this.moveCursorFileStart=function(){this.moveCursorTo(0,0)},this.moveCursorLongWordRight=function(){var e=this.lead.row,t=this.lead.column,n=this.doc.getLine(e),r=n.substring(t);this.session.nonTokenRe.lastIndex=0,this.session.tokenRe.lastIndex=0;var i=this.session.getFoldAt(e,t,1);if(i){this.moveCursorTo(i.end.row,i.end.column);return}this.session.nonTokenRe.exec(r)&&(t+=this.session.nonTokenRe.lastIndex,this.session.nonTokenRe.lastIndex=0,r=n.substring(t));if(t>=n.length){this.moveCursorTo(e,n.length),this.moveCursorRight(),e0&&this.moveCursorWordLeft();return}this.session.tokenRe.exec(s)&&(t-=this.session.tokenRe.lastIndex,this.session.tokenRe.lastIndex=0),this.moveCursorTo(e,t)},this.$shortWordEndIndex=function(e){var t=0,n,r=/\s/,i=this.session.tokenRe;i.lastIndex=0;if(this.session.tokenRe.exec(e))t=this.session.tokenRe.lastIndex;else{while((n=e[t])&&r.test(n))t++;if(t<1){i.lastIndex=0;while((n=e[t])&&!i.test(n)){i.lastIndex=0,t++;if(r.test(n)){if(t>2){t--;break}while((n=e[t])&&r.test(n))t++;if(t>2)break}}}}return i.lastIndex=0,t},this.moveCursorShortWordRight=function(){var e=this.lead.row,t=this.lead.column,n=this.doc.getLine(e),r=n.substring(t),i=this.session.getFoldAt(e,t,1);if(i)return this.moveCursorTo(i.end.row,i.end.column);if(t==n.length){var s=this.doc.getLength();do e++,r=this.doc.getLine(e);while(e0&&/^\s*$/.test(r));t=r.length,/\s+$/.test(r)||(r="")}var s=i.stringReverse(r),o=this.$shortWordEndIndex(s);return this.moveCursorTo(e,t-o)},this.moveCursorWordRight=function(){this.session.$selectLongWords?this.moveCursorLongWordRight():this.moveCursorShortWordRight()},this.moveCursorWordLeft=function(){this.session.$selectLongWords?this.moveCursorLongWordLeft():this.moveCursorShortWordLeft()},this.moveCursorBy=function(e,t){var n=this.session.documentToScreenPosition(this.lead.row,this.lead.column),r;t===0&&(e!==0&&(this.session.$bidiHandler.isBidiRow(n.row,this.lead.row)?(r=this.session.$bidiHandler.getPosLeft(n.column),n.column=Math.round(r/this.session.$bidiHandler.charWidths[0])):r=n.column*this.session.$bidiHandler.charWidths[0]),this.$desiredColumn?n.column=this.$desiredColumn:this.$desiredColumn=n.column);var i=this.session.screenToDocumentPosition(n.row+e,n.column,r);e!==0&&t===0&&i.row===this.lead.row&&i.column===this.lead.column&&this.session.lineWidgets&&this.session.lineWidgets[i.row]&&(i.row>0||e>0)&&i.row++,this.moveCursorTo(i.row,i.column+t,t===0)},this.moveCursorToPosition=function(e){this.moveCursorTo(e.row,e.column)},this.moveCursorTo=function(e,t,n){var r=this.session.getFoldAt(e,t,1);r&&(e=r.start.row,t=r.start.column),this.$keepDesiredColumnOnChange=!0;var i=this.session.getLine(e);/[\uDC00-\uDFFF]/.test(i.charAt(t))&&i.charAt(t-1)&&(this.lead.row==e&&this.lead.column==t+1?t-=1:t+=1),this.lead.setPosition(e,t),this.$keepDesiredColumnOnChange=!1,n||(this.$desiredColumn=null)},this.moveCursorToScreen=function(e,t,n){var r=this.session.screenToDocumentPosition(e,t);this.moveCursorTo(r.row,r.column,n)},this.detach=function(){this.lead.detach(),this.anchor.detach(),this.session=this.doc=null},this.fromOrientedRange=function(e){this.setSelectionRange(e,e.cursor==e.start),this.$desiredColumn=e.desiredColumn||this.$desiredColumn},this.toOrientedRange=function(e){var t=this.getRange();return e?(e.start.column=t.start.column,e.start.row=t.start.row,e.end.column=t.end.column,e.end.row=t.end.row):e=t,e.cursor=this.isBackwards()?e.start:e.end,e.desiredColumn=this.$desiredColumn,e},this.getRangeOfMovements=function(e){var t=this.getCursor();try{e(this);var n=this.getCursor();return o.fromPoints(t,n)}catch(r){return o.fromPoints(t,t)}finally{this.moveCursorToPosition(t)}},this.toJSON=function(){if(this.rangeCount)var e=this.ranges.map(function(e){var t=e.clone();return t.isBackwards=e.cursor==e.start,t});else{var e=this.getRange();e.isBackwards=this.isBackwards()}return e},this.fromJSON=function(e){if(e.start==undefined){if(this.rangeList&&e.length>1){this.toSingleRange(e[0]);for(var t=e.length;t--;){var n=o.fromPoints(e[t].start,e[t].end);e[t].isBackwards&&(n.cursor=n.start),this.addRange(n,!0)}return}e=e[0]}this.rangeList&&this.toSingleRange(e),this.setSelectionRange(e,e.isBackwards)},this.isEqual=function(e){if((e.length||this.rangeCount)&&e.length!=this.rangeCount)return!1;if(!e.length||!this.ranges)return this.getRange().isEqual(e);for(var t=this.ranges.length;t--;)if(!this.ranges[t].isEqual(e[t]))return!1;return!0}}).call(u.prototype),t.Selection=u}),ace.define("ace/tokenizer",["require","exports","module","ace/config"],function(e,t,n){"use strict";var r=e("./config"),i=2e3,s=function(e){this.states=e,this.regExps={},this.matchMappings={};for(var t in this.states){var n=this.states[t],r=[],i=0,s=this.matchMappings[t]={defaultToken:"text"},o="g",u=[];for(var a=0;a1?f.onMatch=this.$applyToken:f.onMatch=f.token),c>1&&(/\\\d/.test(f.regex)?l=f.regex.replace(/\\([0-9]+)/g,function(e,t){return"\\"+(parseInt(t,10)+i+1)}):(c=1,l=this.removeCapturingGroups(f.regex)),!f.splitRegex&&typeof f.token!="string"&&u.push(f)),s[i]=a,i+=c,r.push(l),f.onMatch||(f.onMatch=null)}r.length||(s[0]=0,r.push("$")),u.forEach(function(e){e.splitRegex=this.createSplitterRegexp(e.regex,o)},this),this.regExps[t]=new RegExp("("+r.join(")|(")+")|($)",o)}};(function(){this.$setMaxTokenCount=function(e){i=e|0},this.$applyToken=function(e){var t=this.splitRegex.exec(e).slice(1),n=this.token.apply(this,t);if(typeof n=="string")return[{type:n,value:e}];var r=[];for(var i=0,s=n.length;il){var g=e.substring(l,m-v.length);h.type==p?h.value+=g:(h.type&&f.push(h),h={type:p,value:g})}for(var y=0;yi){c>2*e.length&&this.reportError("infinite loop with in ace tokenizer",{startState:t,line:e});while(l1&&n[0]!==r&&n.unshift("#tmp",r),{tokens:f,state:n.length?n:r}},this.reportError=r.reportError}).call(s.prototype),t.Tokenizer=s}),ace.define("ace/mode/text_highlight_rules",["require","exports","module","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../lib/lang"),i=function(){this.$rules={start:[{token:"empty_line",regex:"^$"},{defaultToken:"text"}]}};(function(){this.addRules=function(e,t){if(!t){for(var n in e)this.$rules[n]=e[n];return}for(var n in e){var r=e[n];for(var i=0;i=this.$rowTokens.length){this.$row+=1,e||(e=this.$session.getLength());if(this.$row>=e)return this.$row=e-1,null;this.$rowTokens=this.$session.getTokens(this.$row),this.$tokenIndex=0}return this.$rowTokens[this.$tokenIndex]},this.getCurrentToken=function(){return this.$rowTokens[this.$tokenIndex]},this.getCurrentTokenRow=function(){return this.$row},this.getCurrentTokenColumn=function(){var e=this.$rowTokens,t=this.$tokenIndex,n=e[t].start;if(n!==undefined)return n;n=0;while(t>0)t-=1,n+=e[t].value.length;return n},this.getCurrentTokenPosition=function(){return{row:this.$row,column:this.getCurrentTokenColumn()}},this.getCurrentTokenRange=function(){var e=this.$rowTokens[this.$tokenIndex],t=this.getCurrentTokenColumn();return new r(this.$row,t,this.$row,t+e.value.length)}}).call(i.prototype),t.TokenIterator=i}),ace.define("ace/mode/behaviour/cstyle",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),u=["text","paren.rparen","rparen","paren","punctuation.operator"],a=["text","paren.rparen","rparen","paren","punctuation.operator","comment"],f,l={},c={'"':'"',"'":"'"},h=function(e){var t=-1;e.multiSelect&&(t=e.selection.index,l.rangeCount!=e.multiSelect.rangeCount&&(l={rangeCount:e.multiSelect.rangeCount}));if(l[t])return f=l[t];f=l[t]={autoInsertedBrackets:0,autoInsertedRow:-1,autoInsertedLineEnd:"",maybeInsertedBrackets:0,maybeInsertedRow:-1,maybeInsertedLineStart:"",maybeInsertedLineEnd:""}},p=function(e,t,n,r){var i=e.end.row-e.start.row;return{text:n+t+r,selection:[0,e.start.column+1,i,e.end.column+(i?0:1)]}},d=function(e){this.add("braces","insertion",function(t,n,r,i,s){var u=r.getCursorPosition(),a=i.doc.getLine(u.row);if(s=="{"){h(r);var l=r.getSelectionRange(),c=i.doc.getTextRange(l);if(c!==""&&c!=="{"&&r.getWrapBehavioursEnabled())return p(l,c,"{","}");if(d.isSaneInsertion(r,i))return/[\]\}\)]/.test(a[u.column])||r.inMultiSelectMode||e&&e.braces?(d.recordAutoInsert(r,i,"}"),{text:"{}",selection:[1,1]}):(d.recordMaybeInsert(r,i,"{"),{text:"{",selection:[1,1]})}else if(s=="}"){h(r);var v=a.substring(u.column,u.column+1);if(v=="}"){var m=i.$findOpeningBracket("}",{column:u.column+1,row:u.row});if(m!==null&&d.isAutoInsertedClosing(u,a,s))return d.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}else{if(s=="\n"||s=="\r\n"){h(r);var g="";d.isMaybeInsertedClosing(u,a)&&(g=o.stringRepeat("}",f.maybeInsertedBrackets),d.clearMaybeInsertedClosing());var v=a.substring(u.column,u.column+1);if(v==="}"){var y=i.findMatchingBracket({row:u.row,column:u.column+1},"}");if(!y)return null;var b=this.$getIndent(i.getLine(y.row))}else{if(!g){d.clearMaybeInsertedClosing();return}var b=this.$getIndent(a)}var w=b+i.getTabString();return{text:"\n"+w+"\n"+b+g,selection:[1,w.length,1,w.length]}}d.clearMaybeInsertedClosing()}}),this.add("braces","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="{"){h(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.end.column,i.end.column+1);if(u=="}")return i.end.column++,i;f.maybeInsertedBrackets--}}),this.add("parens","insertion",function(e,t,n,r,i){if(i=="("){h(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return p(s,o,"(",")");if(d.isSaneInsertion(n,r))return d.recordAutoInsert(n,r,")"),{text:"()",selection:[1,1]}}else if(i==")"){h(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f==")"){var l=r.$findOpeningBracket(")",{column:u.column+1,row:u.row});if(l!==null&&d.isAutoInsertedClosing(u,a,i))return d.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("parens","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="("){h(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==")")return i.end.column++,i}}),this.add("brackets","insertion",function(e,t,n,r,i){if(i=="["){h(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return p(s,o,"[","]");if(d.isSaneInsertion(n,r))return d.recordAutoInsert(n,r,"]"),{text:"[]",selection:[1,1]}}else if(i=="]"){h(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f=="]"){var l=r.$findOpeningBracket("]",{column:u.column+1,row:u.row});if(l!==null&&d.isAutoInsertedClosing(u,a,i))return d.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("brackets","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="["){h(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u=="]")return i.end.column++,i}}),this.add("string_dquotes","insertion",function(e,t,n,r,i){var s=r.$mode.$quotes||c;if(i.length==1&&s[i]){if(this.lineCommentStart&&this.lineCommentStart.indexOf(i)!=-1)return;h(n);var o=i,u=n.getSelectionRange(),a=r.doc.getTextRange(u);if(a!==""&&(a.length!=1||!s[a])&&n.getWrapBehavioursEnabled())return p(u,a,o,o);if(!a){var f=n.getCursorPosition(),l=r.doc.getLine(f.row),d=l.substring(f.column-1,f.column),v=l.substring(f.column,f.column+1),m=r.getTokenAt(f.row,f.column),g=r.getTokenAt(f.row,f.column+1);if(d=="\\"&&m&&/escape/.test(m.type))return null;var y=m&&/string|escape/.test(m.type),b=!g||/string|escape/.test(g.type),w;if(v==o)w=y!==b,w&&/string\.end/.test(g.type)&&(w=!1);else{if(y&&!b)return null;if(y&&b)return null;var E=r.$mode.tokenRe;E.lastIndex=0;var S=E.test(d);E.lastIndex=0;var x=E.test(d);if(S||x)return null;if(v&&!/[\s;,.})\]\\]/.test(v))return null;var T=l[f.column-2];if(!(d!=o||T!=o&&!E.test(T)))return null;w=!0}return{text:w?o+o:"",selection:[1,1]}}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.$mode.$quotes||c,o=r.doc.getTextRange(i);if(!i.isMultiLine()&&s.hasOwnProperty(o)){h(n);var u=r.doc.getLine(i.start.row),a=u.substring(i.start.column+1,i.start.column+2);if(a==o)return i.end.column++,i}})};d.isSaneInsertion=function(e,t){var n=e.getCursorPosition(),r=new s(t,n.row,n.column);if(!this.$matchTokenType(r.getCurrentToken()||"text",u)){if(/[)}\]]/.test(e.session.getLine(n.row)[n.column]))return!0;var i=new s(t,n.row,n.column+1);if(!this.$matchTokenType(i.getCurrentToken()||"text",u))return!1}return r.stepForward(),r.getCurrentTokenRow()!==n.row||this.$matchTokenType(r.getCurrentToken()||"text",a)},d.$matchTokenType=function(e,t){return t.indexOf(e.type||e)>-1},d.recordAutoInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isAutoInsertedClosing(r,i,f.autoInsertedLineEnd[0])||(f.autoInsertedBrackets=0),f.autoInsertedRow=r.row,f.autoInsertedLineEnd=n+i.substr(r.column),f.autoInsertedBrackets++},d.recordMaybeInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isMaybeInsertedClosing(r,i)||(f.maybeInsertedBrackets=0),f.maybeInsertedRow=r.row,f.maybeInsertedLineStart=i.substr(0,r.column)+n,f.maybeInsertedLineEnd=i.substr(r.column),f.maybeInsertedBrackets++},d.isAutoInsertedClosing=function(e,t,n){return f.autoInsertedBrackets>0&&e.row===f.autoInsertedRow&&n===f.autoInsertedLineEnd[0]&&t.substr(e.column)===f.autoInsertedLineEnd},d.isMaybeInsertedClosing=function(e,t){return f.maybeInsertedBrackets>0&&e.row===f.maybeInsertedRow&&t.substr(e.column)===f.maybeInsertedLineEnd&&t.substr(0,e.column)==f.maybeInsertedLineStart},d.popAutoInsertedClosing=function(){f.autoInsertedLineEnd=f.autoInsertedLineEnd.substr(1),f.autoInsertedBrackets--},d.clearMaybeInsertedClosing=function(){f&&(f.maybeInsertedBrackets=0,f.maybeInsertedRow=-1)},r.inherits(d,i),t.CstyleBehaviour=d}),ace.define("ace/unicode",["require","exports","module"],function(e,t,n){"use strict";var r=[48,9,8,25,5,0,2,25,48,0,11,0,5,0,6,22,2,30,2,457,5,11,15,4,8,0,2,0,18,116,2,1,3,3,9,0,2,2,2,0,2,19,2,82,2,138,2,4,3,155,12,37,3,0,8,38,10,44,2,0,2,1,2,1,2,0,9,26,6,2,30,10,7,61,2,9,5,101,2,7,3,9,2,18,3,0,17,58,3,100,15,53,5,0,6,45,211,57,3,18,2,5,3,11,3,9,2,1,7,6,2,2,2,7,3,1,3,21,2,6,2,0,4,3,3,8,3,1,3,3,9,0,5,1,2,4,3,11,16,2,2,5,5,1,3,21,2,6,2,1,2,1,2,1,3,0,2,4,5,1,3,2,4,0,8,3,2,0,8,15,12,2,2,8,2,2,2,21,2,6,2,1,2,4,3,9,2,2,2,2,3,0,16,3,3,9,18,2,2,7,3,1,3,21,2,6,2,1,2,4,3,8,3,1,3,2,9,1,5,1,2,4,3,9,2,0,17,1,2,5,4,2,2,3,4,1,2,0,2,1,4,1,4,2,4,11,5,4,4,2,2,3,3,0,7,0,15,9,18,2,2,7,2,2,2,22,2,9,2,4,4,7,2,2,2,3,8,1,2,1,7,3,3,9,19,1,2,7,2,2,2,22,2,9,2,4,3,8,2,2,2,3,8,1,8,0,2,3,3,9,19,1,2,7,2,2,2,22,2,15,4,7,2,2,2,3,10,0,9,3,3,9,11,5,3,1,2,17,4,23,2,8,2,0,3,6,4,0,5,5,2,0,2,7,19,1,14,57,6,14,2,9,40,1,2,0,3,1,2,0,3,0,7,3,2,6,2,2,2,0,2,0,3,1,2,12,2,2,3,4,2,0,2,5,3,9,3,1,35,0,24,1,7,9,12,0,2,0,2,0,5,9,2,35,5,19,2,5,5,7,2,35,10,0,58,73,7,77,3,37,11,42,2,0,4,328,2,3,3,6,2,0,2,3,3,40,2,3,3,32,2,3,3,6,2,0,2,3,3,14,2,56,2,3,3,66,5,0,33,15,17,84,13,619,3,16,2,25,6,74,22,12,2,6,12,20,12,19,13,12,2,2,2,1,13,51,3,29,4,0,5,1,3,9,34,2,3,9,7,87,9,42,6,69,11,28,4,11,5,11,11,39,3,4,12,43,5,25,7,10,38,27,5,62,2,28,3,10,7,9,14,0,89,75,5,9,18,8,13,42,4,11,71,55,9,9,4,48,83,2,2,30,14,230,23,280,3,5,3,37,3,5,3,7,2,0,2,0,2,0,2,30,3,52,2,6,2,0,4,2,2,6,4,3,3,5,5,12,6,2,2,6,67,1,20,0,29,0,14,0,17,4,60,12,5,0,4,11,18,0,5,0,3,9,2,0,4,4,7,0,2,0,2,0,2,3,2,10,3,3,6,4,5,0,53,1,2684,46,2,46,2,132,7,6,15,37,11,53,10,0,17,22,10,6,2,6,2,6,2,6,2,6,2,6,2,6,2,6,2,31,48,0,470,1,36,5,2,4,6,1,5,85,3,1,3,2,2,89,2,3,6,40,4,93,18,23,57,15,513,6581,75,20939,53,1164,68,45,3,268,4,27,21,31,3,13,13,1,2,24,9,69,11,1,38,8,3,102,3,1,111,44,25,51,13,68,12,9,7,23,4,0,5,45,3,35,13,28,4,64,15,10,39,54,10,13,3,9,7,22,4,1,5,66,25,2,227,42,2,1,3,9,7,11171,13,22,5,48,8453,301,3,61,3,105,39,6,13,4,6,11,2,12,2,4,2,0,2,1,2,1,2,107,34,362,19,63,3,53,41,11,5,15,17,6,13,1,25,2,33,4,2,134,20,9,8,25,5,0,2,25,12,88,4,5,3,5,3,5,3,2],i=0,s=[];for(var o=0;o2?r%f!=f-1:r%f==0}}var E=Infinity;w(function(e,t){var n=e.search(/\S/);n!==-1?(ne.length&&(E=e.length)}),u==Infinity&&(u=E,s=!1,o=!1),l&&u%f!=0&&(u=Math.floor(u/f)*f),w(o?m:v)},this.toggleBlockComment=function(e,t,n,r){var i=this.blockComment;if(!i)return;!i.start&&i[0]&&(i=i[0]);var s=new f(t,r.row,r.column),o=s.getCurrentToken(),u=t.selection,a=t.selection.toOrientedRange(),c,h;if(o&&/comment/.test(o.type)){var p,d;while(o&&/comment/.test(o.type)){var v=o.value.indexOf(i.start);if(v!=-1){var m=s.getCurrentTokenRow(),g=s.getCurrentTokenColumn()+v;p=new l(m,g,m,g+i.start.length);break}o=s.stepBackward()}var s=new f(t,r.row,r.column),o=s.getCurrentToken();while(o&&/comment/.test(o.type)){var v=o.value.indexOf(i.end);if(v!=-1){var m=s.getCurrentTokenRow(),g=s.getCurrentTokenColumn()+v;d=new l(m,g,m,g+i.end.length);break}o=s.stepForward()}d&&t.remove(d),p&&(t.remove(p),c=p.start.row,h=-i.start.length)}else h=i.start.length,c=n.start.row,t.insert(n.end,i.end),t.insert(n.start,i.start);a.start.row==c&&(a.start.column+=h),a.end.row==c&&(a.end.column+=h),t.selection.fromOrientedRange(a)},this.getNextLineIndent=function(e,t,n){return this.$getIndent(t)},this.checkOutdent=function(e,t,n){return!1},this.autoOutdent=function(e,t,n){},this.$getIndent=function(e){return e.match(/^\s*/)[0]},this.createWorker=function(e){return null},this.createModeDelegates=function(e){this.$embeds=[],this.$modes={};for(var t in e)if(e[t]){var n=e[t],i=n.prototype.$id,s=r.$modes[i];s||(r.$modes[i]=s=new n),r.$modes[t]||(r.$modes[t]=s),this.$embeds.push(t),this.$modes[t]=s}var o=["toggleBlockComment","toggleCommentLines","getNextLineIndent","checkOutdent","autoOutdent","transformAction","getCompletions"];for(var t=0;t=0&&t.row=0&&t.column<=e[t.row].length}function s(e,t){t.action!="insert"&&t.action!="remove"&&r(t,"delta.action must be 'insert' or 'remove'"),t.lines instanceof Array||r(t,"delta.lines must be an Array"),(!t.start||!t.end)&&r(t,"delta.start/end must be an present");var n=t.start;i(e,t.start)||r(t,"delta.start must be contained in document");var s=t.end;t.action=="remove"&&!i(e,s)&&r(t,"delta.end must contained in document for 'remove' actions");var o=s.row-n.row,u=s.column-(o==0?n.column:0);(o!=t.lines.length-1||t.lines[o].length!=u)&&r(t,"delta.range must match delta lines")}t.applyDelta=function(e,t,n){var r=t.start.row,i=t.start.column,s=e[r]||"";switch(t.action){case"insert":var o=t.lines;if(o.length===1)e[r]=s.substring(0,i)+t.lines[0]+s.substring(i);else{var u=[r,1].concat(t.lines);e.splice.apply(e,u),e[r]=s.substring(0,i)+e[r],e[r+t.lines.length-1]+=s.substring(i)}break;case"remove":var a=t.end.column,f=t.end.row;r===f?e[r]=s.substring(0,i)+s.substring(a):e.splice(r,f-r+1,s.substring(0,i)+e[f].substring(a))}}}),ace.define("ace/anchor",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/event_emitter").EventEmitter,s=t.Anchor=function(e,t,n){this.$onChange=this.onChange.bind(this),this.attach(e),typeof n=="undefined"?this.setPosition(t.row,t.column):this.setPosition(t,n)};(function(){function e(e,t,n){var r=n?e.column<=t.column:e.columnthis.row)return;var n=t(e,{row:this.row,column:this.column},this.$insertRight);this.setPosition(n.row,n.column,!0)},this.setPosition=function(e,t,n){var r;n?r={row:e,column:t}:r=this.$clipPositionToDocument(e,t);if(this.row==r.row&&this.column==r.column)return;var i={row:this.row,column:this.column};this.row=r.row,this.column=r.column,this._signal("change",{old:i,value:r})},this.detach=function(){this.document.removeEventListener("change",this.$onChange)},this.attach=function(e){this.document=e||this.document,this.document.on("change",this.$onChange)},this.$clipPositionToDocument=function(e,t){var n={};return e>=this.document.getLength()?(n.row=Math.max(0,this.document.getLength()-1),n.column=this.document.getLine(n.row).length):e<0?(n.row=0,n.column=0):(n.row=e,n.column=Math.min(this.document.getLine(n.row).length,Math.max(0,t))),t<0&&(n.column=0),n}}).call(s.prototype)}),ace.define("ace/document",["require","exports","module","ace/lib/oop","ace/apply_delta","ace/lib/event_emitter","ace/range","ace/anchor"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./apply_delta").applyDelta,s=e("./lib/event_emitter").EventEmitter,o=e("./range").Range,u=e("./anchor").Anchor,a=function(e){this.$lines=[""],e.length===0?this.$lines=[""]:Array.isArray(e)?this.insertMergedLines({row:0,column:0},e):this.insert({row:0,column:0},e)};(function(){r.implement(this,s),this.setValue=function(e){var t=this.getLength()-1;this.remove(new o(0,0,t,this.getLine(t).length)),this.insert({row:0,column:0},e)},this.getValue=function(){return this.getAllLines().join(this.getNewLineCharacter())},this.createAnchor=function(e,t){return new u(this,e,t)},"aaa".split(/a/).length===0?this.$split=function(e){return e.replace(/\r\n|\r/g,"\n").split("\n")}:this.$split=function(e){return e.split(/\r\n|\r|\n/)},this.$detectNewLine=function(e){var t=e.match(/^.*?(\r\n|\r|\n)/m);this.$autoNewLine=t?t[1]:"\n",this._signal("changeNewLineMode")},this.getNewLineCharacter=function(){switch(this.$newLineMode){case"windows":return"\r\n";case"unix":return"\n";default:return this.$autoNewLine||"\n"}},this.$autoNewLine="",this.$newLineMode="auto",this.setNewLineMode=function(e){if(this.$newLineMode===e)return;this.$newLineMode=e,this._signal("changeNewLineMode")},this.getNewLineMode=function(){return this.$newLineMode},this.isNewLine=function(e){return e=="\r\n"||e=="\r"||e=="\n"},this.getLine=function(e){return this.$lines[e]||""},this.getLines=function(e,t){return this.$lines.slice(e,t+1)},this.getAllLines=function(){return this.getLines(0,this.getLength())},this.getLength=function(){return this.$lines.length},this.getTextRange=function(e){return this.getLinesForRange(e).join(this.getNewLineCharacter())},this.getLinesForRange=function(e){var t;if(e.start.row===e.end.row)t=[this.getLine(e.start.row).substring(e.start.column,e.end.column)];else{t=this.getLines(e.start.row,e.end.row),t[0]=(t[0]||"").substring(e.start.column);var n=t.length-1;e.end.row-e.start.row==n&&(t[n]=t[n].substring(0,e.end.column))}return t},this.insertLines=function(e,t){return console.warn("Use of document.insertLines is deprecated. Use the insertFullLines method instead."),this.insertFullLines(e,t)},this.removeLines=function(e,t){return console.warn("Use of document.removeLines is deprecated. Use the removeFullLines method instead."),this.removeFullLines(e,t)},this.insertNewLine=function(e){return console.warn("Use of document.insertNewLine is deprecated. Use insertMergedLines(position, ['', '']) instead."),this.insertMergedLines(e,["",""])},this.insert=function(e,t){return this.getLength()<=1&&this.$detectNewLine(t),this.insertMergedLines(e,this.$split(t))},this.insertInLine=function(e,t){var n=this.clippedPos(e.row,e.column),r=this.pos(e.row,e.column+t.length);return this.applyDelta({start:n,end:r,action:"insert",lines:[t]},!0),this.clonePos(r)},this.clippedPos=function(e,t){var n=this.getLength();e===undefined?e=n:e<0?e=0:e>=n&&(e=n-1,t=undefined);var r=this.getLine(e);return t==undefined&&(t=r.length),t=Math.min(Math.max(t,0),r.length),{row:e,column:t}},this.clonePos=function(e){return{row:e.row,column:e.column}},this.pos=function(e,t){return{row:e,column:t}},this.$clipPosition=function(e){var t=this.getLength();return e.row>=t?(e.row=Math.max(0,t-1),e.column=this.getLine(t-1).length):(e.row=Math.max(0,e.row),e.column=Math.min(Math.max(e.column,0),this.getLine(e.row).length)),e},this.insertFullLines=function(e,t){e=Math.min(Math.max(e,0),this.getLength());var n=0;e0,r=t=0&&this.applyDelta({start:this.pos(e,this.getLine(e).length),end:this.pos(e+1,0),action:"remove",lines:["",""]})},this.replace=function(e,t){e instanceof o||(e=o.fromPoints(e.start,e.end));if(t.length===0&&e.isEmpty())return e.start;if(t==this.getTextRange(e))return e.end;this.remove(e);var n;return t?n=this.insert(e.start,t):n=e.start,n},this.applyDeltas=function(e){for(var t=0;t=0;t--)this.revertDelta(e[t])},this.applyDelta=function(e,t){var n=e.action=="insert";if(n?e.lines.length<=1&&!e.lines[0]:!o.comparePoints(e.start,e.end))return;n&&e.lines.length>2e4?this.$splitAndapplyLargeDelta(e,2e4):(i(this.$lines,e,t),this._signal("change",e))},this.$splitAndapplyLargeDelta=function(e,t){var n=e.lines,r=n.length-t+1,i=e.start.row,s=e.start.column;for(var o=0,u=0;o20){n.running=setTimeout(n.$worker,20);break}}n.currentLine=t,r==-1&&(r=t),s<=r&&n.fireUpdateEvent(s,r)}};(function(){r.implement(this,i),this.setTokenizer=function(e){this.tokenizer=e,this.lines=[],this.states=[],this.start(0)},this.setDocument=function(e){this.doc=e,this.lines=[],this.states=[],this.stop()},this.fireUpdateEvent=function(e,t){var n={first:e,last:t};this._signal("update",{data:n})},this.start=function(e){this.currentLine=Math.min(e||0,this.currentLine,this.doc.getLength()),this.lines.splice(this.currentLine,this.lines.length),this.states.splice(this.currentLine,this.states.length),this.stop(),this.running=setTimeout(this.$worker,700)},this.scheduleStart=function(){this.running||(this.running=setTimeout(this.$worker,700))},this.$updateOnChange=function(e){var t=e.start.row,n=e.end.row-t;if(n===0)this.lines[t]=null;else if(e.action=="remove")this.lines.splice(t,n+1,null),this.states.splice(t,n+1,null);else{var r=Array(n+1);r.unshift(t,1),this.lines.splice.apply(this.lines,r),this.states.splice.apply(this.states,r)}this.currentLine=Math.min(t,this.currentLine,this.doc.getLength()),this.stop()},this.stop=function(){this.running&&clearTimeout(this.running),this.running=!1},this.getTokens=function(e){return this.lines[e]||this.$tokenizeRow(e)},this.getState=function(e){return this.currentLine==e&&this.$tokenizeRow(e),this.states[e]||"start"},this.$tokenizeRow=function(e){var t=this.doc.getLine(e),n=this.states[e-1],r=this.tokenizer.getLineTokens(t,n,e);return this.states[e]+""!=r.state+""?(this.states[e]=r.state,this.lines[e+1]=null,this.currentLine>e+1&&(this.currentLine=e+1)):this.currentLine==e&&(this.currentLine=e+1),this.lines[e]=r.tokens}}).call(s.prototype),t.BackgroundTokenizer=s}),ace.define("ace/search_highlight",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"],function(e,t,n){"use strict";var r=e("./lib/lang"),i=e("./lib/oop"),s=e("./range").Range,o=function(e,t,n){this.setRegexp(e),this.clazz=t,this.type=n||"text"};(function(){this.MAX_RANGES=500,this.setRegexp=function(e){if(this.regExp+""==e+"")return;this.regExp=e,this.cache=[]},this.update=function(e,t,n,i){if(!this.regExp)return;var o=i.firstRow,u=i.lastRow;for(var a=o;a<=u;a++){var f=this.cache[a];f==null&&(f=r.getMatchOffsets(n.getLine(a),this.regExp),f.length>this.MAX_RANGES&&(f=f.slice(0,this.MAX_RANGES)),f=f.map(function(e){return new s(a,e.offset,a,e.offset+e.length)}),this.cache[a]=f.length?f:"");for(var l=f.length;l--;)t.drawSingleLineMarker(e,f[l].toScreenRange(n),this.clazz,i)}}}).call(o.prototype),t.SearchHighlight=o}),ace.define("ace/edit_session/fold_line",["require","exports","module","ace/range"],function(e,t,n){"use strict";function i(e,t){this.foldData=e,Array.isArray(t)?this.folds=t:t=this.folds=[t];var n=t[t.length-1];this.range=new r(t[0].start.row,t[0].start.column,n.end.row,n.end.column),this.start=this.range.start,this.end=this.range.end,this.folds.forEach(function(e){e.setFoldLine(this)},this)}var r=e("../range").Range;(function(){this.shiftRow=function(e){this.start.row+=e,this.end.row+=e,this.folds.forEach(function(t){t.start.row+=e,t.end.row+=e})},this.addFold=function(e){if(e.sameRow){if(e.start.rowthis.endRow)throw new Error("Can't add a fold to this FoldLine as it has no connection");this.folds.push(e),this.folds.sort(function(e,t){return-e.range.compareEnd(t.start.row,t.start.column)}),this.range.compareEnd(e.start.row,e.start.column)>0?(this.end.row=e.end.row,this.end.column=e.end.column):this.range.compareStart(e.end.row,e.end.column)<0&&(this.start.row=e.start.row,this.start.column=e.start.column)}else if(e.start.row==this.end.row)this.folds.push(e),this.end.row=e.end.row,this.end.column=e.end.column;else{if(e.end.row!=this.start.row)throw new Error("Trying to add fold to FoldRow that doesn't have a matching row");this.folds.unshift(e),this.start.row=e.start.row,this.start.column=e.start.column}e.foldLine=this},this.containsRow=function(e){return e>=this.start.row&&e<=this.end.row},this.walk=function(e,t,n){var r=0,i=this.folds,s,o,u,a=!0;t==null&&(t=this.end.row,n=this.end.column);for(var f=0;f0)continue;var a=i(e,o.start);return u===0?t&&a!==0?-s-2:s:a>0||a===0&&!t?s:-s-1}return-s-1},this.add=function(e){var t=!e.isEmpty(),n=this.pointIndex(e.start,t);n<0&&(n=-n-1);var r=this.pointIndex(e.end,t,n);return r<0?r=-r-1:r++,this.ranges.splice(n,r-n,e)},this.addList=function(e){var t=[];for(var n=e.length;n--;)t.push.apply(t,this.add(e[n]));return t},this.substractPoint=function(e){var t=this.pointIndex(e);if(t>=0)return this.ranges.splice(t,1)},this.merge=function(){var e=[],t=this.ranges;t=t.sort(function(e,t){return i(e.start,t.start)});var n=t[0],r;for(var s=1;s=0},this.containsPoint=function(e){return this.pointIndex(e)>=0},this.rangeAtPoint=function(e){var t=this.pointIndex(e);if(t>=0)return this.ranges[t]},this.clipRows=function(e,t){var n=this.ranges;if(n[0].start.row>t||n[n.length-1].start.row=r)break}if(e.action=="insert"){var f=i-r,l=-t.column+n.column;for(;or)break;a.start.row==r&&a.start.column>=t.column&&(a.start.column==t.column&&this.$bias<=0||(a.start.column+=l,a.start.row+=f));if(a.end.row==r&&a.end.column>=t.column){if(a.end.column==t.column&&this.$bias<0)continue;a.end.column==t.column&&l>0&&oa.start.column&&a.end.column==s[o+1].start.column&&(a.end.column-=l),a.end.column+=l,a.end.row+=f}}}else{var f=r-i,l=t.column-n.column;for(;oi)break;if(a.end.rowt.column)a.end.column=t.column,a.end.row=t.row}else a.end.column+=l,a.end.row+=f;else a.end.row>i&&(a.end.row+=f);if(a.start.rowt.column)a.start.column=t.column,a.start.row=t.row}else a.start.column+=l,a.start.row+=f;else a.start.row>i&&(a.start.row+=f)}}if(f!=0&&o=e)return i;if(i.end.row>e)return null}return null},this.getNextFoldLine=function(e,t){var n=this.$foldData,r=0;t&&(r=n.indexOf(t)),r==-1&&(r=0);for(r;r=e)return i}return null},this.getFoldedRowCount=function(e,t){var n=this.$foldData,r=t-e+1;for(var i=0;i=t){u=e?r-=t-u:r=0);break}o>=e&&(u>=e?r-=o-u:r-=o-e+1)}return r},this.$addFoldLine=function(e){return this.$foldData.push(e),this.$foldData.sort(function(e,t){return e.start.row-t.start.row}),e},this.addFold=function(e,t){var n=this.$foldData,r=!1,o;e instanceof s?o=e:(o=new s(t,e),o.collapseChildren=t.collapseChildren),this.$clipRangeToDocument(o.range);var u=o.start.row,a=o.start.column,f=o.end.row,l=o.end.column,c=this.getFoldAt(u,a,1),h=this.getFoldAt(f,l,-1);if(c&&h==c)return c.addSubFold(o);c&&!c.range.isStart(u,a)&&this.removeFold(c),h&&!h.range.isEnd(f,l)&&this.removeFold(h);var p=this.getFoldsInRange(o.range);p.length>0&&(this.removeFolds(p),p.forEach(function(e){o.addSubFold(e)}));for(var d=0;d0&&this.foldAll(e.start.row+1,e.end.row,e.collapseChildren-1),e.subFolds=[]},this.expandFolds=function(e){e.forEach(function(e){this.expandFold(e)},this)},this.unfold=function(e,t){var n,i;e==null?(n=new r(0,0,this.getLength(),0),t=!0):typeof e=="number"?n=new r(e,0,e,this.getLine(e).length):"row"in e?n=r.fromPoints(e,e):n=e,i=this.getFoldsInRangeList(n);if(t)this.removeFolds(i);else{var s=i;while(s.length)this.expandFolds(s),s=this.getFoldsInRangeList(n)}if(i.length)return i},this.isRowFolded=function(e,t){return!!this.getFoldLine(e,t)},this.getRowFoldEnd=function(e,t){var n=this.getFoldLine(e,t);return n?n.end.row:e},this.getRowFoldStart=function(e,t){var n=this.getFoldLine(e,t);return n?n.start.row:e},this.getFoldDisplayLine=function(e,t,n,r,i){r==null&&(r=e.start.row),i==null&&(i=0),t==null&&(t=e.end.row),n==null&&(n=this.getLine(t).length);var s=this.doc,o="";return e.walk(function(e,t,n,u){if(tl)break}while(s&&a.test(s.type));s=i.stepBackward()}else s=i.getCurrentToken();return f.end.row=i.getCurrentTokenRow(),f.end.column=i.getCurrentTokenColumn()+s.value.length-2,f}},this.foldAll=function(e,t,n){n==undefined&&(n=1e5);var r=this.foldWidgets;if(!r)return;t=t||this.getLength(),e=e||0;for(var i=e;i=e){i=s.end.row;try{var o=this.addFold("...",s);o&&(o.collapseChildren=n)}catch(u){}}}},this.$foldStyles={manual:1,markbegin:1,markbeginend:1},this.$foldStyle="markbegin",this.setFoldStyle=function(e){if(!this.$foldStyles[e])throw new Error("invalid fold style: "+e+"["+Object.keys(this.$foldStyles).join(", ")+"]");if(this.$foldStyle==e)return;this.$foldStyle=e,e=="manual"&&this.unfold();var t=this.$foldMode;this.$setFolding(null),this.$setFolding(t)},this.$setFolding=function(e){if(this.$foldMode==e)return;this.$foldMode=e,this.off("change",this.$updateFoldWidgets),this.off("tokenizerUpdate",this.$tokenizerUpdateFoldWidgets),this._signal("changeAnnotation");if(!e||this.$foldStyle=="manual"){this.foldWidgets=null;return}this.foldWidgets=[],this.getFoldWidget=e.getFoldWidget.bind(e,this,this.$foldStyle),this.getFoldWidgetRange=e.getFoldWidgetRange.bind(e,this,this.$foldStyle),this.$updateFoldWidgets=this.updateFoldWidgets.bind(this),this.$tokenizerUpdateFoldWidgets=this.tokenizerUpdateFoldWidgets.bind(this),this.on("change",this.$updateFoldWidgets),this.on("tokenizerUpdate",this.$tokenizerUpdateFoldWidgets)},this.getParentFoldRangeData=function(e,t){var n=this.foldWidgets;if(!n||t&&n[e])return{};var r=e-1,i;while(r>=0){var s=n[r];s==null&&(s=n[r]=this.getFoldWidget(r));if(s=="start"){var o=this.getFoldWidgetRange(r);i||(i=o);if(o&&o.end.row>=e)break}r--}return{range:r!==-1&&o,firstRange:i}},this.onFoldWidgetClick=function(e,t){t=t.domEvent;var n={children:t.shiftKey,all:t.ctrlKey||t.metaKey,siblings:t.altKey},r=this.$toggleFoldWidget(e,n);if(!r){var i=t.target||t.srcElement;i&&/ace_fold-widget/.test(i.className)&&(i.className+=" ace_invalid")}},this.$toggleFoldWidget=function(e,t){if(!this.getFoldWidget)return;var n=this.getFoldWidget(e),r=this.getLine(e),i=n==="end"?-1:1,s=this.getFoldAt(e,i===-1?0:r.length,i);if(s)return t.children||t.all?this.removeFold(s):this.expandFold(s),s;var o=this.getFoldWidgetRange(e,!0);if(o&&!o.isMultiLine()){s=this.getFoldAt(o.start.row,o.start.column,1);if(s&&o.isEqual(s.range))return this.removeFold(s),s}if(t.siblings){var u=this.getParentFoldRangeData(e);if(u.range)var a=u.range.start.row+1,f=u.range.end.row;this.foldAll(a,f,t.all?1e4:0)}else t.children?(f=o?o.end.row:this.getLength(),this.foldAll(e+1,f,t.all?1e4:0)):o&&(t.all&&(o.collapseChildren=1e4),this.addFold("...",o));return o},this.toggleFoldWidget=function(e){var t=this.selection.getCursor().row;t=this.getRowFoldStart(t);var n=this.$toggleFoldWidget(t,{});if(n)return;var r=this.getParentFoldRangeData(t,!0);n=r.range||r.firstRange;if(n){t=n.start.row;var i=this.getFoldAt(t,this.getLine(t).length,1);i?this.removeFold(i):this.addFold("...",n)}},this.updateFoldWidgets=function(e){var t=e.start.row,n=e.end.row-t;if(n===0)this.foldWidgets[t]=null;else if(e.action=="remove")this.foldWidgets.splice(t,n+1,null);else{var r=Array(n+1);r.unshift(t,1),this.foldWidgets.splice.apply(this.foldWidgets,r)}},this.tokenizerUpdateFoldWidgets=function(e){var t=e.data;t.first!=t.last&&this.foldWidgets.length>t.first&&this.foldWidgets.splice(t.first,this.foldWidgets.length)}}var r=e("../range").Range,i=e("./fold_line").FoldLine,s=e("./fold").Fold,o=e("../token_iterator").TokenIterator;t.Folding=u}),ace.define("ace/edit_session/bracket_match",["require","exports","module","ace/token_iterator","ace/range"],function(e,t,n){"use strict";function s(){this.findMatchingBracket=function(e,t){if(e.column==0)return null;var n=t||this.getLine(e.row).charAt(e.column-1);if(n=="")return null;var r=n.match(/([\(\[\{])|([\)\]\}])/);return r?r[1]?this.$findClosingBracket(r[1],e):this.$findOpeningBracket(r[2],e):null},this.getBracketRange=function(e){var t=this.getLine(e.row),n=!0,r,s=t.charAt(e.column-1),o=s&&s.match(/([\(\[\{])|([\)\]\}])/);o||(s=t.charAt(e.column),e={row:e.row,column:e.column+1},o=s&&s.match(/([\(\[\{])|([\)\]\}])/),n=!1);if(!o)return null;if(o[1]){var u=this.$findClosingBracket(o[1],e);if(!u)return null;r=i.fromPoints(e,u),n||(r.end.column++,r.start.column--),r.cursor=r.end}else{var u=this.$findOpeningBracket(o[2],e);if(!u)return null;r=i.fromPoints(u,e),n||(r.start.column++,r.end.column--),r.cursor=r.start}return r},this.$brackets={")":"(","(":")","]":"[","[":"]","{":"}","}":"{","<":">",">":"<"},this.$findOpeningBracket=function(e,t,n){var i=this.$brackets[e],s=1,o=new r(this,t.row,t.column),u=o.getCurrentToken();u||(u=o.stepForward());if(!u)return;n||(n=new RegExp("(\\.?"+u.type.replace(".","\\.").replace("rparen",".paren").replace(/\b(?:end)\b/,"(?:start|begin|end)")+")+"));var a=t.column-o.getCurrentTokenColumn()-2,f=u.value;for(;;){while(a>=0){var l=f.charAt(a);if(l==i){s-=1;if(s==0)return{row:o.getCurrentTokenRow(),column:a+o.getCurrentTokenColumn()}}else l==e&&(s+=1);a-=1}do u=o.stepBackward();while(u&&!n.test(u.type));if(u==null)break;f=u.value,a=f.length-1}return null},this.$findClosingBracket=function(e,t,n){var i=this.$brackets[e],s=1,o=new r(this,t.row,t.column),u=o.getCurrentToken();u||(u=o.stepForward());if(!u)return;n||(n=new RegExp("(\\.?"+u.type.replace(".","\\.").replace("lparen",".paren").replace(/\b(?:start|begin)\b/,"(?:start|begin|end)")+")+"));var a=t.column-o.getCurrentTokenColumn();for(;;){var f=u.value,l=f.length;while(a=4352&&e<=4447||e>=4515&&e<=4519||e>=4602&&e<=4607||e>=9001&&e<=9002||e>=11904&&e<=11929||e>=11931&&e<=12019||e>=12032&&e<=12245||e>=12272&&e<=12283||e>=12288&&e<=12350||e>=12353&&e<=12438||e>=12441&&e<=12543||e>=12549&&e<=12589||e>=12593&&e<=12686||e>=12688&&e<=12730||e>=12736&&e<=12771||e>=12784&&e<=12830||e>=12832&&e<=12871||e>=12880&&e<=13054||e>=13056&&e<=19903||e>=19968&&e<=42124||e>=42128&&e<=42182||e>=43360&&e<=43388||e>=44032&&e<=55203||e>=55216&&e<=55238||e>=55243&&e<=55291||e>=63744&&e<=64255||e>=65040&&e<=65049||e>=65072&&e<=65106||e>=65108&&e<=65126||e>=65128&&e<=65131||e>=65281&&e<=65376||e>=65504&&e<=65510}r.implement(this,u),this.setDocument=function(e){this.doc&&this.doc.removeListener("change",this.$onChange),this.doc=e,e.on("change",this.$onChange),this.bgTokenizer&&this.bgTokenizer.setDocument(this.getDocument()),this.resetCaches()},this.getDocument=function(){return this.doc},this.$resetRowCache=function(e){if(!e){this.$docRowCache=[],this.$screenRowCache=[];return}var t=this.$docRowCache.length,n=this.$getRowCacheIndex(this.$docRowCache,e)+1;t>n&&(this.$docRowCache.splice(n,t),this.$screenRowCache.splice(n,t))},this.$getRowCacheIndex=function(e,t){var n=0,r=e.length-1;while(n<=r){var i=n+r>>1,s=e[i];if(t>s)n=i+1;else{if(!(t=t)break}return r=n[s],r?(r.index=s,r.start=i-r.value.length,r):null},this.setUndoManager=function(e){this.$undoManager=e,this.$informUndoManager&&this.$informUndoManager.cancel();if(e){var t=this;e.addSession(this),this.$syncInformUndoManager=function(){t.$informUndoManager.cancel(),t.mergeUndoDeltas=!1},this.$informUndoManager=i.delayedCall(this.$syncInformUndoManager)}else this.$syncInformUndoManager=function(){}},this.markUndoGroup=function(){this.$syncInformUndoManager&&this.$syncInformUndoManager()},this.$defaultUndoManager={undo:function(){},redo:function(){},hasUndo:function(){},hasRedo:function(){},reset:function(){},add:function(){},addSelection:function(){},startNewGroup:function(){},addSession:function(){}},this.getUndoManager=function(){return this.$undoManager||this.$defaultUndoManager},this.getTabString=function(){return this.getUseSoftTabs()?i.stringRepeat(" ",this.getTabSize()):" "},this.setUseSoftTabs=function(e){this.setOption("useSoftTabs",e)},this.getUseSoftTabs=function(){return this.$useSoftTabs&&!this.$mode.$indentWithTabs},this.setTabSize=function(e){this.setOption("tabSize",e)},this.getTabSize=function(){return this.$tabSize},this.isTabStop=function(e){return this.$useSoftTabs&&e.column%this.$tabSize===0},this.setNavigateWithinSoftTabs=function(e){this.setOption("navigateWithinSoftTabs",e)},this.getNavigateWithinSoftTabs=function(){return this.$navigateWithinSoftTabs},this.$overwrite=!1,this.setOverwrite=function(e){this.setOption("overwrite",e)},this.getOverwrite=function(){return this.$overwrite},this.toggleOverwrite=function(){this.setOverwrite(!this.$overwrite)},this.addGutterDecoration=function(e,t){this.$decorations[e]||(this.$decorations[e]=""),this.$decorations[e]+=" "+t,this._signal("changeBreakpoint",{})},this.removeGutterDecoration=function(e,t){this.$decorations[e]=(this.$decorations[e]||"").replace(" "+t,""),this._signal("changeBreakpoint",{})},this.getBreakpoints=function(){return this.$breakpoints},this.setBreakpoints=function(e){this.$breakpoints=[];for(var t=0;t0&&(r=!!n.charAt(t-1).match(this.tokenRe)),r||(r=!!n.charAt(t).match(this.tokenRe));if(r)var i=this.tokenRe;else if(/^\s+$/.test(n.slice(t-1,t+1)))var i=/\s/;else var i=this.nonTokenRe;var s=t;if(s>0){do s--;while(s>=0&&n.charAt(s).match(i));s++}var o=t;while(oe&&(e=t.screenWidth)}),this.lineWidgetWidth=e},this.$computeWidth=function(e){if(this.$modified||e){this.$modified=!1;if(this.$useWrapMode)return this.screenWidth=this.$wrapLimit;var t=this.doc.getAllLines(),n=this.$rowLengthCache,r=0,i=0,s=this.$foldData[i],o=s?s.start.row:Infinity,u=t.length;for(var a=0;ao){a=s.end.row+1;if(a>=u)break;s=this.$foldData[i++],o=s?s.start.row:Infinity}n[a]==null&&(n[a]=this.$getStringScreenWidth(t[a])[0]),n[a]>r&&(r=n[a])}this.screenWidth=r}},this.getLine=function(e){return this.doc.getLine(e)},this.getLines=function(e,t){return this.doc.getLines(e,t)},this.getLength=function(){return this.doc.getLength()},this.getTextRange=function(e){return this.doc.getTextRange(e||this.selection.getRange())},this.insert=function(e,t){return this.doc.insert(e,t)},this.remove=function(e){return this.doc.remove(e)},this.removeFullLines=function(e,t){return this.doc.removeFullLines(e,t)},this.undoChanges=function(e,t){if(!e.length)return;this.$fromUndo=!0;for(var n=e.length-1;n!=-1;n--){var r=e[n];r.action=="insert"||r.action=="remove"?this.doc.revertDelta(r):r.folds&&this.addFolds(r.folds)}!t&&this.$undoSelect&&(e.selectionBefore?this.selection.fromJSON(e.selectionBefore):this.selection.setRange(this.$getUndoSelection(e,!0))),this.$fromUndo=!1},this.redoChanges=function(e,t){if(!e.length)return;this.$fromUndo=!0;for(var n=0;ne.end.column&&(s.start.column+=u),s.end.row==e.end.row&&s.end.column>e.end.column&&(s.end.column+=u)),o&&s.start.row>=e.end.row&&(s.start.row+=o,s.end.row+=o)}s.end=this.insert(s.start,r);if(i.length){var a=e.start,f=s.start,o=f.row-a.row,u=f.column-a.column;this.addFolds(i.map(function(e){return e=e.clone(),e.start.row==a.row&&(e.start.column+=u),e.end.row==a.row&&(e.end.column+=u),e.start.row+=o,e.end.row+=o,e}))}return s},this.indentRows=function(e,t,n){n=n.replace(/\t/g,this.getTabString());for(var r=e;r<=t;r++)this.doc.insertInLine({row:r,column:0},n)},this.outdentRows=function(e){var t=e.collapseRows(),n=new l(0,0,0,0),r=this.getTabSize();for(var i=t.start.row;i<=t.end.row;++i){var s=this.getLine(i);n.start.row=i,n.end.row=i;for(var o=0;o0){var r=this.getRowFoldEnd(t+n);if(r>this.doc.getLength()-1)return 0;var i=r-t}else{e=this.$clipRowToDocument(e),t=this.$clipRowToDocument(t);var i=t-e+1}var s=new l(e,0,t,Number.MAX_VALUE),o=this.getFoldsInRange(s).map(function(e){return e=e.clone(),e.start.row+=i,e.end.row+=i,e}),u=n==0?this.doc.getLines(e,t):this.doc.removeFullLines(e,t);return this.doc.insertFullLines(e+i,u),o.length&&this.addFolds(o),i},this.moveLinesUp=function(e,t){return this.$moveLines(e,t,-1)},this.moveLinesDown=function(e,t){return this.$moveLines(e,t,1)},this.duplicateLines=function(e,t){return this.$moveLines(e,t,0)},this.$clipRowToDocument=function(e){return Math.max(0,Math.min(e,this.doc.getLength()-1))},this.$clipColumnToRow=function(e,t){return t<0?0:Math.min(this.doc.getLine(e).length,t)},this.$clipPositionToDocument=function(e,t){t=Math.max(0,t);if(e<0)e=0,t=0;else{var n=this.doc.getLength();e>=n?(e=n-1,t=this.doc.getLine(n-1).length):t=Math.min(this.doc.getLine(e).length,t)}return{row:e,column:t}},this.$clipRangeToDocument=function(e){e.start.row<0?(e.start.row=0,e.start.column=0):e.start.column=this.$clipColumnToRow(e.start.row,e.start.column);var t=this.doc.getLength()-1;return e.end.row>t?(e.end.row=t,e.end.column=this.doc.getLine(t).length):e.end.column=this.$clipColumnToRow(e.end.row,e.end.column),e},this.$wrapLimit=80,this.$useWrapMode=!1,this.$wrapLimitRange={min:null,max:null},this.setUseWrapMode=function(e){if(e!=this.$useWrapMode){this.$useWrapMode=e,this.$modified=!0,this.$resetRowCache(0);if(e){var t=this.getLength();this.$wrapData=Array(t),this.$updateWrapData(0,t-1)}this._signal("changeWrapMode")}},this.getUseWrapMode=function(){return this.$useWrapMode},this.setWrapLimitRange=function(e,t){if(this.$wrapLimitRange.min!==e||this.$wrapLimitRange.max!==t)this.$wrapLimitRange={min:e,max:t},this.$modified=!0,this.$bidiHandler.markAsDirty(),this.$useWrapMode&&this._signal("changeWrapMode")},this.adjustWrapLimit=function(e,t){var n=this.$wrapLimitRange;n.max<0&&(n={min:t,max:t});var r=this.$constrainWrapLimit(e,n.min,n.max);return r!=this.$wrapLimit&&r>1?(this.$wrapLimit=r,this.$modified=!0,this.$useWrapMode&&(this.$updateWrapData(0,this.getLength()-1),this.$resetRowCache(0),this._signal("changeWrapLimit")),!0):!1},this.$constrainWrapLimit=function(e,t,n){return t&&(e=Math.max(t,e)),n&&(e=Math.min(n,e)),e},this.getWrapLimit=function(){return this.$wrapLimit},this.setWrapLimit=function(e){this.setWrapLimitRange(e,e)},this.getWrapLimitRange=function(){return{min:this.$wrapLimitRange.min,max:this.$wrapLimitRange.max}},this.$updateInternalDataOnChange=function(e){var t=this.$useWrapMode,n=e.action,r=e.start,i=e.end,s=r.row,o=i.row,u=o-s,a=null;this.$updating=!0;if(u!=0)if(n==="remove"){this[t?"$wrapData":"$rowLengthCache"].splice(s,u);var f=this.$foldData;a=this.getFoldsInRange(e),this.removeFolds(a);var l=this.getFoldLine(i.row),c=0;if(l){l.addRemoveChars(i.row,i.column,r.column-i.column),l.shiftRow(-u);var h=this.getFoldLine(s);h&&h!==l&&(h.merge(l),l=h),c=f.indexOf(l)+1}for(c;c=i.row&&l.shiftRow(-u)}o=s}else{var p=Array(u);p.unshift(s,0);var d=t?this.$wrapData:this.$rowLengthCache;d.splice.apply(d,p);var f=this.$foldData,l=this.getFoldLine(s),c=0;if(l){var v=l.range.compareInside(r.row,r.column);v==0?(l=l.split(r.row,r.column),l&&(l.shiftRow(u),l.addRemoveChars(o,0,i.column-r.column))):v==-1&&(l.addRemoveChars(s,0,i.column-r.column),l.shiftRow(u)),c=f.indexOf(l)+1}for(c;c=s&&l.shiftRow(u)}}else{u=Math.abs(e.start.column-e.end.column),n==="remove"&&(a=this.getFoldsInRange(e),this.removeFolds(a),u=-u);var l=this.getFoldLine(s);l&&l.addRemoveChars(s,r.column,u)}return t&&this.$wrapData.length!=this.doc.getLength()&&console.error("doc.getLength() and $wrapData.length have to be the same!"),this.$updating=!1,t?this.$updateWrapData(s,o):this.$updateRowLengthCache(s,o),a},this.$updateRowLengthCache=function(e,t,n){this.$rowLengthCache[e]=null,this.$rowLengthCache[t]=null},this.$updateWrapData=function(e,t){var r=this.doc.getAllLines(),i=this.getTabSize(),o=this.$wrapData,u=this.$wrapLimit,a,f,l=e;t=Math.min(t,r.length-1);while(l<=t)f=this.getFoldLine(l,f),f?(a=[],f.walk(function(e,t,i,o){var u;if(e!=null){u=this.$getDisplayTokens(e,a.length),u[0]=n;for(var f=1;fr-b){var w=f+r-b;if(e[w-1]>=c&&e[w]>=c){y(w);continue}if(e[w]==n||e[w]==s){for(w;w!=f-1;w--)if(e[w]==n)break;if(w>f){y(w);continue}w=f+r;for(w;w>2)),f-1);while(w>E&&e[w]E&&e[w]E&&e[w]==a)w--}else while(w>E&&e[w]E){y(++w);continue}w=f+r,e[w]==t&&w--,y(w-b)}return o},this.$getDisplayTokens=function(n,r){var i=[],s;r=r||0;for(var o=0;o39&&u<48||u>57&&u<64?i.push(a):u>=4352&&m(u)?i.push(e,t):i.push(e)}return i},this.$getStringScreenWidth=function(e,t,n){if(t==0)return[0,0];t==null&&(t=Infinity),n=n||0;var r,i;for(i=0;i=4352&&m(r)?n+=2:n+=1;if(n>t)break}return[n,i]},this.lineWidgets=null,this.getRowLength=function(e){if(this.lineWidgets)var t=this.lineWidgets[e]&&this.lineWidgets[e].rowCount||0;else t=0;return!this.$useWrapMode||!this.$wrapData[e]?1+t:this.$wrapData[e].length+1+t},this.getRowLineCount=function(e){return!this.$useWrapMode||!this.$wrapData[e]?1:this.$wrapData[e].length+1},this.getRowWrapIndent=function(e){if(this.$useWrapMode){var t=this.screenToDocumentPosition(e,Number.MAX_VALUE),n=this.$wrapData[t.row];return n.length&&n[0]=0)var u=f[l],i=this.$docRowCache[l],h=e>f[c-1];else var h=!c;var p=this.getLength()-1,d=this.getNextFoldLine(i),v=d?d.start.row:Infinity;while(u<=e){a=this.getRowLength(i);if(u+a>e||i>=p)break;u+=a,i++,i>v&&(i=d.end.row+1,d=this.getNextFoldLine(i,d),v=d?d.start.row:Infinity),h&&(this.$docRowCache.push(i),this.$screenRowCache.push(u))}if(d&&d.start.row<=i)r=this.getFoldDisplayLine(d),i=d.start.row;else{if(u+a<=e||i>p)return{row:p,column:this.getLine(p).length};r=this.getLine(i),d=null}var m=0,g=Math.floor(e-u);if(this.$useWrapMode){var y=this.$wrapData[i];y&&(o=y[g],g>0&&y.length&&(m=y.indent,s=y[g-1]||y[y.length-1],r=r.substring(s)))}return n!==undefined&&this.$bidiHandler.isBidiRow(u+g,i,g)&&(t=this.$bidiHandler.offsetToCol(n)),s+=this.$getStringScreenWidth(r,t-m)[1],this.$useWrapMode&&s>=o&&(s=o-1),d?d.idxToPosition(s):{row:i,column:s}},this.documentToScreenPosition=function(e,t){if(typeof t=="undefined")var n=this.$clipPositionToDocument(e.row,e.column);else n=this.$clipPositionToDocument(e,t);e=n.row,t=n.column;var r=0,i=null,s=null;s=this.getFoldAt(e,t,1),s&&(e=s.start.row,t=s.start.column);var o,u=0,a=this.$docRowCache,f=this.$getRowCacheIndex(a,e),l=a.length;if(l&&f>=0)var u=a[f],r=this.$screenRowCache[f],c=e>a[l-1];else var c=!l;var h=this.getNextFoldLine(u),p=h?h.start.row:Infinity;while(u=p){o=h.end.row+1;if(o>e)break;h=this.getNextFoldLine(o,h),p=h?h.start.row:Infinity}else o=u+1;r+=this.getRowLength(u),u=o,c&&(this.$docRowCache.push(u),this.$screenRowCache.push(r))}var d="";h&&u>=p?(d=this.getFoldDisplayLine(h,e,t),i=h.start.row):(d=this.getLine(e).substring(0,t),i=e);var v=0;if(this.$useWrapMode){var m=this.$wrapData[i];if(m){var g=0;while(d.length>=m[g])r++,g++;d=d.substring(m[g-1]||0,d.length),v=g>0?m.indent:0}}return{row:r,column:v+this.$getStringScreenWidth(d)[0]}},this.documentToScreenColumn=function(e,t){return this.documentToScreenPosition(e,t).column},this.documentToScreenRow=function(e,t){return this.documentToScreenPosition(e,t).row},this.getScreenLength=function(){var e=0,t=null;if(!this.$useWrapMode){e=this.getLength();var n=this.$foldData;for(var r=0;ro&&(s=t.end.row+1,t=this.$foldData[r++],o=t?t.start.row:Infinity)}}return this.lineWidgets&&(e+=this.$getWidgetScreenLength()),e},this.$setFontMetrics=function(e){if(!this.$enableVarChar)return;this.$getStringScreenWidth=function(t,n,r){if(n===0)return[0,0];n||(n=Infinity),r=r||0;var i,s;for(s=0;sn)break}return[r,s]}},this.destroy=function(){this.bgTokenizer&&(this.bgTokenizer.setDocument(null),this.bgTokenizer=null),this.$stopWorker()},this.isFullWidth=m}.call(d.prototype),e("./edit_session/folding").Folding.call(d.prototype),e("./edit_session/bracket_match").BracketMatch.call(d.prototype),o.defineOptions(d.prototype,"session",{wrap:{set:function(e){!e||e=="off"?e=!1:e=="free"?e=!0:e=="printMargin"?e=-1:typeof e=="string"&&(e=parseInt(e,10)||!1);if(this.$wrap==e)return;this.$wrap=e;if(!e)this.setUseWrapMode(!1);else{var t=typeof e=="number"?e:null;this.setWrapLimitRange(t,t),this.setUseWrapMode(!0)}},get:function(){return this.getUseWrapMode()?this.$wrap==-1?"printMargin":this.getWrapLimitRange().min?this.$wrap:"free":"off"},handlesSet:!0},wrapMethod:{set:function(e){e=e=="auto"?this.$mode.type!="text":e!="text",e!=this.$wrapAsCode&&(this.$wrapAsCode=e,this.$useWrapMode&&(this.$useWrapMode=!1,this.setUseWrapMode(!0)))},initialValue:"auto"},indentedSoftWrap:{set:function(){this.$useWrapMode&&(this.$useWrapMode=!1,this.setUseWrapMode(!0))},initialValue:!0},firstLineNumber:{set:function(){this._signal("changeBreakpoint")},initialValue:1},useWorker:{set:function(e){this.$useWorker=e,this.$stopWorker(),e&&this.$startWorker()},initialValue:!0},useSoftTabs:{initialValue:!0},tabSize:{set:function(e){e=parseInt(e),e>0&&this.$tabSize!==e&&(this.$modified=!0,this.$rowLengthCache=[],this.$tabSize=e,this._signal("changeTabSize"))},initialValue:4,handlesSet:!0},navigateWithinSoftTabs:{initialValue:!1},foldStyle:{set:function(e){this.setFoldStyle(e)},handlesSet:!0},overwrite:{set:function(e){this._signal("changeOverwrite")},initialValue:!1},newLineMode:{set:function(e){this.doc.setNewLineMode(e)},get:function(){return this.doc.getNewLineMode()},handlesSet:!0},mode:{set:function(e){this.setMode(e)},get:function(){return this.$modeId},handlesSet:!0}}),t.EditSession=d}),ace.define("ace/search",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"],function(e,t,n){"use strict";function u(e,t){function n(e){return/\w/.test(e)||t.regExp?"\\b":""}return n(e[0])+e+n(e[e.length-1])}var r=e("./lib/lang"),i=e("./lib/oop"),s=e("./range").Range,o=function(){this.$options={}};(function(){this.set=function(e){return i.mixin(this.$options,e),this},this.getOptions=function(){return r.copyObject(this.$options)},this.setOptions=function(e){this.$options=e},this.find=function(e){var t=this.$options,n=this.$matchIterator(e,t);if(!n)return!1;var r=null;return n.forEach(function(e,n,i,o){return r=new s(e,n,i,o),n==o&&t.start&&t.start.start&&t.skipCurrent!=0&&r.isEqual(t.start)?(r=null,!1):!0}),r},this.findAll=function(e){var t=this.$options;if(!t.needle)return[];this.$assembleRegExp(t);var n=t.range,i=n?e.getLines(n.start.row,n.end.row):e.doc.getAllLines(),o=[],u=t.re;if(t.$isMultiLine){var a=u.length,f=i.length-a,l;e:for(var c=u.offset||0;c<=f;c++){for(var h=0;hv)continue;o.push(l=new s(c,v,c+a-1,m)),a>2&&(c=c+a-2)}}else for(var g=0;gE&&o[h].end.row==n.end.row)h--;o=o.slice(g,h+1);for(g=0,h=o.length;g=u;n--)if(c(n,Number.MAX_VALUE,e))return;if(t.wrap==0)return;for(n=a,u=o.row;n>=u;n--)if(c(n,Number.MAX_VALUE,e))return};else var f=function(e){var n=o.row;if(c(n,o.column,e))return;for(n+=1;n<=a;n++)if(c(n,0,e))return;if(t.wrap==0)return;for(n=u,a=o.row;n<=a;n++)if(c(n,0,e))return};if(t.$isMultiLine)var l=n.length,c=function(t,i,s){var o=r?t-l+1:t;if(o<0)return;var u=e.getLine(o),a=u.search(n[0]);if(!r&&ai)return;if(s(o,a,o+l-1,c))return!0};else if(r)var c=function(t,r,i){var s=e.getLine(t),o=[],u,a=0;n.lastIndex=0;while(u=n.exec(s)){var f=u[0].length;a=u.index;if(!f){if(a>=s.length)break;n.lastIndex=a+=1}if(u.index+f>r)break;o.push(u.index,f)}for(var l=o.length-1;l>=0;l-=2){var c=o[l-1],f=o[l];if(i(t,c,t,c+f))return!0}};else var c=function(t,r,i){var s=e.getLine(t),o,u;n.lastIndex=r;while(u=n.exec(s)){var a=u[0].length;o=u.index;if(i(t,o,t,o+a))return!0;if(!a){n.lastIndex=o+=1;if(o>=s.length)return!1}}};return{forEach:f}}}).call(o.prototype),t.Search=o}),ace.define("ace/keyboard/hash_handler",["require","exports","module","ace/lib/keys","ace/lib/useragent"],function(e,t,n){"use strict";function o(e,t){this.platform=t||(i.isMac?"mac":"win"),this.commands={},this.commandKeyBinding={},this.addCommands(e),this.$singleCommand=!0}function u(e,t){o.call(this,e,t),this.$singleCommand=!1}var r=e("../lib/keys"),i=e("../lib/useragent"),s=r.KEY_MODS;u.prototype=o.prototype,function(){function e(e){return typeof e=="object"&&e.bindKey&&e.bindKey.position||(e.isDefault?-100:0)}this.addCommand=function(e){this.commands[e.name]&&this.removeCommand(e),this.commands[e.name]=e,e.bindKey&&this._buildKeyHash(e)},this.removeCommand=function(e,t){var n=e&&(typeof e=="string"?e:e.name);e=this.commands[n],t||delete this.commands[n];var r=this.commandKeyBinding;for(var i in r){var s=r[i];if(s==e)delete r[i];else if(Array.isArray(s)){var o=s.indexOf(e);o!=-1&&(s.splice(o,1),s.length==1&&(r[i]=s[0]))}}},this.bindKey=function(e,t,n){typeof e=="object"&&e&&(n==undefined&&(n=e.position),e=e[this.platform]);if(!e)return;if(typeof t=="function")return this.addCommand({exec:t,bindKey:e,name:t.name||e});e.split("|").forEach(function(e){var r="";if(e.indexOf(" ")!=-1){var i=e.split(/\s+/);e=i.pop(),i.forEach(function(e){var t=this.parseKeys(e),n=s[t.hashId]+t.key;r+=(r?" ":"")+n,this._addCommandToBinding(r,"chainKeys")},this),r+=" "}var o=this.parseKeys(e),u=s[o.hashId]+o.key;this._addCommandToBinding(r+u,t,n)},this)},this._addCommandToBinding=function(t,n,r){var i=this.commandKeyBinding,s;if(!n)delete i[t];else if(!i[t]||this.$singleCommand)i[t]=n;else{Array.isArray(i[t])?(s=i[t].indexOf(n))!=-1&&i[t].splice(s,1):i[t]=[i[t]],typeof r!="number"&&(r=e(n));var o=i[t];for(s=0;sr)break}o.splice(s,0,n)}},this.addCommands=function(e){e&&Object.keys(e).forEach(function(t){var n=e[t];if(!n)return;if(typeof n=="string")return this.bindKey(n,t);typeof n=="function"&&(n={exec:n});if(typeof n!="object")return;n.name||(n.name=t),this.addCommand(n)},this)},this.removeCommands=function(e){Object.keys(e).forEach(function(t){this.removeCommand(e[t])},this)},this.bindKeys=function(e){Object.keys(e).forEach(function(t){this.bindKey(t,e[t])},this)},this._buildKeyHash=function(e){this.bindKey(e.bindKey,e)},this.parseKeys=function(e){var t=e.toLowerCase().split(/[\-\+]([\-\+])?/).filter(function(e){return e}),n=t.pop(),i=r[n];if(r.FUNCTION_KEYS[i])n=r.FUNCTION_KEYS[i].toLowerCase();else{if(!t.length)return{key:n,hashId:-1};if(t.length==1&&t[0]=="shift")return{key:n.toUpperCase(),hashId:-1}}var s=0;for(var o=t.length;o--;){var u=r.KEY_MODS[t[o]];if(u==null)return typeof console!="undefined"&&console.error("invalid modifier "+t[o]+" in "+e),!1;s|=u}return{key:n,hashId:s}},this.findKeyCommand=function(t,n){var r=s[t]+n;return this.commandKeyBinding[r]},this.handleKeyboard=function(e,t,n,r){if(r<0)return;var i=s[t]+n,o=this.commandKeyBinding[i];e.$keyChain&&(e.$keyChain+=" "+i,o=this.commandKeyBinding[e.$keyChain]||o);if(o)if(o=="chainKeys"||o[o.length-1]=="chainKeys")return e.$keyChain=e.$keyChain||i,{command:"null"};if(e.$keyChain)if(!!t&&t!=4||n.length!=1){if(t==-1||r>0)e.$keyChain=""}else e.$keyChain=e.$keyChain.slice(0,-i.length-1);return{command:o}},this.getStatusText=function(e,t){return t.$keyChain||""}}.call(o.prototype),t.HashHandler=o,t.MultiHashHandler=u}),ace.define("ace/commands/command_manager",["require","exports","module","ace/lib/oop","ace/keyboard/hash_handler","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../keyboard/hash_handler").MultiHashHandler,s=e("../lib/event_emitter").EventEmitter,o=function(e,t){i.call(this,t,e),this.byName=this.commands,this.setDefaultHandler("exec",function(e){return e.command.exec(e.editor,e.args||{})})};r.inherits(o,i),function(){r.implement(this,s),this.exec=function(e,t,n){if(Array.isArray(e)){for(var r=e.length;r--;)if(this.exec(e[r],t,n))return!0;return!1}typeof e=="string"&&(e=this.commands[e]);if(!e)return!1;if(t&&t.$readOnly&&!e.readOnly)return!1;if(this.$checkCommandState!=0&&e.isAvailable&&!e.isAvailable(t))return!1;var i={editor:t,command:e,args:n};return i.returnValue=this._emit("exec",i),this._signal("afterExec",i),i.returnValue===!1?!1:!0},this.toggleRecording=function(e){if(this.$inReplay)return;return e&&e._emit("changeStatus"),this.recording?(this.macro.pop(),this.removeEventListener("exec",this.$addCommandToMacro),this.macro.length||(this.macro=this.oldMacro),this.recording=!1):(this.$addCommandToMacro||(this.$addCommandToMacro=function(e){this.macro.push([e.command,e.args])}.bind(this)),this.oldMacro=this.macro,this.macro=[],this.on("exec",this.$addCommandToMacro),this.recording=!0)},this.replay=function(e){if(this.$inReplay||!this.macro)return;if(this.recording)return this.toggleRecording(e);try{this.$inReplay=!0,this.macro.forEach(function(t){typeof t=="string"?this.exec(t,e):this.exec(t[0],e,t[1])},this)}finally{this.$inReplay=!1}},this.trimMacro=function(e){return e.map(function(e){return typeof e[0]!="string"&&(e[0]=e[0].name),e[1]||(e=e[0]),e})}}.call(o.prototype),t.CommandManager=o}),ace.define("ace/commands/default_commands",["require","exports","module","ace/lib/lang","ace/config","ace/range"],function(e,t,n){"use strict";function o(e,t){return{win:e,mac:t}}var r=e("../lib/lang"),i=e("../config"),s=e("../range").Range;t.commands=[{name:"showSettingsMenu",bindKey:o("Ctrl-,","Command-,"),exec:function(e){i.loadModule("ace/ext/settings_menu",function(t){t.init(e),e.showSettingsMenu()})},readOnly:!0},{name:"goToNextError",bindKey:o("Alt-E","F4"),exec:function(e){i.loadModule("./ext/error_marker",function(t){t.showErrorMarker(e,1)})},scrollIntoView:"animate",readOnly:!0},{name:"goToPreviousError",bindKey:o("Alt-Shift-E","Shift-F4"),exec:function(e){i.loadModule("./ext/error_marker",function(t){t.showErrorMarker(e,-1)})},scrollIntoView:"animate",readOnly:!0},{name:"selectall",description:"Select all",bindKey:o("Ctrl-A","Command-A"),exec:function(e){e.selectAll()},readOnly:!0},{name:"centerselection",description:"Center selection",bindKey:o(null,"Ctrl-L"),exec:function(e){e.centerSelection()},readOnly:!0},{name:"gotoline",description:"Go to line...",bindKey:o("Ctrl-L","Command-L"),exec:function(e,t){typeof t=="number"&&!isNaN(t)&&e.gotoLine(t),e.prompt({$type:"gotoLine"})},readOnly:!0},{name:"fold",bindKey:o("Alt-L|Ctrl-F1","Command-Alt-L|Command-F1"),exec:function(e){e.session.toggleFold(!1)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"unfold",bindKey:o("Alt-Shift-L|Ctrl-Shift-F1","Command-Alt-Shift-L|Command-Shift-F1"),exec:function(e){e.session.toggleFold(!0)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"toggleFoldWidget",bindKey:o("F2","F2"),exec:function(e){e.session.toggleFoldWidget()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"toggleParentFoldWidget",bindKey:o("Alt-F2","Alt-F2"),exec:function(e){e.session.toggleFoldWidget(!0)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"foldall",description:"Fold all",bindKey:o(null,"Ctrl-Command-Option-0"),exec:function(e){e.session.foldAll()},scrollIntoView:"center",readOnly:!0},{name:"foldOther",description:"Fold other",bindKey:o("Alt-0","Command-Option-0"),exec:function(e){e.session.foldAll(),e.session.unfold(e.selection.getAllRanges())},scrollIntoView:"center",readOnly:!0},{name:"unfoldall",description:"Unfold all",bindKey:o("Alt-Shift-0","Command-Option-Shift-0"),exec:function(e){e.session.unfold()},scrollIntoView:"center",readOnly:!0},{name:"findnext",description:"Find next",bindKey:o("Ctrl-K","Command-G"),exec:function(e){e.findNext()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"findprevious",description:"Find previous",bindKey:o("Ctrl-Shift-K","Command-Shift-G"),exec:function(e){e.findPrevious()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"selectOrFindNext",description:"Select or find next",bindKey:o("Alt-K","Ctrl-G"),exec:function(e){e.selection.isEmpty()?e.selection.selectWord():e.findNext()},readOnly:!0},{name:"selectOrFindPrevious",description:"Select or find previous",bindKey:o("Alt-Shift-K","Ctrl-Shift-G"),exec:function(e){e.selection.isEmpty()?e.selection.selectWord():e.findPrevious()},readOnly:!0},{name:"find",description:"Find",bindKey:o("Ctrl-F","Command-F"),exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e)})},readOnly:!0},{name:"overwrite",description:"Overwrite",bindKey:"Insert",exec:function(e){e.toggleOverwrite()},readOnly:!0},{name:"selecttostart",description:"Select to start",bindKey:o("Ctrl-Shift-Home","Command-Shift-Home|Command-Shift-Up"),exec:function(e){e.getSelection().selectFileStart()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"gotostart",description:"Go to start",bindKey:o("Ctrl-Home","Command-Home|Command-Up"),exec:function(e){e.navigateFileStart()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"selectup",description:"Select up",bindKey:o("Shift-Up","Shift-Up|Ctrl-Shift-P"),exec:function(e){e.getSelection().selectUp()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"golineup",description:"Go line up",bindKey:o("Up","Up|Ctrl-P"),exec:function(e,t){e.navigateUp(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttoend",description:"Select to end",bindKey:o("Ctrl-Shift-End","Command-Shift-End|Command-Shift-Down"),exec:function(e){e.getSelection().selectFileEnd()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"gotoend",description:"Go to end",bindKey:o("Ctrl-End","Command-End|Command-Down"),exec:function(e){e.navigateFileEnd()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"selectdown",description:"Select down",bindKey:o("Shift-Down","Shift-Down|Ctrl-Shift-N"),exec:function(e){e.getSelection().selectDown()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"golinedown",description:"Go line down",bindKey:o("Down","Down|Ctrl-N"),exec:function(e,t){e.navigateDown(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectwordleft",description:"Select word left",bindKey:o("Ctrl-Shift-Left","Option-Shift-Left"),exec:function(e){e.getSelection().selectWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotowordleft",description:"Go to word left",bindKey:o("Ctrl-Left","Option-Left"),exec:function(e){e.navigateWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttolinestart",description:"Select to line start",bindKey:o("Alt-Shift-Left","Command-Shift-Left|Ctrl-Shift-A"),exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotolinestart",description:"Go to line start",bindKey:o("Alt-Left|Home","Command-Left|Home|Ctrl-A"),exec:function(e){e.navigateLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectleft",description:"Select left",bindKey:o("Shift-Left","Shift-Left|Ctrl-Shift-B"),exec:function(e){e.getSelection().selectLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotoleft",description:"Go to left",bindKey:o("Left","Left|Ctrl-B"),exec:function(e,t){e.navigateLeft(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectwordright",description:"Select word right",bindKey:o("Ctrl-Shift-Right","Option-Shift-Right"),exec:function(e){e.getSelection().selectWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotowordright",description:"Go to word right",bindKey:o("Ctrl-Right","Option-Right"),exec:function(e){e.navigateWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttolineend",description:"Select to line end",bindKey:o("Alt-Shift-Right","Command-Shift-Right|Shift-End|Ctrl-Shift-E"),exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotolineend",description:"Go to line end",bindKey:o("Alt-Right|End","Command-Right|End|Ctrl-E"),exec:function(e){e.navigateLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectright",description:"Select right",bindKey:o("Shift-Right","Shift-Right"),exec:function(e){e.getSelection().selectRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotoright",description:"Go to right",bindKey:o("Right","Right|Ctrl-F"),exec:function(e,t){e.navigateRight(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectpagedown",description:"Select page down",bindKey:"Shift-PageDown",exec:function(e){e.selectPageDown()},readOnly:!0},{name:"pagedown",description:"Page down",bindKey:o(null,"Option-PageDown"),exec:function(e){e.scrollPageDown()},readOnly:!0},{name:"gotopagedown",description:"Go to page down",bindKey:o("PageDown","PageDown|Ctrl-V"),exec:function(e){e.gotoPageDown()},readOnly:!0},{name:"selectpageup",description:"Select page up",bindKey:"Shift-PageUp",exec:function(e){e.selectPageUp()},readOnly:!0},{name:"pageup",description:"Page up",bindKey:o(null,"Option-PageUp"),exec:function(e){e.scrollPageUp()},readOnly:!0},{name:"gotopageup",description:"Go to page up",bindKey:"PageUp",exec:function(e){e.gotoPageUp()},readOnly:!0},{name:"scrollup",description:"Scroll up",bindKey:o("Ctrl-Up",null),exec:function(e){e.renderer.scrollBy(0,-2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"scrolldown",description:"Scroll down",bindKey:o("Ctrl-Down",null),exec:function(e){e.renderer.scrollBy(0,2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"selectlinestart",description:"Select line start",bindKey:"Shift-Home",exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectlineend",description:"Select line end",bindKey:"Shift-End",exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"togglerecording",description:"Toggle recording",bindKey:o("Ctrl-Alt-E","Command-Option-E"),exec:function(e){e.commands.toggleRecording(e)},readOnly:!0},{name:"replaymacro",description:"Replay macro",bindKey:o("Ctrl-Shift-E","Command-Shift-E"),exec:function(e){e.commands.replay(e)},readOnly:!0},{name:"jumptomatching",description:"Jump to matching",bindKey:o("Ctrl-\\|Ctrl-P","Command-\\"),exec:function(e){e.jumpToMatching()},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"selecttomatching",description:"Select to matching",bindKey:o("Ctrl-Shift-\\|Ctrl-Shift-P","Command-Shift-\\"),exec:function(e){e.jumpToMatching(!0)},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"expandToMatching",description:"Expand to matching",bindKey:o("Ctrl-Shift-M","Ctrl-Shift-M"),exec:function(e){e.jumpToMatching(!0,!0)},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"passKeysToBrowser",description:"Pass keys to browser",bindKey:o(null,null),exec:function(){},passEvent:!0,readOnly:!0},{name:"copy",description:"Copy",exec:function(e){},readOnly:!0},{name:"cut",description:"Cut",exec:function(e){var t=e.$copyWithEmptySelection&&e.selection.isEmpty(),n=t?e.selection.getLineRange():e.selection.getRange();e._emit("cut",n),n.isEmpty()||e.session.remove(n),e.clearSelection()},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"paste",description:"Paste",exec:function(e,t){e.$handlePaste(t)},scrollIntoView:"cursor"},{name:"removeline",description:"Remove line",bindKey:o("Ctrl-D","Command-D"),exec:function(e){e.removeLines()},scrollIntoView:"cursor",multiSelectAction:"forEachLine"},{name:"duplicateSelection",description:"Duplicate selection",bindKey:o("Ctrl-Shift-D","Command-Shift-D"),exec:function(e){e.duplicateSelection()},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"sortlines",description:"Sort lines",bindKey:o("Ctrl-Alt-S","Command-Alt-S"),exec:function(e){e.sortLines()},scrollIntoView:"selection",multiSelectAction:"forEachLine"},{name:"togglecomment",description:"Toggle comment",bindKey:o("Ctrl-/","Command-/"),exec:function(e){e.toggleCommentLines()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"toggleBlockComment",description:"Toggle block comment",bindKey:o("Ctrl-Shift-/","Command-Shift-/"),exec:function(e){e.toggleBlockComment()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"modifyNumberUp",description:"Modify number up",bindKey:o("Ctrl-Shift-Up","Alt-Shift-Up"),exec:function(e){e.modifyNumber(1)},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"modifyNumberDown",description:"Modify number down",bindKey:o("Ctrl-Shift-Down","Alt-Shift-Down"),exec:function(e){e.modifyNumber(-1)},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"replace",description:"Replace",bindKey:o("Ctrl-H","Command-Option-F"),exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e,!0)})}},{name:"undo",description:"Undo",bindKey:o("Ctrl-Z","Command-Z"),exec:function(e){e.undo()}},{name:"redo",description:"Redo",bindKey:o("Ctrl-Shift-Z|Ctrl-Y","Command-Shift-Z|Command-Y"),exec:function(e){e.redo()}},{name:"copylinesup",description:"Copy lines up",bindKey:o("Alt-Shift-Up","Command-Option-Up"),exec:function(e){e.copyLinesUp()},scrollIntoView:"cursor"},{name:"movelinesup",description:"Move lines up",bindKey:o("Alt-Up","Option-Up"),exec:function(e){e.moveLinesUp()},scrollIntoView:"cursor"},{name:"copylinesdown",description:"Copy lines down",bindKey:o("Alt-Shift-Down","Command-Option-Down"),exec:function(e){e.copyLinesDown()},scrollIntoView:"cursor"},{name:"movelinesdown",description:"Move lines down",bindKey:o("Alt-Down","Option-Down"),exec:function(e){e.moveLinesDown()},scrollIntoView:"cursor"},{name:"del",description:"Delete",bindKey:o("Delete","Delete|Ctrl-D|Shift-Delete"),exec:function(e){e.remove("right")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"backspace",description:"Backspace",bindKey:o("Shift-Backspace|Backspace","Ctrl-Backspace|Shift-Backspace|Backspace|Ctrl-H"),exec:function(e){e.remove("left")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"cut_or_delete",description:"Cut or delete",bindKey:o("Shift-Delete",null),exec:function(e){if(!e.selection.isEmpty())return!1;e.remove("left")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolinestart",description:"Remove to line start",bindKey:o("Alt-Backspace","Command-Backspace"),exec:function(e){e.removeToLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolineend",description:"Remove to line end",bindKey:o("Alt-Delete","Ctrl-K|Command-Delete"),exec:function(e){e.removeToLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolinestarthard",description:"Remove to line start hard",bindKey:o("Ctrl-Shift-Backspace",null),exec:function(e){var t=e.selection.getRange();t.start.column=0,e.session.remove(t)},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolineendhard",description:"Remove to line end hard",bindKey:o("Ctrl-Shift-Delete",null),exec:function(e){var t=e.selection.getRange();t.end.column=Number.MAX_VALUE,e.session.remove(t)},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removewordleft",description:"Remove word left",bindKey:o("Ctrl-Backspace","Alt-Backspace|Ctrl-Alt-Backspace"),exec:function(e){e.removeWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removewordright",description:"Remove word right",bindKey:o("Ctrl-Delete","Alt-Delete"),exec:function(e){e.removeWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"outdent",description:"Outdent",bindKey:o("Shift-Tab","Shift-Tab"),exec:function(e){e.blockOutdent()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"indent",description:"Indent",bindKey:o("Tab","Tab"),exec:function(e){e.indent()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"blockoutdent",description:"Block outdent",bindKey:o("Ctrl-[","Ctrl-["),exec:function(e){e.blockOutdent()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"blockindent",description:"Block indent",bindKey:o("Ctrl-]","Ctrl-]"),exec:function(e){e.blockIndent()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"insertstring",description:"Insert string",exec:function(e,t){e.insert(t)},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"inserttext",description:"Insert text",exec:function(e,t){e.insert(r.stringRepeat(t.text||"",t.times||1))},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"splitline",description:"Split line",bindKey:o(null,"Ctrl-O"),exec:function(e){e.splitLine()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"transposeletters",description:"Transpose letters",bindKey:o("Alt-Shift-X","Ctrl-T"),exec:function(e){e.transposeLetters()},multiSelectAction:function(e){e.transposeSelections(1)},scrollIntoView:"cursor"},{name:"touppercase",description:"To uppercase",bindKey:o("Ctrl-U","Ctrl-U"),exec:function(e){e.toUpperCase()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"tolowercase",description:"To lowercase",bindKey:o("Ctrl-Shift-U","Ctrl-Shift-U"),exec:function(e){e.toLowerCase()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"expandtoline",description:"Expand to line",bindKey:o("Ctrl-Shift-L","Command-Shift-L"),exec:function(e){var t=e.selection.getRange();t.start.column=t.end.column=0,t.end.row++,e.selection.setRange(t,!1)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"joinlines",description:"Join lines",bindKey:o(null,null),exec:function(e){var t=e.selection.isBackwards(),n=t?e.selection.getSelectionLead():e.selection.getSelectionAnchor(),i=t?e.selection.getSelectionAnchor():e.selection.getSelectionLead(),o=e.session.doc.getLine(n.row).length,u=e.session.doc.getTextRange(e.selection.getRange()),a=u.replace(/\n\s*/," ").length,f=e.session.doc.getLine(n.row);for(var l=n.row+1;l<=i.row+1;l++){var c=r.stringTrimLeft(r.stringTrimRight(e.session.doc.getLine(l)));c.length!==0&&(c=" "+c),f+=c}i.row+10?(e.selection.moveCursorTo(n.row,n.column),e.selection.selectTo(n.row,n.column+a)):(o=e.session.doc.getLine(n.row).length>o?o+1:o,e.selection.moveCursorTo(n.row,o))},multiSelectAction:"forEach",readOnly:!0},{name:"invertSelection",description:"Invert selection",bindKey:o(null,null),exec:function(e){var t=e.session.doc.getLength()-1,n=e.session.doc.getLine(t).length,r=e.selection.rangeList.ranges,i=[];r.length<1&&(r=[e.selection.getRange()]);for(var o=0;o=i.lastRow||r.end.row<=i.firstRow)&&this.renderer.scrollSelectionIntoView(this.selection.anchor,this.selection.lead);break;default:}n=="animate"&&this.renderer.animateScrolling(this.curOp.scrollTop)}var s=this.selection.toJSON();this.curOp.selectionAfter=s,this.$lastSel=this.selection.toJSON(),this.session.getUndoManager().addSelection(s),this.prevOp=this.curOp,this.curOp=null}},this.$mergeableCommands=["backspace","del","insertstring"],this.$historyTracker=function(e){if(!this.$mergeUndoDeltas)return;var t=this.prevOp,n=this.$mergeableCommands,r=t.command&&e.command.name==t.command.name;if(e.command.name=="insertstring"){var i=e.args;this.mergeNextCommand===undefined&&(this.mergeNextCommand=!0),r=r&&this.mergeNextCommand&&(!/\s/.test(i)||/\s/.test(t.args)),this.mergeNextCommand=!0}else r=r&&n.indexOf(e.command.name)!==-1;this.$mergeUndoDeltas!="always"&&Date.now()-this.sequenceStartTime>2e3&&(r=!1),r?this.session.mergeUndoDeltas=!0:n.indexOf(e.command.name)!==-1&&(this.sequenceStartTime=Date.now())},this.setKeyboardHandler=function(e,t){if(e&&typeof e=="string"&&e!="ace"){this.$keybindingId=e;var n=this;g.loadModule(["keybinding",e],function(r){n.$keybindingId==e&&n.keyBinding.setKeyboardHandler(r&&r.handler),t&&t()})}else this.$keybindingId=null,this.keyBinding.setKeyboardHandler(e),t&&t()},this.getKeyboardHandler=function(){return this.keyBinding.getKeyboardHandler()},this.setSession=function(e){if(this.session==e)return;this.curOp&&this.endOperation(),this.curOp={};var t=this.session;if(t){this.session.off("change",this.$onDocumentChange),this.session.off("changeMode",this.$onChangeMode),this.session.off("tokenizerUpdate",this.$onTokenizerUpdate),this.session.off("changeTabSize",this.$onChangeTabSize),this.session.off("changeWrapLimit",this.$onChangeWrapLimit),this.session.off("changeWrapMode",this.$onChangeWrapMode),this.session.off("changeFold",this.$onChangeFold),this.session.off("changeFrontMarker",this.$onChangeFrontMarker),this.session.off("changeBackMarker",this.$onChangeBackMarker),this.session.off("changeBreakpoint",this.$onChangeBreakpoint),this.session.off("changeAnnotation",this.$onChangeAnnotation),this.session.off("changeOverwrite",this.$onCursorChange),this.session.off("changeScrollTop",this.$onScrollTopChange),this.session.off("changeScrollLeft",this.$onScrollLeftChange);var n=this.session.getSelection();n.off("changeCursor",this.$onCursorChange),n.off("changeSelection",this.$onSelectionChange)}this.session=e,e?(this.$onDocumentChange=this.onDocumentChange.bind(this),e.on("change",this.$onDocumentChange),this.renderer.setSession(e),this.$onChangeMode=this.onChangeMode.bind(this),e.on("changeMode",this.$onChangeMode),this.$onTokenizerUpdate=this.onTokenizerUpdate.bind(this),e.on("tokenizerUpdate",this.$onTokenizerUpdate),this.$onChangeTabSize=this.renderer.onChangeTabSize.bind(this.renderer),e.on("changeTabSize",this.$onChangeTabSize),this.$onChangeWrapLimit=this.onChangeWrapLimit.bind(this),e.on("changeWrapLimit",this.$onChangeWrapLimit),this.$onChangeWrapMode=this.onChangeWrapMode.bind(this),e.on("changeWrapMode",this.$onChangeWrapMode),this.$onChangeFold=this.onChangeFold.bind(this),e.on("changeFold",this.$onChangeFold),this.$onChangeFrontMarker=this.onChangeFrontMarker.bind(this),this.session.on("changeFrontMarker",this.$onChangeFrontMarker),this.$onChangeBackMarker=this.onChangeBackMarker.bind(this),this.session.on("changeBackMarker",this.$onChangeBackMarker),this.$onChangeBreakpoint=this.onChangeBreakpoint.bind(this),this.session.on("changeBreakpoint",this.$onChangeBreakpoint),this.$onChangeAnnotation=this.onChangeAnnotation.bind(this),this.session.on("changeAnnotation",this.$onChangeAnnotation),this.$onCursorChange=this.onCursorChange.bind(this),this.session.on("changeOverwrite",this.$onCursorChange),this.$onScrollTopChange=this.onScrollTopChange.bind(this),this.session.on("changeScrollTop",this.$onScrollTopChange),this.$onScrollLeftChange=this.onScrollLeftChange.bind(this),this.session.on("changeScrollLeft",this.$onScrollLeftChange),this.selection=e.getSelection(),this.selection.on("changeCursor",this.$onCursorChange),this.$onSelectionChange=this.onSelectionChange.bind(this),this.selection.on("changeSelection",this.$onSelectionChange),this.onChangeMode(),this.onCursorChange(),this.onScrollTopChange(),this.onScrollLeftChange(),this.onSelectionChange(),this.onChangeFrontMarker(),this.onChangeBackMarker(),this.onChangeBreakpoint(),this.onChangeAnnotation(),this.session.getUseWrapMode()&&this.renderer.adjustWrapLimit(),this.renderer.updateFull()):(this.selection=null,this.renderer.setSession(e)),this._signal("changeSession",{session:e,oldSession:t}),this.curOp=null,t&&t._signal("changeEditor",{oldEditor:this}),e&&e._signal("changeEditor",{editor:this}),e&&e.bgTokenizer&&e.bgTokenizer.scheduleStart()},this.getSession=function(){return this.session},this.setValue=function(e,t){return this.session.doc.setValue(e),t?t==1?this.navigateFileEnd():t==-1&&this.navigateFileStart():this.selectAll(),e},this.getValue=function(){return this.session.getValue()},this.getSelection=function(){return this.selection},this.resize=function(e){this.renderer.onResize(e)},this.setTheme=function(e,t){this.renderer.setTheme(e,t)},this.getTheme=function(){return this.renderer.getTheme()},this.setStyle=function(e){this.renderer.setStyle(e)},this.unsetStyle=function(e){this.renderer.unsetStyle(e)},this.getFontSize=function(){return this.getOption("fontSize")||i.computedStyle(this.container).fontSize},this.setFontSize=function(e){this.setOption("fontSize",e)},this.$highlightBrackets=function(){this.session.$bracketHighlight&&(this.session.removeMarker(this.session.$bracketHighlight),this.session.$bracketHighlight=null);if(this.$highlightPending)return;var e=this;this.$highlightPending=!0,setTimeout(function(){e.$highlightPending=!1;var t=e.session;if(!t||!t.bgTokenizer)return;var n=t.findMatchingBracket(e.getCursorPosition());if(n)var r=new p(n.row,n.column,n.row,n.column+1);else if(t.$mode.getMatching)var r=t.$mode.getMatching(e.session);r&&(t.$bracketHighlight=t.addMarker(r,"ace_bracket","text"))},50)},this.$highlightTags=function(){if(this.$highlightTagPending)return;var e=this;this.$highlightTagPending=!0,setTimeout(function(){e.$highlightTagPending=!1;var t=e.session;if(!t||!t.bgTokenizer)return;var n=e.getCursorPosition(),r=new y(e.session,n.row,n.column),i=r.getCurrentToken();if(!i||!/\b(?:tag-open|tag-name)/.test(i.type)){t.removeMarker(t.$tagHighlight),t.$tagHighlight=null;return}if(i.type.indexOf("tag-open")!=-1){i=r.stepForward();if(!i)return}var s=i.value,o=0,u=r.stepBackward();if(u.value=="<"){do u=i,i=r.stepForward(),i&&i.value===s&&i.type.indexOf("tag-name")!==-1&&(u.value==="<"?o++:u.value==="=0)}else{do i=u,u=r.stepBackward(),i&&i.value===s&&i.type.indexOf("tag-name")!==-1&&(u.value==="<"?o++:u.value==="1)&&(t=!1)}if(e.$highlightLineMarker&&!t)e.removeMarker(e.$highlightLineMarker.id),e.$highlightLineMarker=null;else if(!e.$highlightLineMarker&&t){var n=new p(t.row,t.column,t.row,Infinity);n.id=e.addMarker(n,"ace_active-line","screenLine"),e.$highlightLineMarker=n}else t&&(e.$highlightLineMarker.start.row=t.row,e.$highlightLineMarker.end.row=t.row,e.$highlightLineMarker.start.column=t.column,e._signal("changeBackMarker"))},this.onSelectionChange=function(e){var t=this.session;t.$selectionMarker&&t.removeMarker(t.$selectionMarker),t.$selectionMarker=null;if(!this.selection.isEmpty()){var n=this.selection.getRange(),r=this.getSelectionStyle();t.$selectionMarker=t.addMarker(n,"ace_selection",r)}else this.$updateHighlightActiveLine();var i=this.$highlightSelectedWord&&this.$getSelectionHighLightRegexp();this.session.highlight(i),this._signal("changeSelection")},this.$getSelectionHighLightRegexp=function(){var e=this.session,t=this.getSelectionRange();if(t.isEmpty()||t.isMultiLine())return;var n=t.start.column,r=t.end.column,i=e.getLine(t.start.row),s=i.substring(n,r);if(s.length>5e3||!/[\w\d]/.test(s))return;var o=this.$search.$assembleRegExp({wholeWord:!0,caseSensitive:!0,needle:s}),u=i.substring(n-1,r+1);if(!o.test(u))return;return o},this.onChangeFrontMarker=function(){this.renderer.updateFrontMarkers()},this.onChangeBackMarker=function(){this.renderer.updateBackMarkers()},this.onChangeBreakpoint=function(){this.renderer.updateBreakpoints()},this.onChangeAnnotation=function(){this.renderer.setAnnotations(this.session.getAnnotations())},this.onChangeMode=function(e){this.renderer.updateText(),this._emit("changeMode",e)},this.onChangeWrapLimit=function(){this.renderer.updateFull()},this.onChangeWrapMode=function(){this.renderer.onResize(!0)},this.onChangeFold=function(){this.$updateHighlightActiveLine(),this.renderer.updateFull()},this.getSelectedText=function(){return this.session.getTextRange(this.getSelectionRange())},this.getCopyText=function(){var e=this.getSelectedText(),t=this.session.doc.getNewLineCharacter(),n=!1;if(!e&&this.$copyWithEmptySelection){n=!0;var r=this.selection.getAllRanges();for(var i=0;iu.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);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 h=r.getNextLineIndent(l,u.slice(0,i.column),n.getTabString());n.insert({row:i.row+1,column:0},h)}c&&r.autoOutdent(l,n,i.row)},this.onTextInput=function(e,t){if(!t)return this.keyBinding.onTextInput(e);this.startOperation({command:{name:"insertstring"}});var n=this.applyComposition.bind(this,e,t);this.selection.rangeCount?this.forEachSelection(n):n(),this.endOperation()},this.applyComposition=function(e,t){if(t.extendLeft||t.extendRight){var n=this.selection.getRange();n.start.column-=t.extendLeft,n.end.column+=t.extendRight,this.selection.setRange(n),!e&&!n.isEmpty()&&this.remove()}(e||!this.selection.isEmpty())&&this.insert(e,!0);if(t.restoreStart||t.restoreEnd){var n=this.selection.getRange();n.start.column-=t.restoreStart,n.end.column-=t.restoreEnd,this.selection.setRange(n)}},this.onCommandKey=function(e,t,n){return this.keyBinding.onCommandKey(e,t,n)},this.setOverwrite=function(e){this.session.setOverwrite(e)},this.getOverwrite=function(){return this.session.getOverwrite()},this.toggleOverwrite=function(){this.session.toggleOverwrite()},this.setScrollSpeed=function(e){this.setOption("scrollSpeed",e)},this.getScrollSpeed=function(){return this.getOption("scrollSpeed")},this.setDragDelay=function(e){this.setOption("dragDelay",e)},this.getDragDelay=function(){return this.getOption("dragDelay")},this.setSelectionStyle=function(e){this.setOption("selectionStyle",e)},this.getSelectionStyle=function(){return this.getOption("selectionStyle")},this.setHighlightActiveLine=function(e){this.setOption("highlightActiveLine",e)},this.getHighlightActiveLine=function(){return this.getOption("highlightActiveLine")},this.setHighlightGutterLine=function(e){this.setOption("highlightGutterLine",e)},this.getHighlightGutterLine=function(){return this.getOption("highlightGutterLine")},this.setHighlightSelectedWord=function(e){this.setOption("highlightSelectedWord",e)},this.getHighlightSelectedWord=function(){return this.$highlightSelectedWord},this.setAnimatedScroll=function(e){this.renderer.setAnimatedScroll(e)},this.getAnimatedScroll=function(){return this.renderer.getAnimatedScroll()},this.setShowInvisibles=function(e){this.renderer.setShowInvisibles(e)},this.getShowInvisibles=function(){return this.renderer.getShowInvisibles()},this.setDisplayIndentGuides=function(e){this.renderer.setDisplayIndentGuides(e)},this.getDisplayIndentGuides=function(){return this.renderer.getDisplayIndentGuides()},this.setShowPrintMargin=function(e){this.renderer.setShowPrintMargin(e)},this.getShowPrintMargin=function(){return this.renderer.getShowPrintMargin()},this.setPrintMarginColumn=function(e){this.renderer.setPrintMarginColumn(e)},this.getPrintMarginColumn=function(){return this.renderer.getPrintMarginColumn()},this.setReadOnly=function(e){this.setOption("readOnly",e)},this.getReadOnly=function(){return this.getOption("readOnly")},this.setBehavioursEnabled=function(e){this.setOption("behavioursEnabled",e)},this.getBehavioursEnabled=function(){return this.getOption("behavioursEnabled")},this.setWrapBehavioursEnabled=function(e){this.setOption("wrapBehavioursEnabled",e)},this.getWrapBehavioursEnabled=function(){return this.getOption("wrapBehavioursEnabled")},this.setShowFoldWidgets=function(e){this.setOption("showFoldWidgets",e)},this.getShowFoldWidgets=function(){return this.getOption("showFoldWidgets")},this.setFadeFoldWidgets=function(e){this.setOption("fadeFoldWidgets",e)},this.getFadeFoldWidgets=function(){return this.getOption("fadeFoldWidgets")},this.remove=function(e){this.selection.isEmpty()&&(e=="left"?this.selection.selectLeft():this.selection.selectRight());var t=this.getSelectionRange();if(this.getBehavioursEnabled()){var n=this.session,r=n.getState(t.start.row),i=n.getMode().transformAction(r,"deletion",this,n,t);if(t.end.column===0){var s=n.getTextRange(t);if(s[s.length-1]=="\n"){var o=n.getLine(t.end.row);/^\s+$/.test(o)&&(t.end.column=o.length)}}i&&(t=i)}this.session.remove(t),this.clearSelection()},this.removeWordRight=function(){this.selection.isEmpty()&&this.selection.selectWordRight(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeWordLeft=function(){this.selection.isEmpty()&&this.selection.selectWordLeft(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineStart=function(){this.selection.isEmpty()&&this.selection.selectLineStart(),this.selection.isEmpty()&&this.selection.selectLeft(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineEnd=function(){this.selection.isEmpty()&&this.selection.selectLineEnd();var e=this.getSelectionRange();e.start.column==e.end.column&&e.start.row==e.end.row&&(e.end.column=0,e.end.row++),this.session.remove(e),this.clearSelection()},this.splitLine=function(){this.selection.isEmpty()||(this.session.remove(this.getSelectionRange()),this.clearSelection());var e=this.getCursorPosition();this.insert("\n"),this.moveCursorToPosition(e)},this.transposeLetters=function(){if(!this.selection.isEmpty())return;var e=this.getCursorPosition(),t=e.column;if(t===0)return;var n=this.session.getLine(e.row),r,i;tt.toLowerCase()?1:0});var i=new p(0,0,0,0);for(var r=e.first;r<=e.last;r++){var s=t.getLine(r);i.start.row=r,i.end.row=r,i.end.column=s.length,t.replace(i,n[r-e.first])}},this.toggleCommentLines=function(){var e=this.session.getState(this.getCursorPosition().row),t=this.$getSelectedRows();this.session.getMode().toggleCommentLines(e,this.session,t.first,t.last)},this.toggleBlockComment=function(){var e=this.getCursorPosition(),t=this.session.getState(e.row),n=this.getSelectionRange();this.session.getMode().toggleBlockComment(t,this.session,n,e)},this.getNumberAt=function(e,t){var n=/[\-]?[0-9]+(?:\.[0-9]+)?/g;n.lastIndex=0;var r=this.session.getLine(e);while(n.lastIndex=t){var s={value:i[0],start:i.index,end:i.index+i[0].length};return s}}return null},this.modifyNumber=function(e){var t=this.selection.getCursor().row,n=this.selection.getCursor().column,r=new p(t,n-1,t,n),i=this.session.getTextRange(r);if(!isNaN(parseFloat(i))&&isFinite(i)){var s=this.getNumberAt(t,n);if(s){var o=s.value.indexOf(".")>=0?s.start+s.value.indexOf(".")+1:s.end,u=s.start+s.value.length-o,a=parseFloat(s.value);a*=Math.pow(10,u),o!==s.end&&n=u&&o<=a&&(n=t,f.selection.clearSelection(),f.moveCursorTo(e,u+r),f.selection.selectTo(e,a+r)),u=a});var l=this.$toggleWordPairs,c;for(var h=0;hp+1)break;p=d.last}l--,u=this.session.$moveLines(h,p,t?0:e),t&&e==-1&&(c=l+1);while(c<=l)o[c].moveBy(u,0),c++;t||(u=0),a+=u}i.fromOrientedRange(i.ranges[0]),i.rangeList.attach(this.session),this.inVirtualSelectionMode=!1}},this.$getSelectedRows=function(e){return e=(e||this.getSelectionRange()).collapseRows(),{first:this.session.getRowFoldStart(e.start.row),last:this.session.getRowFoldEnd(e.end.row)}},this.onCompositionStart=function(e){this.renderer.showComposition(e)},this.onCompositionUpdate=function(e){this.renderer.setCompositionText(e)},this.onCompositionEnd=function(){this.renderer.hideComposition()},this.getFirstVisibleRow=function(){return this.renderer.getFirstVisibleRow()},this.getLastVisibleRow=function(){return this.renderer.getLastVisibleRow()},this.isRowVisible=function(e){return e>=this.getFirstVisibleRow()&&e<=this.getLastVisibleRow()},this.isRowFullyVisible=function(e){return e>=this.renderer.getFirstFullyVisibleRow()&&e<=this.renderer.getLastFullyVisibleRow()},this.$getVisibleRowCount=function(){return this.renderer.getScrollBottomRow()-this.renderer.getScrollTopRow()+1},this.$moveByPage=function(e,t){var n=this.renderer,r=this.renderer.layerConfig,i=e*Math.floor(r.height/r.lineHeight);t===!0?this.selection.$moveSelection(function(){this.moveCursorBy(i,0)}):t===!1&&(this.selection.moveCursorBy(i,0),this.selection.clearSelection());var s=n.scrollTop;n.scrollBy(0,i*r.lineHeight),t!=null&&n.scrollCursorIntoView(null,.5),n.animateScrolling(s)},this.selectPageDown=function(){this.$moveByPage(1,!0)},this.selectPageUp=function(){this.$moveByPage(-1,!0)},this.gotoPageDown=function(){this.$moveByPage(1,!1)},this.gotoPageUp=function(){this.$moveByPage(-1,!1)},this.scrollPageDown=function(){this.$moveByPage(1)},this.scrollPageUp=function(){this.$moveByPage(-1)},this.scrollToRow=function(e){this.renderer.scrollToRow(e)},this.scrollToLine=function(e,t,n,r){this.renderer.scrollToLine(e,t,n,r)},this.centerSelection=function(){var e=this.getSelectionRange(),t={row:Math.floor(e.start.row+(e.end.row-e.start.row)/2),column:Math.floor(e.start.column+(e.end.column-e.start.column)/2)};this.renderer.alignCursor(t,.5)},this.getCursorPosition=function(){return this.selection.getCursor()},this.getCursorPositionScreen=function(){return this.session.documentToScreenPosition(this.getCursorPosition())},this.getSelectionRange=function(){return this.selection.getRange()},this.selectAll=function(){this.selection.selectAll()},this.clearSelection=function(){this.selection.clearSelection()},this.moveCursorTo=function(e,t){this.selection.moveCursorTo(e,t)},this.moveCursorToPosition=function(e){this.selection.moveCursorToPosition(e)},this.jumpToMatching=function(e,t){var n=this.getCursorPosition(),r=new y(this.session,n.row,n.column),i=r.getCurrentToken(),s=i||r.stepForward();if(!s)return;var o,u=!1,a={},f=n.column-s.start,l,c={")":"(","(":"(","]":"[","[":"[","{":"{","}":"{"};do{if(s.value.match(/[{}()\[\]]/g))for(;f=0;--s)this.$tryReplace(n[s],e)&&r++;return this.selection.setSelectionRange(i),r},this.$tryReplace=function(e,t){var n=this.session.getTextRange(e);return t=this.$search.replace(n,t),t!==null?(e.end=this.session.replace(e,t),e):null},this.getLastSearchOptions=function(){return this.$search.getOptions()},this.find=function(e,t,n){t||(t={}),typeof e=="string"||e instanceof RegExp?t.needle=e:typeof e=="object"&&r.mixin(t,e);var i=this.selection.getRange();t.needle==null&&(e=this.session.getTextRange(i)||this.$search.$options.needle,e||(i=this.session.getWordRange(i.start.row,i.start.column),e=this.session.getTextRange(i)),this.$search.set({needle:e})),this.$search.set(t),t.start||this.$search.set({start:i});var s=this.$search.find(this.session);if(t.preventScroll)return s;if(s)return this.revealRange(s,n),s;t.backwards?i.start=i.end:i.end=i.start,this.selection.setRange(i)},this.findNext=function(e,t){this.find({skipCurrent:!0,backwards:!1},e,t)},this.findPrevious=function(e,t){this.find(e,{skipCurrent:!0,backwards:!0},t)},this.revealRange=function(e,t){this.session.unfold(e),this.selection.setSelectionRange(e);var n=this.renderer.scrollTop;this.renderer.scrollSelectionIntoView(e.start,e.end,.5),t!==!1&&this.renderer.animateScrolling(n)},this.undo=function(){this.session.getUndoManager().undo(this.session),this.renderer.scrollCursorIntoView(null,.5)},this.redo=function(){this.session.getUndoManager().redo(this.session),this.renderer.scrollCursorIntoView(null,.5)},this.destroy=function(){this.renderer.destroy(),this._signal("destroy",this),this.session&&this.session.destroy()},this.setAutoScrollEditorIntoView=function(e){if(!e)return;var t,n=this,r=!1;this.$scrollAnchor||(this.$scrollAnchor=document.createElement("div"));var i=this.$scrollAnchor;i.style.cssText="position:absolute",this.container.insertBefore(i,this.container.firstChild);var s=this.on("changeSelection",function(){r=!0}),o=this.renderer.on("beforeRender",function(){r&&(t=n.renderer.container.getBoundingClientRect())}),u=this.renderer.on("afterRender",function(){if(r&&t&&(n.isFocused()||n.searchBox&&n.searchBox.isFocused())){var e=n.renderer,s=e.$cursorLayer.$pixelPos,o=e.layerConfig,u=s.top-o.offset;s.top>=0&&u+t.top<0?r=!0:s.topwindow.innerHeight?r=!1:r=null,r!=null&&(i.style.top=u+"px",i.style.left=s.left+"px",i.style.height=o.lineHeight+"px",i.scrollIntoView(r)),r=t=null}});this.setAutoScrollEditorIntoView=function(e){if(e)return;delete this.setAutoScrollEditorIntoView,this.off("changeSelection",s),this.renderer.off("afterRender",u),this.renderer.off("beforeRender",o)}},this.$resetCursorStyle=function(){var e=this.$cursorStyle||"ace",t=this.renderer.$cursorLayer;if(!t)return;t.setSmoothBlinking(/smooth/.test(e)),t.isBlinking=!this.$readOnly&&e!="wide",i.setCssClass(t.element,"ace_slim-cursors",/slim/.test(e))},this.prompt=function(e,t,n){var r=this;g.loadModule("./ext/prompt",function(i){i.prompt(r,e,t,n)})}}.call(w.prototype),g.defineOptions(w.prototype,"editor",{selectionStyle:{set:function(e){this.onSelectionChange(),this._signal("changeSelectionStyle",{data:e})},initialValue:"line"},highlightActiveLine:{set:function(){this.$updateHighlightActiveLine()},initialValue:!0},highlightSelectedWord:{set:function(e){this.$onSelectionChange()},initialValue:!0},readOnly:{set:function(e){this.textInput.setReadOnly(e),this.$resetCursorStyle()},initialValue:!1},copyWithEmptySelection:{set:function(e){this.textInput.setCopyWithEmptySelection(e)},initialValue:!1},cursorStyle:{set:function(e){this.$resetCursorStyle()},values:["ace","slim","smooth","wide"],initialValue:"ace"},mergeUndoDeltas:{values:[!1,!0,"always"],initialValue:!0},behavioursEnabled:{initialValue:!0},wrapBehavioursEnabled:{initialValue:!0},autoScrollEditorIntoView:{set:function(e){this.setAutoScrollEditorIntoView(e)}},keyboardHandler:{set:function(e){this.setKeyboardHandler(e)},get:function(){return this.$keybindingId},handlesSet:!0},value:{set:function(e){this.session.setValue(e)},get:function(){return this.getValue()},handlesSet:!0,hidden:!0},session:{set:function(e){this.setSession(e)},get:function(){return this.session},handlesSet:!0,hidden:!0},showLineNumbers:{set:function(e){this.renderer.$gutterLayer.setShowLineNumbers(e),this.renderer.$loop.schedule(this.renderer.CHANGE_GUTTER),e&&this.$relativeLineNumbers?E.attach(this):E.detach(this)},initialValue:!0},relativeLineNumbers:{set:function(e){this.$showLineNumbers&&e?E.attach(this):E.detach(this)}},hScrollBarAlwaysVisible:"renderer",vScrollBarAlwaysVisible:"renderer",highlightGutterLine:"renderer",animatedScroll:"renderer",showInvisibles:"renderer",showPrintMargin:"renderer",printMarginColumn:"renderer",printMargin:"renderer",fadeFoldWidgets:"renderer",showFoldWidgets:"renderer",displayIndentGuides:"renderer",showGutter:"renderer",fontSize:"renderer",fontFamily:"renderer",maxLines:"renderer",minLines:"renderer",scrollPastEnd:"renderer",fixedWidthGutter:"renderer",theme:"renderer",hasCssTransforms:"renderer",maxPixelHeight:"renderer",useTextareaForIME:"renderer",scrollSpeed:"$mouseHandler",dragDelay:"$mouseHandler",dragEnabled:"$mouseHandler",focusTimeout:"$mouseHandler",tooltipFollowsMouse:"$mouseHandler",firstLineNumber:"session",overwrite:"session",newLineMode:"session",useWorker:"session",useSoftTabs:"session",navigateWithinSoftTabs:"session",tabSize:"session",wrap:"session",indentedSoftWrap:"session",foldStyle:"session",mode:"session"});var E={getText:function(e,t){return(Math.abs(e.selection.lead.row-t)||t+1+(t<9?"\u00b7":""))+""},getWidth:function(e,t,n){return Math.max(t.toString().length,(n.lastRow+1).toString().length,2)*n.characterWidth},update:function(e,t){t.renderer.$loop.schedule(t.renderer.CHANGE_GUTTER)},attach:function(e){e.renderer.$gutterLayer.$renderer=this,e.on("changeSelection",this.update),this.update(null,e)},detach:function(e){e.renderer.$gutterLayer.$renderer==this&&(e.renderer.$gutterLayer.$renderer=null),e.off("changeSelection",this.update),this.update(null,e)}};t.Editor=w}),ace.define("ace/undomanager",["require","exports","module","ace/range"],function(e,t,n){"use strict";function i(e,t){for(var n=t;n--;){var r=e[n];if(r&&!r[0].ignore){while(n0){a.row+=i,a.column+=a.row==r.row?s:0;continue}!t&&l<=0&&(a.row=n.row,a.column=n.column,l===0&&(a.bias=1))}}function f(e){return{row:e.row,column:e.column}}function l(e){return{start:f(e.start),end:f(e.end),action:e.action,lines:e.lines.slice()}}function c(e){e=e||this;if(Array.isArray(e))return e.map(c).join("\n");var t="";e.action?(t=e.action=="insert"?"+":"-",t+="["+e.lines+"]"):e.value&&(Array.isArray(e.value)?t=e.value.map(h).join("\n"):t=h(e.value)),e.start&&(t+=h(e));if(e.id||e.rev)t+=" ("+(e.id||e.rev)+")";return t}function h(e){return e.start.row+":"+e.start.column+"=>"+e.end.row+":"+e.end.column}function p(e,t){var n=e.action=="insert",r=t.action=="insert";if(n&&r)if(o(t.start,e.end)>=0)m(t,e,-1);else{if(!(o(t.start,e.start)<=0))return null;m(e,t,1)}else if(n&&!r)if(o(t.start,e.end)>=0)m(t,e,-1);else{if(!(o(t.end,e.start)<=0))return null;m(e,t,-1)}else if(!n&&r)if(o(t.start,e.start)>=0)m(t,e,1);else{if(!(o(t.start,e.start)<=0))return null;m(e,t,1)}else if(!n&&!r)if(o(t.start,e.start)>=0)m(t,e,1);else{if(!(o(t.end,e.start)<=0))return null;m(e,t,-1)}return[t,e]}function d(e,t){for(var n=e.length;n--;)for(var r=0;r=0?m(e,t,-1):o(e.start,t.start)<=0?m(t,e,1):(m(e,s.fromPoints(t.start,e.start),-1),m(t,e,1));else if(!n&&r)o(t.start,e.end)>=0?m(t,e,-1):o(t.start,e.start)<=0?m(e,t,1):(m(t,s.fromPoints(e.start,t.start),-1),m(e,t,1));else if(!n&&!r)if(o(t.start,e.end)>=0)m(t,e,-1);else{if(!(o(t.end,e.start)<=0)){var i,u;return o(e.start,t.start)<0&&(i=e,e=y(e,t.start)),o(e.end,t.end)>0&&(u=y(e,t.end)),g(t.end,e.start,e.end,-1),u&&!i&&(e.lines=u.lines,e.start=u.start,e.end=u.end,u=e),[t,i,u].filter(Boolean)}m(e,t,-1)}return[t,e]}function m(e,t,n){g(e.start,t.start,t.end,n),g(e.end,t.start,t.end,n)}function g(e,t,n,r){e.row==(r==1?t:n).row&&(e.column+=r*(n.column-t.column)),e.row+=r*(n.row-t.row)}function y(e,t){var n=e.lines,r=e.end;e.end=f(t);var i=e.end.row-e.start.row,s=n.splice(i,n.length),o=i?t.column:t.column-e.start.column;n.push(s[0].substring(0,o)),s[0]=s[0].substr(o);var u={start:f(t),end:r,lines:s,action:e.action};return u}function b(e,t){t=l(t);for(var n=e.length;n--;){var r=e[n];for(var i=0;i0},this.canRedo=function(){return this.$redoStack.length>0},this.bookmark=function(e){e==undefined&&(e=this.$rev),this.mark=e},this.isAtBookmark=function(){return this.$rev===this.mark},this.toJSON=function(){},this.fromJSON=function(){},this.hasUndo=this.canUndo,this.hasRedo=this.canRedo,this.isClean=this.isAtBookmark,this.markClean=this.bookmark,this.$prettyPrint=function(e){return e?c(e):c(this.$undoStack)+"\n---\n"+c(this.$redoStack)}}).call(r.prototype);var s=e("./range").Range,o=s.comparePoints,u=s.comparePoints;t.UndoManager=r}),ace.define("ace/layer/lines",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";var r=e("../lib/dom"),i=function(e,t){this.element=e,this.canvasHeight=t||5e5,this.element.style.height=this.canvasHeight*2+"px",this.cells=[],this.cellCache=[],this.$offsetCoefficient=0};(function(){this.moveContainer=function(e){r.translate(this.element,0,-(e.firstRowScreen*e.lineHeight%this.canvasHeight)-e.offset*this.$offsetCoefficient)},this.pageChanged=function(e,t){return Math.floor(e.firstRowScreen*e.lineHeight/this.canvasHeight)!==Math.floor(t.firstRowScreen*t.lineHeight/this.canvasHeight)},this.computeLineTop=function(e,t,n){var r=t.firstRowScreen*t.lineHeight,i=Math.floor(r/this.canvasHeight),s=n.documentToScreenRow(e,0)*t.lineHeight;return s-i*this.canvasHeight},this.computeLineHeight=function(e,t,n){return t.lineHeight*n.getRowLength(e)},this.getLength=function(){return this.cells.length},this.get=function(e){return this.cells[e]},this.shift=function(){this.$cacheCell(this.cells.shift())},this.pop=function(){this.$cacheCell(this.cells.pop())},this.push=function(e){if(Array.isArray(e)){this.cells.push.apply(this.cells,e);var t=r.createFragment(this.element);for(var n=0;ns&&(a=i.end.row+1,i=t.getNextFoldLine(a,i),s=i?i.start.row:Infinity);if(a>r){while(this.$lines.getLength()>u+1)this.$lines.pop();break}o=this.$lines.get(++u),o?o.row=a:(o=this.$lines.createCell(a,e,this.session,f),this.$lines.push(o)),this.$renderCell(o,e,i,a),a++}this._signal("afterRender"),this.$updateGutterWidth(e)},this.$updateGutterWidth=function(e){var t=this.session,n=t.gutterRenderer||this.$renderer,r=t.$firstLineNumber,i=this.$lines.last()?this.$lines.last().text:"";if(this.$fixedWidth||t.$useWrapMode)i=t.getLength()+r-1;var s=n?n.getWidth(t,i,e):i.toString().length*e.characterWidth,o=this.$padding||this.$computePadding();s+=o.left+o.right,s!==this.gutterWidth&&!isNaN(s)&&(this.gutterWidth=s,this.element.parentNode.style.width=this.element.style.width=Math.ceil(this.gutterWidth)+"px",this._signal("changeGutterWidth",s))},this.$updateCursorRow=function(){if(!this.$highlightGutterLine)return;var e=this.session.selection.getCursor();if(this.$cursorRow===e.row)return;this.$cursorRow=e.row},this.updateLineHighlight=function(){if(!this.$highlightGutterLine)return;var e=this.session.selection.cursor.row;this.$cursorRow=e;if(this.$cursorCell&&this.$cursorCell.row==e)return;this.$cursorCell&&(this.$cursorCell.element.className=this.$cursorCell.element.className.replace("ace_gutter-active-line ",""));var t=this.$lines.cells;this.$cursorCell=null;for(var n=0;n=this.$cursorRow){if(r.row>this.$cursorRow){var i=this.session.getFoldLine(this.$cursorRow);if(!(n>0&&i&&i.start.row==t[n-1].row))break;r=t[n-1]}r.element.className="ace_gutter-active-line "+r.element.className,this.$cursorCell=r;break}}},this.scrollLines=function(e){var t=this.config;this.config=e,this.$updateCursorRow();if(this.$lines.pageChanged(t,e))return this.update(e);this.$lines.moveContainer(e);var n=Math.min(e.lastRow+e.gutterOffset,this.session.getLength()-1),r=this.oldLastRow;this.oldLastRow=n;if(!t||r0;i--)this.$lines.shift();if(r>n)for(var i=this.session.getFoldedRowCount(n+1,r);i>0;i--)this.$lines.pop();e.firstRowr&&this.$lines.push(this.$renderLines(e,r+1,n)),this.updateLineHighlight(),this._signal("afterRender"),this.$updateGutterWidth(e)},this.$renderLines=function(e,t,n){var r=[],i=t,s=this.session.getNextFoldLine(i),o=s?s.start.row:Infinity;for(;;){i>o&&(i=s.end.row+1,s=this.session.getNextFoldLine(i,s),o=s?s.start.row:Infinity);if(i>n)break;var u=this.$lines.createCell(i,e,this.session,f);this.$renderCell(u,e,s,i),r.push(u),i++}return r},this.$renderCell=function(e,t,n,i){var s=e.element,o=this.session,u=s.childNodes[0],a=s.childNodes[1],f=o.$firstLineNumber,l=o.$breakpoints,c=o.$decorations,h=o.gutterRenderer||this.$renderer,p=this.$showFoldWidgets&&o.foldWidgets,d=n?n.start.row:Number.MAX_VALUE,v="ace_gutter-cell ";this.$highlightGutterLine&&(i==this.$cursorRow||n&&i=d&&this.$cursorRow<=n.end.row)&&(v+="ace_gutter-active-line ",this.$cursorCell!=e&&(this.$cursorCell&&(this.$cursorCell.element.className=this.$cursorCell.element.className.replace("ace_gutter-active-line ","")),this.$cursorCell=e)),l[i]&&(v+=l[i]),c[i]&&(v+=c[i]),this.$annotations[i]&&(v+=this.$annotations[i].className),s.className!=v&&(s.className=v);if(p){var m=p[i];m==null&&(m=p[i]=o.getFoldWidget(i))}if(m){var v="ace_fold-widget ace_"+m;m=="start"&&i==d&&in.right-t.right)return"foldWidgets"}}).call(a.prototype),t.Gutter=a}),ace.define("ace/layer/marker",["require","exports","module","ace/range","ace/lib/dom"],function(e,t,n){"use strict";var r=e("../range").Range,i=e("../lib/dom"),s=function(e){this.element=i.createElement("div"),this.element.className="ace_layer ace_marker-layer",e.appendChild(this.element)};(function(){function e(e,t,n,r){return(e?1:0)|(t?2:0)|(n?4:0)|(r?8:0)}this.$padding=0,this.setPadding=function(e){this.$padding=e},this.setSession=function(e){this.session=e},this.setMarkers=function(e){this.markers=e},this.elt=function(e,t){var n=this.i!=-1&&this.element.childNodes[this.i];n?this.i++:(n=document.createElement("div"),this.element.appendChild(n),this.i=-1),n.style.cssText=t,n.className=e},this.update=function(e){if(!e)return;this.config=e,this.i=0;var t;for(var n in this.markers){var r=this.markers[n];if(!r.range){r.update(t,this,this.session,e);continue}var i=r.range.clipRows(e.firstRow,e.lastRow);if(i.isEmpty())continue;i=i.toScreenRange(this.session);if(r.renderer){var s=this.$getTop(i.start.row,e),o=this.$padding+i.start.column*e.characterWidth;r.renderer(t,i,o,s,e)}else r.type=="fullLine"?this.drawFullLineMarker(t,i,r.clazz,e):r.type=="screenLine"?this.drawScreenLineMarker(t,i,r.clazz,e):i.isMultiLine()?r.type=="text"?this.drawTextMarker(t,i,r.clazz,e):this.drawMultiLineMarker(t,i,r.clazz,e):this.drawSingleLineMarker(t,i,r.clazz+" ace_start"+" ace_br15",e)}if(this.i!=-1)while(this.ip,l==f),s,l==f?0:1,o)},this.drawMultiLineMarker=function(e,t,n,r,i){var s=this.$padding,o=r.lineHeight,u=this.$getTop(t.start.row,r),a=s+t.start.column*r.characterWidth;i=i||"";if(this.session.$bidiHandler.isBidiRow(t.start.row)){var f=t.clone();f.end.row=f.start.row,f.end.column=this.session.getLine(f.start.row).length,this.drawBidiSingleLineMarker(e,f,n+" ace_br1 ace_start",r,null,i)}else this.elt(n+" ace_br1 ace_start","height:"+o+"px;"+"right:0;"+"top:"+u+"px;left:"+a+"px;"+(i||""));if(this.session.$bidiHandler.isBidiRow(t.end.row)){var f=t.clone();f.start.row=f.end.row,f.start.column=0,this.drawBidiSingleLineMarker(e,f,n+" ace_br12",r,null,i)}else{u=this.$getTop(t.end.row,r);var l=t.end.column*r.characterWidth;this.elt(n+" ace_br12","height:"+o+"px;"+"width:"+l+"px;"+"top:"+u+"px;"+"left:"+s+"px;"+(i||""))}o=(t.end.row-t.start.row-1)*r.lineHeight;if(o<=0)return;u=this.$getTop(t.start.row+1,r);var c=(t.start.column?1:0)|(t.end.column?0:8);this.elt(n+(c?" ace_br"+c:""),"height:"+o+"px;"+"right:0;"+"top:"+u+"px;"+"left:"+s+"px;"+(i||""))},this.drawSingleLineMarker=function(e,t,n,r,i,s){if(this.session.$bidiHandler.isBidiRow(t.start.row))return this.drawBidiSingleLineMarker(e,t,n,r,i,s);var o=r.lineHeight,u=(t.end.column+(i||0)-t.start.column)*r.characterWidth,a=this.$getTop(t.start.row,r),f=this.$padding+t.start.column*r.characterWidth;this.elt(n,"height:"+o+"px;"+"width:"+u+"px;"+"top:"+a+"px;"+"left:"+f+"px;"+(s||""))},this.drawBidiSingleLineMarker=function(e,t,n,r,i,s){var o=r.lineHeight,u=this.$getTop(t.start.row,r),a=this.$padding,f=this.session.$bidiHandler.getSelections(t.start.column,t.end.column);f.forEach(function(e){this.elt(n,"height:"+o+"px;"+"width:"+e.width+(i||0)+"px;"+"top:"+u+"px;"+"left:"+(a+e.left)+"px;"+(s||""))},this)},this.drawFullLineMarker=function(e,t,n,r,i){var s=this.$getTop(t.start.row,r),o=r.lineHeight;t.start.row!=t.end.row&&(o+=this.$getTop(t.end.row,r)-s),this.elt(n,"height:"+o+"px;"+"top:"+s+"px;"+"left:0;right:0;"+(i||""))},this.drawScreenLineMarker=function(e,t,n,r,i){var s=this.$getTop(t.start.row,r),o=r.lineHeight;this.elt(n,"height:"+o+"px;"+"top:"+s+"px;"+"left:0;right:0;"+(i||""))}}).call(s.prototype),t.Marker=s}),ace.define("ace/layer/text",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/layer/lines","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/dom"),s=e("../lib/lang"),o=e("./lines").Lines,u=e("../lib/event_emitter").EventEmitter,a=function(e){this.dom=i,this.element=this.dom.createElement("div"),this.element.className="ace_layer ace_text-layer",e.appendChild(this.element),this.$updateEolChar=this.$updateEolChar.bind(this),this.$lines=new o(this.element)};(function(){r.implement(this,u),this.EOF_CHAR="\u00b6",this.EOL_CHAR_LF="\u00ac",this.EOL_CHAR_CRLF="\u00a4",this.EOL_CHAR=this.EOL_CHAR_LF,this.TAB_CHAR="\u2014",this.SPACE_CHAR="\u00b7",this.$padding=0,this.MAX_LINE_LENGTH=1e4,this.$updateEolChar=function(){var e=this.session.doc,t=e.getNewLineCharacter()=="\n"&&e.getNewLineMode()!="windows",n=t?this.EOL_CHAR_LF:this.EOL_CHAR_CRLF;if(this.EOL_CHAR!=n)return this.EOL_CHAR=n,!0},this.setPadding=function(e){this.$padding=e,this.element.style.margin="0 "+e+"px"},this.getLineHeight=function(){return this.$fontMetrics.$characterSize.height||0},this.getCharacterWidth=function(){return this.$fontMetrics.$characterSize.width||0},this.$setFontMetrics=function(e){this.$fontMetrics=e,this.$fontMetrics.on("changeCharacterSize",function(e){this._signal("changeCharacterSize",e)}.bind(this)),this.$pollSizeChanges()},this.checkForSizeChanges=function(){this.$fontMetrics.checkForSizeChanges()},this.$pollSizeChanges=function(){return this.$pollSizeChangesTimer=this.$fontMetrics.$pollSizeChanges()},this.setSession=function(e){this.session=e,e&&this.$computeTabString()},this.showInvisibles=!1,this.setShowInvisibles=function(e){return this.showInvisibles==e?!1:(this.showInvisibles=e,this.$computeTabString(),!0)},this.displayIndentGuides=!0,this.setDisplayIndentGuides=function(e){return this.displayIndentGuides==e?!1:(this.displayIndentGuides=e,this.$computeTabString(),!0)},this.$tabStrings=[],this.onChangeTabSize=this.$computeTabString=function(){var e=this.session.getTabSize();this.tabSize=e;var t=this.$tabStrings=[0];for(var n=1;nl&&(u=a.end.row+1,a=this.session.getNextFoldLine(u,a),l=a?a.start.row:Infinity);if(u>i)break;var c=s[o++];if(c){this.dom.removeChildren(c),this.$renderLine(c,u,u==l?a:!1),f&&(c.style.top=this.$lines.computeLineTop(u,e,this.session)+"px");var h=e.lineHeight*this.session.getRowLength(u)+"px";c.style.height!=h&&(f=!0,c.style.height=h)}u++}if(f)while(o0;i--)this.$lines.shift();if(t.lastRow>e.lastRow)for(var i=this.session.getFoldedRowCount(e.lastRow+1,t.lastRow);i>0;i--)this.$lines.pop();e.firstRowt.lastRow&&this.$lines.push(this.$renderLinesFragment(e,t.lastRow+1,e.lastRow))},this.$renderLinesFragment=function(e,t,n){var r=[],s=t,o=this.session.getNextFoldLine(s),u=o?o.start.row:Infinity;for(;;){s>u&&(s=o.end.row+1,o=this.session.getNextFoldLine(s,o),u=o?o.start.row:Infinity);if(s>n)break;var a=this.$lines.createCell(s,e,this.session),f=a.element;this.dom.removeChildren(f),i.setStyle(f.style,"height",this.$lines.computeLineHeight(s,e,this.session)+"px"),i.setStyle(f.style,"top",this.$lines.computeLineTop(s,e,this.session)+"px"),this.$renderLine(f,s,s==u?o:!1),this.$useLineGroups()?f.className="ace_line_group":f.className="ace_line",r.push(a),s++}return r},this.update=function(e){this.$lines.moveContainer(e),this.config=e;var t=e.firstRow,n=e.lastRow,r=this.$lines;while(r.getLength())r.pop();r.push(this.$renderLinesFragment(e,t,n))},this.$textToken={text:!0,rparen:!0,lparen:!0},this.$renderToken=function(e,t,n,r){var i=this,o=/(\t)|( +)|([\x00-\x1f\x80-\xa0\xad\u1680\u180E\u2000-\u200f\u2028\u2029\u202F\u205F\uFEFF\uFFF9-\uFFFC]+)|(\u3000)|([\u1100-\u115F\u11A3-\u11A7\u11FA-\u11FF\u2329-\u232A\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3001-\u303E\u3041-\u3096\u3099-\u30FF\u3105-\u312D\u3131-\u318E\u3190-\u31BA\u31C0-\u31E3\u31F0-\u321E\u3220-\u3247\u3250-\u32FE\u3300-\u4DBF\u4E00-\uA48C\uA490-\uA4C6\uA960-\uA97C\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFAFF\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE66\uFE68-\uFE6B\uFF01-\uFF60\uFFE0-\uFFE6]|[\uD800-\uDBFF][\uDC00-\uDFFF])/g,u=this.dom.createFragment(this.element),a,f=0;while(a=o.exec(r)){var l=a[1],c=a[2],h=a[3],p=a[4],d=a[5];if(!i.showInvisibles&&c)continue;var v=f!=a.index?r.slice(f,a.index):"";f=a.index+a[0].length,v&&u.appendChild(this.dom.createTextNode(v,this.element));if(l){var m=i.session.getScreenTabSize(t+a.index);u.appendChild(i.$tabStrings[m].cloneNode(!0)),t+=m-1}else if(c)if(i.showInvisibles){var g=this.dom.createElement("span");g.className="ace_invisible ace_invisible_space",g.textContent=s.stringRepeat(i.SPACE_CHAR,c.length),u.appendChild(g)}else u.appendChild(this.com.createTextNode(c,this.element));else if(h){var g=this.dom.createElement("span");g.className="ace_invisible ace_invisible_space ace_invalid",g.textContent=s.stringRepeat(i.SPACE_CHAR,h.length),u.appendChild(g)}else if(p){t+=1;var g=this.dom.createElement("span");g.style.width=i.config.characterWidth*2+"px",g.className=i.showInvisibles?"ace_cjk ace_invisible ace_invisible_space":"ace_cjk",g.textContent=i.showInvisibles?i.SPACE_CHAR:p,u.appendChild(g)}else if(d){t+=1;var g=this.dom.createElement("span");g.style.width=i.config.characterWidth*2+"px",g.className="ace_cjk",g.textContent=d,u.appendChild(g)}}u.appendChild(this.dom.createTextNode(f?r.slice(f):r,this.element));if(!this.$textToken[n.type]){var y="ace_"+n.type.replace(/\./g," ace_"),g=this.dom.createElement("span");n.type=="fold"&&(g.style.width=n.value.length*this.config.characterWidth+"px"),g.className=y,g.appendChild(u),e.appendChild(g)}else e.appendChild(u);return t+r.length},this.renderIndentGuide=function(e,t,n){var r=t.search(this.$indentGuideRe);if(r<=0||r>=n)return t;if(t[0]==" "){r-=r%this.tabSize;var i=r/this.tabSize;for(var s=0;s=o)u=this.$renderToken(a,u,l,c.substring(0,o-r)),c=c.substring(o-r),r=o,a=this.$createLineElement(),e.appendChild(a),a.appendChild(this.dom.createTextNode(s.stringRepeat("\u00a0",n.indent),this.element)),i++,u=0,o=n[i]||Number.MAX_VALUE;c.length!=0&&(r+=c.length,u=this.$renderToken(a,u,l,c))}}n[n.length-1]>this.MAX_LINE_LENGTH&&this.$renderOverflowMessage(a,u,null,"",!0)},this.$renderSimpleLine=function(e,t){var n=0,r=t[0],i=r.value;this.displayIndentGuides&&(i=this.renderIndentGuide(e,i)),i&&(n=this.$renderToken(e,n,r,i));for(var s=1;sthis.MAX_LINE_LENGTH)return this.$renderOverflowMessage(e,n,r,i);n=this.$renderToken(e,n,r,i)}},this.$renderOverflowMessage=function(e,t,n,r,i){n&&this.$renderToken(e,t,n,r.slice(0,this.MAX_LINE_LENGTH-t));var s=this.dom.createElement("span");s.className="ace_inline_button ace_keyword ace_toggle_wrap",s.textContent=i?"":"",e.appendChild(s)},this.$renderLine=function(e,t,n){!n&&n!=0&&(n=this.session.getFoldLine(t));if(n)var r=this.$getFoldLineTokens(t,n);else var r=this.session.getTokens(t);var i=e;if(r.length){var s=this.session.getRowSplitData(t);if(s&&s.length){this.$renderWrappedLine(e,r,s);var i=e.lastChild}else{var i=e;this.$useLineGroups()&&(i=this.$createLineElement(),e.appendChild(i)),this.$renderSimpleLine(i,r)}}else this.$useLineGroups()&&(i=this.$createLineElement(),e.appendChild(i));if(this.showInvisibles&&i){n&&(t=n.end.row);var o=this.dom.createElement("span");o.className="ace_invisible ace_invisible_eol",o.textContent=t==this.session.getLength()-1?this.EOF_CHAR:this.EOL_CHAR,i.appendChild(o)}},this.$getFoldLineTokens=function(e,t){function i(e,t,n){var i=0,s=0;while(s+e[i].value.lengthn-t&&(o=o.substring(0,n-t)),r.push({type:e[i].type,value:o}),s=t+o.length,i+=1}while(sn?r.push({type:e[i].type,value:o.substring(0,n-s)}):r.push(e[i]),s+=o.length,i+=1}}var n=this.session,r=[],s=n.getTokens(e);return t.walk(function(e,t,o,u,a){e!=null?r.push({type:"fold",value:e}):(a&&(s=n.getTokens(t)),s.length&&i(s,u,o))},t.end.row,this.session.getLine(t.end.row).length),r},this.$useLineGroups=function(){return this.session.getUseWrapMode()},this.destroy=function(){}}).call(a.prototype),t.Text=a}),ace.define("ace/layer/cursor",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";var r=e("../lib/dom"),i=function(e){this.element=r.createElement("div"),this.element.className="ace_layer ace_cursor-layer",e.appendChild(this.element),this.isVisible=!1,this.isBlinking=!0,this.blinkInterval=1e3,this.smoothBlinking=!1,this.cursors=[],this.cursor=this.addCursor(),r.addCssClass(this.element,"ace_hidden-cursors"),this.$updateCursors=this.$updateOpacity.bind(this)};(function(){this.$updateOpacity=function(e){var t=this.cursors;for(var n=t.length;n--;)r.setStyle(t[n].style,"opacity",e?"":"0")},this.$startCssAnimation=function(){var e=this.cursors;for(var t=e.length;t--;)e[t].style.animationDuration=this.blinkInterval+"ms";setTimeout(function(){r.addCssClass(this.element,"ace_animate-blinking")}.bind(this))},this.$stopCssAnimation=function(){r.removeCssClass(this.element,"ace_animate-blinking")},this.$padding=0,this.setPadding=function(e){this.$padding=e},this.setSession=function(e){this.session=e},this.setBlinking=function(e){e!=this.isBlinking&&(this.isBlinking=e,this.restartTimer())},this.setBlinkInterval=function(e){e!=this.blinkInterval&&(this.blinkInterval=e,this.restartTimer())},this.setSmoothBlinking=function(e){e!=this.smoothBlinking&&(this.smoothBlinking=e,r.setCssClass(this.element,"ace_smooth-blinking",e),this.$updateCursors(!0),this.restartTimer())},this.addCursor=function(){var e=r.createElement("div");return e.className="ace_cursor",this.element.appendChild(e),this.cursors.push(e),e},this.removeCursor=function(){if(this.cursors.length>1){var e=this.cursors.pop();return e.parentNode.removeChild(e),e}},this.hideCursor=function(){this.isVisible=!1,r.addCssClass(this.element,"ace_hidden-cursors"),this.restartTimer()},this.showCursor=function(){this.isVisible=!0,r.removeCssClass(this.element,"ace_hidden-cursors"),this.restartTimer()},this.restartTimer=function(){var e=this.$updateCursors;clearInterval(this.intervalId),clearTimeout(this.timeoutId),this.$stopCssAnimation(),this.smoothBlinking&&r.removeCssClass(this.element,"ace_smooth-blinking"),e(!0);if(!this.isBlinking||!this.blinkInterval||!this.isVisible){this.$stopCssAnimation();return}this.smoothBlinking&&setTimeout(function(){r.addCssClass(this.element,"ace_smooth-blinking")}.bind(this));if(r.HAS_CSS_ANIMATION)this.$startCssAnimation();else{var t=function(){this.timeoutId=setTimeout(function(){e(!1)},.6*this.blinkInterval)}.bind(this);this.intervalId=setInterval(function(){e(!0),t()},this.blinkInterval),t()}},this.getPixelPosition=function(e,t){if(!this.config||!this.session)return{left:0,top:0};e||(e=this.session.selection.getCursor());var n=this.session.documentToScreenPosition(e),r=this.$padding+(this.session.$bidiHandler.isBidiRow(n.row,e.row)?this.session.$bidiHandler.getPosLeft(n.column):n.column*this.config.characterWidth),i=(n.row-(t?this.config.firstRowScreen:0))*this.config.lineHeight;return{left:r,top:i}},this.isCursorInView=function(e,t){return e.top>=0&&e.tope.height+e.offset||o.top<0)&&n>1)continue;var u=this.cursors[i++]||this.addCursor(),a=u.style;this.drawCursor?this.drawCursor(u,o,e,t[n],this.session):this.isCursorInView(o,e)?(r.setStyle(a,"display","block"),r.translate(u,o.left,o.top),r.setStyle(a,"width",Math.round(e.characterWidth)+"px"),r.setStyle(a,"height",e.lineHeight+"px")):r.setStyle(a,"display","none")}while(this.cursors.length>i)this.removeCursor();var f=this.session.getOverwrite();this.$setOverwrite(f),this.$pixelPos=o,this.restartTimer()},this.drawCursor=null,this.$setOverwrite=function(e){e!=this.overwrite&&(this.overwrite=e,e?r.addCssClass(this.element,"ace_overwrite-cursors"):r.removeCssClass(this.element,"ace_overwrite-cursors"))},this.destroy=function(){clearInterval(this.intervalId),clearTimeout(this.timeoutId)}}).call(i.prototype),t.Cursor=i}),ace.define("ace/scrollbar",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/event","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./lib/event"),o=e("./lib/event_emitter").EventEmitter,u=32768,a=function(e){this.element=i.createElement("div"),this.element.className="ace_scrollbar ace_scrollbar"+this.classSuffix,this.inner=i.createElement("div"),this.inner.className="ace_scrollbar-inner",this.inner.textContent="\u00a0",this.element.appendChild(this.inner),e.appendChild(this.element),this.setVisible(!1),this.skipEvent=!1,s.addListener(this.element,"scroll",this.onScroll.bind(this)),s.addListener(this.element,"mousedown",s.preventDefault)};(function(){r.implement(this,o),this.setVisible=function(e){this.element.style.display=e?"":"none",this.isVisible=e,this.coeff=1}}).call(a.prototype);var f=function(e,t){a.call(this,e),this.scrollTop=0,this.scrollHeight=0,t.$scrollbarWidth=this.width=i.scrollbarWidth(e.ownerDocument),this.inner.style.width=this.element.style.width=(this.width||15)+5+"px",this.$minWidth=0};r.inherits(f,a),function(){this.classSuffix="-v",this.onScroll=function(){if(!this.skipEvent){this.scrollTop=this.element.scrollTop;if(this.coeff!=1){var e=this.element.clientHeight/this.scrollHeight;this.scrollTop=this.scrollTop*(1-e)/(this.coeff-e)}this._emit("scroll",{data:this.scrollTop})}this.skipEvent=!1},this.getWidth=function(){return Math.max(this.isVisible?this.width:0,this.$minWidth||0)},this.setHeight=function(e){this.element.style.height=e+"px"},this.setInnerHeight=this.setScrollHeight=function(e){this.scrollHeight=e,e>u?(this.coeff=u/e,e=u):this.coeff!=1&&(this.coeff=1),this.inner.style.height=e+"px"},this.setScrollTop=function(e){this.scrollTop!=e&&(this.skipEvent=!0,this.scrollTop=e,this.element.scrollTop=e*this.coeff)}}.call(f.prototype);var l=function(e,t){a.call(this,e),this.scrollLeft=0,this.height=t.$scrollbarWidth,this.inner.style.height=this.element.style.height=(this.height||15)+5+"px"};r.inherits(l,a),function(){this.classSuffix="-h",this.onScroll=function(){this.skipEvent||(this.scrollLeft=this.element.scrollLeft,this._emit("scroll",{data:this.scrollLeft})),this.skipEvent=!1},this.getHeight=function(){return this.isVisible?this.height:0},this.setWidth=function(e){this.element.style.width=e+"px"},this.setInnerWidth=function(e){this.inner.style.width=e+"px"},this.setScrollWidth=function(e){this.inner.style.width=e+"px"},this.setScrollLeft=function(e){this.scrollLeft!=e&&(this.skipEvent=!0,this.scrollLeft=this.element.scrollLeft=e)}}.call(l.prototype),t.ScrollBar=f,t.ScrollBarV=f,t.ScrollBarH=l,t.VScrollBar=f,t.HScrollBar=l}),ace.define("ace/renderloop",["require","exports","module","ace/lib/event"],function(e,t,n){"use strict";var r=e("./lib/event"),i=function(e,t){this.onRender=e,this.pending=!1,this.changes=0,this.$recursionLimit=2,this.window=t||window;var n=this;this._flush=function(e){n.pending=!1;var t=n.changes;t&&(r.blockIdle(100),n.changes=0,n.onRender(t));if(n.changes){if(n.$recursionLimit--<0)return;n.schedule()}else n.$recursionLimit=2}};(function(){this.schedule=function(e){this.changes=this.changes|e,this.changes&&!this.pending&&(r.nextFrame(this._flush),this.pending=!0)},this.clear=function(e){var t=this.changes;return this.changes=0,t}}).call(i.prototype),t.RenderLoop=i}),ace.define("ace/layer/font_metrics",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/event","ace/lib/useragent","ace/lib/event_emitter"],function(e,t,n){var r=e("../lib/oop"),i=e("../lib/dom"),s=e("../lib/lang"),o=e("../lib/event"),u=e("../lib/useragent"),a=e("../lib/event_emitter").EventEmitter,f=256,l=typeof ResizeObserver=="function",c=200,h=t.FontMetrics=function(e){this.el=i.createElement("div"),this.$setMeasureNodeStyles(this.el.style,!0),this.$main=i.createElement("div"),this.$setMeasureNodeStyles(this.$main.style),this.$measureNode=i.createElement("div"),this.$setMeasureNodeStyles(this.$measureNode.style),this.el.appendChild(this.$main),this.el.appendChild(this.$measureNode),e.appendChild(this.el),this.$measureNode.innerHTML=s.stringRepeat("X",f),this.$characterSize={width:0,height:0},l?this.$addObserver():this.checkForSizeChanges()};(function(){r.implement(this,a),this.$characterSize={width:0,height:0},this.$setMeasureNodeStyles=function(e,t){e.width=e.height="auto",e.left=e.top="0px",e.visibility="hidden",e.position="absolute",e.whiteSpace="pre",u.isIE<8?e["font-family"]="inherit":e.font="inherit",e.overflow=t?"hidden":"visible"},this.checkForSizeChanges=function(e){e===undefined&&(e=this.$measureSizes());if(e&&(this.$characterSize.width!==e.width||this.$characterSize.height!==e.height)){this.$measureNode.style.fontWeight="bold";var t=this.$measureSizes();this.$measureNode.style.fontWeight="",this.$characterSize=e,this.charSizes=Object.create(null),this.allowBoldFonts=t&&t.width===e.width&&t.height===e.height,this._emit("changeCharacterSize",{data:e})}},this.$addObserver=function(){var e=this;this.$observer=new window.ResizeObserver(function(t){var n=t[0].contentRect;e.checkForSizeChanges({height:n.height,width:n.width/f})}),this.$observer.observe(this.$measureNode)},this.$pollSizeChanges=function(){if(this.$pollSizeChangesTimer||this.$observer)return this.$pollSizeChangesTimer;var e=this;return this.$pollSizeChangesTimer=o.onIdle(function t(){e.checkForSizeChanges(),o.onIdle(t,500)},500)},this.setPolling=function(e){e?this.$pollSizeChanges():this.$pollSizeChangesTimer&&(clearInterval(this.$pollSizeChangesTimer),this.$pollSizeChangesTimer=0)},this.$measureSizes=function(e){var t={height:(e||this.$measureNode).clientHeight,width:(e||this.$measureNode).clientWidth/f};return t.width===0||t.height===0?null:t},this.$measureCharWidth=function(e){this.$main.innerHTML=s.stringRepeat(e,f);var t=this.$main.getBoundingClientRect();return t.width/f},this.getCharacterWidth=function(e){var t=this.charSizes[e];return t===undefined&&(t=this.charSizes[e]=this.$measureCharWidth(e)/this.$characterSize.width),t},this.destroy=function(){clearInterval(this.$pollSizeChangesTimer),this.$observer&&this.$observer.disconnect(),this.el&&this.el.parentNode&&this.el.parentNode.removeChild(this.el)},this.$getZoom=function e(t){return t?(window.getComputedStyle(t).zoom||1)*e(t.parentElement):1},this.$initTransformMeasureNodes=function(){var e=function(e,t){return["div",{style:"position: absolute;top:"+e+"px;left:"+t+"px;"}]};this.els=i.buildDom([e(0,0),e(c,0),e(0,c),e(c,c)],this.el)},this.transformCoordinates=function(e,t){function r(e,t,n){var r=e[1]*t[0]-e[0]*t[1];return[(-t[1]*n[0]+t[0]*n[1])/r,(+e[1]*n[0]-e[0]*n[1])/r]}function i(e,t){return[e[0]-t[0],e[1]-t[1]]}function s(e,t){return[e[0]+t[0],e[1]+t[1]]}function o(e,t){return[e*t[0],e*t[1]]}function u(e){var t=e.getBoundingClientRect();return[t.left,t.top]}if(e){var n=this.$getZoom(this.el);e=o(1/n,e)}this.els||this.$initTransformMeasureNodes();var a=u(this.els[0]),f=u(this.els[1]),l=u(this.els[2]),h=u(this.els[3]),p=r(i(h,f),i(h,l),i(s(f,l),s(h,a))),d=o(1+p[0],i(f,a)),v=o(1+p[1],i(l,a));if(t){var m=t,g=p[0]*m[0]/c+p[1]*m[1]/c+1,y=s(o(m[0],d),o(m[1],v));return s(o(1/g/c,y),a)}var b=i(e,a),w=r(i(d,o(p[0],b)),i(v,o(p[1],b)),b);return o(c,w)}}).call(h.prototype)}),ace.define("ace/virtual_renderer",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/config","ace/layer/gutter","ace/layer/marker","ace/layer/text","ace/layer/cursor","ace/scrollbar","ace/scrollbar","ace/renderloop","ace/layer/font_metrics","ace/lib/event_emitter","ace/lib/useragent"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./config"),o=e("./layer/gutter").Gutter,u=e("./layer/marker").Marker,a=e("./layer/text").Text,f=e("./layer/cursor").Cursor,l=e("./scrollbar").HScrollBar,c=e("./scrollbar").VScrollBar,h=e("./renderloop").RenderLoop,p=e("./layer/font_metrics").FontMetrics,d=e("./lib/event_emitter").EventEmitter,v='.ace_br1 {border-top-left-radius : 3px;}.ace_br2 {border-top-right-radius : 3px;}.ace_br3 {border-top-left-radius : 3px; border-top-right-radius: 3px;}.ace_br4 {border-bottom-right-radius: 3px;}.ace_br5 {border-top-left-radius : 3px; border-bottom-right-radius: 3px;}.ace_br6 {border-top-right-radius : 3px; border-bottom-right-radius: 3px;}.ace_br7 {border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px;}.ace_br8 {border-bottom-left-radius : 3px;}.ace_br9 {border-top-left-radius : 3px; border-bottom-left-radius: 3px;}.ace_br10{border-top-right-radius : 3px; border-bottom-left-radius: 3px;}.ace_br11{border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_br12{border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_br13{border-top-left-radius : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_br14{border-top-right-radius : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_br15{border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_editor {position: relative;overflow: hidden;font: 12px/normal \'Monaco\', \'Menlo\', \'Ubuntu Mono\', \'Consolas\', \'source-code-pro\', monospace;direction: ltr;text-align: left;-webkit-tap-highlight-color: rgba(0, 0, 0, 0);}.ace_scroller {position: absolute;overflow: hidden;top: 0;bottom: 0;background-color: inherit;-ms-user-select: none;-moz-user-select: none;-webkit-user-select: none;user-select: none;cursor: text;}.ace_content {position: absolute;box-sizing: border-box;min-width: 100%;contain: style size layout;}.ace_dragging .ace_scroller:before{position: absolute;top: 0;left: 0;right: 0;bottom: 0;content: \'\';background: rgba(250, 250, 250, 0.01);z-index: 1000;}.ace_dragging.ace_dark .ace_scroller:before{background: rgba(0, 0, 0, 0.01);}.ace_selecting, .ace_selecting * {cursor: text !important;}.ace_gutter {position: absolute;overflow : hidden;width: auto;top: 0;bottom: 0;left: 0;cursor: default;z-index: 4;-ms-user-select: none;-moz-user-select: none;-webkit-user-select: none;user-select: none;contain: style size layout;}.ace_gutter-active-line {position: absolute;left: 0;right: 0;}.ace_scroller.ace_scroll-left {box-shadow: 17px 0 16px -16px rgba(0, 0, 0, 0.4) inset;}.ace_gutter-cell {position: absolute;top: 0;left: 0;right: 0;padding-left: 19px;padding-right: 6px;background-repeat: no-repeat;}.ace_gutter-cell.ace_error {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABOFBMVEX/////////QRswFAb/Ui4wFAYwFAYwFAaWGAfDRymzOSH/PxswFAb/SiUwFAYwFAbUPRvjQiDllog5HhHdRybsTi3/Tyv9Tir+Syj/UC3////XurebMBIwFAb/RSHbPx/gUzfdwL3kzMivKBAwFAbbvbnhPx66NhowFAYwFAaZJg8wFAaxKBDZurf/RB6mMxb/SCMwFAYwFAbxQB3+RB4wFAb/Qhy4Oh+4QifbNRcwFAYwFAYwFAb/QRzdNhgwFAYwFAbav7v/Uy7oaE68MBK5LxLewr/r2NXewLswFAaxJw4wFAbkPRy2PyYwFAaxKhLm1tMwFAazPiQwFAaUGAb/QBrfOx3bvrv/VC/maE4wFAbRPBq6MRO8Qynew8Dp2tjfwb0wFAbx6eju5+by6uns4uH9/f36+vr/GkHjAAAAYnRSTlMAGt+64rnWu/bo8eAA4InH3+DwoN7j4eLi4xP99Nfg4+b+/u9B/eDs1MD1mO7+4PHg2MXa347g7vDizMLN4eG+Pv7i5evs/v79yu7S3/DV7/498Yv24eH+4ufQ3Ozu/v7+y13sRqwAAADLSURBVHjaZc/XDsFgGIBhtDrshlitmk2IrbHFqL2pvXf/+78DPokj7+Fz9qpU/9UXJIlhmPaTaQ6QPaz0mm+5gwkgovcV6GZzd5JtCQwgsxoHOvJO15kleRLAnMgHFIESUEPmawB9ngmelTtipwwfASilxOLyiV5UVUyVAfbG0cCPHig+GBkzAENHS0AstVF6bacZIOzgLmxsHbt2OecNgJC83JERmePUYq8ARGkJx6XtFsdddBQgZE2nPR6CICZhawjA4Fb/chv+399kfR+MMMDGOQAAAABJRU5ErkJggg==");background-repeat: no-repeat;background-position: 2px center;}.ace_gutter-cell.ace_warning {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAmVBMVEX///8AAAD///8AAAAAAABPSzb/5sAAAAB/blH/73z/ulkAAAAAAAD85pkAAAAAAAACAgP/vGz/rkDerGbGrV7/pkQICAf////e0IsAAAD/oED/qTvhrnUAAAD/yHD/njcAAADuv2r/nz//oTj/p064oGf/zHAAAAA9Nir/tFIAAAD/tlTiuWf/tkIAAACynXEAAAAAAAAtIRW7zBpBAAAAM3RSTlMAABR1m7RXO8Ln31Z36zT+neXe5OzooRDfn+TZ4p3h2hTf4t3k3ucyrN1K5+Xaks52Sfs9CXgrAAAAjklEQVR42o3PbQ+CIBQFYEwboPhSYgoYunIqqLn6/z8uYdH8Vmdnu9vz4WwXgN/xTPRD2+sgOcZjsge/whXZgUaYYvT8QnuJaUrjrHUQreGczuEafQCO/SJTufTbroWsPgsllVhq3wJEk2jUSzX3CUEDJC84707djRc5MTAQxoLgupWRwW6UB5fS++NV8AbOZgnsC7BpEAAAAABJRU5ErkJggg==");background-position: 2px center;}.ace_gutter-cell.ace_info {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAJ0Uk5TAAB2k804AAAAPklEQVQY02NgIB68QuO3tiLznjAwpKTgNyDbMegwisCHZUETUZV0ZqOquBpXj2rtnpSJT1AEnnRmL2OgGgAAIKkRQap2htgAAAAASUVORK5CYII=");background-position: 2px center;}.ace_dark .ace_gutter-cell.ace_info {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAJFBMVEUAAAChoaGAgIAqKiq+vr6tra1ZWVmUlJSbm5s8PDxubm56enrdgzg3AAAAAXRSTlMAQObYZgAAAClJREFUeNpjYMAPdsMYHegyJZFQBlsUlMFVCWUYKkAZMxZAGdxlDMQBAG+TBP4B6RyJAAAAAElFTkSuQmCC");}.ace_scrollbar {contain: strict;position: absolute;right: 0;bottom: 0;z-index: 6;}.ace_scrollbar-inner {position: absolute;cursor: text;left: 0;top: 0;}.ace_scrollbar-v{overflow-x: hidden;overflow-y: scroll;top: 0;}.ace_scrollbar-h {overflow-x: scroll;overflow-y: hidden;left: 0;}.ace_print-margin {position: absolute;height: 100%;}.ace_text-input {position: absolute;z-index: 0;width: 0.5em;height: 1em;opacity: 0;background: transparent;-moz-appearance: none;appearance: none;border: none;resize: none;outline: none;overflow: hidden;font: inherit;padding: 0 1px;margin: 0 -1px;contain: strict;-ms-user-select: text;-moz-user-select: text;-webkit-user-select: text;user-select: text;white-space: pre!important;}.ace_text-input.ace_composition {background: transparent;color: inherit;z-index: 1000;opacity: 1;}.ace_composition_placeholder { color: transparent }.ace_composition_marker { border-bottom: 1px solid;position: absolute;border-radius: 0;margin-top: 1px;}[ace_nocontext=true] {transform: none!important;filter: none!important;perspective: none!important;clip-path: none!important;mask : none!important;contain: none!important;perspective: none!important;mix-blend-mode: initial!important;z-index: auto;}.ace_layer {z-index: 1;position: absolute;overflow: hidden;word-wrap: normal;white-space: pre;height: 100%;width: 100%;box-sizing: border-box;pointer-events: none;}.ace_gutter-layer {position: relative;width: auto;text-align: right;pointer-events: auto;height: 1000000px;contain: style size layout;}.ace_text-layer {font: inherit !important;position: absolute;height: 1000000px;width: 1000000px;contain: style size layout;}.ace_text-layer > .ace_line, .ace_text-layer > .ace_line_group {contain: style size layout;position: absolute;top: 0;left: 0;right: 0;}.ace_hidpi .ace_text-layer,.ace_hidpi .ace_gutter-layer,.ace_hidpi .ace_content,.ace_hidpi .ace_gutter {contain: strict;will-change: transform;}.ace_hidpi .ace_text-layer > .ace_line, .ace_hidpi .ace_text-layer > .ace_line_group {contain: strict;}.ace_cjk {display: inline-block;text-align: center;}.ace_cursor-layer {z-index: 4;}.ace_cursor {z-index: 4;position: absolute;box-sizing: border-box;border-left: 2px solid;transform: translatez(0);}.ace_multiselect .ace_cursor {border-left-width: 1px;}.ace_slim-cursors .ace_cursor {border-left-width: 1px;}.ace_overwrite-cursors .ace_cursor {border-left-width: 0;border-bottom: 1px solid;}.ace_hidden-cursors .ace_cursor {opacity: 0.2;}.ace_smooth-blinking .ace_cursor {transition: opacity 0.18s;}.ace_animate-blinking .ace_cursor {animation-duration: 1000ms;animation-timing-function: step-end;animation-name: blink-ace-animate;animation-iteration-count: infinite;}.ace_animate-blinking.ace_smooth-blinking .ace_cursor {animation-duration: 1000ms;animation-timing-function: ease-in-out;animation-name: blink-ace-animate-smooth;}@keyframes blink-ace-animate {from, to { opacity: 1; }60% { opacity: 0; }}@keyframes blink-ace-animate-smooth {from, to { opacity: 1; }45% { opacity: 1; }60% { opacity: 0; }85% { opacity: 0; }}.ace_marker-layer .ace_step, .ace_marker-layer .ace_stack {position: absolute;z-index: 3;}.ace_marker-layer .ace_selection {position: absolute;z-index: 5;}.ace_marker-layer .ace_bracket {position: absolute;z-index: 6;}.ace_marker-layer .ace_active-line {position: absolute;z-index: 2;}.ace_marker-layer .ace_selected-word {position: absolute;z-index: 4;box-sizing: border-box;}.ace_line .ace_fold {box-sizing: border-box;display: inline-block;height: 11px;margin-top: -2px;vertical-align: middle;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="),url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACJJREFUeNpi+P//fxgTAwPDBxDxD078RSX+YeEyDFMCIMAAI3INmXiwf2YAAAAASUVORK5CYII=");background-repeat: no-repeat, repeat-x;background-position: center center, top left;color: transparent;border: 1px solid black;border-radius: 2px;cursor: pointer;pointer-events: auto;}.ace_dark .ace_fold {}.ace_fold:hover{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="),url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACBJREFUeNpi+P//fz4TAwPDZxDxD5X4i5fLMEwJgAADAEPVDbjNw87ZAAAAAElFTkSuQmCC");}.ace_tooltip {background-color: #FFF;background-image: linear-gradient(to bottom, transparent, rgba(0, 0, 0, 0.1));border: 1px solid gray;border-radius: 1px;box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);color: black;max-width: 100%;padding: 3px 4px;position: fixed;z-index: 999999;box-sizing: border-box;cursor: default;white-space: pre;word-wrap: break-word;line-height: normal;font-style: normal;font-weight: normal;letter-spacing: normal;pointer-events: none;}.ace_folding-enabled > .ace_gutter-cell {padding-right: 13px;}.ace_fold-widget {box-sizing: border-box;margin: 0 -12px 0 1px;display: none;width: 11px;vertical-align: top;background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42mWKsQ0AMAzC8ixLlrzQjzmBiEjp0A6WwBCSPgKAXoLkqSot7nN3yMwR7pZ32NzpKkVoDBUxKAAAAABJRU5ErkJggg==");background-repeat: no-repeat;background-position: center;border-radius: 3px;border: 1px solid transparent;cursor: pointer;}.ace_folding-enabled .ace_fold-widget {display: inline-block; }.ace_fold-widget.ace_end {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42m3HwQkAMAhD0YzsRchFKI7sAikeWkrxwScEB0nh5e7KTPWimZki4tYfVbX+MNl4pyZXejUO1QAAAABJRU5ErkJggg==");}.ace_fold-widget.ace_closed {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAGCAYAAAAG5SQMAAAAOUlEQVR42jXKwQkAMAgDwKwqKD4EwQ26sSOkVWjgIIHAzPiCgaqiqnJHZnKICBERHN194O5b9vbLuAVRL+l0YWnZAAAAAElFTkSuQmCCXA==");}.ace_fold-widget:hover {border: 1px solid rgba(0, 0, 0, 0.3);background-color: rgba(255, 255, 255, 0.2);box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);}.ace_fold-widget:active {border: 1px solid rgba(0, 0, 0, 0.4);background-color: rgba(0, 0, 0, 0.05);box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);}.ace_dark .ace_fold-widget {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHklEQVQIW2P4//8/AzoGEQ7oGCaLLAhWiSwB146BAQCSTPYocqT0AAAAAElFTkSuQmCC");}.ace_dark .ace_fold-widget.ace_end {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAH0lEQVQIW2P4//8/AxQ7wNjIAjDMgC4AxjCVKBirIAAF0kz2rlhxpAAAAABJRU5ErkJggg==");}.ace_dark .ace_fold-widget.ace_closed {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAHElEQVQIW2P4//+/AxAzgDADlOOAznHAKgPWAwARji8UIDTfQQAAAABJRU5ErkJggg==");}.ace_dark .ace_fold-widget:hover {box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);background-color: rgba(255, 255, 255, 0.1);}.ace_dark .ace_fold-widget:active {box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);}.ace_inline_button {border: 1px solid lightgray;display: inline-block;margin: -1px 8px;padding: 0 5px;pointer-events: auto;cursor: pointer;}.ace_inline_button:hover {border-color: gray;background: rgba(200,200,200,0.2);display: inline-block;pointer-events: auto;}.ace_fold-widget.ace_invalid {background-color: #FFB4B4;border-color: #DE5555;}.ace_fade-fold-widgets .ace_fold-widget {transition: opacity 0.4s ease 0.05s;opacity: 0;}.ace_fade-fold-widgets:hover .ace_fold-widget {transition: opacity 0.05s ease 0.05s;opacity:1;}.ace_underline {text-decoration: underline;}.ace_bold {font-weight: bold;}.ace_nobold .ace_bold {font-weight: normal;}.ace_italic {font-style: italic;}.ace_error-marker {background-color: rgba(255, 0, 0,0.2);position: absolute;z-index: 9;}.ace_highlight-marker {background-color: rgba(255, 255, 0,0.2);position: absolute;z-index: 8;}.ace_mobile-menu {position: absolute;line-height: 1.5;border-radius: 4px;-ms-user-select: none;-moz-user-select: none;-webkit-user-select: none;user-select: none;background: white;box-shadow: 1px 3px 2px grey;border: 1px solid #dcdcdc;color: black;}.ace_dark > .ace_mobile-menu {background: #333;color: #ccc;box-shadow: 1px 3px 2px grey;border: 1px solid #444;}.ace_mobile-button {padding: 2px;cursor: pointer;overflow: hidden;}.ace_mobile-button:hover {background-color: #eee;opacity:1;}.ace_mobile-button:active {background-color: #ddd;}',m=e("./lib/useragent"),g=m.isIE;i.importCssString(v,"ace_editor.css");var y=function(e,t){var n=this;this.container=e||i.createElement("div"),i.addCssClass(this.container,"ace_editor"),i.HI_DPI&&i.addCssClass(this.container,"ace_hidpi"),this.setTheme(t),this.$gutter=i.createElement("div"),this.$gutter.className="ace_gutter",this.container.appendChild(this.$gutter),this.$gutter.setAttribute("aria-hidden",!0),this.scroller=i.createElement("div"),this.scroller.className="ace_scroller",this.container.appendChild(this.scroller),this.content=i.createElement("div"),this.content.className="ace_content",this.scroller.appendChild(this.content),this.$gutterLayer=new o(this.$gutter),this.$gutterLayer.on("changeGutterWidth",this.onGutterResize.bind(this)),this.$markerBack=new u(this.content);var r=this.$textLayer=new a(this.content);this.canvas=r.element,this.$markerFront=new u(this.content),this.$cursorLayer=new f(this.content),this.$horizScroll=!1,this.$vScroll=!1,this.scrollBar=this.scrollBarV=new c(this.container,this),this.scrollBarH=new l(this.container,this),this.scrollBarV.addEventListener("scroll",function(e){n.$scrollAnimation||n.session.setScrollTop(e.data-n.scrollMargin.top)}),this.scrollBarH.addEventListener("scroll",function(e){n.$scrollAnimation||n.session.setScrollLeft(e.data-n.scrollMargin.left)}),this.scrollTop=0,this.scrollLeft=0,this.cursorPos={row:0,column:0},this.$fontMetrics=new p(this.container),this.$textLayer.$setFontMetrics(this.$fontMetrics),this.$textLayer.addEventListener("changeCharacterSize",function(e){n.updateCharacterSize(),n.onResize(!0,n.gutterWidth,n.$size.width,n.$size.height),n._signal("changeCharacterSize",e)}),this.$size={width:0,height:0,scrollerHeight:0,scrollerWidth:0,$dirty:!0},this.layerConfig={width:1,padding:0,firstRow:0,firstRowScreen:0,lastRow:0,lineHeight:0,characterWidth:0,minHeight:1,maxHeight:1,offset:0,height:1,gutterOffset:1},this.scrollMargin={left:0,right:0,top:0,bottom:0,v:0,h:0},this.margin={left:0,right:0,top:0,bottom:0,v:0,h:0},this.$keepTextAreaAtCursor=!m.isIOS,this.$loop=new h(this.$renderChanges.bind(this),this.container.ownerDocument.defaultView),this.$loop.schedule(this.CHANGE_FULL),this.updateCharacterSize(),this.setPadding(4),s.resetOptions(this),s._signal("renderer",this)};(function(){this.CHANGE_CURSOR=1,this.CHANGE_MARKER=2,this.CHANGE_GUTTER=4,this.CHANGE_SCROLL=8,this.CHANGE_LINES=16,this.CHANGE_TEXT=32,this.CHANGE_SIZE=64,this.CHANGE_MARKER_BACK=128,this.CHANGE_MARKER_FRONT=256,this.CHANGE_FULL=512,this.CHANGE_H_SCROLL=1024,r.implement(this,d),this.updateCharacterSize=function(){this.$textLayer.allowBoldFonts!=this.$allowBoldFonts&&(this.$allowBoldFonts=this.$textLayer.allowBoldFonts,this.setStyle("ace_nobold",!this.$allowBoldFonts)),this.layerConfig.characterWidth=this.characterWidth=this.$textLayer.getCharacterWidth(),this.layerConfig.lineHeight=this.lineHeight=this.$textLayer.getLineHeight(),this.$updatePrintMargin(),i.setStyle(this.scroller.style,"line-height",this.lineHeight+"px")},this.setSession=function(e){this.session&&this.session.doc.off("changeNewLineMode",this.onChangeNewLineMode),this.session=e,e&&this.scrollMargin.top&&e.getScrollTop()<=0&&e.setScrollTop(-this.scrollMargin.top),this.$cursorLayer.setSession(e),this.$markerBack.setSession(e),this.$markerFront.setSession(e),this.$gutterLayer.setSession(e),this.$textLayer.setSession(e);if(!e)return;this.$loop.schedule(this.CHANGE_FULL),this.session.$setFontMetrics(this.$fontMetrics),this.scrollBarH.scrollLeft=this.scrollBarV.scrollTop=null,this.onChangeNewLineMode=this.onChangeNewLineMode.bind(this),this.onChangeNewLineMode(),this.session.doc.on("changeNewLineMode",this.onChangeNewLineMode)},this.updateLines=function(e,t,n){t===undefined&&(t=Infinity),this.$changedLines?(this.$changedLines.firstRow>e&&(this.$changedLines.firstRow=e),this.$changedLines.lastRowthis.layerConfig.lastRow)return;this.$loop.schedule(this.CHANGE_LINES)},this.onChangeNewLineMode=function(){this.$loop.schedule(this.CHANGE_TEXT),this.$textLayer.$updateEolChar(),this.session.$bidiHandler.setEolChar(this.$textLayer.EOL_CHAR)},this.onChangeTabSize=function(){this.$loop.schedule(this.CHANGE_TEXT|this.CHANGE_MARKER),this.$textLayer.onChangeTabSize()},this.updateText=function(){this.$loop.schedule(this.CHANGE_TEXT)},this.updateFull=function(e){e?this.$renderChanges(this.CHANGE_FULL,!0):this.$loop.schedule(this.CHANGE_FULL)},this.updateFontSize=function(){this.$textLayer.checkForSizeChanges()},this.$changes=0,this.$updateSizeAsync=function(){this.$loop.pending?this.$size.$dirty=!0:this.onResize()},this.onResize=function(e,t,n,r){if(this.resizing>2)return;this.resizing>0?this.resizing++:this.resizing=e?1:0;var i=this.container;r||(r=i.clientHeight||i.scrollHeight),n||(n=i.clientWidth||i.scrollWidth);var s=this.$updateCachedSize(e,t,n,r);if(!this.$size.scrollerHeight||!n&&!r)return this.resizing=0;e&&(this.$gutterLayer.$padding=null),e?this.$renderChanges(s|this.$changes,!0):this.$loop.schedule(s|this.$changes),this.resizing&&(this.resizing=0),this.scrollBarV.scrollLeft=this.scrollBarV.scrollTop=null},this.$updateCachedSize=function(e,t,n,r){r-=this.$extraHeight||0;var s=0,o=this.$size,u={width:o.width,height:o.height,scrollerHeight:o.scrollerHeight,scrollerWidth:o.scrollerWidth};r&&(e||o.height!=r)&&(o.height=r,s|=this.CHANGE_SIZE,o.scrollerHeight=o.height,this.$horizScroll&&(o.scrollerHeight-=this.scrollBarH.getHeight()),this.scrollBarV.element.style.bottom=this.scrollBarH.getHeight()+"px",s|=this.CHANGE_SCROLL);if(n&&(e||o.width!=n)){s|=this.CHANGE_SIZE,o.width=n,t==null&&(t=this.$showGutter?this.$gutter.offsetWidth:0),this.gutterWidth=t,i.setStyle(this.scrollBarH.element.style,"left",t+"px"),i.setStyle(this.scroller.style,"left",t+this.margin.left+"px"),o.scrollerWidth=Math.max(0,n-t-this.scrollBarV.getWidth()-this.margin.h),i.setStyle(this.$gutter.style,"left",this.margin.left+"px");var a=this.scrollBarV.getWidth()+"px";i.setStyle(this.scrollBarH.element.style,"right",a),i.setStyle(this.scroller.style,"right",a),i.setStyle(this.scroller.style,"bottom",this.scrollBarH.getHeight());if(this.session&&this.session.getUseWrapMode()&&this.adjustWrapLimit()||e)s|=this.CHANGE_FULL}return o.$dirty=!n||!r,s&&this._signal("resize",u),s},this.onGutterResize=function(e){var t=this.$showGutter?e:0;t!=this.gutterWidth&&(this.$changes|=this.$updateCachedSize(!0,t,this.$size.width,this.$size.height)),this.session.getUseWrapMode()&&this.adjustWrapLimit()?this.$loop.schedule(this.CHANGE_FULL):this.$size.$dirty?this.$loop.schedule(this.CHANGE_FULL):this.$computeLayerConfig()},this.adjustWrapLimit=function(){var e=this.$size.scrollerWidth-this.$padding*2,t=Math.floor(e/this.characterWidth);return this.session.adjustWrapLimit(t,this.$showPrintMargin&&this.$printMarginColumn)},this.setAnimatedScroll=function(e){this.setOption("animatedScroll",e)},this.getAnimatedScroll=function(){return this.$animatedScroll},this.setShowInvisibles=function(e){this.setOption("showInvisibles",e),this.session.$bidiHandler.setShowInvisibles(e)},this.getShowInvisibles=function(){return this.getOption("showInvisibles")},this.getDisplayIndentGuides=function(){return this.getOption("displayIndentGuides")},this.setDisplayIndentGuides=function(e){this.setOption("displayIndentGuides",e)},this.setShowPrintMargin=function(e){this.setOption("showPrintMargin",e)},this.getShowPrintMargin=function(){return this.getOption("showPrintMargin")},this.setPrintMarginColumn=function(e){this.setOption("printMarginColumn",e)},this.getPrintMarginColumn=function(){return this.getOption("printMarginColumn")},this.getShowGutter=function(){return this.getOption("showGutter")},this.setShowGutter=function(e){return this.setOption("showGutter",e)},this.getFadeFoldWidgets=function(){return this.getOption("fadeFoldWidgets")},this.setFadeFoldWidgets=function(e){this.setOption("fadeFoldWidgets",e)},this.setHighlightGutterLine=function(e){this.setOption("highlightGutterLine",e)},this.getHighlightGutterLine=function(){return this.getOption("highlightGutterLine")},this.$updatePrintMargin=function(){if(!this.$showPrintMargin&&!this.$printMarginEl)return;if(!this.$printMarginEl){var e=i.createElement("div");e.className="ace_layer ace_print-margin-layer",this.$printMarginEl=i.createElement("div"),this.$printMarginEl.className="ace_print-margin",e.appendChild(this.$printMarginEl),this.content.insertBefore(e,this.content.firstChild)}var t=this.$printMarginEl.style;t.left=Math.round(this.characterWidth*this.$printMarginColumn+this.$padding)+"px",t.visibility=this.$showPrintMargin?"visible":"hidden",this.session&&this.session.$wrap==-1&&this.adjustWrapLimit()},this.getContainerElement=function(){return this.container},this.getMouseEventTarget=function(){return this.scroller},this.getTextAreaContainer=function(){return this.container},this.$moveTextAreaToCursor=function(){if(this.$isMousePressed)return;var e=this.textarea.style,t=this.$composition;if(!this.$keepTextAreaAtCursor&&!t){i.translate(this.textarea,-100,0);return}var n=this.$cursorLayer.$pixelPos;if(!n)return;t&&t.markerRange&&(n=this.$cursorLayer.getPixelPosition(t.markerRange.start,!0));var r=this.layerConfig,s=n.top,o=n.left;s-=r.offset;var u=t&&t.useTextareaForIME?this.lineHeight:g?0:1;if(s<0||s>r.height-u){i.translate(this.textarea,0,0);return}var a=1,f=this.$size.height-u;if(!t)s+=this.lineHeight;else if(t.useTextareaForIME){var l=this.textarea.value;a=this.characterWidth*this.session.$getStringScreenWidth(l)[0],u+=2}else s+=this.lineHeight+2;o-=this.scrollLeft,o>this.$size.scrollerWidth-a&&(o=this.$size.scrollerWidth-a),o+=this.gutterWidth+this.margin.left,i.setStyle(e,"height",u+"px"),i.setStyle(e,"width",a+"px"),i.translate(this.textarea,Math.min(o,this.$size.scrollerWidth-a),Math.min(s,f))},this.getFirstVisibleRow=function(){return this.layerConfig.firstRow},this.getFirstFullyVisibleRow=function(){return this.layerConfig.firstRow+(this.layerConfig.offset===0?0:1)},this.getLastFullyVisibleRow=function(){var e=this.layerConfig,t=e.lastRow,n=this.session.documentToScreenRow(t,0)*e.lineHeight;return n-this.session.getScrollTop()>e.height-e.lineHeight?t-1:t},this.getLastVisibleRow=function(){return this.layerConfig.lastRow},this.$padding=null,this.setPadding=function(e){this.$padding=e,this.$textLayer.setPadding(e),this.$cursorLayer.setPadding(e),this.$markerFront.setPadding(e),this.$markerBack.setPadding(e),this.$loop.schedule(this.CHANGE_FULL),this.$updatePrintMargin()},this.setScrollMargin=function(e,t,n,r){var i=this.scrollMargin;i.top=e|0,i.bottom=t|0,i.right=r|0,i.left=n|0,i.v=i.top+i.bottom,i.h=i.left+i.right,i.top&&this.scrollTop<=0&&this.session&&this.session.setScrollTop(-i.top),this.updateFull()},this.setMargin=function(e,t,n,r){var i=this.margin;i.top=e|0,i.bottom=t|0,i.right=r|0,i.left=n|0,i.v=i.top+i.bottom,i.h=i.left+i.right,this.$updateCachedSize(!0,this.gutterWidth,this.$size.width,this.$size.height),this.updateFull()},this.getHScrollBarAlwaysVisible=function(){return this.$hScrollBarAlwaysVisible},this.setHScrollBarAlwaysVisible=function(e){this.setOption("hScrollBarAlwaysVisible",e)},this.getVScrollBarAlwaysVisible=function(){return this.$vScrollBarAlwaysVisible},this.setVScrollBarAlwaysVisible=function(e){this.setOption("vScrollBarAlwaysVisible",e)},this.$updateScrollBarV=function(){var e=this.layerConfig.maxHeight,t=this.$size.scrollerHeight;!this.$maxLines&&this.$scrollPastEnd&&(e-=(t-this.lineHeight)*this.$scrollPastEnd,this.scrollTop>e-t&&(e=this.scrollTop+t,this.scrollBarV.scrollTop=null)),this.scrollBarV.setScrollHeight(e+this.scrollMargin.v),this.scrollBarV.setScrollTop(this.scrollTop+this.scrollMargin.top)},this.$updateScrollBarH=function(){this.scrollBarH.setScrollWidth(this.layerConfig.width+2*this.$padding+this.scrollMargin.h),this.scrollBarH.setScrollLeft(this.scrollLeft+this.scrollMargin.left)},this.$frozen=!1,this.freeze=function(){this.$frozen=!0},this.unfreeze=function(){this.$frozen=!1},this.$renderChanges=function(e,t){this.$changes&&(e|=this.$changes,this.$changes=0);if(!this.session||!this.container.offsetWidth||this.$frozen||!e&&!t){this.$changes|=e;return}if(this.$size.$dirty)return this.$changes|=e,this.onResize(!0);this.lineHeight||this.$textLayer.checkForSizeChanges(),this._signal("beforeRender"),this.session&&this.session.$bidiHandler&&this.session.$bidiHandler.updateCharacterWidths(this.$fontMetrics);var n=this.layerConfig;if(e&this.CHANGE_FULL||e&this.CHANGE_SIZE||e&this.CHANGE_TEXT||e&this.CHANGE_LINES||e&this.CHANGE_SCROLL||e&this.CHANGE_H_SCROLL){e|=this.$computeLayerConfig()|this.$loop.clear();if(n.firstRow!=this.layerConfig.firstRow&&n.firstRowScreen==this.layerConfig.firstRowScreen){var r=this.scrollTop+(n.firstRow-this.layerConfig.firstRow)*this.lineHeight;r>0&&(this.scrollTop=r,e|=this.CHANGE_SCROLL,e|=this.$computeLayerConfig()|this.$loop.clear())}n=this.layerConfig,this.$updateScrollBarV(),e&this.CHANGE_H_SCROLL&&this.$updateScrollBarH(),i.translate(this.content,-this.scrollLeft,-n.offset);var s=n.width+2*this.$padding+"px",o=n.minHeight+"px";i.setStyle(this.content.style,"width",s),i.setStyle(this.content.style,"height",o)}e&this.CHANGE_H_SCROLL&&(i.translate(this.content,-this.scrollLeft,-n.offset),this.scroller.className=this.scrollLeft<=0?"ace_scroller":"ace_scroller ace_scroll-left");if(e&this.CHANGE_FULL){this.$changedLines=null,this.$textLayer.update(n),this.$showGutter&&this.$gutterLayer.update(n),this.$markerBack.update(n),this.$markerFront.update(n),this.$cursorLayer.update(n),this.$moveTextAreaToCursor(),this._signal("afterRender");return}if(e&this.CHANGE_SCROLL){this.$changedLines=null,e&this.CHANGE_TEXT||e&this.CHANGE_LINES?this.$textLayer.update(n):this.$textLayer.scrollLines(n),this.$showGutter&&(e&this.CHANGE_GUTTER||e&this.CHANGE_LINES?this.$gutterLayer.update(n):this.$gutterLayer.scrollLines(n)),this.$markerBack.update(n),this.$markerFront.update(n),this.$cursorLayer.update(n),this.$moveTextAreaToCursor(),this._signal("afterRender");return}e&this.CHANGE_TEXT?(this.$changedLines=null,this.$textLayer.update(n),this.$showGutter&&this.$gutterLayer.update(n)):e&this.CHANGE_LINES?(this.$updateLines()||e&this.CHANGE_GUTTER&&this.$showGutter)&&this.$gutterLayer.update(n):e&this.CHANGE_TEXT||e&this.CHANGE_GUTTER?this.$showGutter&&this.$gutterLayer.update(n):e&this.CHANGE_CURSOR&&this.$highlightGutterLine&&this.$gutterLayer.updateLineHighlight(n),e&this.CHANGE_CURSOR&&(this.$cursorLayer.update(n),this.$moveTextAreaToCursor()),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_FRONT)&&this.$markerFront.update(n),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_BACK)&&this.$markerBack.update(n),this._signal("afterRender")},this.$autosize=function(){var e=this.session.getScreenLength()*this.lineHeight,t=this.$maxLines*this.lineHeight,n=Math.min(t,Math.max((this.$minLines||1)*this.lineHeight,e))+this.scrollMargin.v+(this.$extraHeight||0);this.$horizScroll&&(n+=this.scrollBarH.getHeight()),this.$maxPixelHeight&&n>this.$maxPixelHeight&&(n=this.$maxPixelHeight);var r=n<=2*this.lineHeight,i=!r&&e>t;if(n!=this.desiredHeight||this.$size.height!=this.desiredHeight||i!=this.$vScroll){i!=this.$vScroll&&(this.$vScroll=i,this.scrollBarV.setVisible(i));var s=this.container.clientWidth;this.container.style.height=n+"px",this.$updateCachedSize(!0,this.$gutterWidth,s,n),this.desiredHeight=n,this._signal("autosize")}},this.$computeLayerConfig=function(){var e=this.session,t=this.$size,n=t.height<=2*this.lineHeight,r=this.session.getScreenLength(),i=r*this.lineHeight,s=this.$getLongestLine(),o=!n&&(this.$hScrollBarAlwaysVisible||t.scrollerWidth-s-2*this.$padding<0),u=this.$horizScroll!==o;u&&(this.$horizScroll=o,this.scrollBarH.setVisible(o));var a=this.$vScroll;this.$maxLines&&this.lineHeight>1&&this.$autosize();var f=t.scrollerHeight+this.lineHeight,l=!this.$maxLines&&this.$scrollPastEnd?(t.scrollerHeight-this.lineHeight)*this.$scrollPastEnd:0;i+=l;var c=this.scrollMargin;this.session.setScrollTop(Math.max(-c.top,Math.min(this.scrollTop,i-t.scrollerHeight+c.bottom))),this.session.setScrollLeft(Math.max(-c.left,Math.min(this.scrollLeft,s+2*this.$padding-t.scrollerWidth+c.right)));var h=!n&&(this.$vScrollBarAlwaysVisible||t.scrollerHeight-i+l<0||this.scrollTop>c.top),p=a!==h;p&&(this.$vScroll=h,this.scrollBarV.setVisible(h));var d=this.scrollTop%this.lineHeight,v=Math.ceil(f/this.lineHeight)-1,m=Math.max(0,Math.round((this.scrollTop-d)/this.lineHeight)),g=m+v,y,b,w=this.lineHeight;m=e.screenToDocumentRow(m,0);var E=e.getFoldLine(m);E&&(m=E.start.row),y=e.documentToScreenRow(m,0),b=e.getRowLength(m)*w,g=Math.min(e.screenToDocumentRow(g,0),e.getLength()-1),f=t.scrollerHeight+e.getRowLength(g)*w+b,d=this.scrollTop-y*w;var S=0;if(this.layerConfig.width!=s||u)S=this.CHANGE_H_SCROLL;if(u||p)S|=this.$updateCachedSize(!0,this.gutterWidth,t.width,t.height),this._signal("scrollbarVisibilityChanged"),p&&(s=this.$getLongestLine());return this.layerConfig={width:s,padding:this.$padding,firstRow:m,firstRowScreen:y,lastRow:g,lineHeight:w,characterWidth:this.characterWidth,minHeight:f,maxHeight:i,offset:d,gutterOffset:w?Math.max(0,Math.ceil((d+t.height-t.scrollerHeight)/w)):0,height:this.$size.scrollerHeight},this.session.$bidiHandler&&this.session.$bidiHandler.setContentWidth(s-this.$padding),S},this.$updateLines=function(){if(!this.$changedLines)return;var e=this.$changedLines.firstRow,t=this.$changedLines.lastRow;this.$changedLines=null;var n=this.layerConfig;if(e>n.lastRow+1)return;if(tthis.$textLayer.MAX_LINE_LENGTH&&(e=this.$textLayer.MAX_LINE_LENGTH+30),Math.max(this.$size.scrollerWidth-2*this.$padding,Math.round(e*this.characterWidth))},this.updateFrontMarkers=function(){this.$markerFront.setMarkers(this.session.getMarkers(!0)),this.$loop.schedule(this.CHANGE_MARKER_FRONT)},this.updateBackMarkers=function(){this.$markerBack.setMarkers(this.session.getMarkers()),this.$loop.schedule(this.CHANGE_MARKER_BACK)},this.addGutterDecoration=function(e,t){this.$gutterLayer.addGutterDecoration(e,t)},this.removeGutterDecoration=function(e,t){this.$gutterLayer.removeGutterDecoration(e,t)},this.updateBreakpoints=function(e){this.$loop.schedule(this.CHANGE_GUTTER)},this.setAnnotations=function(e){this.$gutterLayer.setAnnotations(e),this.$loop.schedule(this.CHANGE_GUTTER)},this.updateCursor=function(){this.$loop.schedule(this.CHANGE_CURSOR)},this.hideCursor=function(){this.$cursorLayer.hideCursor()},this.showCursor=function(){this.$cursorLayer.showCursor()},this.scrollSelectionIntoView=function(e,t,n){this.scrollCursorIntoView(e,n),this.scrollCursorIntoView(t,n)},this.scrollCursorIntoView=function(e,t,n){if(this.$size.scrollerHeight===0)return;var r=this.$cursorLayer.getPixelPosition(e),i=r.left,s=r.top,o=n&&n.top||0,u=n&&n.bottom||0,a=this.$scrollAnimation?this.session.getScrollTop():this.scrollTop;a+o>s?(t&&a+o>s+this.lineHeight&&(s-=t*this.$size.scrollerHeight),s===0&&(s=-this.scrollMargin.top),this.session.setScrollTop(s)):a+this.$size.scrollerHeight-ui?(i=1-this.scrollMargin.top)return!0;if(t>0&&this.session.getScrollTop()+this.$size.scrollerHeight-this.layerConfig.maxHeight<-1+this.scrollMargin.bottom)return!0;if(e<0&&this.session.getScrollLeft()>=1-this.scrollMargin.left)return!0;if(e>0&&this.session.getScrollLeft()+this.$size.scrollerWidth-this.layerConfig.width<-1+this.scrollMargin.right)return!0},this.pixelToScreenCoordinates=function(e,t){var n;if(this.$hasCssTransforms){n={top:0,left:0};var r=this.$fontMetrics.transformCoordinates([e,t]);e=r[1]-this.gutterWidth-this.margin.left,t=r[0]}else n=this.scroller.getBoundingClientRect();var i=e+this.scrollLeft-n.left-this.$padding,s=i/this.characterWidth,o=Math.floor((t+this.scrollTop-n.top)/this.lineHeight),u=this.$blockCursor?Math.floor(s):Math.round(s);return{row:o,column:u,side:s-u>0?1:-1,offsetX:i}},this.screenToTextCoordinates=function(e,t){var n;if(this.$hasCssTransforms){n={top:0,left:0};var r=this.$fontMetrics.transformCoordinates([e,t]);e=r[1]-this.gutterWidth-this.margin.left,t=r[0]}else n=this.scroller.getBoundingClientRect();var i=e+this.scrollLeft-n.left-this.$padding,s=i/this.characterWidth,o=this.$blockCursor?Math.floor(s):Math.round(s),u=Math.floor((t+this.scrollTop-n.top)/this.lineHeight);return this.session.screenToDocumentPosition(u,Math.max(o,0),i)},this.textToScreenCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=this.session.documentToScreenPosition(e,t),i=this.$padding+(this.session.$bidiHandler.isBidiRow(r.row,e)?this.session.$bidiHandler.getPosLeft(r.column):Math.round(r.column*this.characterWidth)),s=r.row*this.lineHeight;return{pageX:n.left+i-this.scrollLeft,pageY:n.top+s-this.scrollTop}},this.visualizeFocus=function(){i.addCssClass(this.container,"ace_focus")},this.visualizeBlur=function(){i.removeCssClass(this.container,"ace_focus")},this.showComposition=function(e){this.$composition=e,e.cssText||(e.cssText=this.textarea.style.cssText),e.useTextareaForIME=this.$useTextareaForIME,this.$useTextareaForIME?(i.addCssClass(this.textarea,"ace_composition"),this.textarea.style.cssText="",this.$moveTextAreaToCursor(),this.$cursorLayer.element.style.display="none"):e.markerId=this.session.addMarker(e.markerRange,"ace_composition_marker","text")},this.setCompositionText=function(e){var t=this.session.selection.cursor;this.addToken(e,"composition_placeholder",t.row,t.column),this.$moveTextAreaToCursor()},this.hideComposition=function(){if(!this.$composition)return;this.$composition.markerId&&this.session.removeMarker(this.$composition.markerId),i.removeCssClass(this.textarea,"ace_composition"),this.textarea.style.cssText=this.$composition.cssText,this.$composition=null,this.$cursorLayer.element.style.display=""},this.addToken=function(e,t,n,r){var i=this.session;i.bgTokenizer.lines[n]=null;var s={type:t,value:e},o=i.getTokens(n);if(r==null)o.push(s);else{var u=0;for(var a=0;a50&&e.length>this.$doc.getLength()>>1?this.call("setValue",[this.$doc.getValue()]):this.emit("change",{data:e})}}).call(f.prototype);var l=function(e,t,n){var r=null,i=!1,u=Object.create(s),a=[],l=new f({messageBuffer:a,terminate:function(){},postMessage:function(e){a.push(e);if(!r)return;i?setTimeout(c):c()}});l.setEmitSync=function(e){i=e};var c=function(){var e=a.shift();e.command?r[e.command].apply(r,e.args):e.event&&u._signal(e.event,e.data)};return u.postMessage=function(e){l.onMessage({data:e})},u.callback=function(e,t){this.postMessage({type:"call",id:t,data:e})},u.emit=function(e,t){this.postMessage({type:"event",name:e,data:t})},o.loadModule(["worker",t],function(e){r=new e[n](u);while(a.length)c()}),l};t.UIWorkerClient=l,t.WorkerClient=f,t.createWorker=a}),ace.define("ace/placeholder",["require","exports","module","ace/range","ace/lib/event_emitter","ace/lib/oop"],function(e,t,n){"use strict";var r=e("./range").Range,i=e("./lib/event_emitter").EventEmitter,s=e("./lib/oop"),o=function(e,t,n,r,i,s){var o=this;this.length=t,this.session=e,this.doc=e.getDocument(),this.mainClass=i,this.othersClass=s,this.$onUpdate=this.onUpdate.bind(this),this.doc.on("change",this.$onUpdate),this.$others=r,this.$onCursorChange=function(){setTimeout(function(){o.onCursorChange()})},this.$pos=n;var u=e.getUndoManager().$undoStack||e.getUndoManager().$undostack||{length:-1};this.$undoStackDepth=u.length,this.setup(),e.selection.on("changeCursor",this.$onCursorChange)};(function(){s.implement(this,i),this.setup=function(){var e=this,t=this.doc,n=this.session;this.selectionBefore=n.selection.toJSON(),n.selection.inMultiSelectMode&&n.selection.toSingleRange(),this.pos=t.createAnchor(this.$pos.row,this.$pos.column);var i=this.pos;i.$insertRight=!0,i.detach(),i.markerId=n.addMarker(new r(i.row,i.column,i.row,i.column+this.length),this.mainClass,null,!1),this.others=[],this.$others.forEach(function(n){var r=t.createAnchor(n.row,n.column);r.$insertRight=!0,r.detach(),e.others.push(r)}),n.setUndoSelect(!1)},this.showOtherMarkers=function(){if(this.othersActive)return;var e=this.session,t=this;this.othersActive=!0,this.others.forEach(function(n){n.markerId=e.addMarker(new r(n.row,n.column,n.row,n.column+t.length),t.othersClass,null,!1)})},this.hideOtherMarkers=function(){if(!this.othersActive)return;this.othersActive=!1;for(var e=0;e=this.pos.column&&t.start.column<=this.pos.column+this.length+1,s=t.start.column-this.pos.column;this.updateAnchors(e),i&&(this.length+=n);if(i&&!this.session.$fromUndo)if(e.action==="insert")for(var o=this.others.length-1;o>=0;o--){var u=this.others[o],a={row:u.row,column:u.column+s};this.doc.insertMergedLines(a,e.lines)}else if(e.action==="remove")for(var o=this.others.length-1;o>=0;o--){var u=this.others[o],a={row:u.row,column:u.column+s};this.doc.remove(new r(a.row,a.column,a.row,a.column-n))}this.$updating=!1,this.updateMarkers()},this.updateAnchors=function(e){this.pos.onChange(e);for(var t=this.others.length;t--;)this.others[t].onChange(e);this.updateMarkers()},this.updateMarkers=function(){if(this.$updating)return;var e=this,t=this.session,n=function(n,i){t.removeMarker(n.markerId),n.markerId=t.addMarker(new r(n.row,n.column,n.row,n.column+e.length),i,null,!1)};n(this.pos,this.mainClass);for(var i=this.others.length;i--;)n(this.others[i],this.othersClass)},this.onCursorChange=function(e){if(this.$updating||!this.session)return;var t=this.session.selection.getCursor();t.row===this.pos.row&&t.column>=this.pos.column&&t.column<=this.pos.column+this.length?(this.showOtherMarkers(),this._emit("cursorEnter",e)):(this.hideOtherMarkers(),this._emit("cursorLeave",e))},this.detach=function(){this.session.removeMarker(this.pos&&this.pos.markerId),this.hideOtherMarkers(),this.doc.removeEventListener("change",this.$onUpdate),this.session.selection.removeEventListener("changeCursor",this.$onCursorChange),this.session.setUndoSelect(!0),this.session=null},this.cancel=function(){if(this.$undoStackDepth===-1)return;var e=this.session.getUndoManager(),t=(e.$undoStack||e.$undostack).length-this.$undoStackDepth;for(var n=0;n1&&!this.inMultiSelectMode&&(this._signal("multiSelect"),this.inMultiSelectMode=!0,this.session.$undoSelect=!1,this.rangeList.attach(this.session)),t||this.fromOrientedRange(e)},this.toSingleRange=function(e){e=e||this.ranges[0];var t=this.rangeList.removeAll();t.length&&this.$onRemoveRange(t),e&&this.fromOrientedRange(e)},this.substractPoint=function(e){var t=this.rangeList.substractPoint(e);if(t)return this.$onRemoveRange(t),t[0]},this.mergeOverlappingRanges=function(){var e=this.rangeList.merge();e.length&&this.$onRemoveRange(e)},this.$onAddRange=function(e){this.rangeCount=this.rangeList.ranges.length,this.ranges.unshift(e),this._signal("addRange",{range:e})},this.$onRemoveRange=function(e){this.rangeCount=this.rangeList.ranges.length;if(this.rangeCount==1&&this.inMultiSelectMode){var t=this.rangeList.ranges.pop();e.push(t),this.rangeCount=0}for(var n=e.length;n--;){var r=this.ranges.indexOf(e[n]);this.ranges.splice(r,1)}this._signal("removeRange",{ranges:e}),this.rangeCount===0&&this.inMultiSelectMode&&(this.inMultiSelectMode=!1,this._signal("singleSelect"),this.session.$undoSelect=!0,this.rangeList.detach(this.session)),t=t||this.ranges[0],t&&!t.isEqual(this.getRange())&&this.fromOrientedRange(t)},this.$initRangeList=function(){if(this.rangeList)return;this.rangeList=new r,this.ranges=[],this.rangeCount=0},this.getAllRanges=function(){return this.rangeCount?this.rangeList.ranges.concat():[this.getRange()]},this.splitIntoLines=function(){if(this.rangeCount>1){var e=this.rangeList.ranges,t=e[e.length-1],n=i.fromPoints(e[0].start,t.end);this.toSingleRange(),this.setSelectionRange(n,t.cursor==t.start)}else{var n=this.getRange(),r=this.isBackwards(),s=n.start.row,o=n.end.row;if(s==o){if(r)var u=n.end,a=n.start;else var u=n.start,a=n.end;this.addRange(i.fromPoints(a,a)),this.addRange(i.fromPoints(u,u));return}var f=[],l=this.getLineRange(s,!0);l.start.column=n.start.column,f.push(l);for(var c=s+1;c1){var e=this.rangeList.ranges,t=e[e.length-1],n=i.fromPoints(e[0].start,t.end);this.toSingleRange(),this.setSelectionRange(n,t.cursor==t.start)}else{var r=this.session.documentToScreenPosition(this.cursor),s=this.session.documentToScreenPosition(this.anchor),o=this.rectangularRangeBlock(r,s);o.forEach(this.addRange,this)}},this.rectangularRangeBlock=function(e,t,n){var r=[],s=e.column0)g--;if(g>0){var y=0;while(r[y].isEmpty())y++}for(var b=g;b>=y;b--)r[b].isEmpty()&&r.splice(b,1)}return r}}.call(s.prototype);var d=e("./editor").Editor;(function(){this.updateSelectionMarkers=function(){this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.addSelectionMarker=function(e){e.cursor||(e.cursor=e.end);var t=this.getSelectionStyle();return e.marker=this.session.addMarker(e,"ace_selection",t),this.session.$selectionMarkers.push(e),this.session.selectionMarkerCount=this.session.$selectionMarkers.length,e},this.removeSelectionMarker=function(e){if(!e.marker)return;this.session.removeMarker(e.marker);var t=this.session.$selectionMarkers.indexOf(e);t!=-1&&this.session.$selectionMarkers.splice(t,1),this.session.selectionMarkerCount=this.session.$selectionMarkers.length},this.removeSelectionMarkers=function(e){var t=this.session.$selectionMarkers;for(var n=e.length;n--;){var r=e[n];if(!r.marker)continue;this.session.removeMarker(r.marker);var i=t.indexOf(r);i!=-1&&t.splice(i,1)}this.session.selectionMarkerCount=t.length},this.$onAddRange=function(e){this.addSelectionMarker(e.range),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onRemoveRange=function(e){this.removeSelectionMarkers(e.ranges),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onMultiSelect=function(e){if(this.inMultiSelectMode)return;this.inMultiSelectMode=!0,this.setStyle("ace_multiselect"),this.keyBinding.addKeyboardHandler(f.keyboardHandler),this.commands.setDefaultHandler("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onSingleSelect=function(e){if(this.session.multiSelect.inVirtualMode)return;this.inMultiSelectMode=!1,this.unsetStyle("ace_multiselect"),this.keyBinding.removeKeyboardHandler(f.keyboardHandler),this.commands.removeDefaultHandler("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers(),this._emit("changeSelection")},this.$onMultiSelectExec=function(e){var t=e.command,n=e.editor;if(!n.multiSelect)return;if(!t.multiSelectAction){var r=t.exec(n,e.args||{});n.multiSelect.addRange(n.multiSelect.toOrientedRange()),n.multiSelect.mergeOverlappingRanges()}else t.multiSelectAction=="forEach"?r=n.forEachSelection(t,e.args):t.multiSelectAction=="forEachLine"?r=n.forEachSelection(t,e.args,!0):t.multiSelectAction=="single"?(n.exitMultiSelectMode(),r=t.exec(n,e.args||{})):r=t.multiSelectAction(n,e.args||{});return r},this.forEachSelection=function(e,t,n){if(this.inVirtualSelectionMode)return;var r=n&&n.keepOrder,i=n==1||n&&n.$byLines,o=this.session,u=this.selection,a=u.rangeList,f=(r?u:a).ranges,l;if(!f.length)return e.exec?e.exec(this,t||{}):e(this,t||{});var c=u._eventRegistry;u._eventRegistry={};var h=new s(o);this.inVirtualSelectionMode=!0;for(var p=f.length;p--;){if(i)while(p>0&&f[p].start.row==f[p-1].end.row)p--;h.fromOrientedRange(f[p]),h.index=p,this.selection=o.selection=h;var d=e.exec?e.exec(this,t||{}):e(this,t||{});!l&&d!==undefined&&(l=d),h.toOrientedRange(f[p])}h.detach(),this.selection=o.selection=u,this.inVirtualSelectionMode=!1,u._eventRegistry=c,u.mergeOverlappingRanges(),u.ranges[0]&&u.fromOrientedRange(u.ranges[0]);var v=this.renderer.$scrollAnimation;return this.onCursorChange(),this.onSelectionChange(),v&&v.from==v.to&&this.renderer.animateScrolling(v.from),l},this.exitMultiSelectMode=function(){if(!this.inMultiSelectMode||this.inVirtualSelectionMode)return;this.multiSelect.toSingleRange()},this.getSelectedText=function(){var e="";if(this.inMultiSelectMode&&!this.inVirtualSelectionMode){var t=this.multiSelect.rangeList.ranges,n=[];for(var r=0;r0);u<0&&(u=0),f>=c&&(f=c-1)}var p=this.session.removeFullLines(u,f);p=this.$reAlignText(p,l),this.session.insert({row:u,column:0},p.join("\n")+"\n"),l||(o.start.column=0,o.end.column=p[p.length-1].length),this.selection.setRange(o)}else{s.forEach(function(e){t.substractPoint(e.cursor)});var d=0,v=Infinity,m=n.map(function(t){var n=t.cursor,r=e.getLine(n.row),i=r.substr(n.column).search(/\S/g);return i==-1&&(i=0),n.column>d&&(d=n.column),io?e.insert(r,a.stringRepeat(" ",s-o)):e.remove(new i(r.row,r.column,r.row,r.column-s+o)),t.start.column=t.end.column=d,t.start.row=t.end.row=r.row,t.cursor=t.end}),t.fromOrientedRange(n[0]),this.renderer.updateCursor(),this.renderer.updateBackMarkers()}},this.$reAlignText=function(e,t){function u(e){return a.stringRepeat(" ",e)}function f(e){return e[2]?u(i)+e[2]+u(s-e[2].length+o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}function l(e){return e[2]?u(i+s-e[2].length)+e[2]+u(o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}function c(e){return e[2]?u(i)+e[2]+u(o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}var n=!0,r=!0,i,s,o;return e.map(function(e){var t=e.match(/(\s*)(.*?)(\s*)([=:].*)/);return t?i==null?(i=t[1].length,s=t[2].length,o=t[3].length,t):(i+s+o!=t[1].length+t[2].length+t[3].length&&(r=!1),i!=t[1].length&&(n=!1),i>t[1].length&&(i=t[1].length),st[3].length&&(o=t[3].length),t):[e]}).map(t?f:n?r?l:f:c)}}).call(d.prototype),t.onSessionChange=function(e){var t=e.session;t&&!t.multiSelect&&(t.$selectionMarkers=[],t.selection.$initRangeList(),t.multiSelect=t.selection),this.multiSelect=t&&t.multiSelect;var n=e.oldSession;n&&(n.multiSelect.off("addRange",this.$onAddRange),n.multiSelect.off("removeRange",this.$onRemoveRange),n.multiSelect.off("multiSelect",this.$onMultiSelect),n.multiSelect.off("singleSelect",this.$onSingleSelect),n.multiSelect.lead.off("change",this.$checkMultiselectChange),n.multiSelect.anchor.off("change",this.$checkMultiselectChange)),t&&(t.multiSelect.on("addRange",this.$onAddRange),t.multiSelect.on("removeRange",this.$onRemoveRange),t.multiSelect.on("multiSelect",this.$onMultiSelect),t.multiSelect.on("singleSelect",this.$onSingleSelect),t.multiSelect.lead.on("change",this.$checkMultiselectChange),t.multiSelect.anchor.on("change",this.$checkMultiselectChange)),t&&this.inMultiSelectMode!=t.selection.inMultiSelectMode&&(t.selection.inMultiSelectMode?this.$onMultiSelect():this.$onSingleSelect())},t.MultiSelect=m,e("./config").defineOptions(d.prototype,"editor",{enableMultiselect:{set:function(e){m(this),e?(this.on("changeSession",this.$multiselectOnSessionChange),this.on("mousedown",o)):(this.off("changeSession",this.$multiselectOnSessionChange),this.off("mousedown",o))},value:!0},enableBlockSelect:{set:function(e){this.$blockSelectEnabled=e},value:!0}})}),ace.define("ace/mode/folding/fold_mode",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../../range").Range,i=t.FoldMode=function(){};(function(){this.foldingStartMarker=null,this.foldingStopMarker=null,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);return this.foldingStartMarker.test(r)?"start":t=="markbeginend"&&this.foldingStopMarker&&this.foldingStopMarker.test(r)?"end":""},this.getFoldWidgetRange=function(e,t,n){return null},this.indentationBlock=function(e,t,n){var i=/\S/,s=e.getLine(t),o=s.search(i);if(o==-1)return;var u=n||s.length,a=e.getLength(),f=t,l=t;while(++tf){var p=e.getLine(l).length;return new r(f,u,l,p)}},this.openingBracketBlock=function(e,t,n,i,s){var o={row:n,column:i+1},u=e.$findClosingBracket(t,o,s);if(!u)return;var a=e.foldWidgets[u.row];return a==null&&(a=e.getFoldWidget(u.row)),a=="start"&&u.row>o.row&&(u.row--,u.column=e.getLine(u.row).length),r.fromPoints(o,u)},this.closingBracketBlock=function(e,t,n,i,s){var o={row:n,column:i},u=e.$findOpeningBracket(t,o);if(!u)return;return u.column++,o.column--,r.fromPoints(u,o)}}).call(i.prototype)}),ace.define("ace/theme/textmate",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";t.isDark=!1,t.cssClass="ace-tm",t.cssText='.ace-tm .ace_gutter {background: #f0f0f0;color: #333;}.ace-tm .ace_print-margin {width: 1px;background: #e8e8e8;}.ace-tm .ace_fold {background-color: #6B72E6;}.ace-tm {background-color: #FFFFFF;color: black;}.ace-tm .ace_cursor {color: black;}.ace-tm .ace_invisible {color: rgb(191, 191, 191);}.ace-tm .ace_storage,.ace-tm .ace_keyword {color: blue;}.ace-tm .ace_constant {color: rgb(197, 6, 11);}.ace-tm .ace_constant.ace_buildin {color: rgb(88, 72, 246);}.ace-tm .ace_constant.ace_language {color: rgb(88, 92, 246);}.ace-tm .ace_constant.ace_library {color: rgb(6, 150, 14);}.ace-tm .ace_invalid {background-color: rgba(255, 0, 0, 0.1);color: red;}.ace-tm .ace_support.ace_function {color: rgb(60, 76, 114);}.ace-tm .ace_support.ace_constant {color: rgb(6, 150, 14);}.ace-tm .ace_support.ace_type,.ace-tm .ace_support.ace_class {color: rgb(109, 121, 222);}.ace-tm .ace_keyword.ace_operator {color: rgb(104, 118, 135);}.ace-tm .ace_string {color: rgb(3, 106, 7);}.ace-tm .ace_comment {color: rgb(76, 136, 107);}.ace-tm .ace_comment.ace_doc {color: rgb(0, 102, 255);}.ace-tm .ace_comment.ace_doc.ace_tag {color: rgb(128, 159, 191);}.ace-tm .ace_constant.ace_numeric {color: rgb(0, 0, 205);}.ace-tm .ace_variable {color: rgb(49, 132, 149);}.ace-tm .ace_xml-pe {color: rgb(104, 104, 91);}.ace-tm .ace_entity.ace_name.ace_function {color: #0000A2;}.ace-tm .ace_heading {color: rgb(12, 7, 255);}.ace-tm .ace_list {color:rgb(185, 6, 144);}.ace-tm .ace_meta.ace_tag {color:rgb(0, 22, 142);}.ace-tm .ace_string.ace_regex {color: rgb(255, 0, 0)}.ace-tm .ace_marker-layer .ace_selection {background: rgb(181, 213, 255);}.ace-tm.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px white;}.ace-tm .ace_marker-layer .ace_step {background: rgb(252, 255, 0);}.ace-tm .ace_marker-layer .ace_stack {background: rgb(164, 229, 101);}.ace-tm .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgb(192, 192, 192);}.ace-tm .ace_marker-layer .ace_active-line {background: rgba(0, 0, 0, 0.07);}.ace-tm .ace_gutter-active-line {background-color : #dcdcdc;}.ace-tm .ace_marker-layer .ace_selected-word {background: rgb(250, 250, 255);border: 1px solid rgb(200, 200, 250);}.ace-tm .ace_indent-guide {background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y;}',t.$id="ace/theme/textmate";var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)}),ace.define("ace/line_widgets",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/range"],function(e,t,n){"use strict";function o(e){this.session=e,this.session.widgetManager=this,this.session.getRowLength=this.getRowLength,this.session.$getWidgetScreenLength=this.$getWidgetScreenLength,this.updateOnChange=this.updateOnChange.bind(this),this.renderWidgets=this.renderWidgets.bind(this),this.measureWidgets=this.measureWidgets.bind(this),this.session._changedWidgets=[],this.$onChangeEditor=this.$onChangeEditor.bind(this),this.session.on("change",this.updateOnChange),this.session.on("changeFold",this.updateOnFold),this.session.on("changeEditor",this.$onChangeEditor)}var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./range").Range;(function(){this.getRowLength=function(e){var t;return this.lineWidgets?t=this.lineWidgets[e]&&this.lineWidgets[e].rowCount||0:t=0,!this.$useWrapMode||!this.$wrapData[e]?1+t:this.$wrapData[e].length+1+t},this.$getWidgetScreenLength=function(){var e=0;return this.lineWidgets.forEach(function(t){t&&t.rowCount&&!t.hidden&&(e+=t.rowCount)}),e},this.$onChangeEditor=function(e){this.attach(e.editor)},this.attach=function(e){e&&e.widgetManager&&e.widgetManager!=this&&e.widgetManager.detach();if(this.editor==e)return;this.detach(),this.editor=e,e&&(e.widgetManager=this,e.renderer.on("beforeRender",this.measureWidgets),e.renderer.on("afterRender",this.renderWidgets))},this.detach=function(e){var t=this.editor;if(!t)return;this.editor=null,t.widgetManager=null,t.renderer.off("beforeRender",this.measureWidgets),t.renderer.off("afterRender",this.renderWidgets);var n=this.session.lineWidgets;n&&n.forEach(function(e){e&&e.el&&e.el.parentNode&&(e._inDocument=!1,e.el.parentNode.removeChild(e.el))})},this.updateOnFold=function(e,t){var n=t.lineWidgets;if(!n||!e.action)return;var r=e.data,i=r.start.row,s=r.end.row,o=e.action=="add";for(var u=i+1;u0&&!r[i])i--;this.firstRow=n.firstRow,this.lastRow=n.lastRow,t.$cursorLayer.config=n;for(var o=i;o<=s;o++){var u=r[o];if(!u||!u.el)continue;if(u.hidden){u.el.style.top=-100-(u.pixelHeight||0)+"px";continue}u._inDocument||(u._inDocument=!0,t.container.appendChild(u.el));var a=t.$cursorLayer.getPixelPosition({row:o,column:0},!0).top;u.coverLine||(a+=n.lineHeight*this.session.getRowLineCount(u.row)),u.el.style.top=a-n.offset+"px";var f=u.coverGutter?0:t.gutterWidth;u.fixedWidth||(f-=t.scrollLeft),u.el.style.left=f+"px",u.fullWidth&&u.screenWidth&&(u.el.style.minWidth=n.width+2*n.padding+"px"),u.fixedWidth?u.el.style.right=t.scrollBar.getWidth()+"px":u.el.style.right=""}}}).call(o.prototype),t.LineWidgets=o}),ace.define("ace/ext/error_marker",["require","exports","module","ace/line_widgets","ace/lib/dom","ace/range"],function(e,t,n){"use strict";function o(e,t,n){var r=0,i=e.length-1;while(r<=i){var s=r+i>>1,o=n(t,e[s]);if(o>0)r=s+1;else{if(!(o<0))return s;i=s-1}}return-(r+1)}function u(e,t,n){var r=e.getAnnotations().sort(s.comparePoints);if(!r.length)return;var i=o(r,{row:t,column:-1},s.comparePoints);i<0&&(i=-i-1),i>=r.length?i=n>0?0:r.length-1:i===0&&n<0&&(i=r.length-1);var u=r[i];if(!u||!n)return;if(u.row===t){do u=r[i+=n];while(u&&u.row===t);if(!u)return r.slice()}var a=[];t=u.row;do a[n<0?"unshift":"push"](u),u=r[i+=n];while(u&&u.row==t);return a.length&&a}var r=e("../line_widgets").LineWidgets,i=e("../lib/dom"),s=e("../range").Range;t.showErrorMarker=function(e,t){var n=e.session;n.widgetManager||(n.widgetManager=new r(n),n.widgetManager.attach(e));var s=e.getCursorPosition(),o=s.row,a=n.widgetManager.getWidgetsAtRow(o).filter(function(e){return e.type=="errorMarker"})[0];a?a.destroy():o-=t;var f=u(n,o,t),l;if(f){var c=f[0];s.column=(c.pos&&typeof c.column!="number"?c.pos.sc:c.column)||0,s.row=c.row,l=e.renderer.$gutterLayer.$annotations[s.row]}else{if(a)return;l={text:["Looks good!"],className:"ace_ok"}}e.session.unfold(s.row),e.selection.moveToPosition(s);var h={row:s.row,fixedWidth:!0,coverGutter:!0,el:i.createElement("div"),type:"errorMarker"},p=h.el.appendChild(i.createElement("div")),d=h.el.appendChild(i.createElement("div"));d.className="error_widget_arrow "+l.className;var v=e.renderer.$cursorLayer.getPixelPosition(s).left;d.style.left=v+e.renderer.gutterWidth-5+"px",h.el.className="error_widget_wrapper",p.className="error_widget "+l.className,p.innerHTML=l.text.join("
"),p.appendChild(i.createElement("div"));var m=function(e,t,n){if(t===0&&(n==="esc"||n==="return"))return h.destroy(),{command:"null"}};h.destroy=function(){if(e.$mouseHandler.isMousePressed)return;e.keyBinding.removeKeyboardHandler(m),n.widgetManager.removeLineWidget(h),e.off("changeSelection",h.destroy),e.off("changeSession",h.destroy),e.off("mouseup",h.destroy),e.off("change",h.destroy)},e.keyBinding.addKeyboardHandler(m),e.on("changeSelection",h.destroy),e.on("changeSession",h.destroy),e.on("mouseup",h.destroy),e.on("change",h.destroy),e.session.widgetManager.addLineWidget(h),h.el.onmousedown=e.focus.bind(e),e.renderer.scrollCursorIntoView(null,.5,{bottom:h.el.offsetHeight})},i.importCssString(" .error_widget_wrapper { background: inherit; color: inherit; border:none } .error_widget { border-top: solid 2px; border-bottom: solid 2px; margin: 5px 0; padding: 10px 40px; white-space: pre-wrap; } .error_widget.ace_error, .error_widget_arrow.ace_error{ border-color: #ff5a5a } .error_widget.ace_warning, .error_widget_arrow.ace_warning{ border-color: #F1D817 } .error_widget.ace_info, .error_widget_arrow.ace_info{ border-color: #5a5a5a } .error_widget.ace_ok, .error_widget_arrow.ace_ok{ border-color: #5aaa5a } .error_widget_arrow { position: absolute; border: solid 5px; border-top-color: transparent!important; border-right-color: transparent!important; border-left-color: transparent!important; top: -5px; }","")}),ace.define("ace/ace",["require","exports","module","ace/lib/fixoldbrowsers","ace/lib/dom","ace/lib/event","ace/range","ace/editor","ace/edit_session","ace/undomanager","ace/virtual_renderer","ace/worker/worker_client","ace/keyboard/hash_handler","ace/placeholder","ace/multi_select","ace/mode/folding/fold_mode","ace/theme/textmate","ace/ext/error_marker","ace/config"],function(e,t,n){"use strict";e("./lib/fixoldbrowsers");var r=e("./lib/dom"),i=e("./lib/event"),s=e("./range").Range,o=e("./editor").Editor,u=e("./edit_session").EditSession,a=e("./undomanager").UndoManager,f=e("./virtual_renderer").VirtualRenderer;e("./worker/worker_client"),e("./keyboard/hash_handler"),e("./placeholder"),e("./multi_select"),e("./mode/folding/fold_mode"),e("./theme/textmate"),e("./ext/error_marker"),t.config=e("./config"),t.require=e,typeof define=="function"&&(t.define=define),t.edit=function(e,n){if(typeof e=="string"){var s=e;e=document.getElementById(s);if(!e)throw new Error("ace.edit can't find div #"+s)}if(e&&e.env&&e.env.editor instanceof o)return e.env.editor;var u="";if(e&&/input|textarea/i.test(e.tagName)){var a=e;u=a.value,e=r.createElement("pre"),a.parentNode.replaceChild(e,a)}else e&&(u=e.textContent,e.innerHTML="");var l=t.createEditSession(u),c=new o(new f(e),l,n),h={document:l,editor:c,onResize:c.resize.bind(c,null)};return a&&(h.textarea=a),i.addListener(window,"resize",h.onResize),c.on("destroy",function(){i.removeListener(window,"resize",h.onResize),h.editor.container.env=null}),c.container.env=c.env=h,c},t.createEditSession=function(e,t){var n=new u(e,t);return n.setUndoManager(new a),n},t.Range=s,t.Editor=o,t.EditSession=u,t.UndoManager=a,t.VirtualRenderer=f,t.version=t.config.version}); (function() { ace.require(["ace/ace"], function(a) { if (a) { a.config.init(true); a.define = ace.define; } if (!window.ace) window.ace = a; for (var key in a) if (a.hasOwnProperty(key)) window.ace[key] = a[key]; window.ace["default"] = window.ace; if (typeof module == "object" && typeof exports == "object" && module) { module.exports = window.ace; } }); })(); ================================================ FILE: static/js/app.js ================================================ var appInfo = {}; var appFeatures = {}; var editor = null; var connected = false; var bookmarks = {}; var default_rows_limit = 100; var currentObject = null; var autocompleteObjects = []; var inputResizing = false; var inputResizeOffset = null; var filterOptions = { "equal": "= 'DATA'", "not_equal": "!= 'DATA'", "greater": "> 'DATA'" , "greater_eq": ">= 'DATA'", "less": "< 'DATA'", "less_eq": "<= 'DATA'", "like": "LIKE 'DATA'", "ilike": "ILIKE 'DATA'", "null": "IS NULL", "not_null": "IS NOT NULL" }; function getSessionId() { var id = sessionStorage.getItem("session_id"); if (!id) { id = guid(); sessionStorage.setItem("session_id", id); } return id; } function setRowsLimit(num) { localStorage.setItem("rows_limit", num); } function getRowsLimit() { return parseInt(localStorage.getItem("rows_limit") || default_rows_limit); } function getPaginationOffset() { var page = $(".current-page").data("page"); var limit = getRowsLimit(); return (page - 1) * limit; } function getPagesCount(rowsCount) { var limit = getRowsLimit(); var num = parseInt(rowsCount / limit); if ((num * limit) < rowsCount) { num++; } return num; } function apiCall(method, path, params, cb) { var timeout = appFeatures.query_timeout; if (timeout == null) { timeout = 300; // in seconds } $.ajax({ timeout: timeout * 1000, // in milliseconds url: "api" + path, method: method, cache: false, data: params, headers: { "x-session-id": getSessionId() }, success: cb, error: function(xhr, status, data) { switch(status) { case "error": if (xhr.readyState == 0) { // 0 = UNSENT showErrorBanner("Sorry, something went wrong with your request. Refresh the page and try again!"); } break; case "timeout": return cb({ error: "Query timeout after " + timeout + "s" }); } var responseText; try { responseText = jQuery.parseJSON(xhr.responseText); } catch { responseText = { error: "Failed to parse the JSON response." }; } cb(responseText); } }); } function getInfo(cb) { apiCall("get", "/info", {}, cb); } function getConnection(cb) { apiCall("get", "/connection", {}, cb); } function getServerSettings(cb) { apiCall("get", "/server_settings", {}, cb); } function getSchemas(cb) { apiCall("get", "/schemas", {}, cb); } function getObjects(cb) { apiCall("get", "/objects", {}, cb); } function getTables(cb) { apiCall("get", "/tables", {}, cb); } function getTableRows(table, opts, cb) { apiCall("get", "/tables/" + table + "/rows", opts, cb); } function getTableStructure(table, opts, cb) { apiCall("get", "/tables/" + table, opts, cb); } function getTableIndexes(table, cb) { apiCall("get", "/tables/" + table + "/indexes", {}, cb); } function getTableConstraints(table, cb) { apiCall("get", "/tables/" + table + "/constraints", {}, cb); } function getTablesStats(cb) { apiCall("get", "/tables_stats", {}, cb); } function getFunction(id, cb) { apiCall("get", "/functions/" + id, {}, cb); } function getHistory(cb) { apiCall("get", "/history", {}, cb); } function getBookmarks(cb) { apiCall("get", "/bookmarks", {}, cb); } function executeQuery(query, cb) { apiCall("post", "/query", { query: query }, cb); } function explainQuery(query, cb) { apiCall("post", "/explain", { query: query }, cb); } function analyzeQuery(query, cb) { apiCall("post", "/analyze", { query: query }, cb); } function disconnect(cb) { apiCall("post", "/disconnect", {}, cb); } function encodeQuery(query) { return Base64.encode(query).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "."); } function showErrorBanner(text) { if (window.errBannerTimeout != null) { clearTimeout(window.errBannerTimeout); } window.errBannerTimeout = setTimeout(function() { $("#error_banner").fadeOut("fast").text(""); }, 3000); $("#error_banner").text(text).show(); } function buildSchemaSection(name, objects) { var section = ""; var titles = { "table": "Tables", "view": "Views", "materialized_view": "Materialized Views", "function": "Functions", "sequence": "Sequences" }; var icons = { "table": '', "view": '', "materialized_view": '', "function": '', "sequence": '' }; var klass = ""; if (name == "public") klass = "expanded"; section += "
"; section += "
" + name + "
"; section += "
"; ["table", "view", "materialized_view", "function", "sequence"].forEach(function(group) { group_klass = ""; if (name == "public" && group == "table") group_klass = "expanded"; section += "
"; section += "
" + titles[group] + " " + objects[group].length + "
"; section += "
    "; if (objects[group]) { objects[group].forEach(function(item) { var id = name + "." + item.name; // Use function OID since multiple functions with the same name might exist if (group == "function") { id = item.oid; } section += "
  • " + icons[group] + " " + item.name + "
  • "; }); section += "
"; } }); section += "
"; return section; } function loadLocalQueries() { if (!appFeatures.local_queries) return; $("body").on("click", "a.load-local-query", function(e) { var id = $(this).data("id"); apiCall("get", "/local_queries/" + id, {}, function(resp) { editor.setValue(resp.query); editor.clearSelection(); }); }); apiCall("get", "/local_queries", {}, function(resp) { if (resp.error) return; var container = $("#load-query-dropdown").find(".dropdown-menu"); resp.forEach(function(item) { var title = item.title || item.id; $("
  • " + title + "
  • ").appendTo(container); }); if (resp.length > 0) $("#load-local-query").prop("disabled", ""); $("#load-query-dropdown").show(); }); } function loadSchemas() { $("#objects").html(""); var emptyObjectList = function() { return { table: [], view: [], materialized_view: [], function: [], sequence: [] } } getSchemas(function(schemasData) { if (schemasData.error) { alert("Error while fetching schemas: " + schemasData.error); return; } getObjects(function(data) { if (data.error) { alert("Error while fetching database objects: " + data.error); return; } if (Object.keys(data).length == 0) { data["public"] = emptyObjectList(); } for (schemaName of schemasData) { // Allow users to see empty schemas if we dont have any objects in them if (!data[schemaName]) { data[schemaName] = emptyObjectList(); } $(buildSchemaSection(schemaName, data[schemaName])).appendTo("#objects"); } if (Object.keys(data).length == 1) { $(".schema").addClass("expanded"); } // Clear out all autocomplete objects autocompleteObjects = []; for (schema in data) { for (kind in data[schema]) { if (!(kind == "table" || kind == "view" || kind == "materialized_view" || kind == "function")) { continue } for (item in data[schema][kind]) { autocompleteObjects.push({ caption: data[schema][kind][item].name, value: data[schema][kind][item].name, meta: kind }); } } } bindContextMenus(); }); }); } function escapeHtml(str) { if (str != null || str != undefined) { return jQuery("
    ").text(str).html(); } return "null"; } function unescapeHtml(str){ var e = document.createElement("div"); e.innerHTML = str; return e.childNodes.length === 0 ? "" : e.childNodes[0].nodeValue; } function getCurrentObject() { return currentObject || { name: "", type: "" }; } function resetTable() { $("#results_header").html(""); $("#results_body").html(""); $("#results_view").html("").hide(); $("#results"). data("mode", ""). removeClass("empty"). removeClass("no-crop"). show(); } function performTableAction(table, action, el) { if (action == "truncate" || action == "delete") { var message = "Are you sure you want to " + action + " table " + table + " ?"; if (!confirm(message)) return; } switch(action) { case "truncate": executeQuery("TRUNCATE TABLE " + table, function(data) { if (data.error) alert(data.error); resetTable(); }); break; case "delete": executeQuery("DROP TABLE " + table, function(data) { if (data.error) alert(data.error); loadSchemas(); resetTable(); }); break; case "export": var format = el.data("format"); var db = $("#current_database").text(); var filename = db + "." + table + "." + format; var query = "SELECT * FROM " + table; openInNewWindow("api/query", { "format": format, "filename": filename, "query": query }); break; case "dump": openInNewWindow("api/export", { "table": table }); break; case "copy": copyToClipboard(table.split('.')[1]); break; case "analyze": executeQuery("ANALYZE " + table, function(data) { if (data.error) alert(data.error); resetTable(); }); break; } } function performViewAction(view, action, el) { if (action == "delete") { var message = "Are you sure you want to " + action + " view " + view + " ?"; if (!confirm(message)) return; } switch(action) { case "delete": executeQuery("DROP VIEW " + view, function(data) { if (data.error) alert(data.error); loadSchemas(); resetTable(); }); break; case "export": var format = el.data("format"); var db = $("#current_database").text(); var filename = db + "." + view + "." + format; var query = "SELECT * FROM " + view; openInNewWindow("api/query", { "format": format, "filename": filename, "query": query }); break; case "copy": copyToClipboard(view.split('.')[1]); break; case "copy_def": executeQuery("SELECT pg_get_viewdef('" + view + "', true);", function(data) { if (data.error) { alert(data.error); return; } copyToClipboard(data.rows[0]); }); break; case "view_def": executeQuery("SELECT pg_get_viewdef('" + view + "', true);", function(data) { if (data.error) { alert(data.error); return; } showViewDefinition(view, data.rows[0]); }); break; } } function performRowAction(action, value) { if (action == "stop_query") { if (!confirm("Are you sure you want to stop the query?")) return; executeQuery("SELECT pg_cancel_backend(" + value + ");", function(data) { if (data.error) alert(data.error); setTimeout(showActivityPanel, 1000); }); } } function sortArrow(direction) { switch (direction) { case "ASC": return "▲"; case "DESC": return "▼"; default: return ""; } } function buildTable(results, sortColumn, sortOrder, options) { if (!options) options = {}; var action = options.action; resetTable(); if (results.error) { $("#results_header").html(""); $("#results_body").html("ERROR: " + results.error + ""); return; } if (results.rows.length == 0) { $("#results_header").html(""); $("#results_body").html("No records found"); if (results.stats) { $("#result-rows-count").html(results.stats.query_duration_ms + " ms"); } else { $("#result-rows-count").html(""); } $("#results").addClass("empty"); return; } var cols = ""; var rows = ""; results.columns.forEach(function(col) { if (col === sortColumn) { cols += "" + col + " " + sortArrow(sortOrder) + ""; } else { cols += "" + col + ""; } }); // No header to make the column non-sortable if (action) { cols += ""; // Determine which column contains the data attribute action.dataColumn = results.columns.indexOf(action.data); } results.rows.forEach(function(row) { var r = ""; // Add all actual row data here for (i in row) { r += "
    " + escapeHtml(row[i]) + "
    "; } // Add row action button if (action) { r += "" + action.title + ""; } rows += "" + r + ""; }); $("#results_header").html(cols); $("#results_body").html(rows); // Show number of rows rendered on the page if (results.stats) { $("#result-rows-count").html(results.stats.rows_count + " rows in " + results.stats.query_duration_ms + " ms"); } else { $("#result-rows-count").html(results.rows.length + " rows"); } } function setCurrentTab(id) { // Pagination should only be visible on rows tab if (id != "table_content") { $("#body").removeClass("with-pagination"); } $("#nav ul li.selected").removeClass("selected"); $("#" + id).addClass("selected"); // Persist tab selection into the session storage sessionStorage.setItem("tab", id); } function showQueryHistory() { getHistory(function(data) { var rows = []; for(i in data) { rows.unshift([parseInt(i) + 1, data[i].query, data[i].timestamp]); } buildTable({ columns: ["id", "query", "timestamp"], rows: rows }); setCurrentTab("table_history"); $("#input").hide(); $("#body").prop("class", "full"); $("#results").addClass("no-crop"); }); } function showTableIndexes() { var name = getCurrentObject().name; if (name.length == 0) { alert("Please select a table!"); return; } getTableIndexes(name, function(data) { setCurrentTab("table_indexes"); buildTable(data); $("#input").hide(); $("#body").prop("class", "full"); $("#results").addClass("no-crop"); }); } function showTableConstraints() { var name = getCurrentObject().name; if (name.length == 0) { alert("Please select a table!"); return; } getTableConstraints(name, function(data) { setCurrentTab("table_constraints"); buildTable(data); $("#input").hide(); $("#body").prop("class", "full"); $("#results").addClass("no-crop"); }); } function showTableInfo() { var name = getCurrentObject().name; if (name.length == 0) { alert("Please select a table!"); return; } apiCall("get", "/tables/" + name + "/info", {}, function(data) { $(".table-information .lines").show(); $("#table_total_size").text(data.total_size); $("#table_data_size").text(data.data_size); $("#table_index_size").text(data.index_size); $("#table_rows_count").text(data.rows_count); $("#table_encoding").text("Unknown"); }); buildTableFilters(name, getCurrentObject().type); } function updatePaginator(pagination) { if (!pagination) { $(".current-page").data("page", 1).data("pages", 1); $("button.page").text("1 of 1"); $(".prev-page, .next-page").prop("disabled", "disabled"); return; } $(".current-page"). data("page", pagination.page). data("pages", pagination.pages_count); if (pagination.page > 1) { $(".prev-page").prop("disabled", ""); } else { $(".prev-page").prop("disabled", "disabled"); } if (pagination.pages_count > 1 && pagination.page < pagination.pages_count) { $(".next-page").prop("disabled", ""); } else { $(".next-page").prop("disabled", "disabled"); } $("#total_records").text(pagination.rows_count); if (pagination.pages_count == 0) pagination.pages_count = 1; $("button.page").text(pagination.page + " of " + pagination.pages_count); } function showTableContent(sortColumn, sortOrder) { var name = getCurrentObject().name; if (name.length == 0) { alert("Please select a table!"); return; } if (getCurrentObject().type == "function") { alert("Cant view rows for a function"); return; } var opts = { limit: getRowsLimit(), offset: getPaginationOffset(), sort_column: sortColumn, sort_order: sortOrder }; var filter = { column: $(".filters select.column").val(), op: $(".filters select.filter").val(), input: $(".filters input").val() }; // Apply filtering only if column is selected if (filter.column && filter.op) { var where = [ '"' + filter.column + '"', filterOptions[filter.op].replace("DATA", filter.input) ].join(" "); opts["where"] = where; } getTableRows(name, opts, function(data) { $("#input").hide(); $("#body").prop("class", "with-pagination"); buildTable(data, sortColumn, sortOrder); setCurrentTab("table_content"); updatePaginator(data.pagination); $("#results").data("mode", "browse").data("table", name); }); } function showPaginatedTableContent() { var activeColumn = $("#results th.active"); var sortColumn = null; var sortOrder = null; if (activeColumn.length) { sortColumn = activeColumn.data("name"); sortOrder = activeColumn.data("order"); } showTableContent(sortColumn, sortOrder); } function showDatabaseStats() { getTablesStats(function(data) { buildTable(data); setCurrentTab("table_structure"); $("#input").hide(); $("#body").prop("class", "full"); $("#results").addClass("no-crop"); }); } function downloadDatabaseStats() { openInNewWindow("api/tables_stats", { format: "csv", export: "true" }); } function showServerSettings() { getServerSettings(function(data) { buildTable(data); setCurrentTab("table_content"); $("#input").hide(); $("#body").prop("class", "full"); $("#results").addClass("no-crop"); }); } function showTableStructure() { var name = getCurrentObject().name; if (name.length == 0) { alert("Please select a table!"); return; } setCurrentTab("table_structure"); $("#input").hide(); $("#body").prop("class", "full"); getTableStructure(name, { type: getCurrentObject().type }, function(data) { if (getCurrentObject().type == "function") { var name = data.rows[0][data.columns.indexOf("proname")]; var definition = data.rows[0][data.columns.indexOf("functiondef")]; showFunctionDefinition(name, definition); return } buildTable(data); $("#results").addClass("no-crop"); }); } function showViewDefinition(viewName, viewDefintion) { setCurrentTab("table_structure"); renderResultsView("View definition for: " + viewName + "", viewDefintion); } function showFunctionDefinition(functionName, definition) { setCurrentTab("table_structure"); renderResultsView("Function definition for: " + functionName + "", definition) } function renderResultsView(title, content) { $("#results").addClass("no-crop"); $("#input").hide(); $("#body").prop("class", "full"); $("#results").hide(); var title = $("
    ").prop("class", "title").html(title); var content = $("
    ").text(content);
    
      $("
    "). html(""). addClass("copy"). appendTo(content); $("#results_view").html(""); title.appendTo("#results_view"); content.appendTo("#results_view"); $("#results_view").show(); } function showQueryPanel() { if (!$("#table_query").hasClass("selected")) { resetTable(); } setCurrentTab("table_query"); editor.focus(); $("#input").show(); $("#body").prop("class", "") } function showConnectionPanel() { setCurrentTab("table_connection"); $("#input").hide(); $("#body").addClass("full"); getConnection(function(data) { var rows = []; for(key in data) { rows.push([key, data[key]]); } buildTable({ columns: ["attribute", "value"], rows: rows }); }); } function showActivityPanel() { var options = { action: { name: "stop_query", title: "stop", data: "pid", style: "danger" } } setCurrentTab("table_activity"); $("#input").hide(); $("#body").addClass("full"); apiCall("get", "/activity", {}, function(data) { buildTable(data, null, null, options); }); } function showQueryProgressMessage() { $("#run, #explain-dropdown-toggle, #csv, #json, #xml, #load-local-query").prop("disabled", true); $("#explain-dropdown").removeClass("open"); $("#query_progress").show(); } function hideQueryProgressMessage() { $("#run, #explain-dropdown-toggle, #csv, #json, #xml, #load-local-query").prop("disabled", false); $("#query_progress").hide(); } function getEditorSelection() { // Return the exact selection if user has one var query = $.trim(editor.getSelectedText()); if (query.length > 0) { return query; } query = editor.getValue(); // Determine which query we should run when there are multiple queries without a delimiter if (query.indexOf(";") == -1) { var subquery = getSubquery(query, editor.getCursorPosition()); if (subquery) { // Highlight query selection so user knows what is being executed if (subquery.numChunks > 1) { editor.selection.setSelectionRange({ start: { row: subquery.startRow, column: 0 }, end: { row: subquery.endRow, column: 0 }, }) } return subquery.text; } } return query; } function getSubquery(text, cursor) { var lines = text.split("\n"); var startRow = undefined; var numChunks = 0; var ranges = []; for (i = 0; i < lines.length; i++) { if (lines[i].trim().length == 0) { if (startRow >= 0 && cursor.row >= startRow && cursor.row <= i) { ranges.push([startRow, i]); } numChunks++; startRow = undefined; continue; } if (startRow === undefined) { startRow = i; } if (i == lines.length - 1) { ranges.push([startRow, i + 1]); numChunks++; } } if (ranges.length > 0) { return { text: lines.slice(ranges[0][0], ranges[0][1]).join("\n"), startRow: ranges[0][0], endRow: ranges[0][1], numChunks: numChunks }; } } function runQuery() { setCurrentTab("table_query"); showQueryProgressMessage(); var query = getEditorSelection(); if (query.length == 0) { hideQueryProgressMessage(); return; } executeQuery(query, function(data) { buildTable(data); hideQueryProgressMessage(); $("#input").show(); $("#body").removeClass("full"); $("#results").data("mode", "query"); if (query.toLowerCase().indexOf("explain") != -1) { $("#results").addClass("no-crop"); } // Reload objects list if anything was created/deleted if (query.match(/(create|drop)\s/i)) { loadSchemas(); } }); } function runExplain() { setCurrentTab("table_query"); showQueryProgressMessage(); var query = getEditorSelection(); if (query.length == 0) { hideQueryProgressMessage(); return; } explainQuery(query, function(data) { buildTable(data); hideQueryProgressMessage(); $("#input").show(); $("#body").removeClass("full"); $("#results").addClass("no-crop"); }); } function runAnalyze() { setCurrentTab("table_query"); showQueryProgressMessage(); var query = getEditorSelection(); if (query.length == 0) { hideQueryProgressMessage(); return; } analyzeQuery(query, function(data) { buildTable(data); hideQueryProgressMessage(); $("#input").show(); $("#body").removeClass("full"); $("#results").addClass("no-crop"); }); } function generateURL(path, params) { var url = new URL(window.location.href.split("#")[0]); url.pathname += path; for (key in params) { url.searchParams.append(key, params[key]); } // Automatically append session id so we dont have to do that everywhere url.searchParams.append("_session_id", getSessionId()); return url.toString(); } function openInNewWindow(path, params) { var url = generateURL(path, params); var win = window.open(url, '_blank'); win.focus(); } function exportTo(format) { var query = getEditorSelection(); if (query.length == 0) { return; } setCurrentTab("table_query"); openInNewWindow("api/query", { "format": format, "query": encodeQuery(query) }) } // Fetch all unique values for the selected column in the table function showUniqueColumnsValues(table, column, showCounts) { var query = 'SELECT DISTINCT "' + column + '" FROM ' + table; // Display results ordered by counts. // This could be slow on large sets without an index. if (showCounts) { query = 'SELECT DISTINCT "' + column + '", COUNT(1) AS total_count FROM ' + table + ' GROUP BY "' + column + '" ORDER BY total_count DESC'; } executeQuery(query, function(data) { $("#input").hide(); $("#body").prop("class", "full"); $("#results").data("mode", "query"); buildTable(data); }); } // Show numeric stats on the field function showFieldNumStats(table, column) { var query = 'SELECT count(1), min(' + column + '), max(' + column + '), avg(' + column + ') FROM ' + table; executeQuery(query, function(data) { $("#input").hide(); $("#body").prop("class", "full"); $("#results").data("mode", "query"); buildTable(data); }); } function buildTableFilters(name, type) { getTableStructure(name, { type: type }, function(data) { if (data.rows.length == 0) { $("#pagination .filters").hide(); } else { $("#pagination .filters").show(); } $("#pagination select.column").html(""); for (var i = 0; i < data.rows.length; i++) { var row = data.rows[i]; var el = $("").appendTo("#connection_bookmarks"); // Add all available bookmarks for (key of data) { $("").appendTo("#connection_bookmarks"); } $(".bookmarks").show(); } else { if (appFeatures.bookmarks_only) { $("#connection_error").html("Running in bookmarks-only mode but NO bookmarks configured.").show(); $(".open-connection").hide(); } else { $(".bookmarks").hide(); } } }); } function initConnectionWindow() { if (appFeatures.bookmarks_only) { $(".connection-group-switch").hide(); $(".connection-scheme-group").hide(); $(".connection-bookmarks-group").show(); $(".connection-standard-group").hide(); $(".connection-ssh-group").hide(); } else { $(".connection-group-switch").show(); $(".connection-scheme-group").hide(); $(".connection-bookmarks-group").show(); $(".connection-standard-group").show(); $(".connection-ssh-group").hide(); } } function getConnectionString() { var url = $.trim($("#connection_url").val()); var mode = $(".connection-group-switch button.active").attr("data"); var ssl = $("#connection_ssl").val(); if (mode == "standard" || mode == "ssh") { var host = $("#pg_host").val(); var port = $("#pg_port").val(); var user = $("#pg_user").val(); var pass = encodeURIComponent($("#pg_password").val()); var db = $("#pg_db").val(); if (port.length == 0) { port = "5432"; } url = "postgres://" + user + ":" + pass + "@" + host + ":" + port + "/" + db + "?sslmode=" + ssl; } else { var local = url.indexOf("localhost") != -1 || url.indexOf("127.0.0.1") != -1; if (local && url.indexOf("sslmode") == -1) { url += "?sslmode=" + ssl; } } return url; } // Add a context menu to the results table header columns function bindTableHeaderMenu() { $("#results_header").contextmenu({ scopes: "th", target: "#results_header_menu", before: function(e, element, target) { // Enable menu for browsing table rows view only. if ($("#results").data("mode") != "browse") { e.preventDefault(); this.closemenu(); return false; } }, onItem: function(context, e) { var menuItem = $(e.target); switch(menuItem.data("action")) { case "copy_name": copyToClipboard($(context).data("name")); break; case "unique_values": showUniqueColumnsValues( $("#results").data("table"), // table name $(context).data("name"), // column name menuItem.data("counts") // display counts ); break; case "num_stats": showFieldNumStats( $("#results").data("table"), // table name $(context).data("name") // column name ); break; } } }); $("#results_body").contextmenu({ scopes: "td", target: "#results_row_menu", before: function(e, element, target) { var browseMode = $("#results").data("mode"); var isEmpty = $("#results").hasClass("empty"); var isAllowed = browseMode == "browse" || browseMode == "query"; if (isEmpty || !isAllowed) { e.preventDefault(); this.closemenu(); return false; } }, onItem: function(context, e) { var menuItem = $(e.target); switch(menuItem.data("action")) { case "display_value": var value = $(context).text(); $("#content_modal .content").text(value); $("#content_modal").show(); break; case "copy_value": copyToClipboard($(context).text()); break; case "filter_by_value": var colIdx = $(context).data("col"); var colValue = $(context).text(); var colName = $("#results_header th").eq(colIdx).data("name"); $("select.column").val(colName); $("select.filter").val("equal"); $("#table_filter_value").val(colValue); $("#rows_filter").submit(); } } }); } function bindCurrentDatabaseMenu() { $("#current_database").contextmenu({ target: "#current_database_context_menu", onItem: function(context, e) { var menuItem = $(e.target); switch(menuItem.data("action")) { case "show_db_stats": showDatabaseStats(); break; case "download_db_stats": downloadDatabaseStats(); break; case "server_settings": showServerSettings(); break; case "export": openInNewWindow("api/export"); break; } } }); } function bindDatabaseObjectsFilter() { var filterTimeout = null; $("#filter_database_objects").on("keyup", function (e) { clearTimeout(filterTimeout); var val = $(this).val().trim(); // Reset search on ESC if (e.keyCode == 27 || val == "") { resetObjectsFilter(); return; } $(".clear-objects-filter").show(); $(".schema-group").addClass("expanded"); filterTimeout = setTimeout(function() { filterObjectsByName(val) }, 200); }); $(".clear-objects-filter").on("click", function(e) { resetObjectsFilter(); }); } function resetObjectsFilter() { $("#filter_database_objects").val(""); $("#objects li.schema-item").show(); $(".clear-objects-filter").hide(); } function filterObjectsByName(query) { $("#objects li.schema-item").each(function (idx, el) { var item = $(el); var name = $(el).data("name"); if (name.indexOf(query) < 0) { item.hide(); } else { item.show(); } }); } function getQuotedSchemaTableName(table) { if (typeof table === "string" && table.indexOf(".") > -1) { var schemaTableComponents = table.split("."); return ['"', schemaTableComponents[0], '"."', schemaTableComponents[1], '"'].join(''); } return table; } function bindContextMenus() { bindTableHeaderMenu(); bindCurrentDatabaseMenu(); $(".schema-group ul").each(function(id, el) { var group = $(el).data("group"); if (group == "table") { $(el).contextmenu({ target: "#tables_context_menu", scopes: "li.schema-table", onItem: function(context, e) { var el = $(e.target); var table = getQuotedSchemaTableName($(context[0]).data("id")); var action = el.data("action"); performTableAction(table, action, el); } }); } if (group == "view") { $(el).contextmenu({ target: "#view_context_menu", scopes: "li.schema-view", onItem: function(context, e) { var el = $(e.target); var table = getQuotedSchemaTableName($(context[0]).data("id")); var action = el.data("action"); performViewAction(table, action, el); } }); } if (group == "materialized_view") { $(el).contextmenu({ target: "#view_context_menu", scopes: "li.schema-materialized_view", onItem: function(context, e) { var el = $(e.target); var table = getQuotedSchemaTableName($(context[0]).data("id")); var action = el.data("action"); performViewAction(table, action, el); } }); } }); } function toggleDatabaseSearch() { $("#current_database").toggle(); $("#database_search").toggle(); } function enableDatabaseSearch(data) { var input = $("#database_search"); input.typeahead("destroy"); input.typeahead({ source: data, minLength: 0, items: "all", autoSelect: false, fitToElement: true }); input.typeahead("lookup").focus(); input.on("focusout", function(e){ toggleDatabaseSearch(); input.off("focusout"); }); } function bindInputResizeEvents() { var height = sessionStorage.getItem("input_height"); if (height) { resizeInput(height); checkInputSize(); } $("body").on("mousemove", onInputResize); $("body").on("mouseup", endInputResize); $("#input_resize_handler").on("mousedown", beginInputResize); $(window).on("resize", checkInputSize); } function checkInputSize() { var inputHeight = $("#input").height(); var bodyHeight = $("#body").height(); if (bodyHeight == 0 || inputHeight == 0) return; if (inputHeight > bodyHeight || bodyHeight - inputHeight < 200) { resizeInput(bodyHeight - 200); } } function resizeInput(height) { if (height < 100) height = 100; var diff = 50 + 12; // actions box + padding $("#input").height(height); $("#input .input-wrapper").height(height - diff); $("#custom_query").height(height - diff); $("#output").css("top", height + "px"); if (editor) { editor.resize(); } } function beginInputResize() { inputResizing = true; inputResizeOffset = $("#input").offset().top; $("html").css("cursor", "row-resize"); $("#input_resize_handler").addClass("dragging"); } function endInputResize() { if (!inputResizing) return; inputResizing = false; inputResizeOffset = null; $("html").css("cursor", "auto"); $("#input_resize_handler").removeClass("dragging"); // Save current settings for page reloads sessionStorage.setItem("input_height", $("#input").height()); } function onInputResize(event) { if (!inputResizing) return; var computedHeight = event.clientY - inputResizeOffset; if (computedHeight < 150) computedHeight = 150; resizeInput(computedHeight); } function bindContentModalEvents() { var contentModal = document.getElementById("content_modal"); $(window).on("click", function(e) { // Automatically hide the modal on any click outside of the modal window if (e.target && !contentModal.contains(e.target)) { $("#content_modal").hide(); } }); $("#content_modal .content-modal-action").on("click", function() { switch ($(this).data("action")) { case "copy": copyToClipboard($("#content_modal pre").text()); break; case "close": $("#content_modal").hide(); break; } }); $("#results").on("dblclick", "td > div", function() { var value = unescapeHtml($(this).html()); if (!value) return; $("#content_modal pre").html(value); $("#content_modal").show(); }) } $(document).ready(function() { bindInputResizeEvents(); bindContentModalEvents(); $("#table_content").on("click", function() { showTableContent(); }); $("#table_structure").on("click", function() { showTableStructure(); }); $("#table_indexes").on("click", function() { showTableIndexes(); }); $("#table_constraints").on("click", function() { showTableConstraints(); }); $("#table_history").on("click", function() { showQueryHistory(); }); $("#table_query").on("click", function() { showQueryPanel(); }); $("#table_connection").on("click", function() { showConnectionPanel(); }); $("#table_activity").on("click", function() { showActivityPanel(); }); $("#run").on("click", function() { runQuery(); }); $("#explain").on("click", function() { runExplain(); }); $("#analyze").on("click", function() { runAnalyze(); }); $("#csv").on("click", function() { exportTo("csv"); }); $("#json").on("click", function() { exportTo("json"); }); $("#xml").on("click", function() { exportTo("xml"); }); $("#results_view").on("click", ".copy", function() { copyToClipboard($(this).parent().text()); }); $("#results").on("click", "tr", function(e) { $("#results tr.selected").removeClass(); $(this).addClass("selected"); }); $("#objects").on("click", ".schema-group-title", function(e) { $(this).parent().toggleClass("expanded"); }); $("#objects").on("click", ".schema-name", function(e) { $(this).parent().toggleClass("expanded"); }); $("#objects").on("click", "li", function(e) { currentObject = { name: $(this).data("id"), type: $(this).data("type") }; $("#objects li").removeClass("active"); $(this).addClass("active"); $(".current-page").data("page", 1); $(".filters select, .filters input").val(""); if (currentObject.type == "function") { sessionStorage.setItem("tab", "table_structure"); } else { showTableInfo(); } switch(sessionStorage.getItem("tab")) { case "table_content": showTableContent(); break; case "table_structure": showTableStructure(); break; case "table_constraints": showTableConstraints(); break; case "table_indexes": showTableIndexes(); break; default: showTableContent(); } }); $("#results").on("click", "a.row-action", function(e) { e.preventDefault(); var action = $(this).data("action"); var value = $(this).data("value"); performRowAction(action, value); }) $("#results").on("click", "th", function(e) { if (!$("#table_content").hasClass("selected")) return; var sortColumn = $(this).data("name"); var sortOrder = $(this).data("order") === "ASC" ? "DESC" : "ASC"; $(this).data("order", sortOrder); showTableContent(sortColumn, sortOrder); }); $("#refresh_tables").on("click", function() { loadSchemas(); }); $("#rows_filter").on("submit", function(e) { e.preventDefault(); $(".current-page").data("page", 1); var column = $(this).find("select.column").val(); var filter = $(this).find("select.filter").val(); var query = $.trim($(this).find("input").val()); if (filter && filterOptions[filter].indexOf("DATA") > 0 && query == "") { alert("Please specify filter query"); return } showTableContent(); }); $(".change-limit").on("click", function() { var limit = prompt("Please specify a new rows limit", getRowsLimit()); if (limit && limit >= 1) { $(".current-page").data("page", 1); setRowsLimit(limit); showTableContent(); } }); $("select.filter").on("change", function(e) { var val = $(this).val(); if (["null", "not_null"].indexOf(val) >= 0) { $(".filters input").hide().val(""); } else { $(".filters input").show(); } }); $("button.reset-filters").on("click", function() { $(".filters select, .filters input").val(""); showTableContent(); }); // Automatically prefill the filter if it's not set yet $("select.column").on("change", function() { if ($("select.filter").val() == "") { $("select.filter").val("equal"); $("#table_filter_value").focus(); } }); $("#pagination .next-page").on("click", function() { var current = $(".current-page").data("page"); var total = $(".current-page").data("pages"); if (total > current) { $(".current-page").data("page", current + 1); showPaginatedTableContent(); if (current + 1 == total) { $(this).prop("disabled", "disabled"); } } if (current > 1) { $(".prev-page").prop("disabled", ""); } }); $("#pagination .prev-page").on("click", function() { var current = $(".current-page").data("page"); if (current > 1) { $(".current-page").data("page", current - 1); $(".next-page").prop("disabled", ""); showPaginatedTableContent(); } if (current == 1) { $(this).prop("disabled", "disabled"); } }); $("#current_database").on("click", function(e) { apiCall("get", "/databases", {}, function(resp) { toggleDatabaseSearch(); enableDatabaseSearch(resp); }); }); $("#database_search").change(function(e) { var current = $("#database_search").typeahead("getActive"); if (current && current == $("#database_search").val()) { apiCall("post", "/switchdb", { db: current }, function(resp) { if (resp.error) { alert(resp.error); return; }; window.location.reload(); }); }; }); $("#edit_connection").on("click", function() { if (connected) { $("#close_connection_window").show(); } showConnectionSettings(); }); $("#close_connection").on("click", function() { if (!confirm("Are you sure you want to disconnect?")) return; disconnect(function() { showConnectionSettings(); resetTable(); $("#close_connection_window").hide(); }); }); $("#close_connection_window").on("click", function() { $("#connection_window").hide(); }); $("#connection_url").on("change", function() { if ($(this).val().indexOf("localhost") != -1) { $("#connection_ssl").val("disable"); } }); $("#pg_host").on("change", function() { var value = $(this).val(); if (value.indexOf("localhost") != -1 || value.indexOf("127.0.0.1") != -1) { $("#connection_ssl").val("disable"); } }); $(".connection-group-switch button").on("click", function() { $(".connection-group-switch button").removeClass("active"); $(this).addClass("active"); switch($(this).attr("data")) { case "scheme": $(".connection-scheme-group").show(); $(".connection-standard-group").hide(); $(".connection-ssh-group").hide(); return; case "standard": $(".connection-scheme-group").hide(); $(".connection-standard-group").show(); $(".connection-ssh-group").hide(); return; case "ssh": $(".connection-scheme-group").hide(); $(".connection-standard-group").show(); $(".connection-ssh-group").show(); return; } }); $("#connection_bookmarks").on("change", function(e) { var selection = $(this).val(); var inputs = [ $("#connection_form input[type='text']"), $("#connection_form input[type='password']"), $("#connection_ssl") ]; inputs.forEach(function(selector) { selector.val("").prop("disabled", selection == "" ? "" : "disabled"); }); }); $("#connection_form").on("submit", function(e) { e.preventDefault(); var button = $(this).find("button.open-connection"); var params = {}; var bookmarkID = $.trim($("#connection_bookmarks").val()); if (bookmarkID != "") { params["bookmark_id"] = $("#connection_bookmarks").val(); } else { params.url = getConnectionString(); if (params.url.length == 0) { return; } if ($(".connection-group-switch button.active").attr("data") == "ssh") { params["ssh"] = 1 params["ssh_host"] = $("#ssh_host").val(); params["ssh_port"] = $("#ssh_port").val(); params["ssh_user"] = $("#ssh_user").val(); params["ssh_password"] = $("#ssh_password").val(); params["ssh_key"] = $("#ssh_key").val(); params["ssh_key_password"] = $("#ssh_key_password").val() } } $("#connection_error").hide(); button.prop("disabled", true).text("Please wait..."); apiCall("post", "/connect", params, function(resp) { button.prop("disabled", false).text("Connect"); if (resp.error) { connected = false; $("#connection_error").text(resp.error).show(); } else { connected = true; loadSchemas(); loadLocalQueries(); $("#connection_window").hide(); $("#current_database").text(resp.current_database); $("#main").show(); } }); }); initEditor(); addShortcutTooltips(); bindDatabaseObjectsFilter(); // Set session from the url var reqUrl = new URL(window.location); var sessionId = reqUrl.searchParams.get("session"); if (sessionId && sessionId != "") { sessionStorage.setItem("session_id", sessionId); window.history.pushState({}, document.title, window.location.pathname); } getInfo(function(resp) { if (resp.error) { alert("Unable to fetch app info: " + resp.error + ". Please reload the browser page."); return; } appInfo = resp.app; appFeatures = resp.features; getConnection(function(resp) { if (resp.error) { connected = false; showConnectionSettings(); $(".connection-actions").show(); return; } connected = true; loadSchemas(); loadLocalQueries(); $("#current_database").text(resp.current_database); $("#main").show(); if (!appFeatures.session_lock) { $(".connection-actions").show(); } }); }); }); ================================================ FILE: static/js/base64.js ================================================ /** * * Base64 encode / decode * http://www.webtoolkit.info/ * **/ var Base64 = { // private property _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", // public method for encoding encode : function (input) { var output = ""; var chr1, chr2, chr3, enc1, enc2, enc3, enc4; var i = 0; input = Base64._utf8_encode(input); while (i < input.length) { chr1 = input.charCodeAt(i++); chr2 = input.charCodeAt(i++); chr3 = input.charCodeAt(i++); enc1 = chr1 >> 2; enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); enc4 = chr3 & 63; if (isNaN(chr2)) { enc3 = enc4 = 64; } else if (isNaN(chr3)) { enc4 = 64; } output = output + this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4); } return output; }, // public method for decoding decode : function (input) { var output = ""; var chr1, chr2, chr3; var enc1, enc2, enc3, enc4; var i = 0; input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); while (i < input.length) { enc1 = this._keyStr.indexOf(input.charAt(i++)); enc2 = this._keyStr.indexOf(input.charAt(i++)); enc3 = this._keyStr.indexOf(input.charAt(i++)); enc4 = this._keyStr.indexOf(input.charAt(i++)); chr1 = (enc1 << 2) | (enc2 >> 4); chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); chr3 = ((enc3 & 3) << 6) | enc4; output = output + String.fromCharCode(chr1); if (enc3 != 64) { output = output + String.fromCharCode(chr2); } if (enc4 != 64) { output = output + String.fromCharCode(chr3); } } output = Base64._utf8_decode(output); return output; }, // private method for UTF-8 encoding _utf8_encode : function (string) { string = string.replace(/\r\n/g,"\n"); var utftext = ""; for (var n = 0; n < string.length; n++) { var c = string.charCodeAt(n); if (c < 128) { utftext += String.fromCharCode(c); } else if((c > 127) && (c < 2048)) { utftext += String.fromCharCode((c >> 6) | 192); utftext += String.fromCharCode((c & 63) | 128); } else { utftext += String.fromCharCode((c >> 12) | 224); utftext += String.fromCharCode(((c >> 6) & 63) | 128); utftext += String.fromCharCode((c & 63) | 128); } } return utftext; }, // private method for UTF-8 decoding _utf8_decode : function (utftext) { var string = ""; var i = 0; var c = c1 = c2 = 0; while ( i < utftext.length ) { c = utftext.charCodeAt(i); if (c < 128) { string += String.fromCharCode(c); i++; } else if((c > 191) && (c < 224)) { c2 = utftext.charCodeAt(i+1); string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); i += 2; } else { c2 = utftext.charCodeAt(i+1); c3 = utftext.charCodeAt(i+2); string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); i += 3; } } return string; } } ================================================ FILE: static/js/bootstrap-contextmenu.js ================================================ /*! * Bootstrap Context Menu * Author: @sydcanem * https://github.com/sydcanem/bootstrap-contextmenu * * Inspired by Bootstrap's dropdown plugin. * Bootstrap (http://getbootstrap.com). * * Licensed under MIT * ========================================================= */ ;(function($) { 'use strict'; /* CONTEXTMENU CLASS DEFINITION * ============================ */ var toggle = '[data-toggle="context"]'; var ContextMenu = function (element, options) { this.$element = $(element); this.before = options.before || this.before; this.onItem = options.onItem || this.onItem; this.scopes = options.scopes || null; if (options.target) { this.$element.data('target', options.target); } this.listen(); }; ContextMenu.prototype = { constructor: ContextMenu ,show: function(e) { var $menu , evt , tp , items , relatedTarget = { relatedTarget: this, target: e.currentTarget }; if (this.isDisabled()) return; this.closemenu(); if (this.before.call(this,e,$(e.currentTarget)) === false) return; $menu = this.getMenu(); $menu.trigger(evt = $.Event('show.bs.context', relatedTarget)); tp = this.getPosition(e, $menu); items = 'li:not(.divider)'; $menu.attr('style', '') .css(tp) .addClass('open') .on('click.context.data-api', items, $.proxy(this.onItem, this, $(e.currentTarget))) .trigger('shown.bs.context', relatedTarget); // Delegating the `closemenu` only on the currently opened menu. // This prevents other opened menus from closing. $('html') .on('click.context.data-api', $menu.selector, $.proxy(this.closemenu, this)); return false; } ,closemenu: function(e) { var $menu , evt , items , relatedTarget; $menu = this.getMenu(); if(!$menu.hasClass('open')) return; relatedTarget = { relatedTarget: this }; $menu.trigger(evt = $.Event('hide.bs.context', relatedTarget)); items = 'li:not(.divider)'; $menu.removeClass('open') .off('click.context.data-api', items) .trigger('hidden.bs.context', relatedTarget); $('html') .off('click.context.data-api', $menu.selector); // Don't propagate click event so other currently // opened menus won't close. e.stopPropagation(); } ,keydown: function(e) { if (e.which == 27) this.closemenu(e); } ,before: function(e) { return true; } ,onItem: function(e) { return true; } ,listen: function () { this.$element.on('contextmenu.context.data-api', this.scopes, $.proxy(this.show, this)); $('html').on('click.context.data-api', $.proxy(this.closemenu, this)); $('html').on('keydown.context.data-api', $.proxy(this.keydown, this)); } ,destroy: function() { this.$element.off('.context.data-api').removeData('context'); $('html').off('.context.data-api'); } ,isDisabled: function() { return this.$element.hasClass('disabled') || this.$element.attr('disabled'); } ,getMenu: function () { var selector = this.$element.data('target') , $menu; if (!selector) { selector = this.$element.attr('href'); selector = selector && selector.replace(/.*(?=#[^\s]*$)/, ''); //strip for ie7 } $menu = $(selector); return $menu && $menu.length ? $menu : this.$element.find(selector); } ,getPosition: function(e, $menu) { var mouseX = e.clientX , mouseY = e.clientY , boundsX = $(window).width() , boundsY = $(window).height() , menuWidth = $menu.find('.dropdown-menu').outerWidth() , menuHeight = $menu.find('.dropdown-menu').outerHeight() , tp = {"position":"absolute","z-index":9999} , Y, X, parentOffset; if (mouseY + menuHeight > boundsY) { Y = {"top": mouseY - menuHeight + $(window).scrollTop()}; } else { Y = {"top": mouseY + $(window).scrollTop()}; } if ((mouseX + menuWidth > boundsX) && ((mouseX - menuWidth) > 0)) { X = {"left": mouseX - menuWidth + $(window).scrollLeft()}; } else { X = {"left": mouseX + $(window).scrollLeft()}; } // If context-menu's parent is positioned using absolute or relative positioning, // the calculated mouse position will be incorrect. // Adjust the position of the menu by its offset parent position. parentOffset = $menu.offsetParent().offset(); X.left = X.left - parentOffset.left; Y.top = Y.top - parentOffset.top; return $.extend(tp, Y, X); } }; /* CONTEXT MENU PLUGIN DEFINITION * ========================== */ $.fn.contextmenu = function (option,e) { return this.each(function () { var $this = $(this) , data = $this.data('context') , options = (typeof option == 'object') && option; if (!data) $this.data('context', (data = new ContextMenu($this, options))); if (typeof option == 'string') data[option].call(data, e); }); }; $.fn.contextmenu.Constructor = ContextMenu; /* APPLY TO STANDARD CONTEXT MENU ELEMENTS * =================================== */ $(document) .on('contextmenu.context.data-api', function() { $(toggle).each(function () { var data = $(this).data('context'); if (!data) return; data.closemenu(); }); }) .on('contextmenu.context.data-api', toggle, function(e) { $(this).contextmenu('show', e); e.preventDefault(); e.stopPropagation(); }); }(jQuery)); ================================================ FILE: static/js/bootstrap-dropdown.js ================================================ /* ======================================================================== * Bootstrap: dropdown.js v3.2.0 * http://getbootstrap.com/javascript/#dropdowns * ======================================================================== * Copyright 2011-2014 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // DROPDOWN CLASS DEFINITION // ========================= var backdrop = '.dropdown-backdrop' var toggle = '[data-toggle="dropdown"]' var Dropdown = function (element) { $(element).on('click.bs.dropdown', this.toggle) } Dropdown.VERSION = '3.2.0' Dropdown.prototype.toggle = function (e) { var $this = $(this) if ($this.is('.disabled, :disabled')) return var $parent = getParent($this) var isActive = $parent.hasClass('open') clearMenus() if (!isActive) { if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { // if mobile we use a backdrop because click events don't delegate $('