diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000000000000000000000000000000000..f59ec20aabf5842d237244ece8c81ab184faeac1 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +* \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000000000000000000000000000000000..d30daf384f1a5149439e53e17edf6c7db68c3422 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +bindata.go -diff diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..5be784943b16c3c377e02d212e073283f79a3904 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.DS_Store +.idea +pgweb +bin +tmp/ +cover.out diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000000000000000000000000000000000..bfe5a8eba98926112ce7cb3742ba07a7c9297a17 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,373 @@ +# 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 meny 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 retuns 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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000000000000000000000000000000000..f93913a0cf0e02090c5842c6e123042502a4ba80 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,7 @@ +- Fork repository +- Create a new git branch +- Make changes +- Run tests: `make test` +- Run tests agains 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 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..2715a909eef71eec6c4cc09c008d203a37420954 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +FROM alpine:3.14 +LABEL maintainer="Dan Sosedoff " +ENV PGWEB_VERSION 0.11.11 + +RUN \ + apk update && \ + apk add --no-cache ca-certificates openssl postgresql wget && \ + update-ca-certificates && \ + rm -rf /var/cache/apk/* && \ + cd /tmp && \ + wget -q https://github.com/sosedoff/pgweb/releases/download/v$PGWEB_VERSION/pgweb_linux_amd64.zip && \ + unzip pgweb_linux_amd64.zip -d /usr/bin && \ + mv /usr/bin/pgweb_linux_amd64 /usr/bin/pgweb && \ + rm -f pgweb_linux_amd64.zip + +EXPOSE 8081 +CMD ["/usr/bin/pgweb", "--bind=0.0.0.0", "--listen=8081"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..b915611d47de26a3ca159f7bea2754e17b51e41c --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2014-2022 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. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..a5323a7f23a128464b3dc5b6a76c35b29815038b --- /dev/null +++ b/Makefile @@ -0,0 +1,81 @@ +TARGETS = darwin/amd64 darwin/arm64 linux/amd64 linux/386 windows/amd64 windows/386 +GIT_COMMIT = $(shell git rev-parse 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 +PKG = github.com/sosedoff/pgweb + +usage: + @echo "" + @echo "Task : Description" + @echo "----------------- : -------------------" + @echo "make setup : Install all necessary dependencies" + @echo "make dev : Generate development build" + @echo "make build : Generate production build for current OS" + @echo "make bootstrap : Install cross-compilation toolchain" + @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 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 -race -cover ./pkg/... + +test-all: + @./script/test_all.sh + @./script/test_cockroach.sh + +dev: + go build + @echo "You can now execute ./pgweb" + +build: + go build + @echo "You can now execute ./pgweb" + +release: LDFLAGS += -X $(PKG)/pkg/command.GitCommit=$(GIT_COMMIT) +release: LDFLAGS += -X $(PKG)/pkg/command.BuildTime=$(BUILD_TIME) +release: LDFLAGS += -X $(PKG)/pkg/command.GoVersion=$(GO_VERSION) +release: + @echo "Building binaries..." + @gox \ + -osarch "$(TARGETS)" \ + -ldflags "$(LDFLAGS)" \ + -output "./bin/pgweb_{{.OS}}_{{.Arch}}" + + @echo "Building ARM binaries..." + GOOS=linux GOARCH=arm GOARM=5 go build -ldflags "$(LDFLAGS)" -o "./bin/pgweb_linux_arm_v5" + + @echo "Building ARM64 binaries..." + GOOS=linux GOARCH=arm64 GOARM=7 go build -ldflags "$(LDFLAGS)" -o "./bin/pgweb_linux_arm64_v7" + + @echo "\nPackaging binaries...\n" + @./script/package.sh + +bootstrap: + gox -build-toolchain + +setup: + go install github.com/mitchellh/gox@v1.0.1 + +clean: + @rm -f ./pgweb + @rm -rf ./bin/* + +docker: + docker build --no-cache -t 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) diff --git a/Procfile b/Procfile new file mode 100644 index 0000000000000000000000000000000000000000..1f3a4d14c1c97cd6e0fbbac3bbbbbbc87f6db0cf --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: pgweb --url=$DATABASE_URL --listen=$PORT --bind=0.0.0.0 --auth-user=$AUTH_USER --auth-pass=$AUTH_PASS diff --git a/README.en.md b/README.en.md deleted file mode 100644 index 3fe8d84ab06814804e100b295b29a2d02d0056b8..0000000000000000000000000000000000000000 --- a/README.en.md +++ /dev/null @@ -1,36 +0,0 @@ -# openGauss-webclient - -#### Description -{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**} - -#### Software Architecture -Software architecture description - -#### Installation - -1. xxxx -2. xxxx -3. xxxx - -#### Instructions - -1. xxxx -2. xxxx -3. xxxx - -#### Contribution - -1. Fork the repository -2. Create Feat_xxx branch -3. Commit your code -4. Create Pull Request - - -#### Gitee Feature - -1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md -2. Gitee blog [blog.gitee.com](https://blog.gitee.com) -3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) -4. The most valuable open source project [GVP](https://gitee.com/gvp) -5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) -6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README.md b/README.md index 1b24502840f4b7b601cd683a60ef6c86afc8f416..cad9b37543e9978fbb14c548ebada5707f31f5c6 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,94 @@ # openGauss-webclient -#### 介绍 -{**以下是 Gitee 平台说明,您可以替换此简介** -Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台 -无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)} +Web-based openGauss database browser written in Go. -#### 软件架构 -软件架构说明 +*Note: This project is a fork and modified version of [pgweb](https://github.com/sosedoff/pgweb)* +## Overview -#### 安装教程 +Pgweb is a web-based database browser for PostgreSQL, written in Go and works +on OSX, Linux and Windows machines. Main idea behind using Go for backend development +is to utilize ability of the compiler to produce zero-dependency binaries for +multiple platforms. Pgweb was created as an attempt to build very simple and portable +application to work with local or remote PostgreSQL databases. -1. xxxx -2. xxxx -3. xxxx -#### 使用说明 +## Features -1. xxxx -2. xxxx -3. xxxx +- Cross-platform support OSX/Linux/Windows 32/64-bit +- Simple installation (distributed as a single binary) +- Zero dependencies +- SSH Connections +- Multiple database sessions +- Simple database browser +- Execute and analyze custom SQL queries +- Table and query data export to CSV/JSON/XML +- Query history +- Server bookmarks -#### 参与贡献 +## Usage -1. Fork 本仓库 -2. 新建 Feat_xxx 分支 -3. 提交代码 -4. 新建 Pull Request +Start server: +``` +openGauss-webclient +``` -#### 特技 +You can also provide connection flags: -1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md -2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) -3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 -4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 -5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) -6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) +``` +openGauss-webclient --host localhost --user myuser --db mydb +``` + +Connection URL scheme is also supported: + +``` +openGauss-webclient --url postgres://user:password@host:port/database?sslmode=[mode] +openGauss-webclient --url "postgres:///database?host=/absolute/path/to/unix/socket/dir" +``` + +### Multiple database sessions + +To enable multiple database sessions in pgweb, start the server with: + +``` +openGauss-webclient --sessions +``` + +Or set environment variable: + +``` +SESSIONS=1 openGauss-webclient +``` + + +## 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 + +## License + +The MIT License (MIT). See [LICENSE](LICENSE) file for more details. diff --git a/SCREENS.md b/SCREENS.md new file mode 100644 index 0000000000000000000000000000000000000000..89c674487ecf8aa0df8efa26c6d6031d4d439396 --- /dev/null +++ b/SCREENS.md @@ -0,0 +1,10 @@ +# Screenshots + +### Connect + + +### Browse table rows + + +### Write SQL queries + \ No newline at end of file diff --git a/app.json b/app.json new file mode 100644 index 0000000000000000000000000000000000000000..043f94105eb79d2a9ad0dafe6d4f45430edb7356 --- /dev/null +++ b/app.json @@ -0,0 +1,27 @@ +{ + "name": "openGauss-webclient", + "description": "A web-based frontend to your openGauss database.", + "website": "https://gitee.com/opengauss/openGauss-webclient", + "repository": "https://gitee.com/opengauss/openGauss-webclient", + "logo": "https://gitee.com/opengauss/openGauss-webclient/raw/master/static/img/icon.pngg", + "success_url": "/", + "keywords": [ + "postgres", + "database", + "golang", + "frontend" + ], + "addons": [ + "heroku-postgresql" + ], + "env": { + "AUTH_USER": { + "description": "A username to log into openGauss-webclient.", + "value": "admin" + }, + "AUTH_PASS": { + "description": "The login password. Please change this.", + "generator": "secret" + } + } +} diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000000000000000000000000000000000000..400fb2f71e6557a9bf303427d3b862184fa85632 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,30 @@ +clone_folder: c:\gopath\src\github.com\sosedoff\pgweb + +environment: + GOPATH: c:\gopath + GOROOT: c:\goroot116\go + GOTOOLDIR: c:\goroot116\go\pkg\tool\windows_amd64 + CGO_ENABLED: 0 + +services: + - postgresql + +install: + - ps: mkdir c:\goroot116 + - ps: iwr -outf c:\goroot116\go1.16.windows-amd64.zip https://golang.org/dl/go1.16.windows-amd64.zip + - ps: Expand-Archive c:\goroot116\go1.16.windows-amd64.zip -DestinationPath c:\goroot116 + - set PATH=%GOPATH%\bin;%PATH% + - set PATH=%GOROOT%\bin;%PATH% + - echo %PATH% + - echo %GOPATH% + - cd %APPVEYOR_BUILD_FOLDER% + - go env + - go version + +build_script: + - go build + +test_script: + - go test -cover ./... + +deploy: false diff --git a/config/pgweb.freebsd_rc b/config/pgweb.freebsd_rc new file mode 100644 index 0000000000000000000000000000000000000000..223aba618be5dd90a373d26d4669459f5ca583e2 --- /dev/null +++ b/config/pgweb.freebsd_rc @@ -0,0 +1,50 @@ +#!/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" diff --git a/config/pgweb.service b/config/pgweb.service new file mode 100644 index 0000000000000000000000000000000000000000..68346584db50f101aababe2f0a1042d30966642a --- /dev/null +++ b/config/pgweb.service @@ -0,0 +1,11 @@ +[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 diff --git a/config/pgweb_initd.conf b/config/pgweb_initd.conf new file mode 100644 index 0000000000000000000000000000000000000000..7f4a21d1a17a8f1c933cfaaf5913c57368b7052b --- /dev/null +++ b/config/pgweb_initd.conf @@ -0,0 +1,67 @@ +#!/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 \ No newline at end of file diff --git a/config/pgweb_upstart.conf b/config/pgweb_upstart.conf new file mode 100644 index 0000000000000000000000000000000000000000..678e770e1b05277cc23c489bb6401672c55d03c5 --- /dev/null +++ b/config/pgweb_upstart.conf @@ -0,0 +1,10 @@ +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 diff --git a/data/bookmark.toml b/data/bookmark.toml new file mode 100644 index 0000000000000000000000000000000000000000..55a1c7f9ba918164434ee0afa9b45e6a12f14c55 --- /dev/null +++ b/data/bookmark.toml @@ -0,0 +1,5 @@ +host = "localhost" +port = 5432 +user = "postgres" +database = "mydatabase" +ssl = "disable" diff --git a/data/bookmark_invalid_ssl.toml b/data/bookmark_invalid_ssl.toml new file mode 100644 index 0000000000000000000000000000000000000000..4dddcda9df83547e75270c9490038275301fc255 --- /dev/null +++ b/data/bookmark_invalid_ssl.toml @@ -0,0 +1,5 @@ +host = "localhost" +port = 5432 +user = "postgres" +database = "mydatabase" +ssl = "disabled" diff --git a/data/bookmark_url.toml b/data/bookmark_url.toml new file mode 100644 index 0000000000000000000000000000000000000000..b4997c14e9e38b4ac53a2bfc151e5e8b933c5ad5 --- /dev/null +++ b/data/bookmark_url.toml @@ -0,0 +1 @@ +url = "postgres://username:password@host:port/database?sslmode=disable" diff --git a/data/booktown.sql b/data/booktown.sql new file mode 100644 index 0000000000000000000000000000000000000000..aecccac6c5ba283073537e1782f9775d8162b867 --- /dev/null +++ b/data/booktown.sql @@ -0,0 +1,1880 @@ +-- +-- 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,ONM4V 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'); + diff --git a/data/invalid.toml b/data/invalid.toml new file mode 100644 index 0000000000000000000000000000000000000000..36f52a5056465a9aefd7d9ad91744caae67cbaf2 --- /dev/null +++ b/data/invalid.toml @@ -0,0 +1 @@ +invalid encoding \ No newline at end of file diff --git a/data/roach.sql b/data/roach.sql new file mode 100644 index 0000000000000000000000000000000000000000..c969947ac3b80f3b8939b4d6f36bf972fc768525 --- /dev/null +++ b/data/roach.sql @@ -0,0 +1,43 @@ +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; \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..7902c73c8c75cdd4cee8fd8c943a635f40f7eb28 --- /dev/null +++ b/go.mod @@ -0,0 +1,43 @@ +module github.com/sosedoff/pgweb + +go 1.17 + +// +heroku goVersion go1.17 + +require ( + github.com/BurntSushi/toml v0.3.1 + github.com/ScaleFT/sshkeys v0.0.0-20181112160850-82451a803681 + github.com/gin-gonic/gin v1.7.4 + github.com/jessevdk/go-flags v1.4.0 + github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0 + github.com/lib/pq v1.1.1 + github.com/mitchellh/go-homedir v1.0.0 + github.com/stretchr/testify v1.7.0 + github.com/tuvistavie/securerandom v0.0.0-20140719024926-15512123a948 + golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-playground/locales v0.14.0 // indirect + github.com/go-playground/universal-translator v0.18.0 // indirect + github.com/go-playground/validator/v10 v10.9.0 // indirect + github.com/go-sql-driver/mysql v1.5.0 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/leodido/go-urn v1.2.1 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mattn/go-sqlite3 v1.14.6 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/mr-tron/base58 v1.2.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/ugorji/go/codec v1.2.6 // indirect + golang.org/x/sys v0.0.0-20210923061019-b8560ed6a9b7 // indirect + golang.org/x/text v0.3.7 // indirect + google.golang.org/protobuf v1.27.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..f6c15fc217bb5156dcd6713fd837d884b046d678 --- /dev/null +++ b/go.sum @@ -0,0 +1,132 @@ +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/ScaleFT/sshkeys v0.0.0-20181112160850-82451a803681 h1:JS2rl38kZmHgWa0xINSaSYH0Whtvem64/4+Ef0+Y5pE= +github.com/ScaleFT/sshkeys v0.0.0-20181112160850-82451a803681/go.mod h1:WfDateMPQ/55dPbZRp5Zxrux5WiEaHsjk9puUhz0KgY= +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/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-gonic/gin v1.7.4 h1:QmUZXrvJ9qZ3GfWvQ+2wnW/1ePrTEJqPKMYEU3lD/DM= +github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/go-playground/validator/v10 v10.9.0 h1:NgTtmN58D0m8+UuxtYmGztBJB7VnPgjj221I1QHci2A= +github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= +github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +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/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0 h1:5B0uxl2lzNRVkJVg+uGHxWtRt4C0Wjc6kJKo5XYx8xE= +github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0/go.mod h1:IiEW3SEiiErVyFdH8NTuWjSifiEQKUoyK3LNqr2kCHU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +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/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +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.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4= +github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +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/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0= +github.com/mitchellh/go-homedir v1.0.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 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +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/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/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +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/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go v1.2.6 h1:tGiWC9HENWE2tqYycIqFTNorMmFRVhNwCpDOpWqnk8E= +github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ= +github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +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-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210923061019-b8560ed6a9b7 h1:c20P3CcPbopVp2f7099WLOqSNKURf30Z0uq66HpijZY= +golang.org/x/sys v0.0.0-20210923061019-b8560ed6a9b7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +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.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go new file mode 100644 index 0000000000000000000000000000000000000000..272bec133cc09eeb4ca472204c4ef9cfb2d9706b --- /dev/null +++ b/main.go @@ -0,0 +1,9 @@ +package main + +import ( + "github.com/sosedoff/pgweb/pkg/cli" +) + +func main() { + cli.Run() +} diff --git a/pkg/api/api.go b/pkg/api/api.go new file mode 100644 index 0000000000000000000000000000000000000000..c9517279003976bf6ee85b2c30776fd4c136bfb8 --- /dev/null +++ b/pkg/api/api.go @@ -0,0 +1,548 @@ +package api + +import ( + "context" + "encoding/base64" + "fmt" + "net/http" + neturl "net/url" + "regexp" + "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/connection" + "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 = map[string]*client.Client{} +) + +// DB returns a database connection from the client context +func DB(c *gin.Context) *client.Client { + if command.Opts.Sessions { + return DbSessions[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[sid] = newClient + return nil +} + +// GetHome renderes the home page +func GetHome(prefix string) http.Handler { + if prefix != "" { + prefix = "/" + prefix + } + return http.StripPrefix(prefix, http.FileServer(http.FS(static.Static))) +} + +func GetAssets(prefix string) http.Handler { + if prefix != "" { + prefix = "/" + prefix + "static/" + } else { + prefix = "/static/" + } + return http.StripPrefix(prefix, http.FileServer(http.FS(static.Static))) +} + +// 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) + return + } + successResponse(c, gin.H{"sessions": len(DbSessions)}) +} + +// ConnectWithBackend creates a new connection based on backend resource +func ConnectWithBackend(c *gin.Context) { + // Setup a new backend client + backend := Backend{ + Endpoint: command.Opts.ConnectBackend, + Token: command.Opts.ConnectToken, + PassHeaders: 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) + 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 sshInfo *shared.SSHInfo + url := c.Request.FormValue("url") + + if url == "" { + badRequest(c, errURLRequired) + return + } + + opts := command.Options{URL: url} + url, err := connection.FormatURL(opts) + + if err != nil { + badRequest(c, err) + return + } + + if c.Request.FormValue("ssh") != "" { + sshInfo = parseSshInfo(c) + } + + cl, err := client.NewFromUrl(url, sshInfo) + 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]) +} + +// 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 + } + + conn := DB(c) + if conn == nil { + badRequest(c, errNotConnected) + return + } + + err := conn.Close() + if err != nil { + badRequest(c, err) + return + } + + 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 + var err error + + if c.Request.FormValue("type") == "materialized_view" { + res, err = DB(c).MaterializedView(c.Params.ByName("table")) + } else { + res, err = DB(c).Table(c.Params.ByName("table")) + } + + 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) { + res, err := DB(c).Info() + + if err != nil { + badRequest(c, err) + return + } + + info := res.Format()[0] + info["session_lock"] = command.Opts.LockSession + + successResponse(c, info) +} + +// 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) +} + +// HandleQuery runs the database query +func HandleQuery(query string, c *gin.Context) { + 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) { + bookmarks, err := bookmarks.ReadAll(bookmarks.Path(command.Opts.BookmarksDir)) + serveResult(c, bookmarks, err) +} + +// GetInfo renders the pgweb system information +func GetInfo(c *gin.Context) { + successResponse(c, gin.H{ + "version": command.Version, + "go_version": command.GoVersion, + "git_sha": command.GitCommit, + "build_time": command.BuildTime, + }) +} + +// 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")), + } + + // If pg_dump is not available the following code will not show an error in browser. + // This is due to the headers being written first. + if !dump.CanExport() { + badRequest(c, errPgDumpNotFound) + return + } + + formattedInfo := info.Format()[0] + filename := formattedInfo["current_database"].(string) + if dump.Table != "" { + filename = filename + "_" + dump.Table + } + reg := regexp.MustCompile("[^._\\w]+") + cleanFilename := reg.ReplaceAllString(filename, "") + + c.Header( + "Content-Disposition", + fmt.Sprintf(`attachment; filename="%s.sql.gz"`, cleanFilename), + ) + + err = dump.Export(db.ConnectionString, c.Writer) + if err != nil { + badRequest(c, err) + } +} diff --git a/pkg/api/api_test.go b/pkg/api/api_test.go new file mode 100644 index 0000000000000000000000000000000000000000..bb8a7f2fb1875d87553f8509fdaa62376fc44f0e --- /dev/null +++ b/pkg/api/api_test.go @@ -0,0 +1,40 @@ +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) + } + } +} diff --git a/pkg/api/backend.go b/pkg/api/backend.go new file mode 100644 index 0000000000000000000000000000000000000000..2d6ac2f154bb1a5730a50848b8e668b7d9c96f55 --- /dev/null +++ b/pkg/api/backend.go @@ -0,0 +1,81 @@ +package api + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "log" + "net/http" + "strings" + + "github.com/gin-gonic/gin" +) + +// Backend represents a third party configuration source +type Backend struct { + Endpoint string + Token string + PassHeaders string +} + +// BackendRequest represents a payload sent to the third-party source +type BackendRequest struct { + Resource string `json:"resource"` + Token string `json:"token"` + Headers map[string]string `json:"headers"` +} + +// BackendCredential represents the third-party response +type BackendCredential struct { + DatabaseURL string `json:"database_url"` +} + +// FetchCredential sends an authentication request to a third-party service +func (be Backend) FetchCredential(ctx context.Context, resource string, c *gin.Context) (*BackendCredential, error) { + request := BackendRequest{ + Resource: resource, + Token: be.Token, + Headers: map[string]string{}, + } + + // Pass white-listed client headers to the backend request + for _, name := range strings.Split(be.PassHeaders, ",") { + request.Headers[strings.ToLower(name)] = c.Request.Header.Get(name) + } + + body, err := json.Marshal(request) + if err != nil { + log.Println("[BACKEND] 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 { + // Any connection-related issues will show up in the server log + log.Println("[BACKEND] unable to fetch credential:", err) + return nil, errBackendConnectError + } + defer resp.Body.Close() + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("received HTTP status code %v", resp.StatusCode) + } + + cred := &BackendCredential{} + if err := json.NewDecoder(resp.Body).Decode(cred); err != nil { + return nil, err + } + if cred.DatabaseURL == "" { + return nil, errConnStringRequired + } + + return cred, nil +} diff --git a/pkg/api/backend_test.go b/pkg/api/backend_test.go new file mode 100644 index 0000000000000000000000000000000000000000..c10be7e54cc22ca3e923ae44077745aa78057fb1 --- /dev/null +++ b/pkg/api/backend_test.go @@ -0,0 +1,148 @@ +package api + +import ( + "context" + "errors" + "net/http" + "testing" + "time" + + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/assert" +) + +func TestBackendFetchCredential(t *testing.T) { + examples := []struct { + name string + backend Backend + resourceName string + cred *BackendCredential + reqCtx *gin.Context + ctx func() (context.Context, context.CancelFunc) + err error + }{ + { + name: "Bad auth token", + backend: Backend{Endpoint: "http://localhost:5555/unauthorized"}, + err: errors.New("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("received HTTP status code 400"), + }, + { + name: "Require header", + backend: Backend{ + Endpoint: "http://localhost:5555/pass-header", + PassHeaders: "x-foo", + }, + reqCtx: &gin.Context{ + Request: &http.Request{ + Header: http.Header{ + "X-Foo": []string{"bar"}, + }, + }, + }, + cred: &BackendCredential{DatabaseURL: "postgres://hostname/bar"}, + }, + { + name: "Success", + backend: Backend{Endpoint: "http://localhost:5555/success"}, + cred: &BackendCredential{DatabaseURL: "postgres://hostname/dbname"}, + }, + } + + srvCtx, srvCancel := context.WithTimeout(context.Background(), time.Minute) + defer srvCancel() + + go startTestBackend(srvCtx, "localhost:5555") + + for _, ex := range examples { + t.Run(ex.name, func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + if ex.ctx != nil { + ctx, cancel = ex.ctx() + } + defer cancel() + + reqCtx := ex.reqCtx + if reqCtx == nil { + reqCtx = &gin.Context{ + Request: &http.Request{}, + } + } + + cred, err := ex.backend.FetchCredential(ctx, ex.resourceName, reqCtx) + 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 := BackendRequest{} + 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} + go server.ListenAndServe() + + select { + case <-ctx.Done(): + server.Shutdown(context.Background()) + } +} diff --git a/pkg/api/errors.go b/pkg/api/errors.go new file mode 100644 index 0000000000000000000000000000000000000000..54f8d80b4d2400ce3b710dd79b2c9de23fb37d95 --- /dev/null +++ b/pkg/api/errors.go @@ -0,0 +1,19 @@ +package api + +import ( + "errors" +) + +var ( + errNotConnected = errors.New("Not connected") + errNotPermitted = errors.New("Not permitted") + errConnStringRequired = errors.New("Connection string is required") + 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") + errPgDumpNotFound = errors.New("pg_dump utility is not found") + errBackendConnectError = errors.New("Unable to connect to the auth backend") +) diff --git a/pkg/api/helpers.go b/pkg/api/helpers.go new file mode 100644 index 0000000000000000000000000000000000000000..f56093ad6444b5257a25d58691d78af25a1ca6b1 --- /dev/null +++ b/pkg/api/helpers.go @@ -0,0 +1,180 @@ +package api + +import ( + "fmt" + "mime" + "net/http" + "path/filepath" + "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{ + "-": "+", + "_": "/", + ".": "=", + } +) + +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 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 { + successResponse(c, result) + } else { + badRequest(c, err) + } +} + +// 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) +} diff --git a/pkg/api/helpers_test.go b/pkg/api/helpers_test.go new file mode 100644 index 0000000000000000000000000000000000000000..7738d10f6b616db218d2201181d49324603a2c9b --- /dev/null +++ b/pkg/api/helpers_test.go @@ -0,0 +1,72 @@ +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_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()) +} diff --git a/pkg/api/middleware.go b/pkg/api/middleware.go new file mode 100644 index 0000000000000000000000000000000000000000..f20dffb813951ba62e104d3bbbf902d9e1d206a3 --- /dev/null +++ b/pkg/api/middleware.go @@ -0,0 +1,67 @@ +package api + +import ( + "log" + "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[sid] + if conn == nil { + badRequest(c, errNotConnected) + return + } + + c.Next() + } +} + +// Middleware to print out request parameters and body for debugging +func requestInspectMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + err := c.Request.ParseForm() + log.Println("Request params:", err, c.Request.Form) + } +} + +// 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) + } +} diff --git a/pkg/api/routes.go b/pkg/api/routes.go new file mode 100644 index 0000000000000000000000000000000000000000..3f94f5f2800d3c91aab23c4e0e50b85bab78fdc8 --- /dev/null +++ b/pkg/api/routes.go @@ -0,0 +1,58 @@ +package api + +import ( + "github.com/gin-gonic/gin" + + "github.com/sosedoff/pgweb/pkg/command" +) + +func SetupMiddlewares(group *gin.RouterGroup) { + if command.Opts.Debug { + group.Use(requestInspectMiddleware()) + } + + 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("/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("/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) +} diff --git a/pkg/api/session_cleanup.go b/pkg/api/session_cleanup.go new file mode 100644 index 0000000000000000000000000000000000000000..862d1219ac730566f833db70adc71c302d6c4e58 --- /dev/null +++ b/pkg/api/session_cleanup.go @@ -0,0 +1,41 @@ +package api + +import ( + "log" + "time" + + "github.com/sosedoff/pgweb/pkg/command" +) + +// StartSessionCleanup starts a goroutine to cleanup idle database sessions +func StartSessionCleanup() { + for range time.Tick(time.Minute) { + if command.Opts.Debug { + log.Println("Triggering idle session deletion") + } + cleanupIdleSessions() + } +} + +func cleanupIdleSessions() { + ids := []string{} + + // Figure out which sessions are idle + for id, client := range DbSessions { + if client.IsIdle() { + ids = append(ids, id) + } + } + if len(ids) == 0 { + return + } + + // Close and delete idle sessions + log.Println("Closing", len(ids), "idle sessions") + for _, id := range ids { + // TODO: concurrent map edit will trigger panic + if err := DbSessions[id].Close(); err == nil { + delete(DbSessions, id) + } + } +} diff --git a/pkg/bookmarks/bookmarks.go b/pkg/bookmarks/bookmarks.go new file mode 100644 index 0000000000000000000000000000000000000000..2648b04702175a8e35dcbb97e8113c39ea9e357c --- /dev/null +++ b/pkg/bookmarks/bookmarks.go @@ -0,0 +1,141 @@ +package bookmarks + +import ( + "fmt" + "io/ioutil" + "path/filepath" + "strings" + + "github.com/BurntSushi/toml" + "github.com/mitchellh/go-homedir" + + "github.com/sosedoff/pgweb/pkg/command" + "github.com/sosedoff/pgweb/pkg/shared" +) + +// Bookmark contains information about bookmarked database connection +type Bookmark struct { + URL string `json:"url"` // Postgres connection URL + Host string `json:"host"` // Server hostname + Port int `json:"port"` // Server port + User string `json:"user"` // Database user + Password string `json:"password"` // User password + Database string `json:"database"` // Database name + Ssl string `json:"ssl"` // Connection SSL mode + SSH *shared.SSHInfo `json:"ssh"` // SSH tunnel config +} + +// SSHInfoIsEmpty returns true if ssh configration 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 { + return command.Options{ + URL: b.URL, + Host: b.Host, + Port: b.Port, + User: b.User, + Pass: b.Password, + DbName: b.Database, + Ssl: b.Ssl, + } +} + +func readServerConfig(path string) (Bookmark, error) { + bookmark := Bookmark{} + + buff, err := ioutil.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.Ssl == 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.Ssl == "" || !valid { + bookmark.Ssl = "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) +} + +// Path returns bookmarks storage path +func Path(overrideDir string) string { + if overrideDir == "" { + path, _ := homedir.Dir() + return fmt.Sprintf("%s/.pgweb/bookmarks", path) + } + return overrideDir +} + +// ReadAll returns all available bookmarks +func ReadAll(path string) (map[string]Bookmark, error) { + results := map[string]Bookmark{} + + files, err := ioutil.ReadDir(path) + if err != nil { + return results, err + } + + for _, file := range files { + if filepath.Ext(file.Name()) != ".toml" { + continue + } + + fullPath := filepath.Join(path, file.Name()) + key := fileBasename(file.Name()) + config, err := readServerConfig(fullPath) + + if err != nil { + fmt.Printf("%s parse error: %s\n", fullPath, err) + continue + } + + results[key] = config + } + + return results, nil +} + +// GetBookmark reads an existing bookmark +func GetBookmark(bookmarkPath string, bookmarkName string) (Bookmark, error) { + bookmarks, err := ReadAll(bookmarkPath) + if err != nil { + return Bookmark{}, err + } + + bookmark, ok := bookmarks[bookmarkName] + if !ok { + return Bookmark{}, fmt.Errorf("couldn't find a bookmark with name %s", bookmarkName) + } + + return bookmark, nil +} diff --git a/pkg/bookmarks/bookmarks_test.go b/pkg/bookmarks/bookmarks_test.go new file mode 100644 index 0000000000000000000000000000000000000000..0b434976cd91d8eebca2b8a53b33552f031ba511 --- /dev/null +++ b/pkg/bookmarks/bookmarks_test.go @@ -0,0 +1,141 @@ +package bookmarks + +import ( + "testing" + + "github.com/sosedoff/pgweb/pkg/command" + "github.com/sosedoff/pgweb/pkg/shared" + "github.com/stretchr/testify/assert" +) + +func Test_Invalid_Bookmark_Files(t *testing.T) { + _, err := readServerConfig("foobar") + assert.Error(t, err) + + _, err = readServerConfig("../../data/invalid.toml") + assert.Error(t, err) + assert.Equal(t, "Near line 1 (last key parsed 'invalid'): expected key separator '=', but got 'e' instead", err.Error()) +} + +func Test_Bookmark(t *testing.T) { + bookmark, err := readServerConfig("../../data/bookmark.toml") + assert.Equal(t, nil, err) + assert.Equal(t, "localhost", bookmark.Host) + assert.Equal(t, 5432, bookmark.Port) + assert.Equal(t, "postgres", bookmark.User) + assert.Equal(t, "mydatabase", bookmark.Database) + assert.Equal(t, "disable", bookmark.Ssl) + assert.Equal(t, "", bookmark.Password) + assert.Equal(t, "", bookmark.URL) + + bookmark, err = readServerConfig("../../data/bookmark_invalid_ssl.toml") + assert.Equal(t, nil, err) + assert.Equal(t, "disable", bookmark.Ssl) +} + +func Test_Bookmark_URL(t *testing.T) { + bookmark, err := readServerConfig("../../data/bookmark_url.toml") + + assert.Equal(t, nil, err) + assert.Equal(t, "postgres://username:password@host:port/database?sslmode=disable", bookmark.URL) + assert.Equal(t, "", bookmark.Host) + assert.Equal(t, 5432, bookmark.Port) + assert.Equal(t, "", bookmark.User) + assert.Equal(t, "", bookmark.Database) + assert.Equal(t, "disable", bookmark.Ssl) + assert.Equal(t, "", bookmark.Password) +} + +func Test_Bookmarks_Path(t *testing.T) { + assert.NotEqual(t, "/.pgweb/bookmarks", Path("")) +} + +func Test_Basename(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_ReadBookmarks_Invalid(t *testing.T) { + bookmarks, err := ReadAll("foobar") + + assert.Error(t, err) + assert.Equal(t, 0, len(bookmarks)) +} + +func Test_ReadBookmarks(t *testing.T) { + bookmarks, err := ReadAll("../../data") + + assert.Equal(t, nil, err) + assert.Equal(t, 3, len(bookmarks)) +} + +func Test_GetBookmark(t *testing.T) { + expBookmark := Bookmark{ + + Host: "localhost", + Port: 5432, + User: "postgres", + Password: "", + Database: "mydatabase", + Ssl: "disable", + } + b, err := GetBookmark("../../data", "bookmark") + if assert.NoError(t, err) { + assert.Equal(t, expBookmark, b) + } + + _, err = GetBookmark("../../data", "bar") + expErrStr := "couldn't find a bookmark with name bar" + assert.Equal(t, expErrStr, err.Error()) + + _, err = GetBookmark("foo", "bookmark") + assert.Error(t, err) +} + +func Test_Bookmark_SSHInfoIsEmpty(t *testing.T) { + emptySSH := &shared.SSHInfo{ + Host: "", + Port: "", + User: "", + } + populatedSSH := &shared.SSHInfo{ + Host: "localhost", + Port: "8080", + User: "postgres", + } + + b := Bookmark{SSH: nil} + assert.True(t, b.SSHInfoIsEmpty()) + + b = Bookmark{SSH: emptySSH} + assert.True(t, b.SSHInfoIsEmpty()) + + b.SSH = populatedSSH + assert.False(t, b.SSHInfoIsEmpty()) +} + +func Test_ConvertToOptions(t *testing.T) { + b := Bookmark{ + URL: "postgres://username:password@host:port/database?sslmode=disable", + Host: "localhost", + Port: 5432, + User: "postgres", + Password: "password", + Database: "mydatabase", + Ssl: "disable", + } + + expOpt := command.Options{ + URL: "postgres://username:password@host:port/database?sslmode=disable", + Host: "localhost", + Port: 5432, + User: "postgres", + Pass: "password", + DbName: "mydatabase", + Ssl: "disable", + } + opt := b.ConvertToOptions() + assert.Equal(t, expOpt, opt) +} diff --git a/pkg/cli/cli.go b/pkg/cli/cli.go new file mode 100644 index 0000000000000000000000000000000000000000..64d527f12fd9d9a04777fc41ac96c1988ff79a1c --- /dev/null +++ b/pkg/cli/cli.go @@ -0,0 +1,251 @@ +package cli + +import ( + "fmt" + "os" + "os/exec" + "os/signal" + "regexp" + "strings" + + "github.com/gin-gonic/gin" + "github.com/jessevdk/go-flags" + + "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/shared" + "github.com/sosedoff/pgweb/pkg/util" +) + +var ( + 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 / change data. +For proper read-only access please follow postgresql role management documentation. +------------------------------------------------------` + + regexErrConnectionRefused = regexp.MustCompile(`(connection|actively) refused`) + regexErrAuthFailed = regexp.MustCompile(`authentication failed`) +) + +func exitWithMessage(message string) { + fmt.Println("Error:", message) + os.Exit(1) +} + +func initClientUsingBookmark(bookmarkPath, bookmarkName string) (*client.Client, error) { + bookmark, err := bookmarks.GetBookmark(bookmarkPath, bookmarkName) + if err != nil { + return nil, err + } + + opt := bookmark.ConvertToOptions() + var connStr string + + if opt.URL != "" { // if the bookmark has url set, use it + connStr = opt.URL + } else { + connStr, err = connection.BuildStringFromOptions(opt) + if err != nil { + return nil, fmt.Errorf("error building connection string: %v", err) + } + } + + var ssh *shared.SSHInfo + if !bookmark.SSHInfoIsEmpty() { + ssh = bookmark.SSH + } + + return client.NewFromUrl(connStr, ssh) +} + +func initClient() { + if connection.IsBlank(command.Opts) && options.Bookmark == "" { + return + } + + var cl *client.Client + var err error + + if options.Bookmark != "" { + cl, err = initClientUsingBookmark(bookmarks.Path(options.BookmarksDir), options.Bookmark) + } else { + cl, err = client.New() + } + if err != nil { + exitWithMessage(err.Error()) + } + + if command.Opts.Debug { + fmt.Println("Server connection string:", cl.ConnectionString) + } + + fmt.Println("Connecting to server...") + if err := cl.Test(); err != nil { + msg := err.Error() + + // Check if we're trying to connect to the default database. + if command.Opts.DbName == "" && command.Opts.URL == "" { + // If database does not exist, allow user to connect from the UI. + if strings.Contains(msg, "database") && strings.Contains(msg, "does not exist") { + fmt.Println("Error:", msg) + return + } + + // Do not bail if local server is not running. + if regexErrConnectionRefused.MatchString(msg) { + fmt.Println("Error:", msg) + return + } + + // Do not bail if local auth is invalid + if regexErrAuthFailed.MatchString(msg) { + fmt.Println("Error:", msg) + return + } + } + + exitWithMessage(msg) + } + + if !command.Opts.Sessions { + fmt.Printf("Connected to %s\n", cl.ServerVersion()) + } + + 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 err.(type) { + case *flags.Error: + // 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 options.ReadOnly { + fmt.Println(readonlyWarning) + } + + if options.BinaryCodec != "" { + if err := client.SetBinaryCodec(options.BinaryCodec); err != nil { + exitWithMessage(err.Error()) + } + } + + printVersion() +} + +func printVersion() { + chunks := []string{fmt.Sprintf("Pgweb v%s", command.Version)} + + if command.GitCommit != "" { + chunks = append(chunks, fmt.Sprintf("(git: %s)", command.GitCommit)) + } + + if command.GoVersion != "" { + chunks = append(chunks, fmt.Sprintf("(go: %s)", command.GoVersion)) + } + + if command.BuildTime != "" { + chunks = append(chunks, fmt.Sprintf("(build time: %s)", command.BuildTime)) + } + + fmt.Println(strings.Join(chunks, " ")) +} + +func startServer() { + router := gin.Default() + + // 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.SetupRoutes(router) + + fmt.Println("Starting server...") + go func() { + err := router.Run(fmt.Sprintf("%v:%v", options.HTTPHost, options.HTTPPort)) + if err != nil { + fmt.Println("Cant start server:", err) + if strings.Contains(err.Error(), "address already in use") { + openPage() + } + os.Exit(1) + } + }() +} + +func handleSignals() { + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt, os.Kill) + <-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 + } + + exec.Command("open", url).Output() +} + +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 && !command.Opts.DisableConnectionIdleTimeout { + go api.StartSessionCleanup() + } + + startServer() + openPage() + handleSignals() +} diff --git a/pkg/client/client.go b/pkg/client/client.go new file mode 100644 index 0000000000000000000000000000000000000000..865a2e386aca30d820319ea5cbea5c2eefa59ca8 --- /dev/null +++ b/pkg/client/client.go @@ -0,0 +1,476 @@ +package client + +import ( + "errors" + "fmt" + "log" + neturl "net/url" + "reflect" + "regexp" + "strings" + "time" + + "github.com/jmoiron/sqlx" + _ "github.com/lib/pq" + + "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 ( + postgresSignature = regexp.MustCompile(`(?i)postgresql ([\d\.]+)\s`) + postgresType = "PostgreSQL" + + cockroachSignature = regexp.MustCompile(`(?i)cockroachdb ccl v([\d\.]+)\s`) + cockroachType = "CockroachDB" +) + +type Client struct { + db *sqlx.DB + tunnel *Tunnel + serverVersion string + serverType string + lastQueryTime time.Time + External bool + History []history.Record `json:"history"` + ConnectionString string `json:"connection_string"` +} + +// Struct to hold table rows browsing options +type 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) +} + +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.setServerVersion() + return &client, nil +} + +func NewFromUrl(url string, sshInfo *shared.SSHInfo) (*Client, error) { + var tunnel *Tunnel + + 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, + ConnectionString: url, + History: history.New(), + } + + client.setServerVersion() + return &client, nil +} + +func (client *Client) setServerVersion() { + res, err := client.query("SELECT version()") + if err != nil || len(res.Rows) < 1 { + return + } + + version := res.Rows[0][0].(string) + + // Detect postgresql + matches := postgresSignature.FindAllStringSubmatch(version, 1) + if len(matches) > 0 { + client.serverType = postgresType + client.serverVersion = matches[0][1] + return + } + + // Detect cockroachdb + matches = cockroachSignature.FindAllStringSubmatch(version, 1) + if len(matches) > 0 { + client.serverType = cockroachType + client.serverVersion = matches[0][1] + return + } +} + +func (client *Client) Test() error { + return client.db.Ping() +} + +func (client *Client) Info() (*Result, error) { + return client.query(statements.Info) +} + +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) 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 +} + +// Returns all active queriers on the server +func (client *Client) Activity() (*Result, error) { + if client.serverType == cockroachType { + return client.query("SHOW QUERIES") + } + + version := getMajorMinorVersion(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) ServerVersion() string { + return fmt.Sprintf("%s %s", client.serverType, client.serverVersion) +} + +func (client *Client) query(query string, args ...interface{}) (*Result, error) { + // 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 { + 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]) + if action == "update" || action == "delete" { + res, err := client.db.Exec(query, args...) + if err != nil { + return nil, err + } + + affected, err := res.RowsAffected() + if err != nil { + return nil, err + } + + result := Result{ + Columns: []string{"Rows Affected"}, + Rows: []Row{ + Row{affected}, + }, + } + + return &result, nil + } + + rows, err := client.db.Queryx(query, args...) + 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 colums + 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.PostProcess() + + return &result, nil +} + +// Close database connection +func (client *Client) Close() error { + if client.tunnel != nil { + client.tunnel.Close() + } + + if client.db != nil { + return client.db.Close() + } + + return nil +} + +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 +} diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go new file mode 100644 index 0000000000000000000000000000000000000000..4d3f59dc2f07de9f199b305ca5d86ceea575a373 --- /dev/null +++ b/pkg/client/client_test.go @@ -0,0 +1,503 @@ +package client + +import ( + "fmt" + "log" + "os" + "os/exec" + "runtime" + "testing" + "time" + + "github.com/sosedoff/pgweb/pkg/command" + + "github.com/stretchr/testify/assert" +) + +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 pgVersion() (int, int) { + var major, minor int + fmt.Sscanf(os.Getenv("PGVERSION"), "%d.%d", &major, &minor) + 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.db.Close() + } +} + +func teardown() { + _, err := exec.Command( + testCommands["dropdb"], + "-U", serverUser, + "-h", serverHost, + "-p", serverPort, + serverDatabase, + ).CombinedOutput() + + if err != nil { + fmt.Println("Teardown error:", err) + } +} + +func testNewClientFromUrl(t *testing.T) { + url := fmt.Sprintf("postgres://%s@%s:%s/%s?sslmode=disable", serverUser, serverHost, serverPort, serverDatabase) + client, err := NewFromUrl(url, nil) + + if err != nil { + defer client.Close() + } + + assert.Equal(t, nil, err) + assert.Equal(t, url, client.ConnectionString) +} + +func testNewClientFromUrl2(t *testing.T) { + url := fmt.Sprintf("postgresql://%s@%s:%s/%s?sslmode=disable", serverUser, serverHost, serverPort, serverDatabase) + client, err := NewFromUrl(url, nil) + + if err != nil { + defer client.Close() + } + + assert.Equal(t, nil, err) + assert.Equal(t, url, client.ConnectionString) +} + +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) { + assert.Equal(t, nil, testClient.Test()) +} + +func testInfo(t *testing.T) { + res, err := testClient.Info() + + assert.Equal(t, nil, err) + assert.NotEqual(t, nil, res) +} + +func testActivity(t *testing.T) { + res, err := testClient.Activity() + + assert.Equal(t, nil, err) + assert.NotEqual(t, nil, res) +} + +func testDatabases(t *testing.T) { + res, err := testClient.Databases() + + assert.Equal(t, nil, err) + assert.Contains(t, res, "booktown") + assert.Contains(t, res, "postgres") +} + +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", + } + + assert.Equal(t, nil, err) + assert.Equal(t, []string{"schema", "name", "type", "owner", "comment"}, res.Columns) + assert.Equal(t, []string{"public"}, mapKeys(objects)) + assert.Equal(t, tables, objects["public"].Tables) + assert.Equal(t, []string{"recent_shipments", "stock_view"}, objects["public"].Views) + assert.Equal(t, []string{"author_ids", "book_ids", "shipments_ship_id_seq", "subject_ids"}, objects["public"].Sequences) + + major, minor := pgVersion() + if minor == 0 || minor >= 3 { + assert.Equal(t, []string{"m_stock_view"}, objects["public"].MaterializedViews) + } else { + t.Logf("Skipping materialized view on %d.%d\n", major, minor) + } +} + +func testTable(t *testing.T) { + res, err := testClient.Table("books") + + columns := []string{ + "column_name", + "data_type", + "is_nullable", + "character_maximum_length", + "character_set_catalog", + "column_default", + "comment", + } + + assert.Equal(t, nil, 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.Equal(t, nil, 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.Equal(t, nil, err) + assert.Equal(t, 4, len(res.Columns)) + assert.Equal(t, 1, len(res.Rows)) +} + +func testEstimatedTableRowsCount(t *testing.T) { + var count int64 = 15 + res, err := testClient.EstimatedTableRowsCount("books", RowsOptions{}) + + assert.Equal(t, nil, err) + assert.Equal(t, []string{"reltuples"}, res.Columns) + assert.Equal(t, []Row{Row{count}}, res.Rows) +} + +func testTableRowsCount(t *testing.T) { + var count int64 = 15 + res, err := testClient.TableRowsCount("books", RowsOptions{}) + + assert.Equal(t, nil, err) + assert.Equal(t, []string{"count"}, res.Columns) + assert.Equal(t, []Row{Row{count}}, res.Rows) +} + +func testTableRowsCountWithLargeTable(t *testing.T) { + var count int64 = 100010 + testClient.db.MustExec(`CREATE TABLE large_table AS SELECT s FROM generate_Series(1,100010) 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{Row{count}}, res.Rows) +} + +func testTableIndexes(t *testing.T) { + res, err := testClient.TableIndexes("books") + + assert.Equal(t, nil, err) + assert.Equal(t, 2, len(res.Columns)) + assert.Equal(t, 2, len(res.Rows)) +} + +func testTableConstraints(t *testing.T) { + res, err := testClient.TableConstraints("editions") + + assert.Equal(t, nil, 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) { + res, err := testClient.Query("SELECT * FROM books") + + assert.Equal(t, nil, err) + assert.Equal(t, 4, len(res.Columns)) + assert.Equal(t, 15, len(res.Rows)) +} + +func testQueryError(t *testing.T) { + res, err := testClient.Query("SELCT * FROM books") + + assert.NotEqual(t, nil, err) + assert.Equal(t, "pq: syntax error at or near \"SELCT\"", err.Error()) + assert.Equal(t, true, res == nil) +} + +func testQueryInvalidTable(t *testing.T) { + res, err := testClient.Query("SELECT * FROM books2") + + assert.NotEqual(t, nil, err) + assert.Equal(t, "pq: relation \"books2\" does not exist", err.Error()) + assert.Equal(t, true, res == nil) +} + +func testTableRowsOrderEscape(t *testing.T) { + rows, err := testClient.TableRows("dummies", RowsOptions{SortColumn: "isDummy"}) + assert.Equal(t, nil, err) + assert.Equal(t, 2, len(rows.Rows)) + + rows, err = testClient.TableRows("dummies", RowsOptions{SortColumn: "isdummy"}) + assert.NotEqual(t, nil, err) + assert.Equal(t, `pq: column "isdummy" does not exist`, err.Error()) + assert.Equal(t, true, rows == nil) +} + +func testResultJSON(t *testing.T) { + result, err := testClient.Query("SELECT 'NaN'::float AS value;") + + assert.NoError(t, err) + assert.Equal(t, `[{"value":null}]`, string(result.JSON())) +} + +func testResultCsv(t *testing.T) { + res, _ := testClient.Query("SELECT * FROM books ORDER BY id ASC LIMIT 1") + csv := res.CSV() + + expected := "id,title,author_id,subject_id\n156,The Tell-Tale Heart,115,9\n" + + assert.Equal(t, expected, string(csv)) +} + +func testHistory(t *testing.T) { + _, err := testClient.Query("SELECT * FROM books WHERE id = 12345") + query := testClient.History[len(testClient.History)-1].Query + + assert.Equal(t, nil, err) + assert.Equal(t, "SELECT * FROM books WHERE id = 12345", query) +} + +func testHistoryError(t *testing.T) { + _, err := testClient.Query("SELECT * FROM books123") + query := testClient.History[len(testClient.History)-1].Query + + assert.NotEqual(t, nil, err) + assert.NotEqual(t, "SELECT * FROM books123", query) +} + +func testHistoryUniqueness(t *testing.T) { + url := fmt.Sprintf("postgres://%s@%s:%s/%s?sslmode=disable", serverUser, serverHost, serverPort, serverDatabase) + client, _ := NewFromUrl(url, nil) + + client.Query("SELECT * FROM books WHERE id = 1") + client.Query("SELECT * FROM books WHERE id = 1") + + 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) + + 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 + client.db.Exec("SET default_transaction_read_only=off;") + + _, 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) +} + +func TestAll(t *testing.T) { + if onWindows() { + t.Log("Unit testing on Windows platform is not supported.") + return + } + + initVars() + setupCommands() + teardown() + setup() + setupClient() + + testNewClientFromUrl(t) + testClientIdleTime(t) + testTest(t) + testInfo(t) + testActivity(t) + testDatabases(t) + testObjects(t) + testTable(t) + testTableRows(t) + testTableInfo(t) + testEstimatedTableRowsCount(t) + testTableRowsCount(t) + testTableRowsCountWithLargeTable(t) + testTableIndexes(t) + testTableConstraints(t) + testTableNameWithCamelCase(t) + testQuery(t) + testQueryError(t) + testQueryInvalidTable(t) + testTableRowsOrderEscape(t) + testResultJSON(t) + testResultCsv(t) + testHistory(t) + testHistoryUniqueness(t) + testHistoryError(t) + testReadOnlyMode(t) + testDumpExport(t) + + teardownClient() + teardown() +} diff --git a/pkg/client/codec.go b/pkg/client/codec.go new file mode 100644 index 0000000000000000000000000000000000000000..82cb2c0c428dc974134942ac9aca6c65c9f2679d --- /dev/null +++ b/pkg/client/codec.go @@ -0,0 +1,45 @@ +package client + +import ( + "encoding/base64" + "encoding/hex" + "fmt" + + "github.com/mr-tron/base58" +) + +const ( + CodecNone = "none" + CodecHex = "hex" + CodecBase58 = "base58" + CodecBase64 = "base64" +) + +var ( + // BinaryEncodingFormat specifies the default 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) + } +} diff --git a/pkg/client/codec_test.go b/pkg/client/codec_test.go new file mode 100644 index 0000000000000000000000000000000000000000..a54357a55ee401372914a923a4339e19915da7f3 --- /dev/null +++ b/pkg/client/codec_test.go @@ -0,0 +1,51 @@ +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)) + }) + } +} diff --git a/pkg/client/dump.go b/pkg/client/dump.go new file mode 100644 index 0000000000000000000000000000000000000000..d9c1705f90cba03d787ac144c1a2c7f3e1988c47 --- /dev/null +++ b/pkg/client/dump.go @@ -0,0 +1,75 @@ +package client + +import ( + "bytes" + "fmt" + "io" + "net/url" + "os/exec" + "strings" +) + +var ( + unsupportedDumpOptions = []string{ + "search_path", + } +) + +// Dump represents a database dump +type Dump struct { + Table string +} + +// CanExport returns true if database dump tool could be used without an error +func (d *Dump) CanExport() bool { + return exec.Command("pg_dump", "--version").Run() == nil +} + +// Export streams the database dump to the specified writer +func (d *Dump) Export(connstr string, writer io.Writer) error { + if str, err := removeUnsupportedOptions(connstr); err != nil { + return err + } else { + connstr = str + } + + errOutput := bytes.NewBuffer(nil) + + 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) + + cmd := exec.Command("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 +} diff --git a/pkg/client/dump_test.go b/pkg/client/dump_test.go new file mode 100644 index 0000000000000000000000000000000000000000..61c51714bd16f465189fa1b6bce170a0619a12f1 --- /dev/null +++ b/pkg/client/dump_test.go @@ -0,0 +1,52 @@ +package client + +import ( + "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.True(t, dump.CanExport()) + + // Test full db dump + err = dump.Export(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(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(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(searchPathURL, saveFile) + assert.NoError(t, err) +} diff --git a/pkg/client/result.go b/pkg/client/result.go new file mode 100644 index 0000000000000000000000000000000000000000..4cef6691ac25f44e210e2f8e2198090104d63c0b --- /dev/null +++ b/pkg/client/result.go @@ -0,0 +1,163 @@ +package client + +import ( + "bytes" + "encoding/csv" + "encoding/json" + "fmt" + "math" + "strconv" + "time" + + "github.com/sosedoff/pgweb/pkg/command" +) + +type Row []interface{} + +type Pagination struct { + Rows int64 `json:"rows_count"` + Page int64 `json:"page"` + Pages int64 `json:"pages_count"` + PerPage int64 `json:"per_page"` +} + +type Result struct { + Pagination *Pagination `json:"pagination,omitempty"` + Columns []string `json:"columns"` + Rows []Row `json:"rows"` +} + +type Objects struct { + Tables []string `json:"table"` + Views []string `json:"view"` + MaterializedViews []string `json:"materialized_view"` + Sequences []string `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) + } + } + } + } +} + +func (res *Result) Format() []map[string]interface{} { + var items []map[string]interface{} + + for _, row := range res.Rows { + item := make(map[string]interface{}) + + for i, c := range res.Columns { + item[c] = row[i] + } + + items = append(items, item) + } + + return items +} + +func (res *Result) CSV() []byte { + buff := &bytes.Buffer{} + writer := csv.NewWriter(buff) + + writer.Write(res.Columns) + + for _, row := range res.Rows { + record := make([]string, len(res.Columns)) + + for i, item := range row { + if item != nil { + switch v := item.(type) { + case time.Time: + record[i] = v.Format("2006-01-02 15:04:05") + default: + record[i] = fmt.Sprintf("%v", item) + } + } else { + record[i] = "" + } + } + + 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 { + schema := row[0].(string) + name := row[1].(string) + object_type := row[2].(string) + + if objects[schema] == nil { + objects[schema] = &Objects{ + Tables: []string{}, + Views: []string{}, + MaterializedViews: []string{}, + Sequences: []string{}, + } + } + + switch object_type { + case "table": + objects[schema].Tables = append(objects[schema].Tables, name) + case "view": + objects[schema].Views = append(objects[schema].Views, name) + case "materialized_view": + objects[schema].MaterializedViews = append(objects[schema].MaterializedViews, name) + case "sequence": + objects[schema].Sequences = append(objects[schema].Sequences, name) + } + } + + return objects +} diff --git a/pkg/client/result_test.go b/pkg/client/result_test.go new file mode 100644 index 0000000000000000000000000000000000000000..b1bdd29535c70621b6ec950eb8aae4064ce51826 --- /dev/null +++ b/pkg/client/result_test.go @@ -0,0 +1,92 @@ +package client + +import ( + "encoding/json" + "testing" + + "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"}, + Rows: []Row{ + {1, "John", "john@example.com"}, + {2, "Bob", "bob@example.com"}, + }, + } + + expected := "id,name,email\n1,John,john@example.com\n2,Bob,bob@example.com\n" + output := string(result.CSV()) + + assert.Equal(t, expected, output) +} + +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) +} diff --git a/pkg/client/tunnel.go b/pkg/client/tunnel.go new file mode 100644 index 0000000000000000000000000000000000000000..784c01e6d6a8c522c375cce0d312898e8533469a --- /dev/null +++ b/pkg/client/tunnel.go @@ -0,0 +1,220 @@ +package client + +import ( + "errors" + "fmt" + "io" + "io/ioutil" + "log" + "net" + "net/url" + "os" + "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 privateKeyPath() string { + return 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 := ioutil.ReadFile(keyPath) + if err != nil { + return nil, err + } + + signer, err := ssh.ParsePrivateKey(buff) + if err != nil { + if strings.Contains(err.Error(), "cannot decode encrypted private keys") { + 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 = privateKeyPath() + } else { + keyPath = expandKeyPath(keyPath) + } + + if !fileExists(keyPath) { + return nil, errors.New("ssh public key not found at " + keyPath) + } + + // Appen 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 +} diff --git a/pkg/client/util.go b/pkg/client/util.go new file mode 100644 index 0000000000000000000000000000000000000000..d83bf1d79bbaaac6b91d76258e387fa5da47d229 --- /dev/null +++ b/pkg/client/util.go @@ -0,0 +1,45 @@ +package client + +import ( + "regexp" + "strings" +) + +var ( + // List of keywords that are not allowed in read-only mode + reRestrictedKeywords = regexp.MustCompile(`(?mi)\s?(CREATE|INSERT|DROP|DELETE|TRUNCATE|GRANT|OPEN|IMPORT|COPY)\s`) + + // Comment regular expressions + reSlashComment = regexp.MustCompile(`(?m)/\*.+\*/`) + reDashComment = regexp.MustCompile(`(?m)--.+`) +) + +// Get short version from the string +// Example: 10.2.3.1 -> 10.2 +func getMajorMinorVersion(str string) string { + chunks := strings.Split(str, ".") + if len(chunks) == 0 { + return str + } + return strings.Join(chunks[0:2], ".") +} + +// 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 +} diff --git a/pkg/command/options.go b/pkg/command/options.go new file mode 100644 index 0000000000000000000000000000000000000000..18d20933606c2a2be3200bb1436a2d72c17d99de --- /dev/null +++ b/pkg/command/options.go @@ -0,0 +1,141 @@ +package command + +import ( + "errors" + "os" + "os/user" + "strings" + + "github.com/jessevdk/go-flags" +) + +type Options struct { + Version bool `short:"v" long:"version" description:"Print version"` + Debug bool `short:"d" long:"debug" description:"Enable debugging mode"` + 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"` + DbName string `long:"db" description:"Database name"` + Ssl 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"` + 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:""` + 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"` + 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"` +} + +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 + } + + if opts.URL == "" { + opts.URL = os.Getenv("DATABASE_URL") + } + + if opts.Prefix == "" { + opts.Prefix = os.Getenv("URL_PREFIX") + } + + // 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 os.Getenv("SESSIONS") != "" { + opts.Sessions = true + } + + if os.Getenv("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.Ssl = "" + } + + if opts.Prefix != "" && !strings.Contains(opts.Prefix, "/") { + opts.Prefix = opts.Prefix + "/" + } + + if opts.AuthUser == "" && os.Getenv("AUTH_USER") != "" { + opts.AuthUser = os.Getenv("AUTH_USER") + } + + if opts.AuthPass == "" && os.Getenv("AUTH_PASS") != "" { + opts.AuthPass = os.Getenv("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") + } + } + + 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") +} diff --git a/pkg/command/options_test.go b/pkg/command/options_test.go new file mode 100644 index 0000000000000000000000000000000000000000..961370c2f9caed81b03f1c77c0ef61a977a04c7d --- /dev/null +++ b/pkg/command/options_test.go @@ -0,0 +1,47 @@ +package command + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestParseOptions(t *testing.T) { + // Test default behavior + 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) + + // Test sessions + opts, err = ParseOptions([]string{"--sessions", "1"}) + assert.NoError(t, err) + assert.Equal(t, true, opts.Sessions) + + // Test url prefix + 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) + + // Test connect backend options + opts, err = ParseOptions([]string{"--connect-backend", "test"}) + assert.EqualError(t, err, "--sessions flag must be set") + + opts, err = ParseOptions([]string{"--connect-backend", "test", "--sessions"}) + assert.EqualError(t, err, "--connect-token flag must be set") + + opts, err = ParseOptions([]string{"--connect-backend", "test", "--sessions", "--connect-token", "token"}) + assert.NoError(t, err) +} diff --git a/pkg/command/version.go b/pkg/command/version.go new file mode 100644 index 0000000000000000000000000000000000000000..31b283a96d2376e302a65b649c678ad87739293f --- /dev/null +++ b/pkg/command/version.go @@ -0,0 +1,17 @@ +package command + +const ( + // Version is the current Pgweb application version + Version = "0.11.11" +) + +var ( + // GitCommit contains the Git commit SHA for the binary + GitCommit string + + // BuildTime contains the binary build time + BuildTime string + + // GoVersion contains the build time Go version + GoVersion string +) diff --git a/pkg/connection/connection_string.go b/pkg/connection/connection_string.go new file mode 100644 index 0000000000000000000000000000000000000000..3ca831c3a3ec60c48ff16529c3572211a2e043e7 --- /dev/null +++ b/pkg/connection/connection_string.go @@ -0,0 +1,137 @@ +package connection + +import ( + "errors" + "fmt" + neturl "net/url" + "os" + "os/user" + "strings" + + "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.Ssl == "" { + // 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.Ssl + } + } + + // 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.Ssl != "" { + query.Add("sslmode", opts.Ssl) + } 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) + } + + 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 +} diff --git a/pkg/connection/connection_string_test.go b/pkg/connection/connection_string_test.go new file mode 100644 index 0000000000000000000000000000000000000000..ce82bfe000663ff91c812fdb8b0a97d86bcbb082 --- /dev/null +++ b/pkg/connection/connection_string_test.go @@ -0,0 +1,174 @@ +package connection + +import ( + "fmt" + "net/url" + "os/user" + "testing" + + "github.com/sosedoff/pgweb/pkg/command" + "github.com/stretchr/testify/assert" +) + +func Test_Invalid_Url(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 Test_Valid_Url(t *testing.T) { + url := "postgres://myhost/database" + str, err := BuildStringFromOptions(command.Options{URL: url}) + + assert.Equal(t, nil, err) + assert.Equal(t, url, str) +} + +func Test_Url_And_Ssl_Flag(t *testing.T) { + str, err := BuildStringFromOptions(command.Options{ + URL: "postgres://myhost/database", + Ssl: "disable", + }) + + assert.Equal(t, nil, err) + assert.Equal(t, "postgres://myhost/database?sslmode=disable", str) +} + +func Test_Localhost_Url_And_No_Ssl_Flag(t *testing.T) { + str, err := BuildStringFromOptions(command.Options{ + URL: "postgres://localhost/database", + }) + assert.Equal(t, nil, err) + assert.Equal(t, "postgres://localhost/database?sslmode=disable", str) + + str, err = BuildStringFromOptions(command.Options{ + URL: "postgres://127.0.0.1/database", + }) + assert.Equal(t, nil, err) + assert.Equal(t, "postgres://127.0.0.1/database?sslmode=disable", str) +} + +func Test_Localhost_Url_And_Ssl_Flag(t *testing.T) { + str, err := BuildStringFromOptions(command.Options{ + URL: "postgres://localhost/database", + Ssl: "require", + }) + assert.Equal(t, nil, err) + assert.Equal(t, "postgres://localhost/database?sslmode=require", str) + + str, err = BuildStringFromOptions(command.Options{ + URL: "postgres://127.0.0.1/database", + Ssl: "require", + }) + assert.Equal(t, nil, err) + assert.Equal(t, "postgres://127.0.0.1/database?sslmode=require", str) +} + +func Test_Localhost_Url_And_Ssl_Arg(t *testing.T) { + str, err := BuildStringFromOptions(command.Options{ + URL: "postgres://localhost/database?sslmode=require", + }) + assert.Equal(t, nil, 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.Equal(t, nil, err) + assert.Equal(t, "postgres://127.0.0.1/database?sslmode=require", str) +} + +func Test_ExtendedSSLFlags(t *testing.T) { + str, err := BuildStringFromOptions(command.Options{ + URL: "postgres://localhost/database?sslmode=require&sslcert=cert&sslkey=key&sslrootcert=ca", + }) + assert.Equal(t, nil, err) + assert.Equal(t, "postgres://localhost/database?sslcert=cert&sslkey=key&sslmode=require&sslrootcert=ca", str) +} + +func Test_Flag_Args(t *testing.T) { + str, err := BuildStringFromOptions(command.Options{ + Host: "host", + Port: 5432, + User: "user", + Pass: "password", + DbName: "db", + }) + + assert.Equal(t, nil, err) + assert.Equal(t, "postgres://user:password@host:5432/db", str) +} + +func Test_Localhost(t *testing.T) { + opts := command.Options{ + Host: "localhost", + Port: 5432, + User: "user", + Pass: "password", + DbName: "db", + } + + str, err := BuildStringFromOptions(opts) + assert.Equal(t, nil, err) + assert.Equal(t, "postgres://user:password@localhost:5432/db?sslmode=disable", str) + + opts.Host = "127.0.0.1" + str, err = BuildStringFromOptions(opts) + assert.Equal(t, nil, err) + assert.Equal(t, "postgres://user:password@127.0.0.1:5432/db?sslmode=disable", str) +} + +func Test_Localhost_And_Ssl(t *testing.T) { + opts := command.Options{ + Host: "localhost", + Port: 5432, + User: "user", + Pass: "password", + DbName: "db", + Ssl: "require", + SslKey: "keyPath", + SslCert: "certPath", + SslRootCert: "caPath", + } + + str, err := BuildStringFromOptions(opts) + assert.Equal(t, nil, err) + assert.Equal(t, "postgres://user:password@localhost:5432/db?sslcert=certPath&sslkey=keyPath&sslmode=require&sslrootcert=caPath", str) +} + +func Test_No_User(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.Equal(t, nil, err) + assert.Equal(t, fmt.Sprintf("postgres://%s@host:5432/db", userAndPass), str) +} + +func Test_Port(t *testing.T) { + opts := command.Options{Host: "host", User: "user", Port: 5000, DbName: "db"} + str, err := BuildStringFromOptions(opts) + + assert.Equal(t, nil, err) + assert.Equal(t, "postgres://user:@host:5000/db", str) +} + +func Test_Blank(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"})) +} diff --git a/pkg/connection/port.go b/pkg/connection/port.go new file mode 100644 index 0000000000000000000000000000000000000000..a596730e849f3bb804fac2772c3ebcc8d2fdd2d0 --- /dev/null +++ b/pkg/connection/port.go @@ -0,0 +1,32 @@ +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 { + if strings.Index(err.Error(), "connection refused") > 0 { + return true + } + return false + } + + 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") +} diff --git a/pkg/connection/port_test.go b/pkg/connection/port_test.go new file mode 100644 index 0000000000000000000000000000000000000000..4ee3eae258862a7a26e4bbfd2a1f1a1b3ee7768b --- /dev/null +++ b/pkg/connection/port_test.go @@ -0,0 +1,75 @@ +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) +} diff --git a/pkg/history/history.go b/pkg/history/history.go new file mode 100644 index 0000000000000000000000000000000000000000..09ccf158de37c2de3b17a52bf8e954fb7b991383 --- /dev/null +++ b/pkg/history/history.go @@ -0,0 +1,21 @@ +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(), + } +} diff --git a/pkg/shared/ssh_info.go b/pkg/shared/ssh_info.go new file mode 100644 index 0000000000000000000000000000000000000000..c1d374a328316a861cd4e1d3e15a88e664eabfcb --- /dev/null +++ b/pkg/shared/ssh_info.go @@ -0,0 +1,19 @@ +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) +} diff --git a/pkg/statements/sql.go b/pkg/statements/sql.go new file mode 100644 index 0000000000000000000000000000000000000000..84a94eb7cc1b22ed9e8fdfc830e13f4ff9597c90 --- /dev/null +++ b/pkg/statements/sql.go @@ -0,0 +1,167 @@ +package statements + +const ( + Databases = ` +SELECT + datname +FROM + pg_database +WHERE + NOT datistemplate +ORDER BY + datname ASC` + + // --------------------------------------------------------------------------- + + Schemas = ` +SELECT + schema_name +FROM + information_schema.schemata +ORDER BY + schema_name ASC` + + // --------------------------------------------------------------------------- + + Info = ` +SELECT + session_user, + current_user, + current_database(), + current_schemas(false), + inet_client_addr(), + inet_client_port(), + inet_server_addr(), + inet_server_port(), + version()` + + // --------------------------------------------------------------------------- + + EstimatedTableRowCount = ` +SELECT + reltuples +FROM + pg_class +WHERE + oid = ('"' || $1::text || '"."' || $2::text || '"')::regclass +` + + // --------------------------------------------------------------------------- + + TableIndexes = ` +SELECT + indexname, indexdef +FROM + pg_indexes +WHERE + schemaname = $1 AND + tablename = $2` + + // --------------------------------------------------------------------------- + + TableConstraints = ` +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 +ORDER BY + contype desc` + + // --------------------------------------------------------------------------- + + TableInfo = ` +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` + + TableInfoCockroach = ` +SELECT + 'n/a' AS data_size, + 'n/a' AS index_size, + 'n/a' AS total_size, + 'n/a' AS rows_count` + + // --------------------------------------------------------------------------- + + TableSchema = ` +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` + + // --------------------------------------------------------------------------- + + MaterializedView = ` +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` + + // --------------------------------------------------------------------------- + + Objects = ` +SELECT + 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' AND + n.nspname NOT IN ('information_schema', 'pg_catalog') AND + has_schema_privilege(n.nspname, 'USAGE') +ORDER BY 1, 2` +) + +var ( + 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()", + } +) diff --git a/pkg/util/profiler.go b/pkg/util/profiler.go new file mode 100644 index 0000000000000000000000000000000000000000..e846ad3549a19bb0ee8c5ca3a5ba2df75f888c2a --- /dev/null +++ b/pkg/util/profiler.go @@ -0,0 +1,32 @@ +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() +} diff --git a/screenshots/browse.png b/screenshots/browse.png new file mode 100644 index 0000000000000000000000000000000000000000..4689e0c3804a6a37c8d1e96416c5341d5127111e Binary files /dev/null and b/screenshots/browse.png differ diff --git a/screenshots/connect.png b/screenshots/connect.png new file mode 100644 index 0000000000000000000000000000000000000000..c16bcc02f972094bd4d62da758af1e6463be68ab Binary files /dev/null and b/screenshots/connect.png differ diff --git a/screenshots/query.png b/screenshots/query.png new file mode 100644 index 0000000000000000000000000000000000000000..05c0e86a5a992f1ca6acef8b24b3b065a89749bc Binary files /dev/null and b/screenshots/query.png differ diff --git a/script/check_formatting.sh b/script/check_formatting.sh new file mode 100644 index 0000000000000000000000000000000000000000..cf8ae957924e781330c29d8ec1c5101dd1d3fd1c --- /dev/null +++ b/script/check_formatting.sh @@ -0,0 +1,12 @@ +#!/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 \ No newline at end of file diff --git a/script/package.sh b/script/package.sh new file mode 100644 index 0000000000000000000000000000000000000000..f46bec20de567e45031fb14ddb299a184a3b7886 --- /dev/null +++ b/script/package.sh @@ -0,0 +1,15 @@ +#!/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 \ No newline at end of file diff --git a/script/test_all.sh b/script/test_all.sh new file mode 100644 index 0000000000000000000000000000000000000000..061381ff3418514a6876f3f6be72a16c5f2f88ac --- /dev/null +++ b/script/test_all.sh @@ -0,0 +1,34 @@ +#!/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 + diff --git a/script/test_cockroach.sh b/script/test_cockroach.sh new file mode 100644 index 0000000000000000000000000000000000000000..a4cf351246a55f3fbc6d94cdec06e567d04a4fad --- /dev/null +++ b/script/test_cockroach.sh @@ -0,0 +1,58 @@ +#!/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 \ No newline at end of file diff --git a/script/update_homebrew.sh b/script/update_homebrew.sh new file mode 100644 index 0000000000000000000000000000000000000000..0280f55ae040fa9f3b65a94f82f5225573ce643e --- /dev/null +++ b/script/update_homebrew.sh @@ -0,0 +1,24 @@ +#!/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 diff --git a/static/css/app.css b/static/css/app.css new file mode 100644 index 0000000000000000000000000000000000000000..4041dc67c178030bbf8ec87955cbe23a9211640f --- /dev/null +++ b/static/css/app.css @@ -0,0 +1,799 @@ +#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 { + height: 255px; +} + +#input .wrapper { + position: relative; +} + +#input .actions { + background: #fff; + padding: 10px; + height: 50px; + border-bottom: solid 1px #ddd; +} + +#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; + 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; +} + +#objects { +} + +#output { + position: absolute; + left: 0px; + top: 256px; + 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; +} + +.full #output { + top: 0px !important; +} + +.with-pagination #output { + top: 50px !important; +} + +.with-pagination #pagination { + display: block; +} + +.left { + float: left; +} + +/* -------------------------------------------------------------------------- */ + +#custom_query { + height: 193px; + margin-top: 12px; +} + +#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; +} \ No newline at end of file diff --git a/static/css/bootstrap.css b/static/css/bootstrap.css new file mode 100644 index 0000000000000000000000000000000000000000..a9f35ceedfac7fc0559b121bed105eaf80f10bf2 --- /dev/null +++ b/static/css/bootstrap.css @@ -0,0 +1,5 @@ +/*! + * 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}} \ No newline at end of file diff --git a/static/css/font-awesome.css b/static/css/font-awesome.css new file mode 100644 index 0000000000000000000000000000000000000000..ec53d4d6d5bf13db8ca757ea991a9c7839f23c89 --- /dev/null +++ b/static/css/font-awesome.css @@ -0,0 +1,4 @@ +/*! + * 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"} \ No newline at end of file diff --git a/static/data.go b/static/data.go new file mode 100644 index 0000000000000000000000000000000000000000..462a3aa5c0eaaf0236be6e0ea34071eff7c3ea1a --- /dev/null +++ b/static/data.go @@ -0,0 +1,7 @@ +package static + +import "embed" + +//go:embed img/* js/* css/* fonts/* +//go:embed index.html +var Static embed.FS \ No newline at end of file diff --git a/static/fonts/FontAwesome.otf b/static/fonts/FontAwesome.otf new file mode 100644 index 0000000000000000000000000000000000000000..81c9ad949b47f64afeca5642ee2494b6e3147f44 Binary files /dev/null and b/static/fonts/FontAwesome.otf differ diff --git a/static/fonts/fontawesome-webfont.eot b/static/fonts/fontawesome-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..84677bc0c5f37f1fac9d87548c4554b5c91717cf Binary files /dev/null and b/static/fonts/fontawesome-webfont.eot differ diff --git a/static/fonts/fontawesome-webfont.svg b/static/fonts/fontawesome-webfont.svg new file mode 100644 index 0000000000000000000000000000000000000000..d907b25ae60ec7e3d32e4027aa6e6b7595de97af --- /dev/null +++ b/static/fonts/fontawesome-webfont.svg @@ -0,0 +1,520 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/static/fonts/fontawesome-webfont.ttf b/static/fonts/fontawesome-webfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..96a3639cdde5e8ab459c6380e3b9524ee81641dc Binary files /dev/null and b/static/fonts/fontawesome-webfont.ttf differ diff --git a/static/fonts/fontawesome-webfont.woff b/static/fonts/fontawesome-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..628b6a52a87e62c6f22426e17c01f6a303aa194e Binary files /dev/null and b/static/fonts/fontawesome-webfont.woff differ diff --git a/static/img/icon.png b/static/img/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7ac484c426e2d3a37a1b04afc7f6b509fde390d0 Binary files /dev/null and b/static/img/icon.png differ diff --git a/static/index.html b/static/index.html new file mode 100644 index 0000000000000000000000000000000000000000..ce5c643567709e75f222a04a80f2b1e866b021ca --- /dev/null +++ b/static/index.html @@ -0,0 +1,316 @@ + + + + openGauss-webclient + + + + + + + + + + + + + + + + + + + +
+ + +
+
+
+
+
+ +
+ + +
+
请稍后,查询执行中...
+
+ + + + +
+
+
+
+
+
+ + + +
+
+
+ +
+
+ +
+
+
+

openGauss-webclient

+
+
+
+ +
+
+
+ + + +
+
+ +
+ +
+
+
+ + +

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

+
+
+
+ +
+
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+
+ +
+
+ +

SSH Connection

+ +
+ +
+ +
+
+ +
+
+ +
+ +
+ +
+
+ +
+
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+ + +
+
+
+
+
+ + +
+ +
+ + + + diff --git a/static/js/ace-pgsql.js b/static/js/ace-pgsql.js new file mode 100644 index 0000000000000000000000000000000000000000..ac19a3107ccbc5df421e8df37c1a413ed74d8fa6 --- /dev/null +++ b/static/js/ace-pgsql.js @@ -0,0 +1,7 @@ +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; + } + }); +})(); diff --git a/static/js/ace.js b/static/js/ace.js new file mode 100644 index 0000000000000000000000000000000000000000..d88c52e10d6aed36c5c4e87b311d0c7855beb00f --- /dev/null +++ b/static/js/ace.js @@ -0,0 +1,16 @@ +(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; + } + }); +})(); diff --git a/static/js/app.js b/static/js/app.js new file mode 100644 index 0000000000000000000000000000000000000000..d09b16062e8d62e34b2ac798999c948db5fe53a6 --- /dev/null +++ b/static/js/app.js @@ -0,0 +1,1566 @@ +var editor = null; +var connected = false; +var bookmarks = {}; +var default_rows_limit = 100; +var currentObject = null; +var autocompleteObjects = []; + +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 = 300000; // 5 mins is enough + + $.ajax({ + timeout: timeout, + url: "api" + path, + method: method, + cache: false, + data: params, + headers: { + "x-session-id": getSessionId() + }, + success: function(data) { + cb(data); + }, + error: function(xhr, status, data) { + if (status == "timeout") { + return cb({ error: "Query timeout after " + (timeout / 1000) + "s" }); + } + + cb(jQuery.parseJSON(xhr.responseText)); + } + }); +} + +function getInfo(cb) { apiCall("get", "/info", {}, 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 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 buildSchemaSection(name, objects) { + var section = ""; + + var titles = { + "table": "Tables", + "view": "Views", + "materialized_view": "Materialized Views", + "sequence": "Sequences" + }; + + var icons = { + "table": '', + "view": '', + "materialized_view": '', + "sequence": '' + }; + + var klass = ""; + if (name == "public") klass = "expanded"; + + section += "
"; + section += "
" + name + "
"; + section += "
"; + + ["table", "view", "materialized_view", "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; + section += "
  • " + icons[group] + " " + item + "
  • "; + }); + section += "
"; + } + }); + + section += "
"; + + return section; +} + +function loadSchemas() { + $("#objects").html(""); + + getObjects(function(data) { + if (Object.keys(data).length == 0) { + data["public"] = { + table: [], + view: [], + materialized_view: [], + sequence: [] + }; + } + + for (schema in data) { + $(buildSchemaSection(schema, data[schema])).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")) { + continue + } + for (item in data[schema][kind]) { + autocompleteObjects.push({ + caption: data[schema][kind][item], + value: data[schema][kind][item], + 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"). + data("mode", ""). + removeClass("empty"). + removeClass("no-crop"); + + $("#results_header").html(""); + $("#results_body").html(""); +} + +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 = window.encodeURI("SELECT * FROM " + table); + var url = window.location.href.split("#")[0] + "api/query?format=" + format + "&filename=" + filename + "&query=" + query + "&_session_id=" + getSessionId(); + var win = window.open(url, "_blank"); + win.focus(); + break; + case "dump": + var url = window.location.href.split("#")[0] + "api/export?table=" + table + "&_session_id=" + getSessionId(); + var win = window.open(url, "_blank"); + win.focus(); + break; + case "copy": + copyToClipboard(table.split('.')[1]); + 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 = window.encodeURI("SELECT * FROM " + view); + var url = window.location.href.split("#")[0] + "api/query?format=" + format + "&filename=" + filename + "&query=" + query + "&_session_id=" + getSessionId(); + var win = window.open(url, "_blank"); + win.focus(); + break; + case "copy": + copyToClipboard(view.split('.')[1]); + 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"); + $("#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 + $("#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; + } + + 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 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) { + buildTable(data); + $("#results").addClass("no-crop"); + }); +} + +function showQueryPanel() { + if (!$("#table_query").hasClass("selected")) { + resetTable(); + } + + setCurrentTab("table_query"); + editor.focus(); + + $("#input").show(); + $("#body").prop("class", "") +} + +function showConnectionPanel() { + setCurrentTab("table_connection"); + + apiCall("get", "/connection", {}, function(data) { + var rows = []; + + for(key in data) { + rows.push([key, data[key]]); + } + + buildTable({ + columns: ["attribute", "value"], + rows: rows + }); + + $("#input").hide(); + $("#body").addClass("full"); + }); +} + +function showActivityPanel() { + var options = { + action: { + name: "stop_query", + title: "stop", + data: "pid", + style: "danger" + } + } + + setCurrentTab("table_activity"); + apiCall("get", "/activity", {}, function(data) { + buildTable(data, null, null, options); + $("#input").hide(); + $("#body").addClass("full"); + }); +} + +function showQueryProgressMessage() { + $("#run, #explain-dropdown-toggle, #csv, #json, #xml").prop("disabled", true); + $("#explain-dropdown").removeClass("open"); + $("#query_progress").show(); +} + +function hideQueryProgressMessage() { + $("#run, #explain-dropdown-toggle, #csv, #json, #xml").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 exportTo(format) { + var query = getEditorSelection(); + if (query.length == 0) { + return; + } + + var url = window.location.href.split("#")[0] + "api/query?format=" + format + "&query=" + encodeQuery(query) + "&_session_id=" + getSessionId(); + var win = window.open(url, '_blank'); + + setCurrentTab("table_query"); + win.focus(); +} + +// 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 in data) { + $("").appendTo("#connection_bookmarks"); + } + + $(".bookmarks").show(); + } + else { + $(".bookmarks").hide(); + } + }); + + $("#connection_window").show(); +} + +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 "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 "export": + var url = window.location.href.split("#")[0] + "api/export?_session_id=" + getSessionId(); + var win = window.open(url, "_blank"); + win.focus(); + 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); + } + }); + } + }); +} + +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"); + }); +} + +$(document).ready(function() { + $("#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").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(""); + + 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 name = $.trim($(this).val()); + if (name == "") return; + + var item = bookmarks[name]; + + // Check if bookmark only has url set + if (item.url && item.url != "") { + $("#connection_url").val(item.url); + $("#connection_scheme").click(); + return; + } + + // Fill in bookmarked connection settings + $("#pg_host").val(item.host); + $("#pg_port").val(item.port); + $("#pg_user").val(item.user); + $("#pg_password").val(item.password); + $("#pg_db").val(item.database); + $("#connection_ssl").val(item.ssl); + + if (item.ssh && Object.keys(item.ssh).length > 0) { + $("#ssh_host").val(item.ssh.host); + $("#ssh_port").val(item.ssh.port); + $("#ssh_user").val(item.ssh.user); + $("#ssh_password").val(item.ssh.password); + $("#ssh_key").val(item.ssh.key); + $("#ssh_key_password").val(item.ssh.keypassword); + $("#connection_ssh").click(); + } + else { + $("#ssh_host").val(""); + $("#ssh_port").val(""); + $("#ssh_user").val(""); + $("#ssh_password").val(""); + $("#ssh_key").val(""); + $("#ssh_key_password").val(""); + $(".connection-ssh-group").hide(); + $("#connection_standard").click(); + } + }); + + $("#connection_form").on("submit", function(e) { + e.preventDefault(); + + var button = $(this).find("button.open-connection"); + var 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(); + + $("#connection_window").hide(); + $("#current_database").text(resp.current_database); + $("#main").show(); + } + }); + }); + + initEditor(); + addShortcutTooltips(); + + // 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); + } + + apiCall("get", "/connection", {}, function(resp) { + if (resp.error) { + connected = false; + showConnectionSettings(); + $(".connection-actions").show(); + } + else { + connected = true; + loadSchemas(); + + $("#current_database").text(resp.current_database); + $("#main").show(); + + if (!resp.session_lock) { + $(".connection-actions").show(); + } + } + }); + + bindDatabaseObjectsFilter(); +}); diff --git a/static/js/base64.js b/static/js/base64.js new file mode 100644 index 0000000000000000000000000000000000000000..c33fa8aea1764bd6f7c59cac18dd8b0b0f934d35 --- /dev/null +++ b/static/js/base64.js @@ -0,0 +1,107 @@ +/** +* +* 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; + } +} \ No newline at end of file diff --git a/static/js/bootstrap-contextmenu.js b/static/js/bootstrap-contextmenu.js new file mode 100644 index 0000000000000000000000000000000000000000..eb11e26fea16a09d8479d9e3557ceb3088333693 --- /dev/null +++ b/static/js/bootstrap-contextmenu.js @@ -0,0 +1,205 @@ +/*! + * 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)); diff --git a/static/js/bootstrap-dropdown.js b/static/js/bootstrap-dropdown.js new file mode 100644 index 0000000000000000000000000000000000000000..6881cd2420f54dcd7f6e20fc895f4263ebb214b7 --- /dev/null +++ b/static/js/bootstrap-dropdown.js @@ -0,0 +1,150 @@ +/* ======================================================================== + * 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 + $('