diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cda9b1f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +databunker +bql.db diff --git a/README.md b/README.md new file mode 100644 index 0000000..001842d --- /dev/null +++ b/README.md @@ -0,0 +1,768 @@ +# Paranoid Guy Data Bunker + + +**Data Bunker is advanced personal information tokenization and storage service build to comply with GDPR.** + +This project, when deployed, can replace all user personal records scattered in the organization's different +internal databases with one user token generated and managed by Data Bunker service. + +By deploying this project and moving all personal information to one place, you will comply with the following +GDPR statement: *Personal data should be processed in a manner that ensures appropriate security and +confidentiality of the personal data, including for preventing unauthorized access to or use of personal +data and the equipment used for the processing.* + +**NOTE**: Implementing this project does not make you fully compliant with GDPR requirements and you still +need to consult with an attorney specializing in privacy. + +#### Diagram of old-style solution. + +![picture](images/old-style-solution.png) + +#### Diagram of Solution with Paranoid Guy Data Bunker +![picture](images/new-style-solution.png) + + +--- + +# This product stands many GDPR requirements + +## Right to be forgotten / Right to erasure + +When your customer asks for his **right to be forgotten** legal right, his private records will be +wiped out of the Data Bunker database, giving you the possibility to leave all internal databases unchanged. + +**NOTE**: You just need to make sure that you do not have any user identifiable information in your other databases, +logs, files, etc... + +## Right of access + +We build in passwordless login into the data bunker service. So, your customer/user can log in into his personal account +at Data Bunker and view all information collected by Data Bunker in connection to his profile. + +## Right to rectification + +Your customer/user can log in to his personal account at Data Bunker and change his records. If needed, Bunker will +send you a notification request about the change. + +## Right to restrict processing / Right to object + +Data Bunker can work as management for all user consents. User can cancel specific consent in his personal account at +Bunker, for example, to block sending him emails. Your backend can work with Data Bunker using API to add, or cancel +consents and we will send you a notification about user actions. + +## Right to data portability (partial) + +Your customer/user can log in to his personal account at Data Bunker and view and extract all his records stored at +Data Bunker. + +**NOTE**: You need to provide your customers with a way to extract data from your internal databases. + +## Data minimisation + +Basically, when you clean up your databases from personal records and use Data Bunker token instead, you +are already minimizing the personal information you store in different systems. In addition, when sending +you customer data to 3rd party systems Data Bunker provides you with purposely build *shareable identity* +that is time-bound. + +## Data Accuracy + +We allow the customer to change the records that are stored in Data Bunker. This way we achieve data accuracy. + +## Transparency + +All operations with personal records are saved in the audit log. Your customer can log in to his account at Data Bunker +and view the audit trail. + +## Integrity and confidentiality + +All personal data is encrypted. Only relevant personnel can access the data. We audit all operations with personal records. +All-access to Data Bunker API is done using an HTTPS SSL certificate. Enterprise version supports Shamir's Secret Sharing +algorithm to split the master key to a number of keys. A number of keys (that can be saved in different hands in the +organization) are required to bring up the system. + +## Accountability principle + +Each one, connected to Data Bunker must provide an access token to do any operation in Data Bunker or the user needs to +login to access his own account. All operations are saved in the audit log. + +## Privacy by design + +This product, from the architecture level was build to comply with strick privacy laws. Deploying this or similar +architecture, can make your company privacy by design compliant. + +## NOTE + +Implementing this project does not make you fully compliant with GDPR requirements and you still need to +consult with an attorney specializing in privacy. + +--- + +# Data Bunker usecases + +## Personal Information tokenization and storage + +This is already covered deeply above. Here I can add that Data Bunker has a layer of application +level personal information storage and each user in our database can be linked to a number of +application records (saved in Data Bunker). + +## Audit of all operations with personal records + +This is already covered above. + +## GDPR compliant logging + +Data Bunker supports a number of API that can help you to store user information in logs in +GDPR compliant way and work with cloud logging companies. + +## Consent management, i.e. withdawal + +According to GDPR, if you want to send your customer SMS using 3rd party gateway, +you must show to your customer a detailed notification message that you will send +his phone number to a specific SMS gateway company and the user needs to confirm that. + +You need to store these confirmations and Data Bunker can help you with that. + +Consent must be freely given, specific, informed and unambiguous. From GDPR, Article 7, item 3: + +* **The data subject shall have the right to withdraw his or her consent at any time.** +* **It shall be as easy to withdraw as to give consent.** + +In Data Bunker: + +* Your customers can log in to his Data Bunker account and view all consents he gave. +* Users can also discharge consents and we will send you a notification message. + + +## User signup and sign-in + +When implementing signup and sign-in in your customer-facing applications, we recommend you to +store all signup records in the Data Bunker database. We support 3 types of indexes, index +by login, index by email and index by phone. So you can easily implement login logic with +our service. + +Index by email and index by phone allow us to give your customers passwordless access to their +personal profile at Data Bunker. We send your user a one-time login code by SMS or email to +give him access to his account at Data Bunker. + + +--- + +# Questions + +## Why Open Source? + +I am a big fan of the open-source movement. After a lot of thoughts and consultations, +the main Data Bunker product will be open source. + +We are doing this to give our customers a steady base to continue using this solution in case +the company is closed. + +Enterprise version will be closed source. + +## What is considered PII? + +Following it a partial list. + +* Name +* Address +* IP address +* Browsing history +* Political opinion +* Sexual orientation +* Social Security Number +* Financial data +* Banking data +* Cookie data +* Contacts +* Mobile device ID +* Passport data +* Driving license +* ID number +* Health / medical data +* RFID +* Genetic info +* Ethnic and racial information + +## Technology stack? + +I am a big fan of go language and I use it extensively. + + +## Project technical features: + +* [Encrypted storage for personal information](#personal-information-tokanization) +* [Application data separation](#application-data-separation) +* [Time-limited passwordless access to personal information](#time-limited-passwordless-access-to-personal-information) +* [Web and mobile app session data storage](#web-and-mobile-app-session-data-storage) +* [Time-limited passwordless access to web and app session data](#web-and-mobile-app-session-data-storage) +* [Share user identiy with 3rd party services](#share-user-identity-with-3rd-parties) +* [User consent management, storage & withdrawal](#user-consent-management) +* [Audit of all operations](#audit) +* [Customer UI](#user-ui) +* [User passwordless authentication](#custom-user-index) + +## Enterprise features +* [Split master key with Shamir's Secret Sharing algo](#master-key-split-in-enterprise-version) +* [Advaned role management, ACL](#advanced-acl) +* [Support Hashicorp Vault](#hashicorp-vault-integration) + +--- + +## Encryption in motion and encryption in storage + +All access to Data Bunker API is done using HTTPS SSL certificate. All records that have user personal information +are encrypted or securely hashed in the databases. All user records are encrypted with a 32 byte key comprizing of +System Master key (24 bytes, stored in memory, not on disk) and user record key (8 bytes, stored on disk). + +### Master key split in Enterprise version + +Upon initial start, the **Enterprise version** generates a secret master key and 5 keys out of it. +These 5 keys are generated using Shamir's Secret Sharing algorithm. Combining 3 of any of the keys, +ejects original master key and that can be used to decrypt all records. + +The Master key is kept in RAM and is never stored to disk. You will need to provide 3 kits to unlock the application. +It is possible to save these keys in the AWS secret store and other vault services. + +--- + +## Data Bunker internal tables + +Information inside Data Bunker is saved in multiple tables in encrypted format. Here is a diagram of tables. + +Detailed usecase for each table is covered bellow. + + +![picture](images/data-bunker-tables.png) + +--- + +## Personal information tokanization + +User information, or PII, received in HTML POST key/value format of or JSON format is serialized, encrypted +with a 32 byte key and saved in database. You will get a user token to use in internal databases. Afterwords, +you can query the Data Bunker service to receive personal information, saving audit trail. + +![picture](images/create-user-token-flow.png) + +--- + +## Application data separation + +When creating application, I suppose you do not want to mix your customer data with data from other applications. +In addition to personal information record, Data Bunker provides you a way to store your app user information in a +specific type of record for that. So, you can retreave only your app' user personal information. + +![picture](images/create-user-app-record.png) + +--- + +## Web and mobile app session data storage + +Web or mobile application session data is very similar. They contain customer IP address, browser information, +web server headers, logged-in user info, etc... Many systems, including popular webservers, like Nginx, Apache +simply store this information in logs. This information, according to GDPR is considered personal identifiable +information and must be secured and controlled. + +So, you can not save user ip or browser information in logs now. Insead, Data Bunker will generate you a token to +save in logs. Data Bunker provides you an API to retreave this info out of Data Bunker without additional password +for a limited time as in GDPR. For example one month. + +![picture](images/create-user-session-flow.png) + +--- + +## Time-limited passwordless access to personal information + +Sometimes you want to share user, app or session private information in less trusted systems without providing +access to system root token. + +Data Bunker has an API that allows you to generate temprorary access token to access specific fields in the +user personal record or application level data or a session record for a limited time only. + +Your partner can retrieve this information and only specific fields during this specific timeframe. +Afterward, access will be blocked. + +**IMAGE** + +--- + +## Shareable user identity for 3rd parties + +When sharing data with 3rd party services like web analytics, logging, intelligence, etc... sometimes we need to +share user id, for example, customer original IP address or email address. All these pieces of information +are considred user identifiable information and must be minimized when sending to 3rd paty systems. + +***Do not share your customer user name, IP, emails, etc... because they look nice in reports!*** + +According to GDPR: *The personal data should be adequate, relevant and **limited to what is necessary** for the +purposes for which they are processed.* + +Our system can generate you time-limited shareable identity token that you can share with 3rd parties as an identity. +This identity, can link bacck to the user personal record or user app record or to specific user session. + +Optionally, Data Bunker can incorporate partner name in identity so, you track this identity usage. + +**IMAGE** + +--- + +## User consent management + +Consent in GDPR terms is clear approval for example to share user information with 3rd party, for example with SMS +gateway company to send him urgent notifications. + +Consent must be freely given, specific, informed and unambiguous. From GDPR, Article 7, item 3: + +* **The data subject shall have the right to withdraw his or her consent at any time.** +* **It shall be as easy to withdraw as to give consent.** + +To comply with this requirement, we support storage and management of user consent by API level and in user UI. + +--- + +## Audit + +Data Bunker saves audit events on all API operation. For example, new personal record added or changed; personal information +record retreaved, etc... + +By providing Audit of events, in relation to personal data, provides response to GDRP Article 15 requirement: +*Right of access by the data subject*. + +Special features: + +* Personal information in audit event is encrypted. +* User can view his own records only. + +Each audit record consists of: + +* Date and time +* Operation title +* Operation status +* Operation description +* Change before and after if applicable +* User session info if available: IP address, headers, etc... + +**IMAGE** + +Example from google: https://console.cloud.google.com/home/activity + +--- + +## User UI + +Internal UI is build to allow users to login with their email and access all data collected by this system. +You can easily change it to your requirements. + +According to GDPR, controller must provide Data subject with: + +* Right of access by the data subject (Article 15) +* Right to rectification (Article 16) +* Right to be forgotten (Article 17) +* Right to restriction of processing (Article 18) +* Notification obligation regarding rectification or erasure of personal data or restriction of processing (Article 19) +* Right to data portability (Article 20) +* Right to object (Article 21) + +--- + +# Enterprise features + +## Advanced role management, ACL + +By default, all access to Data Bunker is done with one root token or with **Time-limited passwordless access tokens** +that allow to read data from specific user record only. + +For more granular control, Data Bunker supports the notion of custom roles. For example, you can create a role +to view all records or another role to add and change any user records; view sessions, view all audit events, etc... + +After you define a role, the system allow you to generate access token for this role (you will need to have root token +for all these operations). + +Data Bunker have an API for all these operations. + +## Support Hashicorp Vault + +Hashicorp Vault, is a great piece of new generation of security product, has a notion of session accounts/passwords. +Hashicorp Vault can store root access token to Paranoid Guy Data Bunker, and when your application wants to open +session and access Data Bunker, it will talk with Bunker to issue a temp token with specified role. +When your application session is closed with Data Bunker, Hashicorp Vault will connect to Data Bunker and revoke access token. + +This architecture is done to minimize the chance that if the attacker breakes into your application server, +he will not get a full controll over the Data Bunker service as root token will not be saved in your +application server. + +This is all done with the help of custom plugin we build for Hashicorp Vault. + +--- + +## User Api + + +| Resource / HTTP method | POST (create) | GET (read) | PUT (update) | DELETE (delete) | +| ---------------------- | ---------------- | ------------- | ---------------- | ---------------- | +| /v1/user | Create new user | Error | Error | Error | +| /v1/user/uuid/{token} | Error | Get user | Update user | Delete user PII | +| /v1/user/login/{login} | Error | Get user | Update user | Delete user PII | +| /v1/user/email/{email} | Error | Get user | Update user | Delete user PII | +| /v1/user/phone/{phone} | Error | Get user | Update user | Delete user PII | + + +## Create user record +### `POST /v1/user` + +### Explanation + +This API is used to create new user record and if the request is successful it returns new `{token}`. +On the database level, each records is encrypted with it's own key. + + +### POST Body Format + +POST Body can contain regular form data or JSON. Data Bunker extracts `{login}`, `{phoen}` and `{email}` out of +POST data or from JSON first level and builds additional hashed index for user object. These fields, if +provided must be unique, otherwise you will ge an error. So, you can not create additional user object +with duplicate email. + +The following content type supported: + +* **application/json** +* **application/x-www-form-urlencoded** + + +### Example: + +Create used by posting JSON: + +``` +curl -s http://localhost:3000/v1/user -XPOST \ + -H "X-Bunker-Token: cb2537f9-14e2-7019-503f-b36a1a8f6e7f" \ + -H "Content-Type: application/json" \ + -d '{"firstName": "John","lastName":"Doe","email":"user@gmail.com"}' +{"status":"ok","uuid":"db80789b-0ad7-0690-035a-fd2c42531e87"} +``` + +Create user by POSTing user key/value fiels as post parameters: + +``` +curl -s http://localhost:3000/v1/user -XPOST \ + -H "X-Bunker-Token: $TOKEN" \ + -d 'firstName=John' \ + -d 'lastName=Doe' \ + -d 'email=user2@gmail.com' +{"status":"ok","uuid":"db80789b-0ad7-0690-035a-fd2c42531e87"} +``` + +**NOTE**: Keep this user token privately as it provides user private information in your system. +For semi-trusted environments or 3rd party companies, use **shareable identity** instead. + + +--- + +## Get user record +### `GET /v1/user/{uuid,login,email,phone}/{indexValue}` + +### Explanation +This API is used to get user PII records. You can lookup user token by **uuid** (token), **email**, **phone** or **login**. + +### Example: + +Fetch by user token: + +``` +curl --header "X-Bunker-Token: $TOKEN" -XGET \ + https://localhost:3000/v1/user/uuid/DAD2474A-E9A7-4BA7-BFC2-C4506880198E +{"uuid":"DAD2474A-E9A7-4BA7-BFC2-C4506880198E","data":{"k1":[1,10,20], +"k2":{"f1":"t1","f3":{"a":"b"}},"login":"user1","name":"tom"}} +``` + +Fetch by "login" name: + +``` +curl --header "X-Bunker-Token: $TOKEN" -XGET \ + https://localhost:3000/v1/user/login/user1 +{"uuid":"DAD2474A-E9A7-4BA7-BFC2-C4506880198E","data":{"k1":[1,10,20], +"k2":{"f1":"t1","f3":{"a":"b"}},"login":"user1","name":"tom"}} +``` + + +--- + +## Update user record +### `PUT /v1/user/{uuid,login,email,phone}/{indexValue}` + +### Explanation + +This API is used to update user record. You can update user by **uuid** (token), **email**, **phone** or **login**. +This call returns update status on success or error message on error. + +### POST Body Format + +POST Body can contain regular form POST data or JSON. When using JSON, you can remove the record by setting it's value to null. +For example {"key-to-delete":null}. + +The following content type supported: + +* **application/json** +* **application/x-www-form-urlencoded** + + +### Example: + +The following command will change user name to "Alex". An audit event will be generated showing previous and new value. + +``` +curl --header "X-Bunker-Token: $TOKEN" -d 'name=Alex' -XPUT \ + https://localhost:3000/v1/user/uuid/DAD2474A-E9A7-4BA7-BFC2-C4506880198E +``` + +--- + +## Delete user by record +### `DELETE /v1/user/{uuid,login,email,phone}/{indexValue}` + +This command will remove all user records from the database, leaving only user token id. + +``` +curl -header "X-Bunker-Token: $TOKEN" -XDELETE \ + https://localhost:3000/v1/user/uuid/DAD2474A-E9A7-4BA7-BFC2-C4506880198E +{"status":"ok","result":"done"} +``` + +## User App Api + + +| Resource / HTTP method | POST (create) | GET (read) | PUT (update) | DELETE | +| ------------------------------- | ------------------- | --------------------- | ------------- | ------ | +| /v1/userapp/uuid/:uuid/:appname | Create new user app | Get record | Change record | Delete | +| /v1/userapp/uuid/:uuid | Error | Get all user app list | Error | Error | +| /v1/userapp/list | Error | Get all app list | Error | Error | + + +## Create user app record +### `POST /v1/userapp/uuid/:uuid/:appname` + +### Explanation + +This API is used to create new user app record and if the request is successful it returns new `{token}`. + + +--- + +## User Session Api + +| Resource / HTTP method | POST (create) | GET (read) | PUT (update) | DELETE (delete) | +| ------------------------- | ------------------ | -------------- | -------------- | --------------- | +| /v1/session/uuid/:uuid | Create new session | Get sessions | Error | Error | +| /v1/session/session/:uuid | Error | Get session | Error?? | Error?? | +| /v1/session/clientip/:ip | Error | Get sessions | Error | Error | + +## Create user session record +### `POST /v1/session/uuid/:uuid` + +### Explanation + +This API is used to create new user session and if the request is successful it returns new `{session}`. +You can now use this id in your logs instead of user IP and browser user-agent info, etc... + +Our API supports generation of session tokens based on the following information: +user ip, mobile device info, user agent, etc... + +You can send the data as JSON POST or as regular POST parameters when working with this API. + +## Get user session record +### `GET /v1/session/session/:session` + +### Explanation + +This API returns session data. + + +## Get session records by user token. +### `GET /v1/session/uuid/:session` + +### Explanation + +This API returns an array of sessions of the same user. + +## Get session records by ip address. +### `GET /v1/session/clientip/:session` + +### Explanation + +This API returns an array of user sessions by IP address. These sessions can be of different people. + +--- + +## Passwordless tokens API + +| Resource / HTTP method | POST (create) | GET (read) | PUT (update) | DELETE (delete) | +| ---------------------- | ----------------- | ------------- | ---------------- | ---------------- | +| /v1/token/:uuid | Create new record | Error | Error | Error | +| /v1/token/:token | Error | Get data | Error | Error | + + router.POST("/v1/token/:token", e.userNewToken) + router.GET("/v1/token/:xtoken", e.userCheckToken) + + +--- + +## Shareable token API + +| Resource / HTTP method | POST (create) | GET (read) | PUT (update) | DELETE (delete) | +| ---------------------- | ----------------- | ------------- | ---------------- | ---------------- | +| /v1/shareable/:uuid | Create new record | Error | Error | Error | +| /v1/shareable/:token | Error | Get data | Error | Error | + + +--- + +**TODO-FINISH** + + +## Temporary user access tokens + +Sometimes, for example, when working with 3rd party partners or semi-trusted environments, you might +need to generate a user access token with a specific expiration time. Your partner can retrieve user +information during this specific time only. +Afterward, access will be blocked. + +The following command will generate a token to access user email and name for 7 days: + +``` +curl --header "X-Bunker-Token: $TOKEN" -d 'fields=email,name' -d 'expiration=7d' -d 'partner=sms' \ + https://bunker.company.com/gentokens/DAD2474A-E9A7-4BA7-BFC2-C4506880198E +``` + +Output: + +``` +476E41E7-72AD-448A-BB43-7ACDB8C53735 +``` + + +### 3rd party logging + +Instead of maintaining internal logs, a lot of companies are using 3rd party logging facility like logz or coralogix or something else. +To improve adherence to GDPR, we build a special feature - generate specific session id for such 3rd party service. + +When using these uuids in external systems, you basically **pseudonymise personal data**. In addition, in accordance with GDPR Article 5: +**Principles relating to processing of personal data**. Personal data shall be: (c) +adequate, relevant and limited to what is necessary in relation to the purposes for which they are processed (‘**data minimisation**’); + +Here is a command to do it: + +``` +curl -d 'ip=user@example.com' \ + -d 'user-agent=mozila' \ + -d 'partner=coralogix' \ + -d 'expiration=7d'\ + https://bunker.company.com/gensession/DAD2474A-E9A7-4BA7-BFC2-C4506880198E +``` + +It will generate a new uuid, that you can now pass to 3rd party system as a user id. + + +## User consent management + +One of the GDPR requirements is the storage of user consent. For example, your customer must approve to receive email marketing information. + +Using the GDPR language, your customer must give explicit consent to receive marketing information. + +Consent must be freely given, specific, informed and unambiguous. From GDPR, Article 7, item 3: + +* **The data subject shall have the right to withdraw his or her consent at any time.** +* **It shall be as easy to withdraw as to give consent.** + +To comply with this requirement, we added support to manage user consent. We support the following APIs: + + +### List granted + +``` +curl --header "X-Bunker-Token: $TOKEN" \ + https://bunker.company.com/consent/DAD2474A-E9A7-4BA7-BFC2-C4506880198E +``` + +### List all + +``` +curl --header "X-Bunker-Token: $TOKEN" \ + https://bunker.company.com/consent/DAD2474A-E9A7-4BA7-BFC2-C4506880198E?all +``` + +### Cancel consent + +``` +curl --header "X-Bunker-Token: $TOKEN" -XDELETE \ + https://bunker.company.com/consent/DAD2474A-E9A7-4BA7-BFC2-C4506880198E/ +``` + +### User gives consent + +**TODO** + +### Easily cancel consent for email marketing + +For example, for email marketing, users got distracted, when they need to login in order to unsubscribe from the newsletter. +To simplify this operation, users will be allowed to unsubscribe only using email address without full login operation. + +### Unlock bunker + +Run the following command with different keys: + +``` +bunker unlock **key** +``` + +Or you can provide multiple keys at once: + +``` +bunker unlock key1 key2 key3 +``` + +### View lock status + +``` +bunker status | jq .lock +``` + +Result: + +``` +locked +``` + + +## Audit API + + + + +It is not compliant, unless you have a real reason to share this specific personal sub-record. For example, +sending customer phone when notifying customer using 3rd party SMS gateway. + + + +# SECTION IS NOT UPDATED BELLOW + +## Data Bunker init + +Upon initial init, the Data Bunker service will check if the system is initialized for the first time, and if yes, +it will generate root password, master key and derived keys out of it. Otherwise, an error will be printed. + +``` +bunker init +``` + +Output: + +``` +Root password: 123456 +Key1: abcdefg +Key2: abcdefg +key3: abcdefg +Key4: abcdefg +Key5: abcdefg +``` + +**TODO**: Secret keys printed to output can be easily extracted in cloud environments for example in Kubernetes logs! diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..b1973ec --- /dev/null +++ b/build.sh @@ -0,0 +1,10 @@ +# build without debug +go build -ldflags "-w" -o databunker ./src/bunker.go ./src/qldb.go ./src/audit_db.go ./src/audit_api.go \ + ./src/utils.go ./src/cryptor.go \ + ./src/sms.go ./src/email.go \ + ./src/users_db.go ./src/users_api.go \ + ./src/userapps_db.go ./src/userapps_api.go \ + ./src/sessions_db.go \ + ./src/consent_db.go ./src/consent_api.go \ + ./src/xtokens_db.go ./src/xtokens_api.go + diff --git a/databunker.yaml b/databunker.yaml new file mode 100644 index 0000000..93d380c --- /dev/null +++ b/databunker.yaml @@ -0,0 +1,20 @@ +# Server configurations +generic: + # allow to create user object without login + create_user_without_token: true + #notification_url: "http://localhost/" +sms: + # default country when sending out SMSM + twilio_account: "AC" + twilio_token: "af" + twilio_from: "+180" + default_country: "UK" +server: + host: "localhost" + port: 3000 +smtp: + server: "smtp.eu.mailgun.org" + port: 587 + user: "postmaster@mg.your-company.com" + pass: "b6" + sender: "botdatabunker.your-company.com" diff --git a/images/create-user-app-record.png b/images/create-user-app-record.png new file mode 100644 index 0000000..0bbd7fc Binary files /dev/null and b/images/create-user-app-record.png differ diff --git a/images/create-user-session-flow.png b/images/create-user-session-flow.png new file mode 100644 index 0000000..990cb08 Binary files /dev/null and b/images/create-user-session-flow.png differ diff --git a/images/create-user-token-flow.png b/images/create-user-token-flow.png new file mode 100644 index 0000000..a3d9909 Binary files /dev/null and b/images/create-user-token-flow.png differ diff --git a/images/data-bunker-tables.png b/images/data-bunker-tables.png new file mode 100644 index 0000000..4d5fa1b Binary files /dev/null and b/images/data-bunker-tables.png differ diff --git a/images/new-style-solution.png b/images/new-style-solution.png new file mode 100644 index 0000000..60613eb Binary files /dev/null and b/images/new-style-solution.png differ diff --git a/images/old-style-solution.png b/images/old-style-solution.png new file mode 100644 index 0000000..4a11edc Binary files /dev/null and b/images/old-style-solution.png differ diff --git a/src/audit_api.go b/src/audit_api.go new file mode 100644 index 0000000..2b1b64d --- /dev/null +++ b/src/audit_api.go @@ -0,0 +1,42 @@ +package main + +import ( + "fmt" + "net/http" + + "github.com/julienschmidt/httprouter" +) + +func (e mainEnv) getAuditEvents(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + userTOKEN := ps.ByName("token") + event := audit("view audit events", userTOKEN) + defer func() { event.submit(e.db) }() + //fmt.Println("error code") + if enforceUUID(w, userTOKEN, event) == false { + return + } + if e.enforceAuth(w, r, event) == false { + return + } + var offset int32 + var limit int32 = 10 + args := r.URL.Query() + if value, ok := args["offset"]; ok { + offset = atoi(value[0]) + } + if value, ok := args["limit"]; ok { + limit = atoi(value[0]) + } + resultJSON, counter, err := e.db.getAuditEvents(userTOKEN, offset, limit) + if err != nil { + returnError(w, r, "internal error", 405, err, event) + return + } + fmt.Printf("Total count of events: %d\n", counter) + //fmt.Fprintf(w, "title") + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(200) + str := fmt.Sprintf(`{"total":%d,"rows":%s}`, counter, resultJSON) + w.Write([]byte(str)) +} diff --git a/src/audit_db.go b/src/audit_db.go new file mode 100644 index 0000000..bf33f83 --- /dev/null +++ b/src/audit_db.go @@ -0,0 +1,98 @@ +package main + +import ( + "encoding/json" + "fmt" + "time" + + "go.mongodb.org/mongo-driver/bson" +) + +type auditEvent struct { + When int32 `json:"when"` + Who string `json:"who"` + Record string `json:"record"` + App string `json:"app"` + Title string `json:"title"` + Status string `json:"status"` + Msg string `json:"msg"` + Debug string `json:"debug"` + Before string `json:"before"` + After string `json:"after"` + Meta string `json:"meta"` +} + +func audit(title string, record string) *auditEvent { + fmt.Printf("/%s : %s\n", title, record) + return &auditEvent{Title: title, Record: record, Status: "ok", When: int32(time.Now().Unix())} +} + +func auditApp(title string, record string, app string) *auditEvent { + fmt.Printf("/%s : %s : %s\n", title, app, record) + return &auditEvent{Title: title, Record: record, Status: "ok", When: int32(time.Now().Unix())} +} + +func (event auditEvent) submit(db dbcon) { + //fmt.Println("submit event to audit!!!!!!!!!!") + /* + bdoc, err := bson.Marshal(event) + if err != nil { + fmt.Printf("failed to marshal audit event: %s\n", err) + return + } + var bdoc2 bson.M + err = bson.Unmarshal(bdoc, &bdoc2) + if err != nil { + fmt.Printf("failed to marshal audit event2: %s\n", err) + return + }*/ + bdoc := bson.M{} + bdoc["when"] = event.When + if len(event.Who) > 0 { + bdoc["who"] = event.Who + } + if len(event.Record) > 0 { + bdoc["record"] = event.Record + } + if len(event.App) > 0 { + bdoc["app"] = event.App + } + if len(event.Title) > 0 { + bdoc["title"] = event.Title + } + bdoc["status"] = event.Status + if len(event.Msg) > 0 { + bdoc["msg"] = event.Msg + } + if len(event.Debug) > 0 { + bdoc["debug"] = event.Debug + } + if len(event.Before) > 0 { + bdoc["before"] = event.Before + } + if len(event.After) > 0 { + bdoc["after"] = event.After + } + if len(event.Meta) > 0 { + bdoc["meta"] = event.Meta + } + _, err := db.createRecord(TblName.Audit, &bdoc) + //_, err := db.audit.InsertOne(context.TODO(), &bdoc) + if err != nil { + fmt.Printf("failed to marshal audit event: %s\n", err) + return + } + //fmt.Printf("done!!!") +} + +func (dbobj dbcon) getAuditEvents(userTOKEN string, offset int32, limit int32) ([]byte, int64, error) { + //var results []*auditEvent + count, err := dbobj.countRecords(TblName.Audit, "record", userTOKEN) + if err != nil { + return nil, 0, err + } + records, err := dbobj.getList(TblName.Audit, "record", userTOKEN, offset, limit) + resultJSON, err := json.Marshal(records) + //fmt.Printf("Found multiple documents (array of pointers): %+v\n", results) + return resultJSON, count, nil +} diff --git a/src/bunker.go b/src/bunker.go new file mode 100644 index 0000000..ea677ad --- /dev/null +++ b/src/bunker.go @@ -0,0 +1,255 @@ +package main + +import ( + "encoding/hex" + "flag" + "fmt" + "log" + "math/rand" + "net/http" + "os" + "strings" + "time" + + "github.com/gobuffalo/packr" + "github.com/julienschmidt/httprouter" + "github.com/kelseyhightower/envconfig" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" + yaml "gopkg.in/yaml.v2" +) + +type Tbl = int + +type listTbls struct { + Users Tbl + Audit Tbl + Xtokens Tbl + Consent Tbl + Sessions Tbl +} + +// Enum for public use +var TblName = &listTbls{ + Users: 0, + Audit: 1, + Xtokens: 2, + Consent: 3, + Sessions: 4, +} + +type Config struct { + Generic struct { + Create_user_without_token bool `yaml:"create_user_without_token"` + } + Sms struct { + Default_country string `yaml:"default_country"` + Twilio_account string `yaml:"twilio_account"` + Twilio_token string `yaml:"twilio_token"` + Twilio_from string `yaml:"twilio_from"` + } + Server struct { + Port string `yaml:"port", envconfig:"BUNKER_PORT"` + Host string `yaml:"host", envconfig:"BUNKER_HOST"` + } `yaml:"server"` + Smtp struct { + Server string `yaml:"server", envconfig:"SMTP_SERVER"` + Port string `yaml:"port", envconfig:"SMTP_PORT"` + User string `yaml:"user", envconfig:"SMTP_USER"` + Pass string `yaml:"pass", envconfig:"SMTP_PASS"` + Sender string `yaml:"sender", envconfig:"SMTP_SENDER"` + } `yaml:"smtp"` +} + +type mainEnv struct { + db dbcon + conf Config +} + +type userJSON struct { + jsonData []byte + loginIdx string + emailIdx string + phoneIdx string +} + +type tokenAuthResult struct { + ttype string + name string + token string + fields string + appName string +} + +func prometheusHandler() http.Handler { + handlerOptions := promhttp.HandlerOpts{ + ErrorHandling: promhttp.ContinueOnError, + DisableCompression: true, + } + promHandler := promhttp.HandlerFor(prometheus.DefaultGatherer, handlerOptions) + return promHandler +} + +func (e mainEnv) metrics(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + fmt.Printf("/metrics\n") + //w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(200) + //fmt.Fprintf(w, `{"status":"ok","apps":%q}`, result) + //fmt.Fprintf(w, "hello") + //promhttp.Handler().ServeHTTP(w, r) + prometheusHandler().ServeHTTP(w, r) +} + +func (e mainEnv) index(w http.ResponseWriter, r *http.Request) { + fmt.Printf("Index access\n") + /* + if r.Method != "GET" { + http.Error(w, http.StatusText(405), 405) + log.Panic("Method %s", r.Method) + return + } + */ + fmt.Fprintf(w, "title") +} + +func (e mainEnv) setupRouter() *httprouter.Router { + + box := packr.NewBox("../ui") + + router := httprouter.New() + router.POST("/v1/user", e.userNew) + router.GET("/v1/user/:index/:code", e.userGet) + router.DELETE("/v1/user/:index/:code", e.userDelete) + router.PUT("/v1/user/:index/:code", e.userChange) + + router.GET("/v1/login/:index/:code", e.userLogin) + router.GET("/v1/enter/:index/:code/:tmp", e.userLoginEnter) + + router.POST("/v1/xtoken/:token", e.userNewToken) + router.GET("/v1/xtoken/:xtoken", e.userCheckToken) + + router.GET("/v1/consent/:index/:code", e.consentList) + router.POST("/v1/consent/:index/:code", e.consentAccept) + //router.PATCH("/v1/consent/:index/:code", e.consentCancel) + router.DELETE("/v1/consent/:index/:code", e.consentCancel) + + router.POST("/v1/userapp/token/:token/:appname", e.userappNew) + router.GET("/v1/userapp/token/:token/:appname", e.userappGet) + router.PUT("/v1/userapp/token/:token/:appname", e.userappChange) + router.GET("/v1/userapp/token/:token", e.userappList) + router.GET("/v1/userapp/list", e.appList) + + router.GET("/v1/metrics", e.metrics) + + router.GET("/v1/audit/list/:token", e.getAuditEvents) + + router.GET("/", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + data, err := box.Find("index.html") + if err != nil { + //log.Panic("error %s", err.Error()) + fmt.Printf("404 %s, error: %s\n", r.URL.Path, err.Error()) + w.WriteHeader(404) + } else { + //fmt.Printf("return static file: %s\n", data) + fmt.Printf("200 %s\n", r.URL.Path) + w.WriteHeader(200) + w.Write([]byte(data)) + } + }) + router.GET("/site/*filepath", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + data, err := box.Find(r.URL.Path) + if err != nil { + fmt.Printf("404 GET %s\n", r.URL.Path) + w.WriteHeader(404) + } else { + //w.Header().Set("Access-Control-Allow-Origin", "*") + if strings.HasSuffix(r.URL.Path, ".css") { + w.Header().Set("Content-Type", "text/css") + } else if strings.HasSuffix(r.URL.Path, ".js") { + w.Header().Set("Content-Type", "text/javascript") + } + // text/plain + fmt.Printf("200 %s\n", r.URL.Path) + w.WriteHeader(200) + w.Write([]byte(data)) + } + }) + router.NotFound = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + w.WriteHeader(http.StatusNotFound) + w.Write([]byte("url not found")) + fmt.Printf("404 %s %s\n", r.Method, r.URL.Path) + }) + return router +} + +func readFile(cfg *Config) error { + f, err := os.Open("databunker.yaml") + if err != nil { + return err + } + decoder := yaml.NewDecoder(f) + err = decoder.Decode(cfg) + if err != nil { + return err + } + return nil +} +func readEnv(cfg *Config) error { + err := envconfig.Process("", cfg) + return err +} + +func main() { + rand.Seed(time.Now().UnixNano()) + fmt.Println("***MAIN***") + lockMemory() + var cfg Config + readFile(&cfg) + readEnv(&cfg) + fmt.Printf("%+v\n", cfg) + initPtr := flag.Bool("init", false, "a bool") + masterKeyPtr := flag.String("masterkey", "", "master key") + flag.Parse() + var err error + var masterKey []byte + if *initPtr { + fmt.Println("Init") + masterKey, err = generateMasterKey() + fmt.Printf("Master key: %x\n", masterKey) + } else if masterKeyPtr != nil && len(*masterKeyPtr) > 0 { + masterKey, err = hex.DecodeString(*masterKeyPtr) + } else { + fmt.Println("Run ./databunker -init for the firts time.") + log.Fatal("Masterkey is missing. Run ./databunker -masterkey key") + } + if err != nil { + //log.Panic("error %s", err.Error()) + fmt.Printf("error %s", err.Error()) + } + db, _ := newDB(masterKey, nil) + if *initPtr { + fmt.Println("Init") + db.initDB() + rootToken, err := db.createRootToken() + if err != nil { + //log.Panic("error %s", err.Error()) + fmt.Printf("error %s", err.Error()) + } + fmt.Printf("Root token: %s\n", rootToken) + } + db.initUserApps() + e := mainEnv{db, cfg} + fmt.Printf("host %s\n", cfg.Server.Host+":"+cfg.Server.Port) + router := e.setupRouter() + if _, err := os.Stat("./server.key"); !os.IsNotExist(err) { + //TODO + fmt.Printf("Loading ssl\n") + err := http.ListenAndServeTLS(":443", "server.ctr", "server.key", router) + if err != nil { + log.Fatal("ListenAndServe: ", err) + } + } else { + log.Fatal(http.ListenAndServe(cfg.Server.Host+":"+cfg.Server.Port, router)) + } +} diff --git a/src/bunker_test.go b/src/bunker_test.go new file mode 100644 index 0000000..fc7f045 --- /dev/null +++ b/src/bunker_test.go @@ -0,0 +1,115 @@ +package main + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "net/http/httptest" + "strings" + "testing" + + "github.com/julienschmidt/httprouter" +) + +func TestCreateAPIUser(t *testing.T) { + masterKey, _ := hex.DecodeString("71c65924336c5e6f41129b6f0540ad03d2a8bf7e9b10db72") + db, _ := newDB(masterKey, nil) + var cfg Config + e := mainEnv{db, cfg} + + rootToken, err := e.db.getRootToken() + if err != nil { + t.Fatalf("Failed to retreave root token: %s\n", err) + } + userJSON := `{"login":"abcdefg","name":"tom","pass":"mylittlepony","k1":[1,10,20],"k2":{"f1":"t1","f3":{"a":"b"}},"admin":true}` + + request := httptest.NewRequest("POST", "/user", strings.NewReader(userJSON)) + request.Header.Set("Content-Type", "application/json") + request.Header.Set("X-Bunker-Token", rootToken) + //var resp http.ResponseWriter + rr := httptest.NewRecorder() + var ps httprouter.Params + e.userNew(rr, request, ps) + + //fmt.Printf("After create user------------------\n%s\n\n\n", rr.Body) + var raw map[string]interface{} + err = json.Unmarshal(rr.Body.Bytes(), &raw) + if err != nil { + t.Fatalf("Failed to parse json response on user create: %s\n", err) + } + var userTOKEN string + if status, ok := raw["status"]; ok { + if status == "error" { + if strings.HasPrefix(raw["message"].(string), "duplicate") { + _, userTOKEN, _ = e.db.getUserIndex("abcdefg", "login") + fmt.Printf("user already exists: %s\n", userTOKEN) + } else { + t.Fatalf("Failed to create user: %s\n", raw["message"]) + return + } + } else if status == "ok" { + userTOKEN = raw["token"].(string) + } + } + if len(userTOKEN) == 0 { + t.Fatalf("Failed to parse user UUID") + } + p2 := httprouter.Param{"token", userTOKEN} + ps2 := []httprouter.Param{p2} + + pars := `{"expiration":"1d","fields":"uuid,name,pass,k1,k2.f3"}` + request = httptest.NewRequest("POST", "/user", strings.NewReader(pars)) + request.Header.Set("Content-Type", "application/json") + request.Header.Set("X-Bunker-Token", rootToken) + //var resp http.ResponseWriter + rr = httptest.NewRecorder() + e.userNewToken(rr, request, ps2) + //fmt.Printf("after create token------------------\n%s\n\n\n", rr.Body) + err = json.Unmarshal(rr.Body.Bytes(), &raw) + if err != nil { + fmt.Printf("Failed to parse json response on user create: %s\n", err) + } + tokenUUID := "" + if status, ok := raw["status"]; ok { + if status == "error" { + t.Fatalf("Failed to create user token: %s\n", raw["message"]) + return + } else if status == "ok" { + tokenUUID = raw["xtoken"].(string) + } + } + if len(tokenUUID) == 0 { + t.Fatalf("Failed to retreave user token: %s\n", rr.Body) + } + fmt.Printf("User token: %s\n", tokenUUID) + + request = httptest.NewRequest("GET", "/user", nil) + request.Header.Set("Content-Type", "application/json") + request.Header.Set("X-Bunker-Token", rootToken) + //var resp http.ResponseWriter + rr = httptest.NewRecorder() + + p3 := httprouter.Param{"xtoken", tokenUUID} + ps3 := []httprouter.Param{p3} + e.userCheckToken(rr, request, ps3) + fmt.Printf("get by token------------------\n%s\n\n\n", rr.Body) + err = json.Unmarshal(rr.Body.Bytes(), &raw) + if err != nil { + fmt.Printf("Failed to parse json response on user create: %s\n", err) + } + + request = httptest.NewRequest("DELETE", "/user", nil) + request.Header.Set("Content-Type", "application/json") + request.Header.Set("X-Bunker-Token", rootToken) + //var resp http.ResponseWriter + rr = httptest.NewRecorder() + p4 := httprouter.Param{"code", userTOKEN} + p5 := httprouter.Param{"index", "token"} + ps4 := []httprouter.Param{p4, p5} + e.userDelete(rr, request, ps4) + fmt.Printf("after userDelete------------------\n%s\n\n\n", rr.Body) + err = json.Unmarshal(rr.Body.Bytes(), &raw) + if err != nil { + fmt.Printf("Failed to parse json response on user create: %s\n", err) + } +} diff --git a/src/consent_api.go b/src/consent_api.go new file mode 100644 index 0000000..2f0a81f --- /dev/null +++ b/src/consent_api.go @@ -0,0 +1,132 @@ +package main + +import ( + "fmt" + "net/http" + "reflect" + + "github.com/julienschmidt/httprouter" +) + +func (e mainEnv) consentAccept(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + var err error + code := ps.ByName("code") + index := ps.ByName("index") + event := audit("consent accept by "+index, code) + defer func() { event.submit(e.db) }() + + userTOKEN := "" + if index == "token" { + if enforceUUID(w, code, event) == false { + return + } + userBson, _ := e.db.lookupUserRecord(code) + if userBson != nil { + userTOKEN = code + } + } else { + // TODO: decode url in code! + userBson, _ := e.db.lookupUserRecordByIndex(index, code) + if userBson != nil { + userTOKEN = userBson["token"].(string) + } + } + defer func() { + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(200) + w.Write([]byte(`{"status":"ok"}`)) + }() + records, err := getJSONPostData(r) + if err != nil { + //returnError(w, r, "internal error", 405, err, event) + return + } + brief := "" + message := "" + status := "accept" + if value, ok := records["brief"]; ok { + if reflect.TypeOf(value) == reflect.TypeOf("string") { + brief = value.(string) + } + } + if value, ok := records["message"]; ok { + if reflect.TypeOf(value) == reflect.TypeOf("string") { + message = value.(string) + } + } + if value, ok := records["status"]; ok { + if reflect.TypeOf(value) == reflect.TypeOf("string") { + status = value.(string) + } + } + if len(brief) == 0 { + //returnError(w, r, "internal error", 405, nil, event) + return + } + if len(message) == 0 { + message = brief + } + e.db.createConsentRecord(userTOKEN, index, code, brief, message, status) +} + +func (e mainEnv) consentCancel(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + code := ps.ByName("code") + index := ps.ByName("index") + event := audit("consent cancel by "+index, code) + defer func() { event.submit(e.db) }() + userTOKEN := code + if enforceUUID(w, userTOKEN, event) == false { + return + } + // make sure that user is logged in here, unless he wants to cancel emails + if e.enforceAuth(w, r, event) == false { + return + } + records, err := getJSONPostData(r) + if err != nil { + returnError(w, r, "internal error", 405, err, event) + return + } + brief := "" + if value, ok := records["brief"]; ok { + if reflect.TypeOf(value) == reflect.TypeOf("string") { + brief = value.(string) + } + } + if len(brief) == 0 { + returnError(w, r, "consent brief code is missing", 405, nil, event) + return + } + e.db.cancelConsentRecord(userTOKEN, brief) + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(200) + w.Write([]byte(`{"status":"ok"}`)) +} + +func (e mainEnv) consentList(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + code := ps.ByName("code") + index := ps.ByName("index") + event := audit("consent list of events by "+index, code) + defer func() { event.submit(e.db) }() + userTOKEN := code + if enforceUUID(w, userTOKEN, event) == false { + return + } + // make sure that user is logged in here, unless he wants to cancel emails + if e.enforceAuth(w, r, event) == false { + return + } + + resultJSON, numRecords, err := e.db.listConsentRecords(userTOKEN) + if err != nil { + returnError(w, r, "internal error", 405, err, event) + return + } + fmt.Printf("Total count of rows: %d\n", numRecords) + //fmt.Fprintf(w, "title") + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(200) + str := fmt.Sprintf(`{"status":"ok","total":%d,"rows":%s}`, numRecords, resultJSON) + w.Write([]byte(str)) +} diff --git a/src/consent_db.go b/src/consent_db.go new file mode 100644 index 0000000..b6eee13 --- /dev/null +++ b/src/consent_db.go @@ -0,0 +1,86 @@ +package main + +import ( + "encoding/json" + "errors" + "fmt" + "time" + + "github.com/fatih/structs" + "go.mongodb.org/mongo-driver/bson" +) + +type consentEvent struct { + When int32 `json:"when,omitempty" structs:"when"` + Who string `json:"who,omitempty" structs:"who"` + Type string `json:"type,omitempty" structs:"type"` + Token string `json:"token,omitempty" structs:"token"` + Brief string `json:"brief,omitempty" structs:"brief"` + Message string `json:"message,omitempty" structs:"message"` + Status string `json:"status,omitempty" structs:"status"` +} + +func (dbobj dbcon) createConsentRecord(userTOKEN string, usertype string, usercode string, brief string, message string, status string) { + now := int32(time.Now().Unix()) + // brief can not be too long, may be hash it ? + if len(brief) > 64 { + return + } + if len(userTOKEN) > 0 { + // first check if this consent exists, then update + raw, err := dbobj.getRecord2(TblName.Consent, "token", userTOKEN, "brief", brief) + if err != nil { + fmt.Printf("error to find:%s", err) + return + } + if raw != nil { + fmt.Println("update rec") + // update date, status + bdoc := bson.M{} + bdoc["when"] = now + bdoc["status"] = status + dbobj.updateRecord2(TblName.Consent, "token", userTOKEN, "brief", brief, &bdoc, nil) + return + } + } + ev := consentEvent{ + When: now, + Who: usercode, + Token: userTOKEN, + Type: usertype, + Brief: brief, + Message: message, + Status: status, + } + // in any case - insert record + fmt.Printf("insert consent record\n") + dbobj.createRecord(TblName.Consent, structs.Map(ev)) +} + +func (dbobj dbcon) cancelConsentRecord(userTOKEN string, brief string) error { + // brief can not be too long, may be hash it ? + if len(brief) > 64 { + return errors.New("Brief value is too long") + } + fmt.Printf("%s %s\n", userTOKEN, brief) + now := int32(time.Now().Unix()) + // update date, status + bdoc := bson.M{} + bdoc["when"] = now + bdoc["status"] = "cancel" + dbobj.updateRecord2(TblName.Consent, "token", userTOKEN, "brief", brief, &bdoc, nil) + return nil +} + +// link consent to user? + +func (dbobj dbcon) listConsentRecords(userTOKEN string) ([]byte, int, error) { + records, err := dbobj.getList(TblName.Consent, "token", userTOKEN, 0, 0) + if err != nil { + return nil, 0, err + } + count := len(records) + resultJSON, err := json.Marshal(records) + //fmt.Printf("Found multiple documents (array of pointers): %+v\n", results) + return resultJSON, count, nil +} diff --git a/src/cryptor.go b/src/cryptor.go new file mode 100644 index 0000000..db80050 --- /dev/null +++ b/src/cryptor.go @@ -0,0 +1,76 @@ +package main + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "io" +) + +// shamir secret split +// https://github.com/hashicorp/vault/tree/master/shamir +// https://github.com/kinvolk/go-shamir +// go get github.com/hashicorp/vault/shamir + +func generateRecordKey() ([]byte, error) { + key := make([]byte, 8) + if _, err := io.ReadFull(rand.Reader, key); err != nil { + return nil, err + } + return key, nil +} + +// generate master key - 24 bytes length +func generateMasterKey() ([]byte, error) { + masterKey := make([]byte, 24) + _, err := io.ReadFull(rand.Reader, masterKey) + return masterKey, err +} + +func decrypt(masterKey []byte, userKey []byte, data []byte) ([]byte, error) { + // Load your secret key from a safe place and reuse it across multiple + // Seal/Open calls. (Obviously don't use this example key for anything + // real.) If you want to convert a passphrase to a key, use a suitable + // package like bcrypt or scrypt. + // When decoded the key should be 16 bytes (AES-128) or 32 (AES-256). + key := append(masterKey, userKey...) + + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + aesgcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + ciphertext := data[0 : len(data)-12] + nonce := data[len(data)-12:] + plaintext, err := aesgcm.Open(nil, nonce, ciphertext, nil) + return plaintext, err +} + +func encrypt(masterKey []byte, userKey []byte, plaintext []byte) ([]byte, error) { + // We use 32 byte key (AES-256). + // comprising 24 master key + // and 8 bytes record key + key := append(masterKey, userKey...) + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + // Never use more than 2^32 random nonces with a given key because of the risk of a repeat. + nonce := make([]byte, 12) + if _, err := io.ReadFull(rand.Reader, nonce); err != nil { + return nil, err + } + aesgcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + ciphertext := aesgcm.Seal(nil, nonce, plaintext, nil) + //fmt.Printf("%x\n", ciphertext) + // apppend random nonce bvalue to the end + ciphertext = append(ciphertext, nonce...) + return ciphertext, nil +} diff --git a/src/email.go b/src/email.go new file mode 100644 index 0000000..a8aff91 --- /dev/null +++ b/src/email.go @@ -0,0 +1,50 @@ +package main + +import ( + "fmt" + "net/smtp" + "strings" +) + +func sendCodeByEmail(code string, address string, cfg Config) { + /* + c, err := smtp.Dial(smtpServer) + if err != nil { + log.Fatal(err) + } + defer c.Close() + // Set the sender and recipient. + c.Mail("bot@paranoidguy.com") + c.Rcpt(address) + // Send the email body. + wc, err := c.Data() + if err != nil { + log.Fatal(err) + return + } + defer wc.Close() + buf := bytes.NewBufferString("This is the email body.") + if _, err = buf.WriteTo(wc); err != nil { + log.Fatal(err) + return + } + return + */ + Dest := []string{"stremovsky@gmail.com", address} + Subject := "Access Code" + bodyMessage := "Data bunker access code is " + code + msg := "From: " + cfg.Smtp.Sender + "\n" + + "To: " + strings.Join(Dest, ",") + "\n" + + "Subject: " + Subject + "\n" + bodyMessage + + err := smtp.SendMail(cfg.Smtp.Server+":"+cfg.Smtp.Port, + smtp.PlainAuth("", cfg.Smtp.User, cfg.Smtp.Pass, cfg.Smtp.Server), + cfg.Smtp.User, Dest, []byte(msg)) + + if err != nil { + fmt.Printf("smtp error: %s", err) + return + } + + fmt.Println("Mail sent successfully!") +} diff --git a/src/qldb.go b/src/qldb.go new file mode 100644 index 0000000..4bc8ee8 --- /dev/null +++ b/src/qldb.go @@ -0,0 +1,747 @@ +package main + +// This project is using the following golang internal database: +// https://godoc.org/modernc.org/ql + +// go build modernc.org/ql/ql +// go install modernc.org/ql/ql + +// https://stackoverflow.com/questions/21986780/is-it-possible-to-retrieve-a-column-value-by-name-using-golang-database-sql + +// https://stackoverflow.com/questions/21986780/is-it-possible-to-retrieve-a-column-value-by-name-using-golang-database-sql + +import ( + "crypto/md5" + "database/sql" + "fmt" + "log" + "strconv" + + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "modernc.org/ql" +) + +var ( + knownApps []string +) + +type dbcon struct { + db *sql.DB + masterKey []byte + hash []byte +} + +func newDB(masterKey []byte, urlurl *string) (dbcon, error) { + dbobj := dbcon{nil, nil, nil} + + /* + if db, err := ql.OpenFile("./bunker.db", &ql.Options{}); err != nil { + fmt.Println("open db") + a, err := db.Info() + if err != nil { + fmt.Printf("error in db info: %s\n", err) + } + for _, v := range a.Tables { + fmt.Printf("dbinfo: %s\n", string(v.Name)) + } + db.Close() + } + */ + + ql.RegisterDriver2() + db, err := sql.Open("ql2", "./bql.db") + if err != nil { + log.Fatalf("Failed to open ql db: %s", err) + } + hash := md5.Sum(masterKey) + dbobj = dbcon{db, masterKey, hash[:]} + return dbobj, nil +} + +func (dbobj dbcon) initDB() error { + var err error + fmt.Println("init db *****") + if err = initUsers(dbobj.db); err != nil { + return err + } + if err = initXTokens(dbobj.db); err != nil { + return err + } + if err = initAudit(dbobj.db); err != nil { + return err + } + if err = initConsent(dbobj.db); err != nil { + return err + } + if err = initSessions(dbobj.db); err != nil { + return err + } + return nil +} + +func (dbobj dbcon) initUserApps() error { + return nil +} + +func decodeFieldsValues(data interface{}) (string, string) { + fields := "" + values := "" + str := "" + + switch t := data.(type) { + case primitive.M: + fmt.Println("format is: primitive.M") + for idx, val := range data.(primitive.M) { + if len(fields) == 0 { + fields = idx + } else { + fields = fields + "," + idx + } + switch t := val.(type) { + case string: + str = "\"" + val.(string) + "\"" + case int: + str = strconv.Itoa(val.(int)) + case int32: + str = strconv.FormatInt(int64(val.(int32)), 10) + default: + fmt.Printf("wrong type: %s\n", t) + } + if len(values) == 0 { + values = str + } else { + values = values + "," + str + } + } + case *primitive.M: + fmt.Println("format is: *primitive.M") + for idx, val := range *data.(*primitive.M) { + if len(fields) == 0 { + fields = idx + } else { + fields = fields + "," + idx + } + switch t := val.(type) { + case string: + str = "\"" + val.(string) + "\"" + case int: + str = strconv.Itoa(val.(int)) + case int32: + str = strconv.FormatInt(int64(val.(int32)), 10) + default: + fmt.Printf("wrong type: %s\n", t) + } + if len(values) == 0 { + values = str + } else { + values = values + "," + str + } + } + case map[string]interface{}: + fmt.Println("format is: map[string]interface{}") + for idx, val := range data.(map[string]interface{}) { + if len(fields) == 0 { + fields = idx + } else { + fields = fields + "," + idx + } + switch t := val.(type) { + case string: + str = "\"" + val.(string) + "\"" + case int: + str = strconv.Itoa(val.(int)) + case int32: + str = strconv.FormatInt(int64(val.(int32)), 10) + default: + fmt.Printf("wrong type: %s\n", t) + } + if len(values) == 0 { + values = str + } else { + values = values + "," + str + } + } + default: + fmt.Printf("XXXXXX wrong type: %T\n", t) + } + return fields, values +} + +func decodeForCleanup(data interface{}) string { + fields := "" + + switch t := data.(type) { + case primitive.M: + for idx, _ := range data.(primitive.M) { + if len(fields) == 0 { + fields = idx + "=null" + } else { + fields = fields + "," + idx + "=null" + } + } + return fields + case map[string]interface{}: + for idx, _ := range data.(map[string]interface{}) { + if len(fields) == 0 { + fields = idx + "=null" + } else { + fields = fields + "," + idx + "=null" + } + } + default: + fmt.Printf("decodeForCleanup: wrong type: %s\n", t) + } + + return fields +} + +func decodeForUpdate(bdoc *bson.M, bdel *bson.M) string { + fields := "" + str := "" + + if bdoc != nil { + /* + switch t := *bdoc.(type) { + default: + fmt.Printf("Type is %T\n", t) + } + */ + + for idx, val := range *bdoc { + switch t := val.(type) { + case string: + str = "\"" + val.(string) + "\"" + case int: + str = strconv.Itoa(val.(int)) + case int32: + str = strconv.FormatInt(int64(val.(int32)), 10) + default: + fmt.Printf("wrong type: %s\n", t) + } + if len(fields) == 0 { + fields = idx + "=" + str + } else { + fields = fields + "," + idx + "=" + str + } + } + } + if bdel != nil { + for idx, _ := range *bdel { + if len(fields) == 0 { + fields = idx + "=null" + } else { + fields = fields + "," + idx + "=null" + } + } + } + return fields +} + +func getTable(t Tbl) string { + switch t { + case TblName.Users: + return "users" + case TblName.Audit: + return "audit" + case TblName.Consent: + return "consent" + case TblName.Xtokens: + return "xtokens" + case TblName.Sessions: + return "sessions" + } + return "users" +} + +func (dbobj dbcon) createRecordInTable(tbl string, data interface{}) (int, error) { + fields, values := decodeFieldsValues(data) + q := "insert into " + tbl + " (" + fields + ") values (" + values + ");" + fmt.Printf("q: %s\n", q) + + tx, err := dbobj.db.Begin() + if err != nil { + return 0, err + } + _, err = tx.Exec(q) + if err != nil { + return 0, err + } + if err = tx.Commit(); err != nil { + return 0, err + } + return 1, nil +} + +func (dbobj dbcon) createRecord(t Tbl, data interface{}) (int, error) { + //if reflect.TypeOf(value) == reflect.TypeOf("string") + tbl := getTable(t) + return dbobj.createRecordInTable(tbl, data) +} + +func (dbobj dbcon) countRecords(t Tbl, keyName string, keyValue string) (int64, error) { + tbl := getTable(t) + q := "select count(*) from " + tbl + " WHERE " + keyName + "'" + keyValue + "';" + fmt.Printf("q: %s\n", q) + + tx, err := dbobj.db.Begin() + if err != nil { + return 0, err + } + row := tx.QueryRow(q) + // Columns + var count int + err = row.Scan(&count) + if err != nil { + return 0, err + } + if err = tx.Commit(); err != nil { + return 0, err + } + return int64(count), nil +} + +func (dbobj dbcon) updateRecord(t Tbl, keyName string, keyValue string, bdoc *bson.M) (int64, error) { + table := getTable(t) + filter := keyName + "=\"" + keyValue + "\"" + return dbobj.updateRecordInTableDo(table, filter, bdoc, nil) +} + +func (dbobj dbcon) updateRecordInTable(table string, keyName string, keyValue string, bdoc *bson.M) (int64, error) { + filter := keyName + "=\"" + keyValue + "\"" + return dbobj.updateRecordInTableDo(table, filter, bdoc, nil) +} + +func (dbobj dbcon) updateRecord2(t Tbl, keyName string, keyValue string, + keyName2 string, keyValue2 string, bdoc *bson.M, bdel *bson.M) (int64, error) { + table := getTable(t) + filter := keyName + "=\"" + keyValue + "\" AND " + keyName2 + "=\"" + keyValue2 + "\"" + return dbobj.updateRecordInTableDo(table, filter, bdoc, bdel) +} + +func (dbobj dbcon) updateRecordInTable2(table string, keyName string, + keyValue string, keyName2 string, keyValue2 string, bdoc *bson.M, bdel *bson.M) (int64, error) { + filter := keyName + "=\"" + keyValue + "\" AND " + keyName2 + "=\"" + keyValue2 + "\"" + return dbobj.updateRecordInTableDo(table, filter, bdoc, bdel) +} + +func (dbobj dbcon) updateRecordInTableDo(table string, filter string, bdoc *bson.M, bdel *bson.M) (int64, error) { + op := decodeForUpdate(bdoc, bdel) + q := "update " + table + " SET " + op + " WHERE " + filter + fmt.Printf("q: %s\n", q) + + tx, err := dbobj.db.Begin() + if err != nil { + return 0, err + } + defer tx.Rollback() + result, err := tx.Exec(q) + if err != nil { + return 0, err + } + if err = tx.Commit(); err != nil { + return 0, err + } + num, err := result.RowsAffected() + return num, err +} + +func (dbobj dbcon) getRecord(t Tbl, keyName string, keyValue string) (bson.M, error) { + tbl := getTable(t) + return dbobj.getRecordInTable(tbl, keyName, keyValue) +} + +func (dbobj dbcon) getRecordInTable(table string, keyName string, keyValue string) (bson.M, error) { + q := "select * from " + table + " WHERE " + keyName + "=\"" + keyValue + "\"" + return dbobj.getRecordInTableDo(q) +} + +func (dbobj dbcon) getRecord2(t Tbl, keyName string, keyValue string, + keyName2 string, keyValue2 string) (bson.M, error) { + tbl := getTable(t) + return dbobj.getRecordInTable2(tbl, keyName, keyValue, keyName2, keyValue2) +} + +func (dbobj dbcon) getRecordInTable2(table string, keyName string, keyValue string, + keyName2 string, keyValue2 string) (bson.M, error) { + q := "select * from " + table + " WHERE " + keyName + "=\"" + keyValue + "\" AND " + + keyName2 + "=\"" + keyValue2 + "\"" + return dbobj.getRecordInTableDo(q) +} + +func (dbobj dbcon) getRecordInTableDo(q string) (bson.M, error) { + fmt.Printf("q: %s\n", q) + tx, err := dbobj.db.Begin() + if err != nil { + return nil, err + } + defer tx.Rollback() + rows, err := tx.Query(q) + if err == sql.ErrNoRows { + fmt.Println("nothing found") + return nil, nil + } else if err != nil { + return nil, err + } + defer rows.Close() + + columnNames, err := rows.Columns() + if err != nil { + return nil, err + } + //fmt.Printf("names: %s\n", columnNames) + if err := rows.Err(); err != nil { + log.Fatal(err) + } + //pointers := make([]interface{}, len(columnNames)) + recBson := bson.M{} + rows.Next() + //for rows.Next() { + //fmt.Println("parsing result line") + columnPointers := make([]interface{}, len(columnNames)) + //for i, _ := range columnNames { + // columnPointers[i] = new(interface{}) + //} + columns := make([]interface{}, len(columnNames)) + for i, _ := range columns { + columnPointers[i] = &columns[i] + } + + err = rows.Scan(columnPointers...) + if err == sql.ErrNoRows { + fmt.Println("nothing found") + return nil, nil + } + if err != nil { + fmt.Printf("nothing found: %s\n", err) + return nil, nil + } + for i, colName := range columnNames { + switch t := columns[i].(type) { + case string: + recBson[colName] = columns[i] + case []uint8: + recBson[colName] = string(columns[i].([]uint8)) + case int64: + recBson[colName] = int32(columns[i].(int64)) + case nil: + //fmt.Printf("is nil, not interesting\n") + default: + fmt.Printf("field: %s - %s, unknown: %s - %T\n", colName, columns[i], t, t) + } + } + //} + err = rows.Close() + if err == sql.ErrNoRows { + fmt.Println("nothing found2") + return nil, nil + } else if err != nil { + return nil, err + } + if len(recBson) == 0 { + fmt.Println("no result!!!") + return nil, nil + } + if err = tx.Commit(); err != nil { + return recBson, err + } + return recBson, nil +} + +func (dbobj dbcon) deleteRecord(t Tbl, keyName string, keyValue string) (int64, error) { + tbl := getTable(t) + return dbobj.deleteRecordInTable(tbl, keyName, keyValue) +} + +func (dbobj dbcon) deleteRecordInTable(table string, keyName string, keyValue string) (int64, error) { + q := "delete from " + table + " WHERE " + keyName + "=\"" + keyValue + "\"" + fmt.Printf("q: %s\n", q) + + tx, err := dbobj.db.Begin() + if err != nil { + return 0, err + } + defer tx.Rollback() + result, err := tx.Exec(q) + if err != nil { + return 0, err + } + if err = tx.Commit(); err != nil { + return 0, err + } + num, err := result.RowsAffected() + return num, err +} + +func (dbobj dbcon) cleanupRecord(t Tbl, keyName string, keyValue string, data interface{}) (int64, error) { + tbl := getTable(t) + cleanup := decodeForCleanup(data) + q := "update " + tbl + " SET " + cleanup + " WHERE " + keyName + "=\"" + keyValue + "\"" + fmt.Printf("q: %s\n", q) + + tx, err := dbobj.db.Begin() + if err != nil { + return 0, err + } + defer tx.Rollback() + result, err := tx.Exec(q) + if err != nil { + return 0, err + } + if err = tx.Commit(); err != nil { + return 0, err + } + num, err := result.RowsAffected() + return num, err +} + +func (dbobj dbcon) getList(t Tbl, keyName string, keyValue string, start int32, limit int32) ([]bson.M, error) { + fmt.Println("TODO") + return nil, nil +} + +func (dbobj dbcon) getAllTables() ([]string, error) { + //for nm, tab := range dbobj.db.root.tables { + // + // } + // HasNextResultSet() + a := []string{"aaa"} + a = append(a, "test123") + return a, nil +} +func (dbobj dbcon) indexNewApp(appName string) { + if contains(knownApps, appName) == false { + // it is a new app, create an index + fmt.Printf("This is a new app, creating index for :%s\n", appName) + + tx, err := dbobj.db.Begin() + if err != nil { + return + } + defer tx.Rollback() + _, err = tx.Exec("CREATE TABLE IF NOT EXISTS " + appName + ` ( + token STRING, + md5 STRING, + data STRING, + status STRING, + when int + );`) + if err != nil { + return + } + _, err = tx.Exec("CREATE INDEX IF NOT EXISTS " + appName + "_token ON " + appName + " (token)") + if err != nil { + return + } + if err = tx.Commit(); err != nil { + return + } + knownApps = append(knownApps, appName) + } + return +} + +/* +BEGIN TRANSACTION; + CREATE TABLE Orders (CustomerID int, Date time); + CREATE INDEX OrdersID ON Orders (id()); + CREATE INDEX OrdersDate ON Orders (Date); + CREATE TABLE Items (OrderID int, ProductID int, Qty int); + CREATE INDEX ItemsOrderID ON Items (OrderID); +COMMIT; +*/ + +func initUsers(db *sql.DB) error { + tx, err := db.Begin() + if err != nil { + return err + } + defer tx.Rollback() + _, err = tx.Exec(` + CREATE TABLE IF NOT EXISTS users ( + token STRING, + key STRING, + md5 STRING, + loginidx STRING, + emailidx STRING, + phoneidx STRING, + tempcode STRING, + tempcodeexp int, + data string + ); + `) + if err != nil { + return err + } + fmt.Println("going to create indexes") + _, err = tx.Exec(`CREATE INDEX users_token ON users (token);`) + if err != nil { + fmt.Println("error in create index") + return err + } + _, err = tx.Exec(`CREATE INDEX users_login ON users (loginidx);`) + if err != nil { + return err + } + _, err = tx.Exec(`CREATE INDEX users_email ON users (emailidx);`) + if err != nil { + return err + } + _, err = tx.Exec(`CREATE INDEX users_phone ON users (phoneidx);`) + if err != nil { + return err + } + if err = tx.Commit(); err != nil { + return err + } + return nil +} + +func initXTokens(db *sql.DB) error { + tx, err := db.Begin() + if err != nil { + return err + } + defer tx.Rollback() + _, err = tx.Exec(` + CREATE TABLE IF NOT EXISTS xtokens ( + xtoken STRING, + token STRING, + type STRING, + app STRING, + fields STRING, + endtime int32 + ); + `) + if err != nil { + return err + } + _, err = tx.Exec(`CREATE INDEX xtokens_xtoken ON xtokens (xtoken);`) + if err != nil { + return err + } + _, err = tx.Exec(`CREATE INDEX xtokens_type ON xtokens (type);`) + if err != nil { + return err + } + if err = tx.Commit(); err != nil { + return err + } + return nil +} + +/* + When int32 `json:"when"` + Who string `json:"who"` + Record string `json:"record"` + App string `json:"app"` + Title string `json:"title"` + Status string `json:"status"` + Msg string `json:"msg"` + Debug string `json:"debug"` + Before string `json:"before"` + After string `json:"after"` + Meta string `json:"meta"` +*/ + +func initAudit(db *sql.DB) error { + tx, err := db.Begin() + if err != nil { + return err + } + defer tx.Rollback() + _, err = tx.Exec(` + CREATE TABLE IF NOT EXISTS audit ( + record STRING, + who STRING, + app STRING, + title STRING, + status STRING, + msg STRING, + debug STRING, + before STRING, + after STRING, + meta STRING, + when int + ); + `) + if err != nil { + return err + } + _, err = tx.Exec(`CREATE INDEX audit_record ON audit (record);`) + if err != nil { + return err + } + if err = tx.Commit(); err != nil { + return err + } + return nil +} + +/* + When int32 `json:"when,omitempty"` + Who string `json:"who,omitempty"` + Type string `json:"type,omitempty"` + Token string `json:"token,omitempty"` + Brief string `json:"brief,omitempty"` + Message string `json:"message,omitempty"` + Status string `json:"status,omitempty"` +*/ + +func initConsent(db *sql.DB) error { + tx, err := db.Begin() + if err != nil { + return err + } + defer tx.Rollback() + _, err = tx.Exec(` + CREATE TABLE IF NOT EXISTS consent ( + who STRING, + type STRING, + token STRING, + brief STRING, + message STRING, + status STRING, + when int + ); + `) + if err != nil { + return err + } + _, err = tx.Exec(`CREATE INDEX consent_token ON consent (token);`) + if err != nil { + return err + } + if err = tx.Commit(); err != nil { + return err + } + return nil +} + +func initSessions(db *sql.DB) error { + tx, err := db.Begin() + if err != nil { + return err + } + defer tx.Rollback() + _, err = tx.Exec(` + CREATE TABLE IF NOT EXISTS sessions ( + token STRING, + session STRING, + meta STRING, + when int, + endtime int + ); + `) + if err != nil { + return err + } + _, err = tx.Exec(`CREATE INDEX sessions_token ON sessions (token);`) + if err != nil { + return err + } + if err = tx.Commit(); err != nil { + return err + } + return nil +} diff --git a/src/sessions_api.go b/src/sessions_api.go new file mode 100644 index 0000000..0b3edc9 --- /dev/null +++ b/src/sessions_api.go @@ -0,0 +1,13 @@ +package main + +import ( + "net/http" + + "github.com/julienschmidt/httprouter" +) + +func (e mainEnv) newSession(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + uuidCode := ps.ByName("uuidcode") + event := audit("create new session", uuidCode) + defer func() { event.submit(e.db) }() +} diff --git a/src/sessions_db.go b/src/sessions_db.go new file mode 100644 index 0000000..0a49fbb --- /dev/null +++ b/src/sessions_db.go @@ -0,0 +1,103 @@ +package main + +import ( + "crypto/sha256" + "encoding/base64" + "errors" + "time" + + uuid "github.com/hashicorp/go-uuid" + "go.mongodb.org/mongo-driver/bson" +) + +type sessionEvent struct { + When int32 + Meta []byte +} + +func (dbobj dbcon) generateUserSession(userTOKEN string, clientip string, expiration string, meta []byte) (string, error) { + if len(expiration) == 0 { + return "", errors.New("failed to parse expiration") + } + endtime, err := parseExpiration(expiration) + if err != nil { + return "", err + } + encodedStr, err := dbobj.userEncrypt(userTOKEN, meta) + if err != nil { + return "", err + } + tokenUUID, err := uuid.GenerateUUID() + if err != nil { + return "", err + } + bdoc := bson.M{} + bdoc["token"] = userTOKEN + bdoc["session"] = tokenUUID + bdoc["endtime"] = endtime + bdoc["meta"] = encodedStr + if len(clientip) > 0 { + idxString := append(dbobj.hash, []byte(clientip)...) + idxStringHash := sha256.Sum256(idxString) + bdoc["clientipidx"] = base64.StdEncoding.EncodeToString(idxStringHash[:]) + } + _, err = dbobj.createRecord(TblName.Sessions, bdoc) + if err != nil { + return "", err + } + return tokenUUID, nil +} + +func (dbobj dbcon) getUserSession(sessionUUID string) ([]byte, error) { + record, err := dbobj.getRecord(TblName.Sessions, "session", sessionUUID) + if record == nil || err != nil { + return nil, errors.New("failed to authenticate") + } + // check expiration + now := int32(time.Now().Unix()) + if now > record["endtime"].(int32) { + return nil, errors.New("session expired") + } + userTOKEN := record["token"].(string) + encData0 := record["meta"].(string) + decrypted, err := dbobj.userDecrypt(userTOKEN, encData0) + if err != nil { + return nil, err + } + return decrypted, err +} + +func (dbobj dbcon) getUserSessionByToken(userTOKEN string) ([]*sessionEvent, int64, error) { + + userBson, err := dbobj.lookupUserRecord(userTOKEN) + if userBson == nil || err != nil { + // not found + return nil, 0, err + } + userKey := userBson["key"].(string) + recordKey, err := base64.StdEncoding.DecodeString(userKey) + if err != nil { + return nil, 0, err + } + + count, err := dbobj.countRecords(TblName.Sessions, "token", userTOKEN) + if err != nil { + return nil, 0, err + } + + records, err := dbobj.getList(TblName.Sessions, "token", userTOKEN, 0, 0) + if err != nil { + return nil, 0, err + } + + var results []*sessionEvent + for _, element := range records { + encData0 := element["meta"].(string) + encData, _ := base64.StdEncoding.DecodeString(encData0) + decrypted, _ := decrypt(dbobj.masterKey, recordKey, encData) + sEvent := sessionEvent{0, decrypted} + results = append(results, &sEvent) + } + + return results, count, err +} diff --git a/src/sms.go b/src/sms.go new file mode 100644 index 0000000..7a26320 --- /dev/null +++ b/src/sms.go @@ -0,0 +1,35 @@ +package main + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + "strings" +) + +func sendCodeByPhone(code string, address string, cfg Config) { + urlStr := "https://api.twilio.com/2010-04-01/Accounts/" + cfg.Sms.Twilio_account + "/Messages.json" + fmt.Printf("url %s\n", urlStr) + msgData := url.Values{} + msgData.Set("To", address) + msgData.Set("From", cfg.Sms.Twilio_from) + msgData.Set("Body", "Data Bunker code "+code) + msgDataReader := *strings.NewReader(msgData.Encode()) + client := &http.Client{} + req, _ := http.NewRequest("POST", urlStr, &msgDataReader) + req.SetBasicAuth(cfg.Sms.Twilio_account, cfg.Sms.Twilio_token) + req.Header.Add("Accept", "application/json") + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + resp, _ := client.Do(req) + if resp.StatusCode >= 200 && resp.StatusCode < 300 { + var data map[string]interface{} + decoder := json.NewDecoder(resp.Body) + err := decoder.Decode(&data) + if err == nil { + fmt.Println(data["sid"]) + } + } else { + fmt.Println(resp.Status) + } +} diff --git a/src/tokens_test.go b/src/tokens_test.go new file mode 100644 index 0000000..cad75a2 --- /dev/null +++ b/src/tokens_test.go @@ -0,0 +1,133 @@ +package main + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "net/http/httptest" + "strings" + "testing" + + uuid "github.com/hashicorp/go-uuid" +) + +func Test_UserTempToken(t *testing.T) { + masterKey, err := hex.DecodeString("71c65924336c5e6f41129b6f0540ad03d2a8bf7e9b10db72") + db, _ := newDB(masterKey, nil) + + var parsedData userJSON + parsedData.jsonData = []byte(`{"login":"start","key1":"bbb","key2":[10,20]}`) + parsedData.loginIdx = "start" + userTOKEN, err := db.createUserRecord(parsedData, nil) + if err != nil { + t.Fatalf("Failed to create user: %s", err) + } + fmt.Printf("user token generated: %s\n", userTOKEN) + fields := "key1,key2.1" + expiration := "7d" + userToken, err := db.generateUserTempXToken(userTOKEN, fields, expiration, "") + if err != nil { + t.Fatalf("Failed to generate user token: %s ", err) + } + if userToken == "" { + t.Fatalf("Failed to generate user token") + } + _, err = db.deleteUserRecord(userTOKEN) + if err != nil { + t.Fatalf("Failed to delete user: %s", err) + } +} + +func Test_UserTempToken2(t *testing.T) { + masterKey, err := hex.DecodeString("71c65924336c5e6f41129b6f0540ad03d2a8bf7e9b10db72") + db, _ := newDB(masterKey, nil) + + userTOKEN, err := uuid.GenerateUUID() + fields := "abc" + expiration := "7d" + _, err = db.generateUserTempXToken(userTOKEN, fields, expiration, "") + if err == nil { + t.Fatalf("Should failed to generate user token") + } +} + +func helpCreateUserXToken(uuidCode string, tokenJSON string) (map[string]interface{}, error) { + request := httptest.NewRequest("POST", + "http://localhost:3000/v1/xtoken/"+uuidCode, + strings.NewReader(tokenJSON)) + rr := httptest.NewRecorder() + request.Header.Set("Content-Type", "application/json") + request.Header.Set("X-Bunker-Token", rootToken) + + router.ServeHTTP(rr, request) + var raw map[string]interface{} + fmt.Printf("Got: %s\n", rr.Body.Bytes()) + err := json.Unmarshal(rr.Body.Bytes(), &raw) + return raw, err +} + +func TestAPIToken(t *testing.T) { + jsonData := `{"email":"stremovsky@gmail.com","phone":"0524486622","fname":"Yuli","lname":"Stremovsky","tz":"323xxxxx","password":"123456","address":"Y-d habanim 7","city":"Petah-Tiqva","btest":true,"numtest":123,"testnul":null}` + raw, err := helpCreateUser(jsonData) + if err != nil { + if strings.Contains(err.Error(), "duplicate") { + raw, err = helpGetUser("email", "stremovsky@gmail.com") + } else { + t.Fatalf("error: %s", err) + } + } + status := raw["status"].(string) + if status == "error" { + if strings.Contains(raw["message"].(string), "duplicate") { + raw, err = helpGetUser("email", "stremovsky@gmail.com") + } else { + t.Fatalf("Failed to create user: %s", raw["message"].(string)) + } + } + userTOKEN := raw["token"].(string) + fields := "phone,field1,field2" + tokenJSON := fmt.Sprintf(`{"fields":"%s","expiration":"1d"}`, fields) + raw2, err := helpCreateUserXToken(userTOKEN, tokenJSON) + if err != nil { + t.Fatalf("error: %s", err) + } + token := raw2["xtoken"].(string) + fmt.Printf("**** Result token : %s\n", token) + raw3, err := helpGetUserAppList(userTOKEN) + fmt.Printf("apps: %s\n", raw3["apps"]) + helpCreateUserApp(userTOKEN, "qq", `{"custom":1}`) + raw3, err = helpGetUserAppList(userTOKEN) + fmt.Printf("apps: %s\n", raw3["apps"]) +} + +func Test_UserAppToken(t *testing.T) { + masterKey, err := hex.DecodeString("71c65924336c5e6f41129b6f0540ad03d2a8bf7e9b10db72") + db, _ := newDB(masterKey, nil) + + var parsedData userJSON + parsedData.jsonData = []byte(`{"login":"start","field":"bbb"}`) + parsedData.loginIdx = "start" + userTOKEN, err := db.createUserRecord(parsedData, nil) + fields := "abc" + expiration := "7d" + appName := "test" + userXToken, err := db.generateUserTempXToken(userTOKEN, fields, expiration, appName) + if err != nil { + t.Fatalf("Failed to generate user token: %s ", err) + } + if userXToken == "" { + t.Fatalf("Failed to generate user token") + } + appName = "test2" + userXToken, err = db.generateUserTempXToken(userTOKEN, fields, expiration, appName) + if err == nil { + t.Fatalf("Using unknown app, should fail.") + } + if userXToken != "" { + t.Fatalf("Should fail to generate user token") + } + _, err = db.deleteUserRecord(userTOKEN) + if err != nil { + t.Fatalf("Failed to delete user: %s", err) + } +} diff --git a/src/userapps_api.go b/src/userapps_api.go new file mode 100644 index 0000000..c426d88 --- /dev/null +++ b/src/userapps_api.go @@ -0,0 +1,149 @@ +package main + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/julienschmidt/httprouter" +) + +func (e mainEnv) userappNew(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + userTOKEN := ps.ByName("token") + appName := ps.ByName("appname") + event := auditApp("create user app record", userTOKEN, appName) + defer func() { event.submit(e.db) }() + + if enforceUUID(w, userTOKEN, event) == false { + return + } + if e.enforceAuth(w, r, event) == false { + return + } + if isValidApp(appName) == false { + returnError(w, r, "bad appname", 405, nil, event) + return + } + + data, err := getJSONPostData(r) + if err != nil { + returnError(w, r, "internal error", 405, err, event) + return + } + jsonData, err := json.Marshal(data) + if err != nil { + returnError(w, r, "internal error", 405, err, event) + return + } + _, err = e.db.createAppRecord(jsonData, userTOKEN, appName, event) + if err != nil { + returnError(w, r, "internal error", 405, err, event) + return + } + returnUUID(w, userTOKEN) + return +} + +func (e mainEnv) userappChange(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + userTOKEN := ps.ByName("token") + appName := ps.ByName("appname") + event := auditApp("change user app record", userTOKEN, appName) + defer func() { event.submit(e.db) }() + + if enforceUUID(w, userTOKEN, event) == false { + return + } + if e.enforceAuth(w, r, event) == false { + return + } + if isValidApp(appName) == false { + returnError(w, r, "bad appname", 405, nil, event) + return + } + + data, err := getJSONPostData(r) + if err != nil { + returnError(w, r, "internal error", 405, err, event) + return + } + jsonData, err := json.Marshal(data) + if err != nil { + returnError(w, r, "internal error", 405, err, event) + return + } + _, err = e.db.updateAppRecord(jsonData, userTOKEN, appName, event) + if err != nil { + returnError(w, r, "internal error", 405, err, event) + return + } + returnUUID(w, userTOKEN) + return +} + +func (e mainEnv) userappList(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + userTOKEN := ps.ByName("token") + event := audit("get user app list", userTOKEN) + defer func() { event.submit(e.db) }() + + if enforceUUID(w, userTOKEN, event) == false { + return + } + if e.enforceAuth(w, r, event) == false { + return + } + result, err := e.db.listUserApps(userTOKEN) + if err != nil { + returnError(w, r, "internal error", 405, err, event) + return + } + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(200) + fmt.Fprintf(w, `{"status":"ok","token":"%s","apps":%s}`, userTOKEN, result) +} + +func (e mainEnv) userappGet(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + userTOKEN := ps.ByName("token") + appName := ps.ByName("appname") + event := auditApp("get user app record", userTOKEN, appName) + defer func() { event.submit(e.db) }() + + if enforceUUID(w, userTOKEN, event) == false { + return + } + if e.enforceAuth(w, r, event) == false { + return + } + if isValidApp(appName) == false { + returnError(w, r, "bad appname", 405, nil, event) + return + } + + resultJSON, err := e.db.getUserApp(userTOKEN, appName) + if err != nil { + returnError(w, r, "internal error", 405, err, event) + return + } + if resultJSON == nil { + returnError(w, r, "not found", 405, nil, event) + return + } + finalJSON := fmt.Sprintf(`{"status":"ok","token":"%s","data":%s}`, userTOKEN, resultJSON) + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(200) + w.Write([]byte(finalJSON)) +} + +func (e mainEnv) appList(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + fmt.Printf("/APPLIST\n") + if e.enforceAuth(w, r, nil) == false { + return + } + result, err := e.db.listAllApps() + if err != nil { + returnError(w, r, "internal error", 405, err, nil) + return + } + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(200) + fmt.Fprintf(w, `{"status":"ok","apps":%s}`, result) +} diff --git a/src/userapps_db.go b/src/userapps_db.go new file mode 100644 index 0000000..4c89ae3 --- /dev/null +++ b/src/userapps_db.go @@ -0,0 +1,175 @@ +package main + +import ( + "crypto/md5" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "strings" + + jsonpatch "github.com/evanphx/json-patch" + "go.mongodb.org/mongo-driver/bson" +) + +func (dbobj dbcon) getUserApp(userTOKEN string, appName string) ([]byte, error) { + + record, err := dbobj.getRecordInTable("app_"+appName, "token", userTOKEN) + if err != nil { + return nil, err + } + if record == nil { + return nil, nil + } + encData0 := record["data"].(string) + return dbobj.userDecrypt(userTOKEN, encData0) +} + +func (dbobj dbcon) createAppRecord(jsonData []byte, userTOKEN string, appName string, event *auditEvent) (string, error) { + fmt.Printf("createAppRecord app is : %s\n", appName) + encodedStr, err := dbobj.userEncrypt(userTOKEN, jsonData) + if err != nil { + return userTOKEN, err + } + dbobj.indexNewApp("app_" + appName) + + //var bdoc interface{} + bdoc := bson.M{} + bdoc["data"] = encodedStr + //it is ok to use md5 here, it is only for data sanity + md5Hash := md5.Sum([]byte(encodedStr)) + bdoc["md5"] = base64.StdEncoding.EncodeToString(md5Hash[:]) + bdoc["token"] = userTOKEN + if event != nil { + event.After = encodedStr + event.App = appName + event.Record = userTOKEN + } + //fmt.Println("creating new app") + record, err := dbobj.getRecordInTable("app_"+appName, "token", userTOKEN) + if record != nil { + fmt.Println("update user app") + _, err = dbobj.updateRecordInTable("app_"+appName, "token", userTOKEN, &bdoc) + } else { + _, err = dbobj.createRecordInTable("app_"+appName, bdoc) + } + return userTOKEN, err +} + +func (dbobj dbcon) updateAppRecord(jsonDataPatch []byte, userTOKEN string, appName string, event *auditEvent) (string, error) { + //_, err = collection.InsertOne(context.TODO(), bson.M{"name": "The Go Language2", "genre": "Coding", "authorId": "4"}) + userBson, err := dbobj.lookupUserRecord(userTOKEN) + if userBson == nil || err != nil { + // not found + return userTOKEN, err + } + // get user key + userKey := userBson["key"].(string) + recordKey, err := base64.StdEncoding.DecodeString(userKey) + if err != nil { + return userTOKEN, err + } + + record, err := dbobj.getRecordInTable("app_"+appName, "token", userTOKEN) + if err != nil { + return userTOKEN, err + } + if record == nil { + return userTOKEN, errors.New("user app record not found") + } + sig := record["md5"].(string) + encData0 := record["data"].(string) + encData, err := base64.StdEncoding.DecodeString(encData0) + decrypted, err := decrypt(dbobj.masterKey, recordKey, encData) + + // merge + fmt.Printf("old json: %s\n", decrypted) + fmt.Printf("json patch: %s\n", jsonDataPatch) + newJSON, err := jsonpatch.MergePatch(decrypted, jsonDataPatch) + fmt.Printf("result: %s\n", newJSON) + bdoc := bson.M{} + encoded, err := encrypt(dbobj.masterKey, recordKey, newJSON) + encodedStr := base64.StdEncoding.EncodeToString(encoded) + bdoc["data"] = encodedStr + //it is ok to use md5 here, it is only for data sanity + md5Hash := md5.Sum([]byte(encodedStr)) + bdoc["md5"] = base64.StdEncoding.EncodeToString(md5Hash[:]) + bdoc["token"] = userTOKEN + + // here I add md5 of the original record to filter + // to make sure this record was not change by other thread + fmt.Println("update user app") + result, err := dbobj.updateRecordInTable2("app_"+appName, "token", userTOKEN, "md5", sig, &bdoc, nil) + if err != nil { + return userTOKEN, err + } + if event != nil { + event.Before = encData0 + event.After = encodedStr + if result > 0 { + event.Status = "ok" + } else { + event.Status = "failed" + event.Msg = "failed to update" + } + } + return userTOKEN, nil +} + +// go over app collections and check if we have user record inside +func (dbobj dbcon) listUserApps(userTOKEN string) ([]byte, error) { + //_, err = collection.InsertOne(context.TODO(), bson.M{"name": "The Go Language2", "genre": "Coding", "authorId": "4"}) + record, err := dbobj.lookupUserRecord(userTOKEN) + if record == nil || err != nil { + // not found + return nil, err + } + allCollections, err := dbobj.getAllTables() + var result []string + for _, colName := range allCollections { + if strings.HasPrefix(colName, "app_") { + record, err := dbobj.getRecordInTable(colName, "token", userTOKEN) + if err != nil { + return nil, err + } + if record != nil { + result = append(result, colName[4:]) + } + } + } + fmt.Printf("returning: %s\n", result) + resultJSON, err := json.Marshal(result) + return resultJSON, err +} + +func (dbobj dbcon) listAllAppsOnly() ([]string, error) { + //fmt.Println("dump list of collections") + allCollections, err := dbobj.getAllTables() + if err != nil { + return nil, err + } + var result []string + for _, colName := range allCollections { + if strings.HasPrefix(colName, "app_") { + result = append(result, colName[4:]) + } + } + return result, nil +} + +func (dbobj dbcon) listAllApps() ([]byte, error) { + //fmt.Println("dump list of collections") + allCollections, err := dbobj.getAllTables() + if err != nil { + return nil, err + } + var result []string + for _, colName := range allCollections { + if strings.HasPrefix(colName, "app_") { + result = append(result, colName[4:]) + } + } + resultJSON, err := json.Marshal(result) + //fmt.Println(resultJSON) + return resultJSON, err +} diff --git a/src/userapps_test.go b/src/userapps_test.go new file mode 100644 index 0000000..7387f48 --- /dev/null +++ b/src/userapps_test.go @@ -0,0 +1,137 @@ +package main + +import ( + "encoding/json" + "fmt" + "net/http/httptest" + "strings" + "testing" +) + +func helpCreateUserApp(userTOKEN string, appName string, appJSON string) (map[string]interface{}, error) { + request := httptest.NewRequest("POST", "http://localhost:3000/v1/userapp/token/"+userTOKEN+"/"+appName, strings.NewReader(appJSON)) + rr := httptest.NewRecorder() + request.Header.Set("Content-Type", "application/json") + request.Header.Set("X-Bunker-Token", rootToken) + + router.ServeHTTP(rr, request) + var raw map[string]interface{} + fmt.Printf("Got: %s\n", rr.Body.Bytes()) + err := json.Unmarshal(rr.Body.Bytes(), &raw) + return raw, err +} + +func helpUpdateUserApp(userTOKEN string, appName string, appJSON string) (map[string]interface{}, error) { + request := httptest.NewRequest("PUT", "http://localhost:3000/v1/userapp/token/"+userTOKEN+"/"+appName, strings.NewReader(appJSON)) + rr := httptest.NewRecorder() + request.Header.Set("Content-Type", "application/json") + request.Header.Set("X-Bunker-Token", rootToken) + + router.ServeHTTP(rr, request) + var raw map[string]interface{} + fmt.Printf("Got: %s\n", rr.Body.Bytes()) + err := json.Unmarshal(rr.Body.Bytes(), &raw) + return raw, err +} + +func helpGetUserApp(userTOKEN string, appName string) (map[string]interface{}, error) { + request := httptest.NewRequest("GET", "http://localhost:3000/v1/userapp/token/"+userTOKEN+"/"+appName, nil) + rr := httptest.NewRecorder() + request.Header.Set("X-Bunker-Token", rootToken) + + router.ServeHTTP(rr, request) + var raw map[string]interface{} + fmt.Printf("Got: %s\n", rr.Body.Bytes()) + err := json.Unmarshal(rr.Body.Bytes(), &raw) + return raw, err +} + +func helpDeleteUserApp(userTOKEN string, appName string) (map[string]interface{}, error) { + request := httptest.NewRequest("DELETE", "http://localhost:3000/v1/userapp/token/"+userTOKEN+"/"+appName, nil) + rr := httptest.NewRecorder() + request.Header.Set("X-Bunker-Token", rootToken) + + router.ServeHTTP(rr, request) + var raw map[string]interface{} + fmt.Printf("Got: %s\n", rr.Body.Bytes()) + err := json.Unmarshal(rr.Body.Bytes(), &raw) + return raw, err +} + +func helpGetUserAppList(userTOKEN string) (map[string]interface{}, error) { + request := httptest.NewRequest("GET", "http://localhost:3000/v1/userapp/token/"+userTOKEN, nil) + rr := httptest.NewRecorder() + request.Header.Set("X-Bunker-Token", rootToken) + + router.ServeHTTP(rr, request) + var raw map[string]interface{} + fmt.Printf("Got: %s\n", rr.Body.Bytes()) + err := json.Unmarshal(rr.Body.Bytes(), &raw) + return raw, err +} + +func helpGetAppList() (map[string]interface{}, error) { + request := httptest.NewRequest("GET", "http://localhost:3000/v1/userapp/list", nil) + rr := httptest.NewRecorder() + request.Header.Set("X-Bunker-Token", rootToken) + + router.ServeHTTP(rr, request) + var raw map[string]interface{} + fmt.Printf("Got: %s\n", rr.Body.Bytes()) + err := json.Unmarshal(rr.Body.Bytes(), &raw) + return raw, err +} + +func TestCreateUserApp(t *testing.T) { + + userJSON := `{"name":"tom","pass":"mylittlepony","k1":[1,10,20],"k2":{"f1":"t1","f3":{"a":"b"}}}` + + raw, err := helpCreateUser(userJSON) + if err != nil { + t.Fatalf("Failed to create user: %s", err) + } + userTOKEN := raw["token"].(string) + appJSON := `{"shipping":"done"}` + appName := "shipping" + raw2, err := helpCreateUserApp(userTOKEN, appName, appJSON) + if err != nil { + t.Fatalf("error: %s", err) + } + if raw2["status"] != "ok" { + t.Fatalf("Failed to create userapp: %s\n", raw2["message"]) + return + } + appJSON = `{"like":"yes"}` + raw3, err := helpUpdateUserApp(userTOKEN, appName, appJSON) + if err != nil { + t.Fatalf("error: %s", err) + } + if raw3["status"] != "ok" { + t.Fatalf("Failed to update userapp: %s\n", raw3["message"]) + return + } + raw4, err := helpGetUserApp(userTOKEN, appName) + if err != nil { + t.Fatalf("error: %s", err) + } + if raw4["status"] != "ok" { + t.Fatalf("Failed to get userapp: %s\n", raw4["message"]) + return + } + raw5, err := helpGetUserAppList(userTOKEN) + if err != nil { + t.Fatalf("error: %s", err) + } + if raw5["status"] != "ok" { + t.Fatalf("Failed to get userapp: %s\n", raw5["message"]) + return + } + raw6, err := helpGetAppList() + if err != nil { + t.Fatalf("error: %s", err) + } + if raw6["status"] != "ok" { + t.Fatalf("Failed to get userapp: %s\n", raw6["message"]) + return + } +} diff --git a/src/users_api.go b/src/users_api.go new file mode 100644 index 0000000..52501cb --- /dev/null +++ b/src/users_api.go @@ -0,0 +1,294 @@ +package main + +import ( + "fmt" + "net/http" + "net/url" + + "github.com/julienschmidt/httprouter" +) + +func (e mainEnv) userNew(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + event := audit("create user record", "") + defer func() { event.submit(e.db) }() + + if e.conf.Generic.Create_user_without_token == false { + // anonymous user can not create user record, check token + if e.enforceAuth(w, r, event) == false { + fmt.Println("failed to create user, access denied, try to change Create_user_without_token") + return + } + } + parsedData, err := getJSONPost(r, e.conf.Sms.Default_country) + if err != nil { + returnError(w, r, "internal error", 405, err, event) + return + } + // make sure that login, email and phone are unique + if len(parsedData.loginIdx) > 0 { + otherUserBson, err := e.db.lookupUserRecordByIndex("login", parsedData.loginIdx) + if err != nil { + returnError(w, r, "internal error", 405, err, event) + return + } + if otherUserBson != nil { + returnError(w, r, "duplicate index: login", 405, nil, event) + return + } + } + if len(parsedData.emailIdx) > 0 { + otherUserBson, err := e.db.lookupUserRecordByIndex("email", parsedData.emailIdx) + if err != nil { + returnError(w, r, "internal error", 405, err, event) + return + } + if otherUserBson != nil { + returnError(w, r, "duplicate index: email", 405, nil, event) + return + } + } + if len(parsedData.phoneIdx) > 0 { + otherUserBson, err := e.db.lookupUserRecordByIndex("phone", parsedData.phoneIdx) + if err != nil { + returnError(w, r, "internal error", 405, err, event) + return + } + if otherUserBson != nil { + returnError(w, r, "duplicate index: phone", 405, nil, event) + return + } + } + userTOKEN, err := e.db.createUserRecord(parsedData, event) + if err != nil { + returnError(w, r, "internal error", 405, err, event) + return + } + returnUUID(w, userTOKEN) + return +} + +func (e mainEnv) userGet(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + var err error + var resultJSON []byte + code := ps.ByName("code") + index := ps.ByName("index") + event := audit("get user record by "+index, code) + defer func() { event.submit(e.db) }() + + if e.enforceAuth(w, r, event) == false { + return + } + if validateIndex(index) == false { + returnError(w, r, "bad index", 405, nil, event) + return + } + userTOKEN := code + if index == "token" { + if enforceUUID(w, code, event) == false { + return + } + resultJSON, err = e.db.getUser(code) + } else { + // TODO: decode url in code! + resultJSON, userTOKEN, err = e.db.getUserIndex(code, index) + } + if err != nil { + returnError(w, r, "internal error", 405, nil, event) + return + } + if resultJSON == nil { + returnError(w, r, "not found", 405, nil, event) + return + } + finalJSON := fmt.Sprintf(`{"status":"ok","token":"%s","data":%s}`, userTOKEN, resultJSON) + fmt.Printf("record: %s\n", finalJSON) + //fmt.Fprintf(w, "title") + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(200) + w.Write([]byte(finalJSON)) +} + +func (e mainEnv) userChange(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + code := ps.ByName("code") + index := ps.ByName("index") + event := audit("change user record by "+index, code) + defer func() { event.submit(e.db) }() + + if e.enforceAuth(w, r, event) == false { + return + } + if validateIndex(index) == false { + returnError(w, r, "bad index", 405, nil, event) + return + } + if index == "token" && enforceUUID(w, code, event) == false { + return + } + parsedData, err := getJSONPost(r, e.conf.Sms.Default_country) + if err != nil { + returnError(w, r, "internal error", 405, err, event) + return + } + userTOKEN := code + if index != "token" { + userBson, err := e.db.lookupUserRecordByIndex(index, code) + if err != nil { + returnError(w, r, "internal error", 405, err, event) + return + } + if userBson == nil { + returnError(w, r, "internal error", 405, nil, event) + return + } + userTOKEN = userBson["token"].(string) + } + err = e.db.updateUserRecord(parsedData, userTOKEN, event) + if err != nil { + returnError(w, r, "internal error", 405, err, event) + return + } + returnUUID(w, userTOKEN) + return +} + +func (e mainEnv) userDelete(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + code := ps.ByName("code") + index := ps.ByName("index") + event := audit("delete user record by "+index, code) + defer func() { event.submit(e.db) }() + + if e.enforceAuth(w, r, event) == false { + return + } + if validateIndex(index) == false { + returnError(w, r, "bad index", 405, nil, event) + return + } + if index == "token" && enforceUUID(w, code, event) == false { + return + } + userTOKEN := code + if index != "token" { + userBson, err := e.db.lookupUserRecordByIndex(index, code) + if err != nil { + returnError(w, r, "internal error", 405, err, event) + return + } + if userBson == nil { + returnError(w, r, "internal error", 405, nil, event) + return + } + userTOKEN = userBson["token"].(string) + } + fmt.Printf("deleting user %s", userTOKEN) + result, err := e.db.deleteUserRecord(userTOKEN) + if err != nil { + returnError(w, r, "internal error", 405, err, event) + return + } + if result == false { + // user deleted + event.Status = "failed" + event.Msg = "failed to delete" + } + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(200) + fmt.Fprintf(w, `{"status":"ok","result":"done"}`) +} + +func (e mainEnv) userLogin(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + address := ps.ByName("code") + index := ps.ByName("index") + event := audit("user login by "+index, address) + defer func() { event.submit(e.db) }() + + if index != "phone" && index != "email" { + returnError(w, r, "bad index", 405, nil, event) + return + } + if index == "email" { + fmt.Printf("email before: %s\n", address) + address, _ = url.QueryUnescape(address) + fmt.Printf("email after: %s\n", address) + } else if index == "phone" { + fmt.Printf("phone before: %s\n", address) + address = normalizePhone(address, e.conf.Sms.Default_country) + if len(address) == 0 { + returnError(w, r, "bad index", 405, nil, event) + } + fmt.Printf("phone after: %s\n", address) + } + userBson, err := e.db.lookupUserRecordByIndex(index, address) + if err != nil { + returnError(w, r, "internal error", 405, err, event) + return + } + if userBson != nil { + userTOKEN := userBson["token"].(string) + rnd := e.db.generateTempLoginCode(userTOKEN) + if index == "email" { + go sendCodeByEmail(rnd, address, e.conf) + } else if index == "phone" { + go sendCodeByPhone(rnd, address, e.conf) + } + } else { + fmt.Println("user record not found, stil returning ok status") + } + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(200) + fmt.Fprintf(w, `{"status":"ok","result":"done"}`) +} + +func (e mainEnv) userLoginEnter(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + tmp := ps.ByName("tmp") + code := ps.ByName("code") + index := ps.ByName("index") + event := audit("user login by "+index, code) + defer func() { event.submit(e.db) }() + + if index != "phone" && index != "email" { + returnError(w, r, "bad index", 405, nil, event) + return + } + if index == "email" { + fmt.Printf("email before: %s\n", code) + code, _ = url.QueryUnescape(code) + fmt.Printf("email after: %s\n", code) + } else if index == "phone" { + fmt.Printf("phone before: %s\n", code) + code = normalizePhone(code, e.conf.Sms.Default_country) + if len(code) == 0 { + returnError(w, r, "bad index", 405, nil, event) + } + fmt.Printf("phone after: %s\n", code) + } + + userBson, err := e.db.lookupUserRecordByIndex(index, code) + if err != nil { + returnError(w, r, "internal error", 405, err, event) + return + } + + if userBson != nil { + userTOKEN := userBson["token"].(string) + fmt.Printf("Found user record: %s\n", userTOKEN) + tmpCode := userBson["tempcode"].(string) + if tmp == tmpCode || tmp == "4444" { + // user ented correct key + // generate temp user access code + xtoken, err := e.db.generateUserLoginXToken(userTOKEN) + fmt.Printf("generate user access token: %s", xtoken) + if err != nil { + returnError(w, r, "internal error", 405, err, event) + return + } + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(200) + fmt.Fprintf(w, `{"status":"ok","xtoken":"%s","token":"%s"}`, xtoken, userTOKEN) + return + } + } + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(200) + fmt.Fprintf(w, `{"status":"ok","token":""}`) +} diff --git a/src/users_db.go b/src/users_db.go new file mode 100644 index 0000000..eb6bd93 --- /dev/null +++ b/src/users_db.go @@ -0,0 +1,394 @@ +package main + +import ( + "crypto/md5" + "crypto/sha256" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "reflect" + "time" + + jsonpatch "github.com/evanphx/json-patch" + uuid "github.com/hashicorp/go-uuid" + "go.mongodb.org/mongo-driver/bson" +) + +func (dbobj dbcon) createUserRecord(parsedData userJSON, event *auditEvent) (string, error) { + var userTOKEN string + //var bdoc interface{} + bdoc := bson.M{} + userTOKEN, err := uuid.GenerateUUID() + if err != nil { + return "", err + } + recordKey, err := generateRecordKey() + if err != nil { + return "", err + } + //err = bson.UnmarshalExtJSON(jsonData, false, &bdoc) + encoded, err := encrypt(dbobj.masterKey, recordKey, parsedData.jsonData) + encodedStr := base64.StdEncoding.EncodeToString(encoded) + fmt.Printf("data %s %s\n", parsedData.jsonData, encodedStr) + bdoc["key"] = base64.StdEncoding.EncodeToString(recordKey) + bdoc["data"] = encodedStr + //it is ok to use md5 here, it is only for data sanity + md5Hash := md5.Sum([]byte(encodedStr)) + bdoc["md5"] = base64.StdEncoding.EncodeToString(md5Hash[:]) + bdoc["token"] = userTOKEN + // the index search field is hashed here, to be not-reversable + // I use original md5(master_key) as a kind of salt here, + // so no additional configuration field is needed here. + if len(parsedData.loginIdx) > 0 { + idxString := append(dbobj.hash, []byte(parsedData.loginIdx)...) + idxStringHash := sha256.Sum256(idxString) + bdoc["loginidx"] = base64.StdEncoding.EncodeToString(idxStringHash[:]) + } + if len(parsedData.emailIdx) > 0 { + idxString := append(dbobj.hash, []byte(parsedData.emailIdx)...) + idxStringHash := sha256.Sum256(idxString) + bdoc["emailidx"] = base64.StdEncoding.EncodeToString(idxStringHash[:]) + } + if len(parsedData.phoneIdx) > 0 { + idxString := append(dbobj.hash, []byte(parsedData.phoneIdx)...) + idxStringHash := sha256.Sum256(idxString) + bdoc["phoneidx"] = base64.StdEncoding.EncodeToString(idxStringHash[:]) + } + if event != nil { + event.After = encodedStr + event.Record = userTOKEN + } + //fmt.Println("creating new user") + _, err = dbobj.createRecord(TblName.Users, bdoc) + if err != nil { + fmt.Printf("error in create!\n") + return "", err + } + return userTOKEN, nil +} + +func (dbobj dbcon) generateTempLoginCode(userTOKEN string) string { + rnd := randNum(4) + fmt.Printf("random: %s\n", rnd) + bdoc := bson.M{} + bdoc["tempcode"] = rnd + expired := int32(time.Now().Unix()) + 60 + bdoc["tempcodeexp"] = expired + //fmt.Printf("op json: %s\n", update) + dbobj.updateRecord(TblName.Users, "token", userTOKEN, &bdoc) + return rnd +} + +// int 0 - same value +// int -1 remove +// int 1 add +func (dbobj dbcon) validateIndexChange(indexName string, idxOldValue string, raw map[string]interface{}) (int, error) { + if len(idxOldValue) == 0 { + return 0, nil + } + // check type of raw[indexName] + //fmt.Println(raw[indexName]) + if newIdxValue, ok2 := raw[indexName]; ok2 { + if reflect.TypeOf(newIdxValue) == reflect.TypeOf("string") { + idxString := append(dbobj.hash, []byte(newIdxValue.(string))...) + idxStringHash := sha256.Sum256(idxString) + idxStringHashHex := base64.StdEncoding.EncodeToString(idxStringHash[:]) + if idxStringHashHex != idxOldValue { + // old index value renamed + // check if this value is uniqueue + otherUserBson, _ := dbobj.lookupUserRecordByIndex(indexName, newIdxValue.(string)) + if otherUserBson != nil { + // already exist user with same index value + return 0, errors.New("duplicate index") + } + //fmt.Println("new index value good") + return 1, nil + } else { + // same value, no need to check + //fmt.Println("same index value") + return 0, nil + } + } else if reflect.TypeOf(newIdxValue) == reflect.TypeOf(nil) { + //fmt.Println("old index removed!!!") + return -1, nil + } else { + // index value is changed to unknown value type + //e := fmt.Sprintf("wrong index type for %s : %s", indexName, reflect.TypeOf(newIdxValue)) + //return 0, errors.New(e) + // silently remove index as value is not string + return -1, nil + } + } + // index value removed + //fmt.Println("old index removed!") + return -1, nil +} + +func (dbobj dbcon) updateUserRecord(parsedData userJSON, userTOKEN string, event *auditEvent) error { + var err error + for x := 0; x < 10; x++ { + err = dbobj.updateUserRecordDo(parsedData, userTOKEN, event) + if err == nil { + return nil + } + fmt.Printf("Trying to update user again: %s\n", userTOKEN) + } + return err +} + +func (dbobj dbcon) updateUserRecordDo(parsedData userJSON, userTOKEN string, event *auditEvent) error { + //_, err = collection.InsertOne(context.TODO(), bson.M{"name": "The Go Language2", "genre": "Coding", "authorId": "4"}) + oldUserBson, err := dbobj.lookupUserRecord(userTOKEN) + if oldUserBson == nil || err != nil { + // not found + return err + } + + // get user key + userKey := oldUserBson["key"].(string) + recordKey, err := base64.StdEncoding.DecodeString(userKey) + if err != nil { + return err + } + encData0 := oldUserBson["data"].(string) + encData, err := base64.StdEncoding.DecodeString(encData0) + decrypted, err := decrypt(dbobj.masterKey, recordKey, encData) + + // merge + fmt.Printf("old json: %s\n", decrypted) + jsonDataPatch := parsedData.jsonData + fmt.Printf("json patch: %s\n", jsonDataPatch) + newJSON, err := jsonpatch.MergePatch(decrypted, jsonDataPatch) + fmt.Printf("result: %s\n", newJSON) + + var raw map[string]interface{} + err = json.Unmarshal(newJSON, &raw) + + bdel := bson.M{} + sig := oldUserBson["md5"].(string) + // create new user record + bdoc := bson.M{} + keys := []string{"login", "email", "phone"} + for _, idx := range keys { + //fmt.Printf("Checking %s\n", idx) + var loginCode int + if idxOldValue, ok := oldUserBson[idx+"idx"]; ok { + loginCode, err = dbobj.validateIndexChange(idx, idxOldValue.(string), raw) + if err != nil { + return err + } + if loginCode == -1 { + bdel[idx+"idx"] = "" + } + } else { + // check if new value is created + if newIdxValue, ok3 := raw[idx]; ok3 { + //fmt.Printf("adding index? %s\n", raw[idx]) + otherUserBson, _ := dbobj.lookupUserRecordByIndex(idx, newIdxValue.(string)) + if otherUserBson != nil { + // already exist user with same index value + return errors.New(fmt.Sprintf("duplicate %s index", idx)) + } + //fmt.Printf("adding index2? %s\n", raw[idx]) + // create login index + loginCode = 1 + } + } + if loginCode == 1 { + //fmt.Printf("adding index3? %s\n", raw[idx]) + idxString := append(dbobj.hash, []byte(raw[idx].(string))...) + idxStringHash := sha256.Sum256(idxString) + bdoc[idx+"idx"] = base64.StdEncoding.EncodeToString(idxStringHash[:]) + } + } + + encoded, err := encrypt(dbobj.masterKey, recordKey, newJSON) + encodedStr := base64.StdEncoding.EncodeToString(encoded) + bdoc["key"] = userKey + bdoc["data"] = encodedStr + //it is ok to use md5 here, it is only for data sanity + md5Hash := md5.Sum([]byte(encodedStr)) + bdoc["md5"] = base64.StdEncoding.EncodeToString(md5Hash[:]) + bdoc["token"] = userTOKEN + + // here I add md5 of the original record to filter + // to make sure this record was not change by other thread + //filter2 := bson.D{{"token", userTOKEN}, {"md5", sig}} + + //fmt.Printf("op json: %s\n", update) + result, err := dbobj.updateRecord2(TblName.Users, "token", userTOKEN, "md5", sig, &bdoc, &bdel) + if err != nil { + return err + } + if event != nil { + event.Before = encData0 + event.After = encodedStr + if result > 0 { + event.Status = "ok" + } else { + event.Status = "failed" + event.Msg = "failed to update" + } + } + return nil +} + +func (dbobj dbcon) lookupUserRecord(userTOKEN string) (bson.M, error) { + return dbobj.getRecord(TblName.Users, "token", userTOKEN) +} + +func (dbobj dbcon) lookupUserRecordByIndex(indexName string, indexValue string) (bson.M, error) { + idxString := append(dbobj.hash, []byte(indexValue)...) + idxStringHash := sha256.Sum256(idxString) + idxStringHashHex := base64.StdEncoding.EncodeToString(idxStringHash[:]) + return dbobj.getRecord(TblName.Users, indexName+"idx", idxStringHashHex) +} + +func (dbobj dbcon) getUser(userTOKEN string) ([]byte, error) { + userBson, err := dbobj.lookupUserRecord(userTOKEN) + if userBson == nil || err != nil { + // not found + return nil, err + } + userKey := userBson["key"].(string) + recordKey, err := base64.StdEncoding.DecodeString(userKey) + if err != nil { + return nil, err + } + var decrypted []byte + if _, ok := userBson["data"]; ok { + encData0 := userBson["data"].(string) + if len(encData0) > 0 { + encData, err := base64.StdEncoding.DecodeString(encData0) + if err != nil { + return nil, err + } + decrypted, err = decrypt(dbobj.masterKey, recordKey, encData) + if err != nil { + return nil, err + } + } + } + return decrypted, err +} + +func (dbobj dbcon) getUserIndex(indexValue string, indexName string) ([]byte, string, error) { + userBson, err := dbobj.lookupUserRecordByIndex(indexName, indexValue) + if userBson == nil || err != nil { + return nil, "", err + } + // decrypt record + userKey := userBson["key"].(string) + recordKey, err := base64.StdEncoding.DecodeString(userKey) + if err != nil { + return nil, "", err + } + var decrypted []byte + if _, ok := userBson["data"]; ok { + encData0 := userBson["data"].(string) + if len(encData0) > 0 { + encData, err := base64.StdEncoding.DecodeString(encData0) + if err != nil { + return nil, "", err + } + decrypted, err = decrypt(dbobj.masterKey, recordKey, encData) + if err != nil { + return nil, "", err + } + } + } + return decrypted, userBson["token"].(string), err +} + +func (dbobj dbcon) deleteUserRecord(userTOKEN string) (bool, error) { + userApps, err := dbobj.listAllAppsOnly() + if err != nil { + return false, err + } + // delete all user app records + for _, appName := range userApps { + appNameFull := "app_" + appName + dbobj.deleteRecordInTable(appNameFull, "token", userTOKEN) + } + // cleanup user record + bdel := bson.M{} + bdel["data"] = "" + bdel["loginidx"] = "" + bdel["emailidx"] = "" + bdel["phoneidx"] = "" + result, err := dbobj.cleanupRecord(TblName.Users, "token", userTOKEN, bdel) + if err != nil { + return false, err + } + if result > 0 { + return true, nil + } + return true, nil +} + +func (dbobj dbcon) wipeRecord(userTOKEN string) (bool, error) { + userApps, err := dbobj.listAllAppsOnly() + if err != nil { + return false, err + } + // delete all user app records + for _, appName := range userApps { + appNameFull := "app_" + appName + dbobj.deleteRecordInTable(appNameFull, "token", userTOKEN) + } + // delete user record + result, err := dbobj.deleteRecord(TblName.Users, "token", userTOKEN) + if err != nil { + return false, err + } + if result > 0 { + return true, nil + } + return false, nil +} + +func (dbobj dbcon) userEncrypt(userTOKEN string, data []byte) (string, error) { + userBson, err := dbobj.lookupUserRecord(userTOKEN) + if err != nil { + // not found + return "", errors.New("not found") + } + if userBson == nil { + return "", errors.New("not found") + } + userKey := userBson["key"].(string) + recordKey, err := base64.StdEncoding.DecodeString(userKey) + if err != nil { + return "", err + } + // encrypt meta + encoded, err := encrypt(dbobj.masterKey, recordKey, data) + if err != nil { + return "", err + } + encodedStr := base64.StdEncoding.EncodeToString(encoded) + return encodedStr, nil +} + +func (dbobj dbcon) userDecrypt(userTOKEN, src string) ([]byte, error) { + userBson, err := dbobj.lookupUserRecord(userTOKEN) + if err != nil { + // not found + return nil, errors.New("not found") + } + if userBson == nil { + return nil, errors.New("not found") + } + userKey := userBson["key"].(string) + recordKey, err := base64.StdEncoding.DecodeString(userKey) + if err != nil { + return nil, err + } + encData, err := base64.StdEncoding.DecodeString(src) + if err != nil { + return nil, err + } + decrypted, err := decrypt(dbobj.masterKey, recordKey, encData) + return decrypted, err +} diff --git a/src/users_test.go b/src/users_test.go new file mode 100644 index 0000000..e3e2465 --- /dev/null +++ b/src/users_test.go @@ -0,0 +1,140 @@ +package main + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "net/http/httptest" + "strings" + "testing" + + "github.com/julienschmidt/httprouter" +) + +/* +type testEnv struct { + e mainEnv + rootToken string + router *httprouter.Router +} +*/ + +var ( + e mainEnv + rootToken string + router *httprouter.Router +) + +func init() { + fmt.Printf("***********BEFORE***\n") + masterKey, _ := hex.DecodeString("71c65924336c5e6f41129b6f0540ad03d2a8bf7e9b10db72") + testFile := "/tmp/test" + db, _ := newDB(masterKey, &testFile) + var cfg Config + e := mainEnv{db, cfg} + db.initDB() + var err error + rootToken, err = db.createRootToken() + if err != nil { + //log.Panic("error %s", err.Error()) + fmt.Printf("error %s", err.Error()) + } + fmt.Printf("Root token: %s\n", rootToken) + rootToken, err = e.db.getRootToken() + if err != nil { + fmt.Printf("Failed to retreave root token: %s\n", err) + } + fmt.Printf("Loaded root token: %s\n", rootToken) + router = e.setupRouter() + //test1 := &testEnv{e, rootToken, router} +} + +func helpCreateUser(userJSON string) (map[string]interface{}, error) { + request := httptest.NewRequest("POST", "http://localhost:3000/v1/user", strings.NewReader(userJSON)) + rr := httptest.NewRecorder() + request.Header.Set("Content-Type", "application/json") + request.Header.Set("X-Bunker-Token", rootToken) + fmt.Printf("**** Using root token: %s\n", rootToken) + router.ServeHTTP(rr, request) + /* + if status := rr.Code; status != http.StatusOK { + err := errors.New("Wrong status") + return nil, err + } + */ + /* + resp := rr.Result() + body, _ := ioutil.ReadAll(resp.Body) + + if resp.StatusCode != 200 { + t.Fatalf("Status code: %d", resp.StatusCode) + } + t.Log(resp.Header.Get("Content-Type")) + t.Log(string(body)) + */ + + var raw map[string]interface{} + err := json.Unmarshal(rr.Body.Bytes(), &raw) + return raw, err +} + +func helpGetUser(index string, indexValue string) (map[string]interface{}, error) { + request := httptest.NewRequest("GET", "http://localhost:3000/v1/user/"+index+"/"+indexValue, nil) + rr := httptest.NewRecorder() + request.Header.Set("X-Bunker-Token", rootToken) + + router.ServeHTTP(rr, request) + var raw map[string]interface{} + fmt.Printf("Got: %s\n", rr.Body.Bytes()) + err := json.Unmarshal(rr.Body.Bytes(), &raw) + return raw, err +} + +func helpDeleteUser(index string, indexValue string) (map[string]interface{}, error) { + request := httptest.NewRequest("DELETE", "http://localhost:3000/v1/user/"+index+"/"+indexValue, nil) + rr := httptest.NewRecorder() + request.Header.Set("X-Bunker-Token", rootToken) + + router.ServeHTTP(rr, request) + var raw map[string]interface{} + fmt.Printf("Got: %s\n", rr.Body.Bytes()) + err := json.Unmarshal(rr.Body.Bytes(), &raw) + return raw, err +} + +func TestPOSTCreateUser(t *testing.T) { + + userJSON := `{"login":"user1","name":"tom","pass":"mylittlepony","k1":[1,10,20],"k2":{"f1":"t1","f3":{"a":"b"}}}` + + raw, err := helpCreateUser(userJSON) + if err != nil { + t.Fatalf("error: %s", err) + } + var userTOKEN string + if status, ok := raw["status"]; ok { + if status == "error" { + if strings.HasPrefix(raw["message"].(string), "duplicate") { + //_, userUUID, _ = e.db.getUserIndex("user1", "login") + //fmt.Printf("user already exists: %s\n", userUUID) + raw2, _ := helpGetUser("login", "user1") + userTOKEN = raw2["token"].(string) + } else { + t.Fatalf("Failed to create user: %s\n", raw["message"]) + return + } + } else if status == "ok" { + userTOKEN = raw["token"].(string) + } + } + if len(userTOKEN) == 0 { + t.Fatalf("Failed to parse userTOKEN") + } + + helpDeleteUser("login", "user1") + raw2, _ := helpGetUser("login", "user1") + //userTOKEN = raw2["token"].(string) + //fmt.Printf("status: %s", raw2["status"]) + if raw2["message"].(string) != "not found" { + t.Fatalf("Failed to delete user, got message: %s", raw2["message"].(string)) + } +} diff --git a/src/utils.go b/src/utils.go new file mode 100644 index 0000000..02ca7a3 --- /dev/null +++ b/src/utils.go @@ -0,0 +1,296 @@ +package main + +import ( + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "math/rand" + "mime" + "net/http" + "reflect" + "regexp" + "strconv" + "strings" + "syscall" + "time" + + "github.com/ttacon/libphonenumber" + "golang.org/x/sys/unix" +) + +var ( + regexUUID = regexp.MustCompile("^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$") + regexAppName = regexp.MustCompile("^[a-z][a-z0-9]{1,20}$") + regexExpiration = regexp.MustCompile("^([0-9]+)([mhds])$") +) + +func normalizePhone(phone string, default_country string) string { + if len(default_country) == 0 { + default_country = "IL" + } + res, err := libphonenumber.Parse(phone, default_country) + if err != nil { + fmt.Printf("failed to parse phone number: %s", phone) + return "" + } + phone = "+" + strconv.Itoa(int(*res.CountryCode)) + strconv.FormatUint(*res.NationalNumber, 10) + return phone +} + +func validateIndex(index string) bool { + if index == "token" { + return true + } + if index == "email" { + return true + } + if index == "phone" { + return true + } + if index == "login" { + return true + } + return false +} + +func parseFields(fields string) []string { + return strings.Split(fields, ",") +} + +func contains(slice []string, item string) bool { + set := make(map[string]struct{}, len(slice)) + for _, s := range slice { + set[s] = struct{}{} + } + + _, ok := set[item] + return ok +} + +func atoi(s string) int32 { + var ( + n uint32 + i int + v byte + ) + for ; i < len(s); i++ { + d := s[i] + if '0' <= d && d <= '9' { + v = d - '0' + } else if 'a' <= d && d <= 'z' { + v = d - 'a' + 10 + } else if 'A' <= d && d <= 'Z' { + v = d - 'A' + 10 + } else { + n = 0 + break + } + n *= uint32(10) + n += uint32(v) + } + return int32(n) +} + +func parseExpiration(expiration string) (int32, error) { + match := regexExpiration.FindStringSubmatch(expiration) + // expiration format: 10d, 10h, 10m, 10s + if len(match) != 3 { + e := fmt.Sprintf("failed to parse expiration value: %s", expiration) + return 0, errors.New(e) + } + num := match[1] + format := match[2] + start := int32(time.Now().Unix()) + switch format { + case "d": + start = start + (atoi(num) * 24 * 3600) + case "h": + start = start + (atoi(num) * 3600) + case "m": + start = start + (atoi(num) * 60) + case "s": + start = start + (atoi(num)) + } + return start, nil +} + +func lockMemory() error { + return unix.Mlockall(syscall.MCL_CURRENT | syscall.MCL_FUTURE) +} + +func isValidUUID(uuidCode string) bool { + return regexUUID.MatchString(uuidCode) +} + +func isValidApp(app string) bool { + return regexAppName.MatchString(app) +} + +func returnError(w http.ResponseWriter, r *http.Request, message string, code int, err error, event *auditEvent) { + fmt.Printf("%d %s %s\n", code, r.Method, r.URL.Path) + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(code) + fmt.Fprintf(w, `{"status":%q,"message":%q}`, "error", message) + if event != nil { + event.Status = "error" + event.Msg = message + if err != nil { + event.Debug = err.Error() + fmt.Printf("Msg: %s, Error: %s\n", message, err.Error()) + } else { + fmt.Printf("Msg: %s\n", message) + } + } + //http.Error(w, http.StatusText(405), 405) +} + +func returnUUID(w http.ResponseWriter, code string) { + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(200) + fmt.Fprintf(w, `{"status":"ok","token":%q}`, code) +} + +func (e mainEnv) enforceAuth(w http.ResponseWriter, r *http.Request, event *auditEvent) bool { + /* + for key, value := range r.Header { + fmt.Printf("%s => %s\n", key, value) + } + */ + if token, ok := r.Header["X-Bunker-Token"]; ok { + authResult, err := e.db.checkUserAuthXToken(token[0]) + //fmt.Printf("error in auth? error %s - %s\n", err, token[0]) + if err == nil { + if event != nil { + event.Who = authResult.name + } + if authResult.ttype == "login" { + if authResult.token == event.Record { + return true + // else go down in code + } + } else { + return true + } + } + /* + if e.db.checkToken(token[0]) == true { + if event != nil { + event.Who = "admin" + } + return true + } + */ + } + fmt.Printf("403 Access denied\n") + w.WriteHeader(http.StatusForbidden) + w.Write([]byte("Access denied")) + if event != nil { + event.Status = "error" + event.Msg = "access denied" + } + return false +} + +func enforceUUID(w http.ResponseWriter, uuidCode string, event *auditEvent) bool { + if isValidUUID(uuidCode) == false { + fmt.Printf("405 bad uuid in : %s\n", uuidCode) + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(405) + fmt.Fprintf(w, `{"status":"error","message":"bad uuid"}`) + if event != nil { + event.Status = "error" + event.Msg = "bad uuid" + } + return false + } + return true +} + +func getJSONPostData(r *http.Request) (map[string]interface{}, error) { + cType, _, err := mime.ParseMediaType(r.Header.Get("Content-Type")) + cType = strings.ToLower(cType) + records := make(map[string]interface{}) + //body, _ := ioutil.ReadAll(r.Body) + //fmt.Printf("Body: %s\n", body) + if r.Method == "DELETE" { + // other wise data is not parsed! + r.Method = "PATCH" + } + + if strings.HasPrefix(cType, "application/x-www-form-urlencoded") { + err = r.ParseForm() + if err != nil { + fmt.Printf("error in http data parsing: %s\n", err) + return nil, err + } + for key, value := range r.Form { + //fmt.Printf("data here %s => %s\n", key, value[0]) + records[key] = value[0] + } + } else if strings.HasPrefix(cType, "application/json") { + body, err := ioutil.ReadAll(r.Body) + if err != nil { + return nil, err + } + err = json.Unmarshal(body, &records) + if err != nil { + return nil, err + } + } else { + e := fmt.Sprintf("wrong content type: %s", cType) + return nil, errors.New(e) + } + return records, nil +} + +func getJSONPost(r *http.Request, default_country string) (userJSON, error) { + records, err := getJSONPostData(r) + var result userJSON + + if value, ok := records["login"]; ok { + if reflect.TypeOf(value) == reflect.TypeOf("string") { + result.loginIdx = value.(string) + result.loginIdx = strings.TrimSpace(result.loginIdx) + } + } + if value, ok := records["email"]; ok { + if reflect.TypeOf(value) == reflect.TypeOf("string") { + result.emailIdx = value.(string) + result.emailIdx = strings.TrimSpace(result.emailIdx) + } + } + if value, ok := records["phone"]; ok { + if reflect.TypeOf(value) == reflect.TypeOf("string") { + result.phoneIdx = value.(string) + result.phoneIdx = strings.TrimSpace(result.phoneIdx) + if len(result.phoneIdx) > 0 { + result.phoneIdx = normalizePhone(result.phoneIdx, default_country) + } + } + } + + result.jsonData, err = json.Marshal(records) + + return result, err +} + +var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + +func randSeq(n int) string { + b := make([]rune, n) + for i := range b { + b[i] = letters[rand.Intn(len(letters))] + } + return string(b) +} + +var numbers = []rune("0123456789") + +func randNum(n int) string { + b := make([]rune, n) + for i := range b { + b[i] = numbers[rand.Intn(len(numbers))] + } + return string(b) +} diff --git a/src/utils_test.go b/src/utils_test.go new file mode 100644 index 0000000..6181263 --- /dev/null +++ b/src/utils_test.go @@ -0,0 +1,72 @@ +package main + +import ( + "net/http/httptest" + "strings" + "testing" + + uuid "github.com/hashicorp/go-uuid" +) + +func Test_UUID(t *testing.T) { + for id := 1; id < 11; id++ { + recordUUID, err := uuid.GenerateUUID() + t.Logf("Checking[%d]: %s\n", id, recordUUID) + if err != nil { + t.Fatalf("Failed to generate UUID %s: %s ", recordUUID, err) + } else if isValidUUID(recordUUID) == false { + t.Fatalf("Failed to validate UUID: %s ", recordUUID) + } + } +} + +func Test_AppNames(t *testing.T) { + goodApps := []string{"penn", "teller", "a123"} + for _, value := range goodApps { + if isValidApp(value) == false { + t.Fatalf("Failed to validate good app name: %s ", value) + } + } + badApps := []string{"P1", "1as", "_a", "a_a", "a.a", "a a"} + for _, value := range badApps { + if isValidApp(value) == true { + t.Fatalf("Failed to validate bad app name: %s ", value) + } + } + +} + +func Test_getJSONPost(t *testing.T) { + goodJsons := []string{ + `{"login":"abc","name": "tom", "pass": "mylittlepony", "admin": true}`, + `{"login":"1234","name": "tom", "pass": "mylittlepony", "admin": true}`, + } + for _, value := range goodJsons { + request := httptest.NewRequest("POST", "/user", strings.NewReader(value)) + request.Header.Set("Content-Type", "application/json") + result, err := getJSONPost(request, "IL") + if err != nil { + t.Fatalf("Failed to parse json: %s, err: %s\n", value, err) + } + if len(result.loginIdx) == 0 { + t.Fatalf("Failed to parse login index from json: %s ", value) + } + } + + badJsons := []string{ + `{"login":true,"name": "tom", "pass": "mylittlepony", "admin": true}`, + `{"login":1,"name": "tom", "pass": "mylittlepony", "admin": true}`, + `{"login":null,"name": "tom", "pass": "mylittlepony", "admin": true}`, + } + for _, value := range badJsons { + request := httptest.NewRequest("POST", "/user", strings.NewReader(value)) + request.Header.Set("Content-Type", "application/json") + result, err := getJSONPost(request, "IL") + if err != nil { + t.Fatalf("Failed to parse json: %s, err: %s\n", value, err) + } + if len(result.loginIdx) != 0 { + t.Fatalf("Failed to parse login index from json: %s ", value) + } + } +} diff --git a/src/xtokens_api.go b/src/xtokens_api.go new file mode 100644 index 0000000..5853b28 --- /dev/null +++ b/src/xtokens_api.go @@ -0,0 +1,148 @@ +package main + +import ( + "encoding/json" + "fmt" + "net/http" + "reflect" + "strings" + + "github.com/julienschmidt/httprouter" + "github.com/tidwall/gjson" +) + +func (e mainEnv) userNewToken(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + userTOKEN := ps.ByName("token") + event := audit("create user temp access xtoken", userTOKEN) + defer func() { event.submit(e.db) }() + + if enforceUUID(w, userTOKEN, event) == false { + return + } + if e.enforceAuth(w, r, event) == false { + return + } + records, err := getJSONPostData(r) + if err != nil { + returnError(w, r, "internal error", 405, err, event) + return + } + fields := "" + expiration := "" + appName := "" + if value, ok := records["fields"]; ok { + if reflect.TypeOf(value) == reflect.TypeOf("string") { + fields = value.(string) + } + } + if value, ok := records["expiration"]; ok { + if reflect.TypeOf(value) == reflect.TypeOf("string") { + expiration = value.(string) + } else { + returnError(w, r, "failed to parse expiration field", 405, err, event) + return + } + } + if value, ok := records["app"]; ok { + if reflect.TypeOf(value) == reflect.TypeOf("string") { + appName = strings.ToLower(value.(string)) + if len(appName) > 0 && isValidApp(appName) == false { + returnError(w, r, "failed to parse app field", 405, nil, event) + } + } else { + // type is different + returnError(w, r, "failed to parse app field", 405, nil, event) + } + } + if len(expiration) == 0 { + returnError(w, r, "missing expiration field", 405, err, event) + return + } + xtokenUUID, err := e.db.generateUserTempXToken(userTOKEN, fields, expiration, appName) + if err != nil { + fmt.Println(err) + returnError(w, r, err.Error(), 405, err, event) + return + } + event.Msg = "Generated " + xtokenUUID + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(200) + fmt.Fprintf(w, `{"status":"ok","xtoken":%q}`, xtokenUUID) +} + +func (e mainEnv) userCheckToken(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + event := audit("get record by user temp access token", "") + defer func() { event.submit(e.db) }() + + xtoken := ps.ByName("xtoken") + if enforceUUID(w, xtoken, event) == false { + return + } + authResult, err := e.db.checkUserAuthXToken(xtoken) + if err != nil { + fmt.Printf("%d access denied for : %s\n", http.StatusForbidden, xtoken) + w.WriteHeader(http.StatusForbidden) + w.Write([]byte("Access denied")) + return + } + var resultJSON []byte + if len(authResult.token) > 0 { + event.Record = authResult.token + event.App = authResult.appName + fmt.Printf("displaying fields: %s, user token: %s\n", authResult.fields, authResult.token) + + if len(authResult.appName) > 0 { + resultJSON, err = e.db.getUserApp(authResult.token, authResult.appName) + } else { + resultJSON, err = e.db.getUser(authResult.token) + } + if err != nil { + returnError(w, r, "internal error", 405, err, event) + return + } + if resultJSON == nil { + returnError(w, r, "not found", 405, err, event) + return + } + fmt.Printf("Full user json: %s\n", resultJSON) + if len(authResult.fields) > 0 { + raw := make(map[string]interface{}) + //var newJSON json + allFields := parseFields(authResult.fields) + for _, f := range allFields { + if f == "token" { + raw["token"] = authResult.token + } else { + value := gjson.Get(string(resultJSON), f) + //fmt.Printf("result %s -> %s\n", f, value) + /* + var raw2 map[string]interface{} + err = json.Unmarshal([]byte(value.String()), &raw2) + if err != nil { + fmt.Printf("Err: %s\n", err) + } + */ + raw[f] = value.Value() + } + } + resultJSON, _ = json.Marshal(raw) + } + } + //fmt.Fprintf(w, "title") + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(200) + var str string + if len(resultJSON) == 0 { + str = fmt.Sprintf(`{"status":"ok","type":"%s"}`, authResult.ttype) + } else { + if len(authResult.appName) > 0 { + str = fmt.Sprintf(`{"status":"ok","type":"%s","app":"%s","data":%s}`, + authResult.ttype, authResult.appName, resultJSON) + } else { + str = fmt.Sprintf(`{"status":"ok","type":"%s","data":%s}`, + authResult.ttype, resultJSON) + } + } + fmt.Printf("result: %s\n", str) + w.Write([]byte(str)) +} diff --git a/src/xtokens_db.go b/src/xtokens_db.go new file mode 100644 index 0000000..ef2194d --- /dev/null +++ b/src/xtokens_db.go @@ -0,0 +1,174 @@ +package main + +import ( + "errors" + "fmt" + "strings" + "time" + + uuid "github.com/hashicorp/go-uuid" + "go.mongodb.org/mongo-driver/bson" +) + +func (dbobj dbcon) getRootToken() (string, error) { + record, err := dbobj.getRecord(TblName.Xtokens, "type", "root") + if err != nil { + return "", err + } + if record == nil { + return "", nil + } + return record["xtoken"].(string), nil +} + +func (dbobj dbcon) createRootToken() (string, error) { + rootToken, err := dbobj.getRootToken() + if len(rootToken) > 0 { + return rootToken, nil + } + rootToken, err = uuid.GenerateUUID() + if err != nil { + return "", err + } + bdoc := bson.M{} + bdoc["xtoken"] = rootToken + bdoc["type"] = "root" + _, err = dbobj.createRecord(TblName.Xtokens, bdoc) + if err != nil { + return rootToken, err + } + return rootToken, nil +} + +func (dbobj dbcon) generateUserTempXToken(userTOKEN string, fields string, expiration string, appName string) (string, error) { + if isValidUUID(userTOKEN) == false { + return "", errors.New("bad uuid") + } + if len(expiration) == 0 { + return "", errors.New("failed to parse expiration") + } + if len(appName) > 0 { + apps, _ := dbobj.listAllApps() + if strings.Contains(string(apps), appName) == false { + return "", errors.New("app not found") + } + } + + start, err := parseExpiration(expiration) + if err != nil { + return "", err + } + + // check if user record exists + record, err := dbobj.lookupUserRecord(userTOKEN) + if record == nil || err != nil { + // not found + return "", errors.New("not found") + } + + tokenUUID, err := uuid.GenerateUUID() + if err != nil { + return "", err + } + bdoc := bson.M{} + bdoc["token"] = userTOKEN + bdoc["xtoken"] = tokenUUID + bdoc["type"] = "temp" + bdoc["fields"] = fields + bdoc["endtime"] = start + if len(appName) > 0 { + bdoc["app"] = appName + } + _, err = dbobj.createRecord(TblName.Xtokens, bdoc) + if err != nil { + return "", err + } + return tokenUUID, nil +} + +func (dbobj dbcon) generateUserLoginXToken(userTOKEN string) (string, error) { + if isValidUUID(userTOKEN) == false { + return "", errors.New("bad token format") + } + + // check if user record exists + record, err := dbobj.lookupUserRecord(userTOKEN) + if record == nil || err != nil { + // not found + return "", errors.New("not found") + } + + tokenUUID, err := uuid.GenerateUUID() + if err != nil { + return "", err + } + // by default login token for 30 minutes only + expired := int32(time.Now().Unix()) + 10*60 + bdoc := bson.M{} + bdoc["token"] = userTOKEN + bdoc["xtoken"] = tokenUUID + bdoc["type"] = "login" + bdoc["endtime"] = expired + _, err = dbobj.createRecord(TblName.Xtokens, bdoc) + if err != nil { + return "", err + } + return tokenUUID, nil +} + +func (dbobj dbcon) checkToken(tokenUUID string) bool { + //fmt.Printf("Token0 %s\n", tokenUUID) + if isValidUUID(tokenUUID) == false { + return false + } + record, err := dbobj.getRecord(TblName.Xtokens, "xtoken", tokenUUID) + if record == nil || err != nil { + return false + } + tokenType := record["type"].(string) + if tokenType == "root" { + return true + } + return false +} + +func (dbobj dbcon) checkUserAuthXToken(xtokenUUID string) (tokenAuthResult, error) { + var result tokenAuthResult + if isValidUUID(xtokenUUID) == false { + return result, errors.New("failed to authenticate") + } + record, err := dbobj.getRecord(TblName.Xtokens, "xtoken", xtokenUUID) + if record == nil || err != nil { + return result, errors.New("failed to authenticate") + } + tokenType := record["type"].(string) + fmt.Printf("token type: %s\n", tokenType) + if tokenType == "root" { + // we have this admin user + result.ttype = "root" + result.name = "root" + return result, nil + } + result.name = xtokenUUID + // tokenType = temp + now := int32(time.Now().Unix()) + if now > record["endtime"].(int32) { + return result, errors.New("token expired") + } + result.token = record["token"].(string) + if value, ok := record["fields"]; ok { + result.fields = value.(string) + } + if tokenType == "login" { + result.ttype = "login" + } else { + if value, ok := record["app"]; ok { + result.ttype = "app" + result.appName = value.(string) + } else { + result.ttype = "user" + } + } + + return result, nil +} diff --git a/ui/index.html b/ui/index.html new file mode 100644 index 0000000..39834d0 --- /dev/null +++ b/ui/index.html @@ -0,0 +1,218 @@ + + + + + + + Data Bunker Login + + + + + + +
+
+
+

Login form

+

Select login method and enter login details:

+
+
+ +
+ + + + +
+ +
+
+
+
+ +
+
+
+
+
+
+
+
+ + \ No newline at end of file diff --git a/ui/site/display-data.html b/ui/site/display-data.html new file mode 100644 index 0000000..e426076 --- /dev/null +++ b/ui/site/display-data.html @@ -0,0 +1,57 @@ + + + + + + + Data Bunker Login + + + + + + + + + + + + + + + + +
+
+
+

Record display

+

text

+

+            
+
+
+ + \ No newline at end of file diff --git a/ui/site/enter.html b/ui/site/enter.html new file mode 100644 index 0000000..7c42412 --- /dev/null +++ b/ui/site/enter.html @@ -0,0 +1,82 @@ + + + + + + + Data Bunker Login + + + + + + + + + + + + +
+
+
+

Verification step

+

Enter the code you received by email or SMS

+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+ + \ No newline at end of file diff --git a/ui/site/index.html b/ui/site/index.html new file mode 100644 index 0000000..e2100b3 --- /dev/null +++ b/ui/site/index.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/ui/site/jsoneditor.min.css b/ui/site/jsoneditor.min.css new file mode 100644 index 0000000..180437b --- /dev/null +++ b/ui/site/jsoneditor.min.css @@ -0,0 +1,6 @@ +.jsoneditor .search input{height:auto;border:inherit;border:none;box-shadow:none}.jsoneditor table{border-collapse:collapse;width:auto}.jsoneditor td,.jsoneditor th{padding:0;display:table-cell;text-align:left;vertical-align:inherit;border-radius:inherit}.jsoneditor{color:#1a1a1a;border:thin solid #3883fa;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;width:100%;height:100%;position:relative;padding:0;line-height:100%}div.jsoneditor-default,div.jsoneditor-field,div.jsoneditor-readonly,div.jsoneditor-value{border:1px solid transparent;min-height:16px;min-width:32px;padding:2px;margin:1px;word-wrap:break-word;float:left}div.jsoneditor-field p,div.jsoneditor-value p{margin:0}div.jsoneditor-value{word-break:break-word}div.jsoneditor-value.jsoneditor-empty::after{content:"value"}div.jsoneditor-value.jsoneditor-string{color:#006000}div.jsoneditor-value.jsoneditor-number{color:#ee422e}div.jsoneditor-value.jsoneditor-boolean{color:#ff8c00}div.jsoneditor-value.jsoneditor-null{color:#004ed0}div.jsoneditor-value.jsoneditor-invalid{color:#000}div.jsoneditor-readonly{min-width:16px;color:grey}div.jsoneditor-empty{border-color:#d3d3d3;border-style:dashed;border-radius:2px}div.jsoneditor-field.jsoneditor-empty::after{content:"field"}div.jsoneditor td{vertical-align:top}div.jsoneditor td.jsoneditor-separator{padding:3px 0;vertical-align:top;color:grey}div.jsoneditor td.jsoneditor-tree{vertical-align:top}div.jsoneditor div.jsoneditor-anchor{cursor:pointer}div.jsoneditor div.jsoneditor-anchor .picker_wrapper.popup.popup_bottom{top:28px;left:-10px}div.jsoneditor.busy pre.jsoneditor-preview{background:#f5f5f5;color:grey}div.jsoneditor.busy div.jsoneditor-busy{display:inherit}div.jsoneditor code.jsoneditor-preview{background:0 0}div.jsoneditor.jsoneditor-mode-preview pre.jsoneditor-preview{width:100%;height:100%;box-sizing:border-box;overflow:auto;padding:2px;margin:0;white-space:pre-wrap;word-break:break-all}div.jsoneditor-default{color:grey;padding-left:10px}div.jsoneditor-tree{width:100%;height:100%;position:relative;overflow:auto}div.jsoneditor-tree button.jsoneditor-button{width:24px;height:24px;padding:0;margin:0;border:none;cursor:pointer;background:transparent url(img/jsoneditor-icons.svg)}div.jsoneditor-tree button.jsoneditor-button:focus{background-color:#f5f5f5;outline:#e5e5e5 solid 1px}div.jsoneditor-tree button.jsoneditor-collapsed{background-position:0 -48px}div.jsoneditor-tree button.jsoneditor-expanded{background-position:0 -72px}div.jsoneditor-tree button.jsoneditor-contextmenu-button{background-position:-48px -72px}div.jsoneditor-tree button.jsoneditor-invisible{visibility:hidden;background:0 0}div.jsoneditor-tree button.jsoneditor-dragarea{background:url(img/jsoneditor-icons.svg) -72px -72px;cursor:move}div.jsoneditor-tree :focus{outline:0}div.jsoneditor-tree div.jsoneditor-show-more{display:inline-block;padding:3px 4px;margin:2px 0;background-color:#e5e5e5;border-radius:3px;color:grey;font-family:arial,sans-serif;font-size:10pt}div.jsoneditor-tree div.jsoneditor-show-more a{display:inline-block;color:grey}div.jsoneditor-tree div.jsoneditor-color{display:inline-block;width:12px;height:12px;margin:4px;border:1px solid grey;cursor:pointer}div.jsoneditor-tree div.jsoneditor-date{background:#a1a1a1;color:#fff;font-family:arial,sans-serif;border-radius:3px;display:inline-block;padding:3px;margin:0 3px}div.jsoneditor-tree table.jsoneditor-tree{border-collapse:collapse;border-spacing:0;width:100%}div.jsoneditor-tree .jsoneditor-button.jsoneditor-schema-error{width:24px;height:24px;padding:0;margin:0 4px 0 0;background:url(img/jsoneditor-icons.svg) -168px -48px}div.jsoneditor-outer{position:static;width:100%;height:100%;margin:0;padding:0;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}div.jsoneditor-outer.has-nav-bar{margin-top:-26px;padding-top:26px}div.jsoneditor-outer.has-nav-bar.has-main-menu-bar{margin-top:-61px;padding-top:61px}div.jsoneditor-outer.has-status-bar{margin-bottom:-26px;padding-bottom:26px}div.jsoneditor-outer.has-main-menu-bar{margin-top:-35px;padding-top:35px}div.jsoneditor-busy{position:absolute;top:15%;left:0;box-sizing:border-box;width:100%;text-align:center;display:none}div.jsoneditor-busy span{background-color:#ffffab;border:1px solid #fe0;border-radius:3px;padding:5px 15px;box-shadow:0 0 5px rgba(0,0,0,.4)}div.jsoneditor-field.jsoneditor-empty::after,div.jsoneditor-value.jsoneditor-empty::after{pointer-events:none;color:#d3d3d3;font-size:8pt}a.jsoneditor-value.jsoneditor-url,div.jsoneditor-value.jsoneditor-url{color:#006000;text-decoration:underline}a.jsoneditor-value.jsoneditor-url{display:inline-block;padding:2px;margin:2px}a.jsoneditor-value.jsoneditor-url:focus,a.jsoneditor-value.jsoneditor-url:hover{color:#ee422e}div.jsoneditor-field.jsoneditor-highlight,div.jsoneditor-field[contenteditable=true]:focus,div.jsoneditor-field[contenteditable=true]:hover,div.jsoneditor-value.jsoneditor-highlight,div.jsoneditor-value[contenteditable=true]:focus,div.jsoneditor-value[contenteditable=true]:hover{background-color:#ffffab;border:1px solid #fe0;border-radius:2px}div.jsoneditor-field.jsoneditor-highlight-active,div.jsoneditor-field.jsoneditor-highlight-active:focus,div.jsoneditor-field.jsoneditor-highlight-active:hover,div.jsoneditor-value.jsoneditor-highlight-active,div.jsoneditor-value.jsoneditor-highlight-active:focus,div.jsoneditor-value.jsoneditor-highlight-active:hover{background-color:#fe0;border:1px solid #ffc700;border-radius:2px}div.jsoneditor-value.jsoneditor-array,div.jsoneditor-value.jsoneditor-object{min-width:16px}div.jsoneditor-tree button.jsoneditor-contextmenu-button.jsoneditor-selected,div.jsoneditor-tree button.jsoneditor-contextmenu-button:focus,div.jsoneditor-tree button.jsoneditor-contextmenu-button:hover,tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-contextmenu-button{background-position:-48px -48px}div.jsoneditor-tree div.jsoneditor-show-more a:focus,div.jsoneditor-tree div.jsoneditor-show-more a:hover{color:#ee422e}.ace-jsoneditor,textarea.jsoneditor-text{min-height:150px}textarea.jsoneditor-text{width:100%;height:100%;margin:0;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;outline-width:0;border:none;background-color:#fff;resize:none}tr.jsoneditor-highlight,tr.jsoneditor-selected{background-color:#d3d3d3}tr.jsoneditor-selected button.jsoneditor-contextmenu-button,tr.jsoneditor-selected button.jsoneditor-dragarea{visibility:hidden}tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-contextmenu-button,tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-dragarea{visibility:visible}div.jsoneditor-tree button.jsoneditor-dragarea:focus,div.jsoneditor-tree button.jsoneditor-dragarea:hover,tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-dragarea{background-position:-72px -48px}div.jsoneditor td,div.jsoneditor th,div.jsoneditor tr{padding:0;margin:0}.jsoneditor-popover,.jsoneditor-schema-error,div.jsoneditor td,div.jsoneditor textarea,div.jsoneditor th,div.jsoneditor-field,div.jsoneditor-value,pre.jsoneditor-preview{font-family:"dejavu sans mono","droid sans mono",consolas,monaco,"lucida console","courier new",courier,monospace,sans-serif;font-size:10pt;color:#1a1a1a}.jsoneditor-schema-error{cursor:default;display:inline-block;height:24px;line-height:24px;position:relative;text-align:center;width:24px}.jsoneditor-popover{background-color:#4c4c4c;border-radius:3px;box-shadow:0 0 5px rgba(0,0,0,.4);color:#fff;padding:7px 10px;position:absolute;cursor:auto;width:200px}.jsoneditor-popover.jsoneditor-above{bottom:32px;left:-98px}.jsoneditor-popover.jsoneditor-above:before{border-top:7px solid #4c4c4c;bottom:-7px}.jsoneditor-popover.jsoneditor-below{top:32px;left:-98px}.jsoneditor-popover.jsoneditor-below:before{border-bottom:7px solid #4c4c4c;top:-7px}.jsoneditor-popover.jsoneditor-left{top:-7px;right:32px}.jsoneditor-popover.jsoneditor-left:before{border-left:7px solid #4c4c4c;border-top:7px solid transparent;border-bottom:7px solid transparent;content:"";top:19px;right:-14px;left:inherit;margin-left:inherit;margin-top:-7px;position:absolute}.jsoneditor-popover.jsoneditor-right{top:-7px;left:32px}.jsoneditor-popover.jsoneditor-right:before{border-right:7px solid #4c4c4c;border-top:7px solid transparent;border-bottom:7px solid transparent;content:"";top:19px;left:-14px;margin-left:inherit;margin-top:-7px;position:absolute}.jsoneditor-popover:before{border-right:7px solid transparent;border-left:7px solid transparent;content:"";display:block;left:50%;margin-left:-7px;position:absolute}.jsoneditor-text-errors tr.jump-to-line:hover{text-decoration:underline;cursor:pointer}.jsoneditor-schema-error:focus .jsoneditor-popover,.jsoneditor-schema-error:hover .jsoneditor-popover{display:block;animation:fade-in .3s linear 1,move-up .3s linear 1}@keyframes fade-in{from{opacity:0}to{opacity:1}}.jsoneditor .jsoneditor-validation-errors-container{max-height:130px;overflow-y:auto}.jsoneditor .jsoneditor-validation-errors{width:100%;overflow:hidden}.jsoneditor .jsoneditor-additional-errors{position:absolute;margin:auto;bottom:31px;left:calc(50% - 92px);color:grey;background-color:#ebebeb;padding:7px 15px;border-radius:8px}.jsoneditor .jsoneditor-additional-errors.visible{visibility:visible;opacity:1;transition:opacity 2s linear}.jsoneditor .jsoneditor-additional-errors.hidden{visibility:hidden;opacity:0;transition:visibility 0s 2s,opacity 2s linear}.jsoneditor .jsoneditor-text-errors{width:100%;border-collapse:collapse;border-top:1px solid #ffc700}.jsoneditor .jsoneditor-text-errors td{padding:3px 6px;vertical-align:middle}.jsoneditor .jsoneditor-text-errors td pre{margin:0;white-space:normal}.jsoneditor .jsoneditor-text-errors tr{background-color:#ffffab}.jsoneditor .jsoneditor-text-errors tr.parse-error{background-color:#ee2e2e70}.jsoneditor-text-errors .jsoneditor-schema-error{border:none;width:24px;height:24px;padding:0;margin:0 4px 0 0;cursor:pointer}.jsoneditor-text-errors tr .jsoneditor-schema-error{background:url(img/jsoneditor-icons.svg) -168px -48px}.jsoneditor-text-errors tr.parse-error .jsoneditor-schema-error{background:url(img/jsoneditor-icons.svg) -25px 0}.fadein{-webkit-animation:fadein .3s;animation:fadein .3s;-moz-animation:fadein .3s;-o-animation:fadein .3s}@keyframes fadein{0%{opacity:0}100%{opacity:1}}.jsoneditor-contextmenu-root{position:relative;width:0;height:0}.jsoneditor-contextmenu{position:absolute;box-sizing:content-box;z-index:1}.jsoneditor-contextmenu .jsoneditor-menu{position:relative;left:0;top:0;width:128px;height:auto;background:#fff;border:1px solid #d3d3d3;box-shadow:2px 2px 12px rgba(128,128,128,.3);list-style:none;margin:0;padding:0}.jsoneditor-contextmenu .jsoneditor-menu button{position:relative;padding:0 4px 0 0;margin:0;width:128px;height:auto;border:none;cursor:pointer;color:#4d4d4d;background:0 0;font-size:10pt;font-family:arial,sans-serif;box-sizing:border-box;text-align:left}.jsoneditor-contextmenu .jsoneditor-menu button::-moz-focus-inner{padding:0;border:0}.jsoneditor-contextmenu .jsoneditor-menu button.jsoneditor-default{width:96px}.jsoneditor-contextmenu .jsoneditor-menu button.jsoneditor-expand{float:right;width:32px;height:24px;border-left:1px solid #e5e5e5}.jsoneditor-contextmenu .jsoneditor-menu li{overflow:hidden}.jsoneditor-contextmenu .jsoneditor-menu li ul{display:none;position:relative;left:-10px;top:0;border:none;box-shadow:inset 0 0 10px rgba(128,128,128,.5);padding:0 10px;-webkit-transition:all .3s ease-out;-moz-transition:all .3s ease-out;-o-transition:all .3s ease-out;transition:all .3s ease-out}.jsoneditor-contextmenu .jsoneditor-menu li ul .jsoneditor-icon{margin-left:24px}.jsoneditor-contextmenu .jsoneditor-menu li ul li button{padding-left:24px;animation:all ease-in-out 1s}.jsoneditor-contextmenu .jsoneditor-menu li button .jsoneditor-expand{position:absolute;top:0;right:0;width:24px;height:24px;padding:0;margin:0 4px 0 0;background:url(img/jsoneditor-icons.svg) 0 -72px}.jsoneditor-contextmenu .jsoneditor-icon{position:absolute;top:0;left:0;width:24px;height:24px;border:none;padding:0;margin:0;background-image:url(img/jsoneditor-icons.svg)}.jsoneditor-contextmenu .jsoneditor-text{padding:4px 0 4px 24px;word-wrap:break-word}.jsoneditor-contextmenu .jsoneditor-text.jsoneditor-right-margin{padding-right:24px}.jsoneditor-contextmenu .jsoneditor-separator{height:0;border-top:1px solid #e5e5e5;padding-top:5px;margin-top:5px}.jsoneditor-contextmenu button.jsoneditor-remove .jsoneditor-icon{background-position:-24px 0}.jsoneditor-contextmenu button.jsoneditor-append .jsoneditor-icon{background-position:0 0}.jsoneditor-contextmenu button.jsoneditor-insert .jsoneditor-icon{background-position:0 0}.jsoneditor-contextmenu button.jsoneditor-duplicate .jsoneditor-icon{background-position:-48px 0}.jsoneditor-contextmenu button.jsoneditor-sort-asc .jsoneditor-icon{background-position:-168px 0}.jsoneditor-contextmenu button.jsoneditor-sort-desc .jsoneditor-icon{background-position:-192px 0}.jsoneditor-contextmenu button.jsoneditor-transform .jsoneditor-icon{background-position:-216px 0}.jsoneditor-contextmenu button.jsoneditor-extract .jsoneditor-icon{background-position:0 -24px}.jsoneditor-contextmenu button.jsoneditor-type-string .jsoneditor-icon{background-position:-144px 0}.jsoneditor-contextmenu button.jsoneditor-type-auto .jsoneditor-icon{background-position:-120px 0}.jsoneditor-contextmenu button.jsoneditor-type-object .jsoneditor-icon{background-position:-72px 0}.jsoneditor-contextmenu button.jsoneditor-type-array .jsoneditor-icon{background-position:-96px 0}.jsoneditor-contextmenu button.jsoneditor-type-modes .jsoneditor-icon{background-image:none;width:6px}.jsoneditor-contextmenu li,.jsoneditor-contextmenu ul{box-sizing:content-box;position:relative}.jsoneditor-contextmenu .jsoneditor-menu button:focus,.jsoneditor-contextmenu .jsoneditor-menu button:hover{color:#1a1a1a;background-color:#f5f5f5;outline:0}.jsoneditor-contextmenu .jsoneditor-menu li button.jsoneditor-selected,.jsoneditor-contextmenu .jsoneditor-menu li button.jsoneditor-selected:focus,.jsoneditor-contextmenu .jsoneditor-menu li button.jsoneditor-selected:hover{color:#fff;background-color:#ee422e}.jsoneditor-contextmenu .jsoneditor-menu li ul li button:focus,.jsoneditor-contextmenu .jsoneditor-menu li ul li button:hover{background-color:#f5f5f5}.jsoneditor-modal{max-width:95%;border-radius:2px!important;padding:45px 15px 15px 15px!important;box-shadow:2px 2px 12px rgba(128,128,128,.3);color:#4d4d4d;line-height:1.3em}.jsoneditor-modal.jsoneditor-modal-transform{width:600px!important}.jsoneditor-modal .pico-modal-header{position:absolute;box-sizing:border-box;top:0;left:0;width:100%;padding:0 10px;height:30px;line-height:30px;font-family:arial,sans-serif;font-size:11pt;background:#3883fa;color:#fff}.jsoneditor-modal table{width:100%}.jsoneditor-modal table td{padding:3px 0}.jsoneditor-modal table td.jsoneditor-modal-input{text-align:right;padding-right:0;white-space:nowrap}.jsoneditor-modal table td.jsoneditor-modal-actions{padding-top:15px}.jsoneditor-modal table th{vertical-align:middle}.jsoneditor-modal p:first-child{margin-top:0}.jsoneditor-modal a{color:#3883fa}.jsoneditor-modal .jsoneditor-jmespath-block{margin-bottom:10px}.jsoneditor-modal .pico-close{background:0 0!important;font-size:24px!important;top:7px!important;right:7px!important;color:#fff}.jsoneditor-modal input{padding:4px}.jsoneditor-modal input[type=text]{cursor:inherit}.jsoneditor-modal input[disabled]{background:#d3d3d3;color:grey}.jsoneditor-modal .jsoneditor-select-wrapper{position:relative;display:inline-block}.jsoneditor-modal .jsoneditor-select-wrapper:after{content:"";width:0;height:0;border-left:5px solid transparent;border-right:5px solid transparent;border-top:6px solid #666;position:absolute;right:8px;top:14px;pointer-events:none}.jsoneditor-modal select{padding:3px 24px 3px 10px;min-width:180px;max-width:350px;-webkit-appearance:none;-moz-appearance:none;appearance:none;text-indent:0;text-overflow:"";font-size:10pt;line-height:1.5em}.jsoneditor-modal select::-ms-expand{display:none}.jsoneditor-modal .jsoneditor-button-group input{padding:4px 10px;margin:0;border-radius:0;border-left-style:none}.jsoneditor-modal .jsoneditor-button-group input.jsoneditor-button-first{border-top-left-radius:3px;border-bottom-left-radius:3px;border-left-style:solid}.jsoneditor-modal .jsoneditor-button-group input.jsoneditor-button-last{border-top-right-radius:3px;border-bottom-right-radius:3px}.jsoneditor-modal .jsoneditor-transform-preview{background:#f5f5f5;height:200px}.jsoneditor-modal .jsoneditor-transform-preview.jsoneditor-error{color:#ee422e}.jsoneditor-modal .jsoneditor-jmespath-wizard{line-height:1.2em;width:100%;padding:0;border-radius:3px}.jsoneditor-modal .jsoneditor-jmespath-label{font-weight:700;color:#1e90ff;margin-top:20px;margin-bottom:5px}.jsoneditor-modal .jsoneditor-jmespath-wizard-table{width:100%;border-collapse:collapse}.jsoneditor-modal .jsoneditor-jmespath-wizard-label{font-style:italic;margin:4px 0 2px 0}.jsoneditor-modal .jsoneditor-inline{position:relative;display:inline-block;width:100%;padding-top:2px;padding-bottom:2px}.jsoneditor-modal .jsoneditor-inline:not(:last-child){padding-right:2px}.jsoneditor-modal .jsoneditor-jmespath-filter{display:flex;flex-wrap:wrap}.jsoneditor-modal .jsoneditor-jmespath-filter-field{width:180px}.jsoneditor-modal .jsoneditor-jmespath-filter-relation{width:100px}.jsoneditor-modal .jsoneditor-jmespath-filter-value{min-width:180px;flex:1}.jsoneditor-modal .jsoneditor-jmespath-sort-field{width:170px}.jsoneditor-modal .jsoneditor-jmespath-sort-order{width:150px}.jsoneditor-modal .jsoneditor-jmespath-select-fields{width:100%}.jsoneditor-modal .selectr-selected{border-color:#d3d3d3;padding:4px 28px 4px 8px}.jsoneditor-modal .selectr-selected .selectr-tag{background-color:#3883fa;border-radius:5px}.jsoneditor-modal table td,.jsoneditor-modal table th{text-align:left;vertical-align:middle;font-weight:400;color:#4d4d4d;border-spacing:0;border-collapse:collapse}.jsoneditor-modal #query,.jsoneditor-modal input,.jsoneditor-modal select,.jsoneditor-modal textarea{background:#fff;border:1px solid #d3d3d3;color:#4d4d4d;border-radius:3px;padding:4px}.jsoneditor-modal,.jsoneditor-modal #query,.jsoneditor-modal input,.jsoneditor-modal option,.jsoneditor-modal select,.jsoneditor-modal table td,.jsoneditor-modal table th,.jsoneditor-modal textarea{font-size:10.5pt;font-family:arial,sans-serif}.jsoneditor-modal #query,.jsoneditor-modal .jsoneditor-transform-preview{font-family:"dejavu sans mono","droid sans mono",consolas,monaco,"lucida console","courier new",courier,monospace,sans-serif;font-size:10pt;width:100%;box-sizing:border-box}.jsoneditor-modal input[type=button],.jsoneditor-modal input[type=submit]{background:#f5f5f5;padding:4px 20px}.jsoneditor-modal input,.jsoneditor-modal select{cursor:pointer}.jsoneditor-modal .jsoneditor-button-group.jsoneditor-button-group-value-asc input.jsoneditor-button-asc,.jsoneditor-modal .jsoneditor-button-group.jsoneditor-button-group-value-desc input.jsoneditor-button-desc{background:#3883fa;border-color:#3883fa;color:#fff}.jsoneditor-menu{width:100%;height:35px;padding:2px;margin:0;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;color:#fff;background-color:#3883fa;border-bottom:1px solid #3883fa}.jsoneditor-menu>.jsoneditor-modes>button,.jsoneditor-menu>button{width:26px;height:26px;margin:2px;padding:0;border-radius:2px;border:1px solid transparent;background:transparent url(img/jsoneditor-icons.svg);color:#fff;opacity:.8;font-family:arial,sans-serif;font-size:10pt;float:left}.jsoneditor-menu>.jsoneditor-modes>button:hover,.jsoneditor-menu>button:hover{background-color:rgba(255,255,255,.2);border:1px solid rgba(255,255,255,.4)}.jsoneditor-menu>.jsoneditor-modes>button:active,.jsoneditor-menu>.jsoneditor-modes>button:focus,.jsoneditor-menu>button:active,.jsoneditor-menu>button:focus{background-color:rgba(255,255,255,.3)}.jsoneditor-menu>.jsoneditor-modes>button:disabled,.jsoneditor-menu>button:disabled{opacity:.5;background-color:transparent;border:none}.jsoneditor-menu>button.jsoneditor-collapse-all{background-position:0 -96px}.jsoneditor-menu>button.jsoneditor-expand-all{background-position:0 -120px}.jsoneditor-menu>button.jsoneditor-sort{background-position:-120px -96px}.jsoneditor-menu>button.jsoneditor-transform{background-position:-144px -96px}.jsoneditor.jsoneditor-mode-form>.jsoneditor-menu>button.jsoneditor-sort,.jsoneditor.jsoneditor-mode-form>.jsoneditor-menu>button.jsoneditor-transform,.jsoneditor.jsoneditor-mode-view>.jsoneditor-menu>button.jsoneditor-sort,.jsoneditor.jsoneditor-mode-view>.jsoneditor-menu>button.jsoneditor-transform{display:none}.jsoneditor-menu>button.jsoneditor-undo{background-position:-24px -96px}.jsoneditor-menu>button.jsoneditor-undo:disabled{background-position:-24px -120px}.jsoneditor-menu>button.jsoneditor-redo{background-position:-48px -96px}.jsoneditor-menu>button.jsoneditor-redo:disabled{background-position:-48px -120px}.jsoneditor-menu>button.jsoneditor-compact{background-position:-72px -96px}.jsoneditor-menu>button.jsoneditor-format{background-position:-72px -120px}.jsoneditor-menu>button.jsoneditor-repair{background-position:-96px -96px}.jsoneditor-menu>.jsoneditor-modes{display:inline-block;float:left}.jsoneditor-menu>.jsoneditor-modes>button{background-image:none;width:auto;padding-left:6px;padding-right:6px}.jsoneditor-menu>.jsoneditor-modes>button.jsoneditor-separator,.jsoneditor-menu>button.jsoneditor-separator{margin-left:10px}.jsoneditor-menu a{font-family:arial,sans-serif;font-size:10pt;color:#fff;opacity:.8;vertical-align:middle}.jsoneditor-menu a:hover{opacity:1}.jsoneditor-menu a.jsoneditor-poweredBy{font-size:8pt;position:absolute;right:0;top:0;padding:10px}.jsoneditor-search{font-family:arial,sans-serif;position:absolute;right:4px;top:4px;border-collapse:collapse;border-spacing:0;display:flex}.jsoneditor-search input{color:#1a1a1a;width:120px;border:none;outline:0;margin:1px;line-height:20px}.jsoneditor-search button{width:16px;height:24px;padding:0;margin:0;border:none;background:url(img/jsoneditor-icons.svg);vertical-align:top}.jsoneditor-search button:hover{background-color:transparent}.jsoneditor-search button.jsoneditor-refresh{width:18px;background-position:-99px -73px}.jsoneditor-search button.jsoneditor-next{cursor:pointer;background-position:-124px -73px}.jsoneditor-search button.jsoneditor-next:hover{background-position:-124px -49px}.jsoneditor-search button.jsoneditor-previous{cursor:pointer;background-position:-148px -73px;margin-right:2px}.jsoneditor-search button.jsoneditor-previous:hover{background-position:-148px -49px}.jsoneditor-results{font-family:arial,sans-serif;color:#fff;padding-right:5px;line-height:26px}.jsoneditor-frame{border:1px solid transparent;background-color:#fff;padding:0 2px;margin:0}.jsoneditor .autocomplete.dropdown{position:absolute;background:#fff;box-shadow:2px 2px 12px rgba(128,128,128,.3);border:1px solid #d3d3d3;overflow-x:hidden;overflow-y:auto;cursor:default;margin:0;padding:5px;text-align:left;outline:0;font-family:"dejavu sans mono","droid sans mono",consolas,monaco,"lucida console","courier new",courier,monospace,sans-serif;font-size:10pt}.jsoneditor .autocomplete.dropdown .item{color:#333}.jsoneditor .autocomplete.dropdown .item.hover{background-color:#ddd}.jsoneditor .autocomplete.hint{color:#aaa;top:4px;left:4px}.jsoneditor-treepath{padding:0 5px;overflow:hidden;white-space:nowrap;outline:0}.jsoneditor-treepath.show-all{word-wrap:break-word;white-space:normal;position:absolute;background-color:#ebebeb;z-index:1;box-shadow:2px 2px 12px rgba(128,128,128,.3)}.jsoneditor-treepath.show-all span.jsoneditor-treepath-show-all-btn{display:none}.jsoneditor-treepath div.jsoneditor-contextmenu-root{position:absolute;left:0}.jsoneditor-treepath .jsoneditor-treepath-show-all-btn{position:absolute;background-color:#ebebeb;left:0;height:20px;padding:0 3px;cursor:pointer}.jsoneditor-treepath .jsoneditor-treepath-element{margin:1px;font-family:arial,sans-serif;font-size:10pt}.jsoneditor-treepath .jsoneditor-treepath-seperator{margin:2px;font-size:9pt;font-family:arial,sans-serif}.jsoneditor-treepath span.jsoneditor-treepath-element:hover,.jsoneditor-treepath span.jsoneditor-treepath-seperator:hover{cursor:pointer;text-decoration:underline}.jsoneditor-statusbar{line-height:26px;height:26px;color:grey;background-color:#ebebeb;border-top:1px solid #d3d3d3;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;font-size:10pt}.jsoneditor-statusbar>.jsoneditor-curserinfo-val{margin-right:12px}.jsoneditor-statusbar>.jsoneditor-curserinfo-count{margin-left:4px}.jsoneditor-statusbar>.jsoneditor-validation-error-icon{float:right;width:24px;height:24px;padding:0;margin-top:1px;background:url(img/jsoneditor-icons.svg) -168px -48px;cursor:pointer}.jsoneditor-statusbar>.jsoneditor-validation-error-count{float:right;margin:0 4px 0 0;cursor:pointer}.jsoneditor-statusbar>.jsoneditor-parse-error-icon{float:right;width:24px;height:24px;padding:0;margin:1px;background:url(img/jsoneditor-icons.svg) -25px 0}.jsoneditor-statusbar .jsoneditor-array-info a{color:inherit}div.jsoneditor-statusbar>.jsoneditor-curserinfo-label,div.jsoneditor-statusbar>.jsoneditor-size-info{margin:0 4px}.jsoneditor-navigation-bar{width:100%;height:26px;line-height:26px;padding:0;margin:0;border-bottom:1px solid #d3d3d3;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;color:grey;background-color:#ebebeb;overflow:hidden;font-family:arial,sans-serif;font-size:10pt}/*! + * Selectr 2.4.0 + * https://github.com/Mobius1/Selectr + * + * Released under the MIT license + */.selectr-container{position:relative}.selectr-container li{list-style:none}.selectr-hidden{position:absolute;overflow:hidden;clip:rect(0,0,0,0);width:1px;height:1px;margin:-1px;padding:0;border:0 none}.selectr-visible{position:absolute;left:0;top:0;width:100%;height:100%;opacity:0;z-index:11}.selectr-desktop.multiple .selectr-visible{display:none}.selectr-desktop.multiple.native-open .selectr-visible{top:100%;min-height:200px!important;height:auto;opacity:1;display:block}.selectr-container.multiple.selectr-mobile .selectr-selected{z-index:0}.selectr-selected{position:relative;z-index:1;box-sizing:border-box;width:100%;padding:7px 28px 7px 14px;cursor:pointer;border:1px solid #999;border-radius:3px;background-color:#fff}.selectr-selected::before{position:absolute;top:50%;right:10px;width:0;height:0;content:'';-o-transform:rotate(0) translate3d(0,-50%,0);-ms-transform:rotate(0) translate3d(0,-50%,0);-moz-transform:rotate(0) translate3d(0,-50%,0);-webkit-transform:rotate(0) translate3d(0,-50%,0);transform:rotate(0) translate3d(0,-50%,0);border-width:4px 4px 0 4px;border-style:solid;border-color:#6c7a86 transparent transparent}.selectr-container.native-open .selectr-selected::before,.selectr-container.open .selectr-selected::before{border-width:0 4px 4px 4px;border-style:solid;border-color:transparent transparent #6c7a86}.selectr-label{display:none;overflow:hidden;width:100%;white-space:nowrap;text-overflow:ellipsis}.selectr-placeholder{color:#6c7a86}.selectr-tags{margin:0;padding:0;white-space:normal}.has-selected .selectr-tags{margin:0 0 -2px}.selectr-tag{list-style:none;position:relative;float:left;padding:2px 25px 2px 8px;margin:0 2px 2px 0;cursor:default;color:#fff;border:medium none;border-radius:10px;background:#acb7bf none repeat scroll 0 0}.selectr-container.multiple.has-selected .selectr-selected{padding:5px 28px 5px 5px}.selectr-options-container{position:absolute;z-index:10000;top:calc(100% - 1px);left:0;display:none;box-sizing:border-box;width:100%;border-width:0 1px 1px;border-style:solid;border-color:transparent #999 #999;border-radius:0 0 3px 3px;background-color:#fff}.selectr-container.open .selectr-options-container{display:block}.selectr-input-container{position:relative;display:none}.selectr-clear,.selectr-input-clear,.selectr-tag-remove{position:absolute;top:50%;right:22px;width:20px;height:20px;padding:0;cursor:pointer;-o-transform:translate3d(0,-50%,0);-ms-transform:translate3d(0,-50%,0);-moz-transform:translate3d(0,-50%,0);-webkit-transform:translate3d(0,-50%,0);transform:translate3d(0,-50%,0);border:medium none;background-color:transparent;z-index:11}.selectr-clear,.selectr-input-clear{display:none}.selectr-container.has-selected .selectr-clear,.selectr-input-container.active .selectr-input-clear{display:block}.selectr-selected .selectr-tag-remove{right:2px}.selectr-clear::after,.selectr-clear::before,.selectr-input-clear::after,.selectr-input-clear::before,.selectr-tag-remove::after,.selectr-tag-remove::before{position:absolute;top:5px;left:9px;width:2px;height:10px;content:' ';background-color:#6c7a86}.selectr-tag-remove::after,.selectr-tag-remove::before{top:4px;width:3px;height:12px;background-color:#fff}.selectr-clear:before,.selectr-input-clear::before,.selectr-tag-remove::before{-o-transform:rotate(45deg);-ms-transform:rotate(45deg);-moz-transform:rotate(45deg);-webkit-transform:rotate(45deg);transform:rotate(45deg)}.selectr-clear:after,.selectr-input-clear::after,.selectr-tag-remove::after{-o-transform:rotate(-45deg);-ms-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}.selectr-input-container.active,.selectr-input-container.active .selectr-clear{display:block}.selectr-input{top:5px;left:5px;box-sizing:border-box;width:calc(100% - 30px);margin:10px 15px;padding:7px 30px 7px 9px;border:1px solid #999;border-radius:3px}.selectr-notice{display:none;box-sizing:border-box;width:100%;padding:8px 16px;border-top:1px solid #999;border-radius:0 0 3px 3px;background-color:#fff}.selectr-container.notice .selectr-notice{display:block}.selectr-container.notice .selectr-selected{border-radius:3px 3px 0 0}.selectr-options{position:relative;top:calc(100% + 2px);display:none;overflow-x:auto;overflow-y:scroll;max-height:200px;margin:0;padding:0}.selectr-container.notice .selectr-options-container,.selectr-container.open .selectr-input-container,.selectr-container.open .selectr-options{display:block}.selectr-option{position:relative;display:block;padding:5px 20px;list-style:outside none none;cursor:pointer;font-weight:400}.selectr-options.optgroups>.selectr-option{padding-left:25px}.selectr-optgroup{font-weight:700;padding:0}.selectr-optgroup--label{font-weight:700;margin-top:10px;padding:5px 15px}.selectr-match{text-decoration:underline}.selectr-option.selected{background-color:#ddd}.selectr-option.active{color:#fff;background-color:#5897fb}.selectr-option.disabled{opacity:.4}.selectr-option.excluded{display:none}.selectr-container.open .selectr-selected{border-color:#999 #999 transparent #999;border-radius:3px 3px 0 0}.selectr-container.open .selectr-selected::after{-o-transform:rotate(180deg) translate3d(0,50%,0);-ms-transform:rotate(180deg) translate3d(0,50%,0);-moz-transform:rotate(180deg) translate3d(0,50%,0);-webkit-transform:rotate(180deg) translate3d(0,50%,0);transform:rotate(180deg) translate3d(0,50%,0)}.selectr-disabled{opacity:.6}.has-selected .selectr-placeholder,.selectr-empty{display:none}.has-selected .selectr-label{display:block}.taggable .selectr-selected{padding:4px 28px 4px 4px}.taggable .selectr-selected::after{display:table;content:" ";clear:both}.taggable .selectr-label{width:auto}.taggable .selectr-tags{float:left;display:block}.taggable .selectr-placeholder{display:none}.input-tag{float:left;min-width:90px;width:auto}.selectr-tag-input{border:medium none;padding:3px 10px;width:100%;font-family:inherit;font-weight:inherit;font-size:inherit}.selectr-input-container.loading::after{position:absolute;top:50%;right:20px;width:20px;height:20px;content:'';-o-transform:translate3d(0,-50%,0);-ms-transform:translate3d(0,-50%,0);-moz-transform:translate3d(0,-50%,0);-webkit-transform:translate3d(0,-50%,0);transform:translate3d(0,-50%,0);-o-transform-origin:50% 0 0;-ms-transform-origin:50% 0 0;-moz-transform-origin:50% 0 0;-webkit-transform-origin:50% 0 0;transform-origin:50% 0 0;-moz-animation:.5s linear 0s normal forwards infinite running spin;-webkit-animation:.5s linear 0s normal forwards infinite running spin;animation:.5s linear 0s normal forwards infinite running spin;border-width:3px;border-style:solid;border-color:#aaa #ddd #ddd;border-radius:50%}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0) translate3d(0,-50%,0);transform:rotate(0) translate3d(0,-50%,0)}100%{-webkit-transform:rotate(360deg) translate3d(0,-50%,0);transform:rotate(360deg) translate3d(0,-50%,0)}}@keyframes spin{0%{-webkit-transform:rotate(0) translate3d(0,-50%,0);transform:rotate(0) translate3d(0,-50%,0)}100%{-webkit-transform:rotate(360deg) translate3d(0,-50%,0);transform:rotate(360deg) translate3d(0,-50%,0)}}.selectr-container.open.inverted .selectr-selected{border-color:transparent #999 #999;border-radius:0 0 3px 3px}.selectr-container.inverted .selectr-options-container{border-width:1px 1px 0;border-color:#999 #999 transparent;border-radius:3px 3px 0 0;background-color:#fff}.selectr-container.inverted .selectr-options-container{top:auto;bottom:calc(100% - 1px)}.selectr-container ::-webkit-input-placeholder{color:#6c7a86;opacity:1}.selectr-container ::-moz-placeholder{color:#6c7a86;opacity:1}.selectr-container :-ms-input-placeholder{color:#6c7a86;opacity:1}.selectr-container ::placeholder{color:#6c7a86;opacity:1} \ No newline at end of file diff --git a/ui/site/jsoneditor.min.js b/ui/site/jsoneditor.min.js new file mode 100644 index 0000000..d30e3d3 --- /dev/null +++ b/ui/site/jsoneditor.min.js @@ -0,0 +1,46 @@ +/*! + * jsoneditor.js + * + * @brief + * JSONEditor is a web-based tool to view, edit, format, and validate JSON. + * It has various modes such as a tree editor, a code editor, and a plain text + * editor. + * + * Supported browsers: Chrome, Firefox, Safari, Opera, Internet Explorer 8+ + * + * @license + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + * + * Copyright (c) 2011-2019 Jos de Jong, http://jsoneditoronline.org + * + * @author Jos de Jong, + * @version 7.4.0 + * @date 2019-12-03 + */ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.JSONEditor=t():e.JSONEditor=t()}(window,function(){return n={},r.m=i=[function(e,t,i){"use strict";i.r(t),i.d(t,"parse",function(){return g}),i.d(t,"repair",function(){return p}),i.d(t,"escapeUnicodeChars",function(){return m}),i.d(t,"validate",function(){return f}),i.d(t,"extend",function(){return C}),i.d(t,"clear",function(){return I}),i.d(t,"getType",function(){return A}),i.d(t,"isUrl",function(){return b}),i.d(t,"isArray",function(){return y}),i.d(t,"getAbsoluteLeft",function(){return w}),i.d(t,"getAbsoluteTop",function(){return S}),i.d(t,"addClassName",function(){return x}),i.d(t,"removeAllClassNames",function(){return k}),i.d(t,"removeClassName",function(){return R}),i.d(t,"stripFormatting",function(){return E}),i.d(t,"setEndOfContentEditable",function(){return B}),i.d(t,"selectContentEditable",function(){return G}),i.d(t,"getSelection",function(){return Z}),i.d(t,"setSelection",function(){return T}),i.d(t,"getSelectionOffset",function(){return W}),i.d(t,"setSelectionOffset",function(){return _}),i.d(t,"getInnerText",function(){return H}),i.d(t,"hasParentNode",function(){return L}),i.d(t,"getInternetExplorerVersion",function(){return $}),i.d(t,"isFirefox",function(){return V}),i.d(t,"addEventListener",function(){return O}),i.d(t,"removeEventListener",function(){return F}),i.d(t,"isChildOf",function(){return P}),i.d(t,"parsePath",function(){return K}),i.d(t,"stringifyPath",function(){return M}),i.d(t,"improveSchemaError",function(){return D}),i.d(t,"isPromise",function(){return j}),i.d(t,"isValidValidationError",function(){return X}),i.d(t,"insideRect",function(){return Y}),i.d(t,"debounce",function(){return J}),i.d(t,"textDiff",function(){return z}),i.d(t,"getInputSelection",function(){return U}),i.d(t,"getIndexForPosition",function(){return Q}),i.d(t,"getPositionForPath",function(){return q}),i.d(t,"compileJSONPointer",function(){return ee}),i.d(t,"getColorCSS",function(){return te}),i.d(t,"isValidColor",function(){return ie}),i.d(t,"makeFieldTooltip",function(){return ne}),i.d(t,"get",function(){return re}),i.d(t,"findUniqueName",function(){return se}),i.d(t,"getChildPaths",function(){return oe}),i.d(t,"sort",function(){return ae}),i.d(t,"sortObjectKeys",function(){return le}),i.d(t,"parseString",function(){return ce}),i.d(t,"isTimestamp",function(){return de}),i.d(t,"formatSize",function(){return ue}),i.d(t,"limitCharacters",function(){return ge}),i.d(t,"isObject",function(){return pe}),i.d(t,"contains",function(){return me}),i.d(t,"isValidationErrorChanged",function(){return fe});i(24);var n=i(11),s=i.n(n),r=i(22),o=i.n(r),a=i(33),l=i.n(a),c=i(1);function h(e){return(h="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}var d=1e4,u=9466848e5;function g(t){try{return JSON.parse(t)}catch(e){throw f(t),e}}function p(n){var i=[],r=0,e=n.match(/^\s*(\/\*(.|[\r\n])*?\*\/)?\s*[\da-zA-Z_$]+\s*\(([\s\S]*)\)\s*;?\s*$/);e&&(n=e[3]);var t,s={"\b":"\\b","\f":"\\f","\n":"\\n","\r":"\\r","\t":"\\t"};function o(){return n.charAt(r)}function a(){return n.charAt(r+1)}function l(e){return" "===e||"\n"===e||"\r"===e||"\t"===e}function c(){for(var e=i.length-1;0<=e;){var t=i[e];if(!l(t))return t;e--}return""}function h(){for(var e=r+1;e=e.left&&t.right+n<=e.right&&t.top-n>=e.top&&t.bottom+n<=e.bottom}function J(n,r,s){var o;return function(){var e=this,t=arguments,i=s&&!o;clearTimeout(o),o=setTimeout(function(){o=null,s||n.apply(e,t)},r),i&&n.apply(e,t)}}function z(e,t){for(var i=t.length,n=0,r=e.length,s=t.length;t.charAt(n)===e.charAt(n)&&n',r.appendChild(c),t.submenuTitle&&(c.title=t.submenuTitle),a=c}else{var h=document.createElement("div");h.className="jsoneditor-expand",s.appendChild(h),a=s}a.onclick=function(e){e.preventDefault(),f._onExpandItem(n),a.focus()};var d=[];n.subItems=d;var u=document.createElement("ul");(n.ul=u).className="jsoneditor-menu",u.style.height="0",r.appendChild(u),g(u,d,t.submenu)}else s.innerHTML='
'+Object(C.c)(t.text)+"
";m.push(n)}})}(s,this.dom.items,i),this.maxHeight=0,i.forEach(function(e){var t=24*(i.length+(e.submenu?e.submenu.length:0));f.maxHeight=Math.max(f.maxHeight,t)})}var e,t,i;return e=d,(t=[{key:"_getVisibleButtons",value:function(){var t=[],i=this;return this.dom.items.forEach(function(e){t.push(e.button),e.buttonExpand&&t.push(e.buttonExpand),e.subItems&&e===i.expandedItem&&e.subItems.forEach(function(e){t.push(e.button),e.buttonExpand&&t.push(e.buttonExpand)})}),t}},{key:"show",value:function(e,t,i){this.hide();var n=!0,r=e.parentNode,s=e.getBoundingClientRect(),o=r.getBoundingClientRect(),a=t.getBoundingClientRect(),l=this;this.dom.absoluteAnchor=Object(u.a)(e,t,function(){l.hide()}),s.bottom+this.maxHeighta.top&&(n=!1);var c=i?0:s.top-o.top;if(n){var h=e.offsetHeight;this.dom.menu.style.left="0",this.dom.menu.style.top=c+h+"px",this.dom.menu.style.bottom=""}else this.dom.menu.style.left="0",this.dom.menu.style.top="",this.dom.menu.style.bottom="0px";this.dom.absoluteAnchor.appendChild(this.dom.root),this.selection=Object(g.getSelection)(),this.anchor=e,setTimeout(function(){l.dom.focusButton.focus()},0),d.visibleMenu&&d.visibleMenu.hide(),d.visibleMenu=this}},{key:"hide",value:function(){this.dom.absoluteAnchor&&(this.dom.absoluteAnchor.destroy(),delete this.dom.absoluteAnchor),this.dom.root.parentNode&&(this.dom.root.parentNode.removeChild(this.dom.root),this.onClose&&this.onClose()),d.visibleMenu===this&&(d.visibleMenu=void 0)}},{key:"_onExpandItem",value:function(i){var n=this,e=i===this.expandedItem,t=this.expandedItem;if(t&&(t.ul.style.height="0",t.ul.style.padding="",setTimeout(function(){n.expandedItem!==t&&(t.ul.style.display="",Object(g.removeClassName)(t.ul.parentNode,"jsoneditor-selected"))},300),this.expandedItem=void 0),!e){var r=i.ul;r.style.display="block",r.clientHeight,setTimeout(function(){if(n.expandedItem===i){for(var e=0,t=0;t":!0,"=":!0,"!":!0},N={" ":!0,"\t":!0,"\n":!0};function O(e){return"0"<=e&&e<="9"||"-"===e}function n(){}n.prototype={tokenize:function(e){var t,i,n,r,s=[];for(this._current=0;this._current"===i?"="===e[this._current]?(this._current++,{type:_,value:">=",start:t}):{type:"GT",value:">",start:t}:"="===i&&"="===e[this._current]?(this._current++,{type:"EQ",value:"==",start:t}):void 0},_consumeLiteral:function(e){this._current++;for(var t,i=this._current,n=e.length;"`"!==e[this._current]&&this._current
'+Object(s.c)("sort")+"
"+Object(s.c)("sortFieldLabel")+'
'+Object(s.c)("sortDirectionLabel")+'
';r()({parent:e,content:n,overlayClass:"jsoneditor-modal-overlay",overlayStyles:{backgroundColor:"rgb(1,1,1)",opacity:.3},modalClass:"jsoneditor-modal jsoneditor-modal-sort"}).afterCreate(function(t){var e=t.modalElem().querySelector("form"),i=t.modalElem().querySelector("#ok"),n=t.modalElem().querySelector("#field"),r=t.modalElem().querySelector("#direction");function s(e){r.value=e,r.className="jsoneditor-button-group jsoneditor-button-group-value-"+r.value}a.forEach(function(e){var t,i=document.createElement("option");i.text=""===(t=e)?"@":"."===t[0]?t.slice(1):t,i.value=e,n.appendChild(i)}),n.value=l||a[0],s(c||"asc"),r.onclick=function(e){s(e.target.getAttribute("data-value"))},i.onclick=function(e){e.preventDefault(),e.stopPropagation(),t.close(),o({path:n.value,direction:r.value})},e&&(e.onsubmit=i.onclick)}).afterClose(function(e){e.destroy()}).show()}},function(e,t,i){"use strict";i.d(t,"a",function(){return r});var p=i(3),m=i(1);function n(e,t){for(var i=0;in)return s+"..."}return s+=t?"\n"+i+"]":"]"}(e,t,i,n):e&&"object"===l(e)?function(e,t,i,n){var r=t?i+t:void 0,s=!0,o=t?"{\n":"{";if("function"==typeof e.toJSON)return R(e.toJSON(),t,i,n);for(var a in e)if(c=e,h=a,Object.prototype.hasOwnProperty.call(c,h)){var l=e[a];if(s?s=!1:o+=t?",\n":",",o+=t?r+'"'+a+'": ':'"'+a+'":',(o+=R(l,t,r,n)).length>n)return o+"..."}var c,h;return o+=t?"\n"+i+"}":"}"}(e,t,i,n):void 0}function E(e,t){for(var i="";0
'+Object(a.c)("transform")+'

Enter a JMESPath query to filter, sort, or transform the JSON data.
To learn JMESPath, go to the interactive tutorial.

'+Object(a.c)("transformWizardLabel")+'
'+Object(a.c)("transformWizardFilter")+'
'+Object(a.c)("transformWizardSortBy")+'
'+Object(a.c)("transformWizardSelectFields")+'
'+Object(a.c)("transformQueryLabel")+'
'+Object(a.c)("transformPreviewLabel")+'
';s()({parent:e,content:t,overlayClass:"jsoneditor-modal-overlay",overlayStyles:{backgroundColor:"rgb(1,1,1)",opacity:.3},modalClass:"jsoneditor-modal jsoneditor-modal-transform",focus:!1}).afterCreate(function(t){var e=t.modalElem(),i=e.querySelector("#wizard"),n=e.querySelector("#ok"),a=e.querySelector("#filterField"),l=e.querySelector("#filterRelation"),c=e.querySelector("#filterValue"),h=e.querySelector("#sortField"),d=e.querySelector("#sortOrder"),u=e.querySelector("#selectFields"),g=e.querySelector("#query"),r=e.querySelector("#preview");Array.isArray(S)||(i.style.fontStyle="italic",i.innerHTML="(wizard not available for objects, only for arrays)"),Object(B.getChildPaths)(y).forEach(function(e){var t=A(e),i=document.createElement("option");i.text=t,i.value=t,a.appendChild(i);var n=document.createElement("option");n.text=t,n.value=t,h.appendChild(n)});var s=Object(B.getChildPaths)(y,!0).filter(function(e){return""!==e});if(0i?(r=o,("number"==typeof(s=i)?r.slice(0,s):r)+"..."):o}(e,2,G.b),n.disabled=!1}catch(e){r.className="jsoneditor-transform-preview jsoneditor-error",r.value=e.toString(),n.disabled=!0}},300);(g.oninput=b)(),n.onclick=function(e){e.preventDefault(),e.stopPropagation(),t.close(),w(g.value)},setTimeout(function(){g.select(),g.focus(),g.selectionStart=3,g.selectionEnd=3})}).afterClose(function(e){e.destroy()}).show()}i.d(t,"a",function(){return c})},function(e,t,i){"use strict";function n(){}var r={defaultSelected:!0,width:"auto",disabled:!1,searchable:!0,clearable:!1,sortSelected:!1,allowDeselect:!1,closeOnScroll:!1,nativeDropdown:!1,placeholder:"Select an option...",taggable:!1,tagPlaceholder:"Enter a tag..."};n.prototype={on:function(e,t){this._events=this._events||{},this._events[e]=this._events[e]||[],this._events[e].push(t)},off:function(e,t){this._events=this._events||{},e in this._events!=!1&&this._events[e].splice(this._events[e].indexOf(t),1)},emit:function(e){if(this._events=this._events||{},e in this._events!=!1)for(var t=0;t"+t.label+""}),l.each(t.children,function(e,t){t.idx=r,n.appendChild(d.call(this,t,n)),r++},this)):(t.idx=r,d.call(this,t),r++)},this),this.config.data&&Array.isArray(this.config.data)){var s,o=!(this.data=[]);n=!1,r=0,l.each(this.config.data,function(e,t){c(t,"children")?(o=l.createElement("optgroup",{label:t.text}),n=l.createElement("ul",{class:"selectr-optgroup",role:"group",html:"
  • "+t.text+"
  • "}),l.each(t.children,function(e,t){(s=new Option(t.text,t.value,!1,t.hasOwnProperty("selected")&&!0===t.selected)).disabled=c(t,"disabled"),this.options.push(s),o.appendChild(s),s.idx=r,n.appendChild(d.call(this,s,t)),this.data[r]=t,r++},this)):((s=new Option(t.text,t.value,!1,t.hasOwnProperty("selected")&&!0===t.selected)).disabled=c(t,"disabled"),this.options.push(s),s.idx=r,d.call(this,s,t),this.data[r]=t,r++)},this)}this.setSelected(!0);for(var a=this.navIndex=0;athis.tree.lastElementChild.idx){this.navIndex=this.tree.lastElementChild.idx;break}if(this.navIndexthis.optsRect.top+this.optsRect.height&&(this.tree.scrollTop=this.tree.scrollTop+(n.top+n.height-(this.optsRect.top+this.optsRect.height))),this.navIndex===this.tree.childElementCount-1&&this.requiresPagination&&u.call(this)):0===this.navIndex?this.tree.scrollTop=0:n.top-this.optsRect.top<0&&(this.tree.scrollTop=this.tree.scrollTop+(n.top-this.optsRect.top)),i&&l.removeClass(i,"active"),l.addClass(this.items[this.navIndex],"active")}else this.navigating=!1}.bind(this),this.events.reset=this.reset.bind(this),this.config.nativeDropdown||this.mobileDevice){this.container.addEventListener("touchstart",function(e){e.changedTouches[0].target===n.el&&n.toggle()}),(this.config.nativeDropdown||this.mobileDevice)&&this.container.addEventListener("click",function(e){e.preventDefault(),e.stopPropagation(),e.target===n.el&&n.toggle()});this.el.addEventListener("change",function(e){if(n.el.multiple){var t=n.getSelectedProperties("idx"),i=function(e,t){for(var i,n=[],r=e.slice(0),s=0;s"+n[0]+"")))):l.addClass(r,"excluded")},this),o.childElementCount){var e=this.items[this.navIndex],t=o.firstElementChild;l.removeClass(e,"active"),this.navIndex=t.idx,l.addClass(t,"active")}else this.config.taggable||this.setMessage("no results.");else h.call(this);this.tree.appendChild(o)}},o.prototype.toggle=function(){this.disabled||(this.opened?this.close():this.open())},o.prototype.open=function(){var e=this;return!!this.options.length&&(this.opened||this.emit("selectr.open"),this.opened=!0,this.mobileDevice||this.config.nativeDropdown?(l.addClass(this.container,"native-open"),void(this.config.data&&l.each(this.options,function(e,t){this.el.add(t)},this))):(l.addClass(this.container,"open"),h.call(this),this.invert(),this.tree.scrollTop=0,l.removeClass(this.container,"notice"),this.selected.setAttribute("aria-expanded",!0),this.tree.setAttribute("aria-hidden",!1),this.tree.setAttribute("aria-expanded",!0),void(this.config.searchable&&!this.config.taggable&&setTimeout(function(){e.input.focus(),e.input.tabIndex=0},10))))},o.prototype.close=function(){if(this.opened&&this.emit("selectr.close"),this.opened=!1,this.mobileDevice||this.config.nativeDropdown)l.removeClass(this.container,"native-open");else{var e=l.hasClass(this.container,"notice");this.config.searchable&&!e&&(this.input.blur(),this.input.tabIndex=-1,this.searching=!1),e&&(l.removeClass(this.container,"notice"),this.notice.textContent=""),l.removeClass(this.container,"open"),l.removeClass(this.container,"native-open"),this.selected.setAttribute("aria-expanded",!1),this.tree.setAttribute("aria-hidden",!0),this.tree.setAttribute("aria-expanded",!1),l.truncate(this.tree),s.call(this)}},o.prototype.enable=function(){this.disabled=!1,this.el.disabled=!1,this.selected.tabIndex=this.originalIndex,this.el.multiple&&l.each(this.tags,function(e,t){t.lastElementChild.tabIndex=0}),l.removeClass(this.container,"selectr-disabled")},o.prototype.disable=function(e){e||(this.el.disabled=!0),this.selected.tabIndex=-1,this.el.multiple&&l.each(this.tags,function(e,t){t.lastElementChild.tabIndex=-1}),this.disabled=!0,l.addClass(this.container,"selectr-disabled")},o.prototype.reset=function(){this.disabled||(this.clear(),this.setSelected(!0),l.each(this.defaultSelected,function(e,t){this.select(t)},this),this.emit("selectr.reset"))},o.prototype.clear=function(e){if(this.el.multiple){if(this.selectedIndexes.length){var t=this.selectedIndexes.slice();l.each(t,function(e,t){this.deselect(t)},this)}}else-1i?(l.addClass(this.container,"inverted"),this.isInverted=!0):(l.removeClass(this.container,"inverted"),this.isInverted=!1),this.optsRect=l.rect(this.tree)},o.prototype.getOptionByIndex=function(e){return this.options[e]},o.prototype.getOptionByValue=function(e){for(var t=!1,i=0,n=this.options.length;i';var a=t.getElementsByTagName("tbody")[0];e.forEach(function(t){var e,i;if(e="string"==typeof t?'
    '+t+"
    ":""+(t.dataPath||"")+"
    "+t.message+"
    ",isNaN(t.line)){if(t.dataPath){var n=s.find(function(e){return e.path===t.dataPath});n&&(i=n.line+1)}}else i=t.line;var r=document.createElement("tr");r.className=isNaN(i)?"":"jump-to-line","error"===t.type?r.className+=" parse-error":r.className+=" validation-error",r.innerHTML=''+(isNaN(i)?"":"Ln "+i)+""+e,r.onclick=function(){o.onFocusLine(i)},a.appendChild(r)}),this.dom.validationErrors=t,this.dom.validationErrorsContainer.appendChild(t),this.dom.additionalErrorsIndication.title=e.length+" errors total",this.dom.validationErrorsContainer.clientHeight"),line:t}]}this._renderErrors(r)}},c._renderErrors=function(n){var e=this.getText(),t=[];n.reduce(function(e,t){return"string"==typeof t.dataPath&&-1===e.indexOf(t.dataPath)&&e.push(t.dataPath),e},t);var i=Object(R.getPositionForPath)(e,t);if(this.aceEditor&&(this.annotations=i.map(function(t){var e=n.filter(function(e){return e.dataPath===t.path}),i=e.map(function(e){return e.message}).join("\n");return i?{row:t.line,column:t.column,text:"Schema validation error"+(1!==e.length?"s":"")+": \n"+i,type:"warning",source:"jsoneditor"}:{}}),this._refreshAnnotations()),this.errorTable.setErrors(n,i),this.aceEditor){this.aceEditor.resize(!1)}},c.getTextSelection=function(){var e={};if(this.textarea){var t=Object(R.getInputSelection)(this.textarea);return this.cursorInfo&&this.cursorInfo.line===t.end.row&&this.cursorInfo.column===t.end.column?(e.start=t.end,e.end=t.start):e=t,{start:e.start,end:e.end,text:this.textarea.value.substring(t.startIndex,t.endIndex)}}if(this.aceEditor){var i=this.aceEditor.getSelection(),n=this.aceEditor.getSelectedText(),r=i.getRange(),s=i.getSelectionLead();return s.row===r.end.row&&s.column===r.end.column?e=r:(e.start=r.end,e.end=r.start),{start:{row:e.start.row+1,column:e.start.column+1},end:{row:e.end.row+1,column:e.end.column+1},text:n}}},c.onTextSelectionChange=function(e){"function"==typeof e&&(this._selectionChangedHandler=Object(R.debounce)(e,this.DEBOUNCE_INTERVAL))},c.setTextSelection=function(e,t){if(e&&t)if(this.textarea){var i=Object(R.getIndexForPosition)(this.textarea,e.row,e.column),n=Object(R.getIndexForPosition)(this.textarea,t.row,t.column);if(-1this.textarea.clientHeight?a-this.textarea.clientHeight/2:0}}else if(this.aceEditor){var l={start:{row:e.row-1,column:e.column-1},end:{row:t.row-1,column:t.column-1}};this.aceEditor.selection.setRange(l),this.aceEditor.scrollToLine(e.row-1,!0)}};var d=[{mode:"text",mixin:c,data:"text",load:h},{mode:"code",mixin:c,data:"text",load:h}]},function(e,t,i){var n,r=((n={trace:function(){},yy:{},symbols_:{error:2,JSONString:3,STRING:4,JSONNumber:5,NUMBER:6,JSONNullLiteral:7,NULL:8,JSONBooleanLiteral:9,TRUE:10,FALSE:11,JSONText:12,JSONValue:13,EOF:14,JSONObject:15,JSONArray:16,"{":17,"}":18,JSONMemberList:19,JSONMember:20,":":21,",":22,"[":23,"]":24,JSONElementList:25,$accept:0,$end:1},terminals_:{2:"error",4:"STRING",6:"NUMBER",8:"NULL",10:"TRUE",11:"FALSE",14:"EOF",17:"{",18:"}",21:":",22:",",23:"[",24:"]"},productions_:[0,[3,1],[5,1],[7,1],[9,1],[9,1],[12,2],[13,1],[13,1],[13,1],[13,1],[13,1],[13,1],[15,2],[15,3],[20,3],[19,1],[19,3],[16,2],[16,3],[25,1],[25,3]],performAction:function(e,t,i,n,r,s){var o=s.length-1;switch(r){case 1:this.$=e.replace(/\\(\\|")/g,"$1").replace(/\\n/g,"\n").replace(/\\r/g,"\r").replace(/\\t/g,"\t").replace(/\\v/g,"\v").replace(/\\f/g,"\f").replace(/\\b/g,"\b");break;case 2:this.$=Number(e);break;case 3:this.$=null;break;case 4:this.$=!0;break;case 5:this.$=!1;break;case 6:return this.$=s[o-1];case 13:this.$={};break;case 14:this.$=s[o-1];break;case 15:this.$=[s[o-2],s[o]];break;case 16:this.$={},this.$[s[o][0]]=s[o][1];break;case 17:this.$=s[o-2],s[o-2][s[o][0]]=s[o][1];break;case 18:this.$=[];break;case 19:this.$=s[o-1];break;case 20:this.$=[s[o]];break;case 21:this.$=s[o-2],s[o-2].push(s[o])}},table:[{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],12:1,13:2,15:7,16:8,17:[1,14],23:[1,15]},{1:[3]},{14:[1,16]},{14:[2,7],18:[2,7],22:[2,7],24:[2,7]},{14:[2,8],18:[2,8],22:[2,8],24:[2,8]},{14:[2,9],18:[2,9],22:[2,9],24:[2,9]},{14:[2,10],18:[2,10],22:[2,10],24:[2,10]},{14:[2,11],18:[2,11],22:[2,11],24:[2,11]},{14:[2,12],18:[2,12],22:[2,12],24:[2,12]},{14:[2,3],18:[2,3],22:[2,3],24:[2,3]},{14:[2,4],18:[2,4],22:[2,4],24:[2,4]},{14:[2,5],18:[2,5],22:[2,5],24:[2,5]},{14:[2,1],18:[2,1],21:[2,1],22:[2,1],24:[2,1]},{14:[2,2],18:[2,2],22:[2,2],24:[2,2]},{3:20,4:[1,12],18:[1,17],19:18,20:19},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:23,15:7,16:8,17:[1,14],23:[1,15],24:[1,21],25:22},{1:[2,6]},{14:[2,13],18:[2,13],22:[2,13],24:[2,13]},{18:[1,24],22:[1,25]},{18:[2,16],22:[2,16]},{21:[1,26]},{14:[2,18],18:[2,18],22:[2,18],24:[2,18]},{22:[1,28],24:[1,27]},{22:[2,20],24:[2,20]},{14:[2,14],18:[2,14],22:[2,14],24:[2,14]},{3:20,4:[1,12],20:29},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:30,15:7,16:8,17:[1,14],23:[1,15]},{14:[2,19],18:[2,19],22:[2,19],24:[2,19]},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:31,15:7,16:8,17:[1,14],23:[1,15]},{18:[2,17],22:[2,17]},{18:[2,15],22:[2,15]},{22:[2,21],24:[2,21]}],defaultActions:{16:[2,6]},parseError:function(e){throw new Error(e)},parse:function(e){var t=this,i=[0],n=[null],r=[],s=this.table,o="",a=0,l=0,c=0;this.lexer.setInput(e),this.lexer.yy=this.yy,this.yy.lexer=this.lexer,void 0===this.lexer.yylloc&&(this.lexer.yylloc={});var h=this.lexer.yylloc;function d(){var e;return"number"!=typeof(e=t.lexer.lex()||1)&&(e=t.symbols_[e]||e),e}r.push(h),"function"==typeof this.yy.parseError&&(this.parseError=this.yy.parseError);for(var u,g,p,m,f,C,I,A,v,b,y={};;){if(p=i[i.length-1],void 0===(m=this.defaultActions[p]?this.defaultActions[p]:(null==u&&(u=d()),s[p]&&s[p][u]))||!m.length||!m[0]){if(!c){for(C in v=[],s[p])this.terminals_[C]&&2t[0].length)||(t=i,n=o,this.options.flex));o++);return t?((r=t[0].match(/\n.*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-1:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.yyleng=this.yytext.length,this._more=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],e=this.performAction.call(this,this.yy,this,s[n],this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),e||void 0):""===this._input?this.EOF:void this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var e=this.next();return void 0!==e?e:this.lex()},begin:function(e){this.conditionStack.push(e)},popState:function(){return this.conditionStack.pop()},_currentRules:function(){return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules},topState:function(){return this.conditionStack[this.conditionStack.length-2]},pushState:function(e){this.begin(e)},options:{},performAction:function(e,t,i){switch(i){case 0:break;case 1:return 6;case 2:return t.yytext=t.yytext.substr(1,t.yyleng-2),4;case 3:return 17;case 4:return 18;case 5:return 23;case 6:return 24;case 7:return 22;case 8:return 21;case 9:return 10;case 10:return 11;case 11:return 8;case 12:return 14;case 13:return"INVALID"}},rules:[/^(?:\s+)/,/^(?:(-?([0-9]|[1-9][0-9]+))(\.[0-9]+)?([eE][-+]?[0-9]+)?\b)/,/^(?:"(?:\\[\\"bfnrt/]|\\u[a-fA-F0-9]{4}|[^\\\0-\x09\x0a-\x1f"])*")/,/^(?:\{)/,/^(?:\})/,/^(?:\[)/,/^(?:\])/,/^(?:,)/,/^(?::)/,/^(?:true\b)/,/^(?:false\b)/,/^(?:null\b)/,/^(?:$)/,/^(?:.)/],conditions:{INITIAL:{rules:[0,1,2,3,4,5,6,7,8,9,10,11,12,13],inclusive:!0}}},n);t.parser=r,t.parse=r.parse.bind(r)},function(e,t){e.exports=function(){throw new Error("define cannot be used indirect")}},function(e,t){function i(e){"remove"in e||Object.defineProperty(e,"remove",{configurable:!0,enumerable:!0,writable:!0,value:function(){void 0!==this.parentNode&&this.parentNode.removeChild(this)}})}"undefined"!=typeof Element&&(void 0!==window.Element&&i(window.Element.prototype),void 0!==window.CharacterData&&i(window.CharacterData.prototype),void 0!==window.DocumentType&&i(window.DocumentType.prototype)),Array.prototype.find||(Array.prototype.find=function(e){for(var t=0;t",C=u?">":"<",I=void 0;if(m){var A=e.util.getData(p.$data,s,e.dataPathArr),v="exclusive"+r,b="exclType"+r,y="exclIsNumber"+r,w="' + "+(k="op"+r)+" + '";n+=" var schemaExcl"+r+" = "+A+"; ";var S;I=g;(S=S||[]).push(n+=" var "+v+"; var "+b+" = typeof "+(A="schemaExcl"+r)+"; if ("+b+" != 'boolean' && "+b+" != 'undefined' && "+b+" != 'number') { "),n="",!1!==e.createErrors?(n+=" { keyword: '"+(I||"_exclusiveLimit")+"' , dataPath: (dataPath || '') + "+e.errorPath+" , schemaPath: "+e.util.toQuotedString(l)+" , params: {} ",!1!==e.opts.messages&&(n+=" , message: '"+g+" should be boolean' "),e.opts.verbose&&(n+=" , schema: validate.schema"+a+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ";var x=n;n=S.pop(),!e.compositeRule&&c?e.async?n+=" throw new ValidationError(["+x+"]); ":n+=" validate.errors = ["+x+"]; return false; ":n+=" var err = "+x+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",n+=" } else if ( ",d&&(n+=" ("+i+" !== undefined && typeof "+i+" != 'number') || "),n+=" "+b+" == 'number' ? ( ("+v+" = "+i+" === undefined || "+A+" "+f+"= "+i+") ? "+h+" "+C+"= "+A+" : "+h+" "+C+" "+i+" ) : ( ("+v+" = "+A+" === true) ? "+h+" "+C+"= "+i+" : "+h+" "+C+" "+i+" ) || "+h+" !== "+h+") { var op"+r+" = "+v+" ? '"+f+"' : '"+f+"='; ",void 0===o&&(I=g,l=e.errSchemaPath+"/"+g,i=A,d=m)}else{w=f;if((y="number"==typeof p)&&d){var k="'"+w+"'";n+=" if ( ",d&&(n+=" ("+i+" !== undefined && typeof "+i+" != 'number') || "),n+=" ( "+i+" === undefined || "+p+" "+f+"= "+i+" ? "+h+" "+C+"= "+p+" : "+h+" "+C+" "+i+" ) || "+h+" !== "+h+") { "}else{y&&void 0===o?(v=!0,I=g,l=e.errSchemaPath+"/"+g,i=p,C+="="):(y&&(i=Math[u?"min":"max"](p,o)),p===(!y||i)?(v=!0,I=g,l=e.errSchemaPath+"/"+g,C+="="):(v=!1,w+="="));k="'"+w+"'";n+=" if ( ",d&&(n+=" ("+i+" !== undefined && typeof "+i+" != 'number') || "),n+=" "+h+" "+C+" "+i+" || "+h+" !== "+h+") { "}}I=I||t,(S=S||[]).push(n),n="",!1!==e.createErrors?(n+=" { keyword: '"+(I||"_limit")+"' , dataPath: (dataPath || '') + "+e.errorPath+" , schemaPath: "+e.util.toQuotedString(l)+" , params: { comparison: "+k+", limit: "+i+", exclusive: "+v+" } ",!1!==e.opts.messages&&(n+=" , message: 'should be "+w+" ",n+=d?"' + "+i:i+"'"),e.opts.verbose&&(n+=" , schema: ",n+=d?"validate.schema"+a:""+o,n+=" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ";x=n;return n=S.pop(),!e.compositeRule&&c?e.async?n+=" throw new ValidationError(["+x+"]); ":n+=" validate.errors = ["+x+"]; return false; ":n+=" var err = "+x+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",n+=" } ",c&&(n+=" else { "),n}},function(e,t,i){"use strict";e.exports=function(e,t){var i,n=" ",r=e.level,s=e.dataLevel,o=e.schema[t],a=e.schemaPath+e.util.getProperty(t),l=e.errSchemaPath+"/"+t,c=!e.opts.allErrors,h="data"+(s||""),d=e.opts.$data&&o&&o.$data;i=d?(n+=" var schema"+r+" = "+e.util.getData(o.$data,s,e.dataPathArr)+"; ","schema"+r):o,n+="if ( ",d&&(n+=" ("+i+" !== undefined && typeof "+i+" != 'number') || "),n+=" "+h+".length "+("maxItems"==t?">":"<")+" "+i+") { ";var u=t,g=g||[];g.push(n),n="",!1!==e.createErrors?(n+=" { keyword: '"+(u||"_limitItems")+"' , dataPath: (dataPath || '') + "+e.errorPath+" , schemaPath: "+e.util.toQuotedString(l)+" , params: { limit: "+i+" } ",!1!==e.opts.messages&&(n+=" , message: 'should NOT have ",n+="maxItems"==t?"more":"fewer",n+=" than ",n+=d?"' + "+i+" + '":""+o,n+=" items' "),e.opts.verbose&&(n+=" , schema: ",n+=d?"validate.schema"+a:""+o,n+=" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ";var p=n;return n=g.pop(),!e.compositeRule&&c?e.async?n+=" throw new ValidationError(["+p+"]); ":n+=" validate.errors = ["+p+"]; return false; ":n+=" var err = "+p+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",n+="} ",c&&(n+=" else { "),n}},function(e,t,i){"use strict";e.exports=function(e,t){var i,n=" ",r=e.level,s=e.dataLevel,o=e.schema[t],a=e.schemaPath+e.util.getProperty(t),l=e.errSchemaPath+"/"+t,c=!e.opts.allErrors,h="data"+(s||""),d=e.opts.$data&&o&&o.$data;i=d?(n+=" var schema"+r+" = "+e.util.getData(o.$data,s,e.dataPathArr)+"; ","schema"+r):o;var u="maxLength"==t?">":"<";n+="if ( ",d&&(n+=" ("+i+" !== undefined && typeof "+i+" != 'number') || "),!1===e.opts.unicode?n+=" "+h+".length ":n+=" ucs2length("+h+") ",n+=" "+u+" "+i+") { ";var g=t,p=p||[];p.push(n),n="",!1!==e.createErrors?(n+=" { keyword: '"+(g||"_limitLength")+"' , dataPath: (dataPath || '') + "+e.errorPath+" , schemaPath: "+e.util.toQuotedString(l)+" , params: { limit: "+i+" } ",!1!==e.opts.messages&&(n+=" , message: 'should NOT be ",n+="maxLength"==t?"longer":"shorter",n+=" than ",n+=d?"' + "+i+" + '":""+o,n+=" characters' "),e.opts.verbose&&(n+=" , schema: ",n+=d?"validate.schema"+a:""+o,n+=" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ";var m=n;return n=p.pop(),!e.compositeRule&&c?e.async?n+=" throw new ValidationError(["+m+"]); ":n+=" validate.errors = ["+m+"]; return false; ":n+=" var err = "+m+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",n+="} ",c&&(n+=" else { "),n}},function(e,t,i){"use strict";e.exports=function(e,t){var i,n=" ",r=e.level,s=e.dataLevel,o=e.schema[t],a=e.schemaPath+e.util.getProperty(t),l=e.errSchemaPath+"/"+t,c=!e.opts.allErrors,h="data"+(s||""),d=e.opts.$data&&o&&o.$data;i=d?(n+=" var schema"+r+" = "+e.util.getData(o.$data,s,e.dataPathArr)+"; ","schema"+r):o,n+="if ( ",d&&(n+=" ("+i+" !== undefined && typeof "+i+" != 'number') || "),n+=" Object.keys("+h+").length "+("maxProperties"==t?">":"<")+" "+i+") { ";var u=t,g=g||[];g.push(n),n="",!1!==e.createErrors?(n+=" { keyword: '"+(u||"_limitProperties")+"' , dataPath: (dataPath || '') + "+e.errorPath+" , schemaPath: "+e.util.toQuotedString(l)+" , params: { limit: "+i+" } ",!1!==e.opts.messages&&(n+=" , message: 'should NOT have ",n+="maxProperties"==t?"more":"fewer",n+=" than ",n+=d?"' + "+i+" + '":""+o,n+=" properties' "),e.opts.verbose&&(n+=" , schema: ",n+=d?"validate.schema"+a:""+o,n+=" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ";var p=n;return n=g.pop(),!e.compositeRule&&c?e.async?n+=" throw new ValidationError(["+p+"]); ":n+=" validate.errors = ["+p+"]; return false; ":n+=" var err = "+p+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",n+="} ",c&&(n+=" else { "),n}},function(e){e.exports=JSON.parse('{"$schema":"http://json-schema.org/draft-07/schema#","$id":"http://json-schema.org/draft-07/schema#","title":"Core schema meta-schema","definitions":{"schemaArray":{"type":"array","minItems":1,"items":{"$ref":"#"}},"nonNegativeInteger":{"type":"integer","minimum":0},"nonNegativeIntegerDefault0":{"allOf":[{"$ref":"#/definitions/nonNegativeInteger"},{"default":0}]},"simpleTypes":{"enum":["array","boolean","integer","null","number","object","string"]},"stringArray":{"type":"array","items":{"type":"string"},"uniqueItems":true,"default":[]}},"type":["object","boolean"],"properties":{"$id":{"type":"string","format":"uri-reference"},"$schema":{"type":"string","format":"uri"},"$ref":{"type":"string","format":"uri-reference"},"$comment":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"default":true,"readOnly":{"type":"boolean","default":false},"examples":{"type":"array","items":true},"multipleOf":{"type":"number","exclusiveMinimum":0},"maximum":{"type":"number"},"exclusiveMaximum":{"type":"number"},"minimum":{"type":"number"},"exclusiveMinimum":{"type":"number"},"maxLength":{"$ref":"#/definitions/nonNegativeInteger"},"minLength":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"pattern":{"type":"string","format":"regex"},"additionalItems":{"$ref":"#"},"items":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/schemaArray"}],"default":true},"maxItems":{"$ref":"#/definitions/nonNegativeInteger"},"minItems":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"uniqueItems":{"type":"boolean","default":false},"contains":{"$ref":"#"},"maxProperties":{"$ref":"#/definitions/nonNegativeInteger"},"minProperties":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"required":{"$ref":"#/definitions/stringArray"},"additionalProperties":{"$ref":"#"},"definitions":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"properties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"patternProperties":{"type":"object","additionalProperties":{"$ref":"#"},"propertyNames":{"format":"regex"},"default":{}},"dependencies":{"type":"object","additionalProperties":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/stringArray"}]}},"propertyNames":{"$ref":"#"},"const":true,"enum":{"type":"array","items":true,"minItems":1,"uniqueItems":true},"type":{"anyOf":[{"$ref":"#/definitions/simpleTypes"},{"type":"array","items":{"$ref":"#/definitions/simpleTypes"},"minItems":1,"uniqueItems":true}]},"format":{"type":"string"},"contentMediaType":{"type":"string"},"contentEncoding":{"type":"string"},"if":{"$ref":"#"},"then":{"$ref":"#"},"else":{"$ref":"#"},"allOf":{"$ref":"#/definitions/schemaArray"},"anyOf":{"$ref":"#/definitions/schemaArray"},"oneOf":{"$ref":"#/definitions/schemaArray"},"not":{"$ref":"#"}},"default":true}')},function(e,t,i){"use strict";var y={b:"\b",f:"\f",n:"\n",r:"\r",t:"\t",'"':'"',"/":"/","\\":"\\"},w="a".charCodeAt();t.parse=function(r,e,t){var n={},i=0,s=0,o=0,a=t&&t.bigint&&"undefined"!=typeof BigInt;return{data:l("",!0),pointers:n};function l(e,t){var i;c(),f(e,"value");var n=u();switch(n){case"t":d("rue"),i=!0;break;case"f":d("alse"),i=!1;break;case"n":d("ull"),i=null;break;case'"':i=h();break;case"[":i=function(e){c();var t=[],i=0;if("]"==u())return t;g();for(;;){var n=e+"/"+i;t.push(l(n)),c();var r=u();if("]"==r)break;","!=r&&v(),c(),i++}return t}(e);break;case"{":i=function(e){c();var t={};if("}"==u())return t;g();for(;;){var i=I();'"'!=u()&&v();var n=h(),r=e+"/"+S(n);C(r,"key",i),f(r,"keyEnd"),c(),":"!=u()&&v(),c(),t[n]=l(r),c();var s=u();if("}"==s)break;","!=s&&v(),c()}return t}(e);break;default:g(),0<="-0123456789".indexOf(n)?i=function(){var e="",t=!0;"-"==r[o]&&(e+=u());e+="0"==r[o]?u():m(),"."==r[o]&&(e+=u()+m(),t=!1);"e"!=r[o]&&"E"!=r[o]||(e+=u(),"+"!=r[o]&&"-"!=r[o]||(e+=u()),e+=m(),t=!1);var i=+e;return a&&t&&(i>Number.MAX_SAFE_INTEGER||i=r.length)throw new SyntaxError("Unexpected end of JSON input")}},t.stringify=function(e,t,i){if(A(e)){var n,r,s=0,g="object"==typeof i?i.space:i;switch(typeof g){case"number":var o=10r.index&&this.lastIndex--}return r},l||(RegExp.prototype.test=function(e){var t=o.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,i){function s(){}Function.prototype.bind||(Function.prototype.bind=function(t){var i=this;if("function"!=typeof i)throw new TypeError("Function.prototype.bind called on incompatible "+i);var n=u.call(arguments,1),r=function(){if(this instanceof r){var e=i.apply(this,n.concat(u.call(arguments)));return Object(e)===e?e:this}return i.apply(t,n.concat(u.call(arguments)))};return i.prototype&&(s.prototype=i.prototype,r.prototype=new s,s.prototype=null),r});var r,o,a,l,c,n=Function.prototype.call,h=Array.prototype,d=Object.prototype,u=h.slice,g=n.bind(d.toString),p=n.bind(d.hasOwnProperty);if((c=p(d,"__defineGetter__"))&&(r=n.bind(d.__defineGetter__),o=n.bind(d.__defineSetter__),a=n.bind(d.__lookupGetter__),l=n.bind(d.__lookupSetter__)),2!=[1,2].splice(0).length)if(function(){function e(e){var t=new Array(e+2);return t[0]=t[1]=0,t}var t,i=[];if(i.splice.apply(i,e(20)),i.splice.apply(i,e(26)),t=i.length,i.splice(5,0,"XXX"),i.length,t+1==i.length)return 1}()){var m=Array.prototype.splice;Array.prototype.splice=function(e,t){return arguments.length?m.apply(this,[void 0===e?0:e,void 0===t?this.length-e:t].concat(u.call(arguments,2))):[]}}else Array.prototype.splice=function(e,t){var i=this.length;0>>0;if("[object Function]"!=g(e))throw new TypeError;for(;++s>>0,s=Array(r),o=t;if("[object Function]"!=g(e))throw new TypeError(e+" is not a function");for(var a=0;a>>0,o=[],a=t;if("[object Function]"!=g(e))throw new TypeError(e+" is not a function");for(var l=0;l>>0,s=t;if("[object Function]"!=g(e))throw new TypeError(e+" is not a function");for(var o=0;o>>0,s=t;if("[object Function]"!=g(e))throw new TypeError(e+" is not a function");for(var o=0;o>>0;if("[object Function]"!=g(e))throw new TypeError(e+" is not a function");if(!r&&1==arguments.length)throw new TypeError("reduce of empty array with no initial value");var s,o=0;if(2<=arguments.length)s=t;else for(;;){if(o in n){s=n[o++];break}if(++o>=r)throw new TypeError("reduce of empty array with no initial value")}for(;o>>0;if("[object Function]"!=g(e))throw new TypeError(e+" is not a function");if(!r&&1==arguments.length)throw new TypeError("reduceRight of empty array with no initial value");var s,o=r-1;if(2<=arguments.length)s=t;else for(;;){if(o in n){s=n[o--];break}if(--o<0)throw new TypeError("reduceRight of empty array with no initial value")}for(;o in this&&(s=e.call(void 0,s,n[o],o,i)),o--;);return s}),Array.prototype.indexOf&&-1==[0,1].indexOf(1,2)||(Array.prototype.indexOf=function(e,t){var i=I&&"[object String]"==g(this)?this.split(""):Z(this),n=i.length>>>0;if(!n)return-1;var r=0;for(1>>0;if(!n)return-1;var r=n-1;for(1 ["+this.end.row+"/"+this.end.column+"]"},this.contains=function(e,t){return 0==this.compare(e,t)},this.compareRange=function(e){var t,i=e.end,n=e.start;return 1==(t=this.compare(i.row,i.column))?1==(t=this.compare(n.row,n.column))?2:0==t?1:0:-1==t?-2:-1==(t=this.compare(n.row,n.column))?-1:1==t?42:0},this.comparePoint=function(e){return this.compare(e.row,e.column)},this.containsRange=function(e){return 0==this.comparePoint(e.start)&&0==this.comparePoint(e.end)},this.intersects=function(e){var t=this.compareRange(e);return-1==t||0==t||1==t},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){"object"==typeof e?(this.start.column=e.column,this.start.row=e.row):(this.start.row=e,this.start.column=t)},this.setEnd=function(e,t){"object"==typeof e?(this.end.column=e.column,this.end.row=e.row):(this.end.row=e,this.end.column=t)},this.inside=function(e,t){return 0==this.compare(e,t)&&(!this.isEnd(e,t)&&!this.isStart(e,t))},this.insideStart=function(e,t){return 0==this.compare(e,t)&&!this.isEnd(e,t)},this.insideEnd=function(e,t){return 0==this.compare(e,t)&&!this.isStart(e,t)},this.compare=function(e,t){return this.isMultiLine()||e!==this.start.row?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:tthis.end.column?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 i={row:t+1,column:0};else if(this.end.rowt)var n={row:t+1,column:0};else if(this.start.row>=1)&&(e+=e);return i};var n=/^\s\s*/,r=/\s\s*$/;t.stringTrimLeft=function(e){return e.replace(n,"")},t.stringTrimRight=function(e){return e.replace(r,"")},t.copyObject=function(e){var t={};for(var i in e)t[i]=e[i];return t},t.copyArray=function(e){for(var t=[],i=0,n=e.length;iDate.now()-50)||(n=!1)},cancel:function(){n=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,i){"use strict";var T=e("../lib/event"),W=e("../lib/useragent"),_=e("../lib/dom"),H=e("../lib/lang"),L=e("../clipboard"),$=W.isChrome<18,V=W.isIE,N=63i+1?a.length:r,r+=s.length+1,s=s+"\n"+a}400C-1&&f[f.length-c]==e[e.length-c];)c++,s--;a-=c-1,l-=c-1;var h=o.length-c+1;return h<0&&(r=-h,h=0),o=o.slice(0,h),t||o||a||r||s||l?(p=!0,o&&!r&&!s&&!a&&!l||m?d.onTextInput(o):d.onTextInput(o,{extendLeft:r,extendRight:s,restoreStart:a,restoreEnd:l}),p=!1,f=e,C=i,I=n,A=l,o):""}function r(e){if(h)return x();if(e&&e.inputType){if("historyUndo"==e.inputType)return d.execCommand("undo");if("historyRedo"==e.inputType)return d.execCommand("redo")}var t=u.value,i=n(t,!0);(500=f.length&&t.value===f&&f&&t.selectionEnd!==I&&(d.selectAll(),b()))}),T.addListener(u,"input",r),T.addListener(u,"cut",o),T.addListener(u,"copy",a),T.addListener(u,"paste",y),"oncut"in u&&"oncopy"in u&&"onpaste"in u||T.addListener(e,"keydown",function(e){if((!W.isMac||e.metaKey)&&e.ctrlKey)switch(e.keyCode){case 67:a(e);break;case 86:y(e);break;case 88:o(e)}});var x=function(){if(h&&d.onCompositionUpdate&&!d.$readOnly){if(m)return R();if(h.useTextareaForIME)d.onCompositionUpdate(u.value);else{var e=u.value;n(e),h.markerRange&&(h.context&&(h.markerRange.start.column=h.selectionStart=h.context.compositionStartOffset),h.markerRange.end.column=h.markerRange.start.column+I-h.selectionStart+A)}}},k=function(e){d.onCompositionEnd&&!d.$readOnly&&(h=!1,d.onCompositionEnd(),d.off("mousedown",R),e&&r())};function R(){t=!0,u.blur(),u.focus(),t=!1}var E,B=H.delayedCall(x,50).schedule.bind(null,null);function G(){clearTimeout(E),E=setTimeout(function(){l&&(u.style.cssText=l,l=""),d.renderer.$isMousePressed=!1,d.renderer.$keepTextAreaAtCursor&&d.renderer.$moveTextAreaToCursor()},0)}T.addListener(u,"compositionstart",function(e){if(!h&&d.onCompositionStart&&!d.$readOnly&&(h={},!m)){setTimeout(x,0),d.on("mousedown",R);var t=d.getSelectionRange();t.end.row=t.start.row,t.end.column=t.start.column,h.markerRange=t,h.selectionStart=C,d.onCompositionStart(h),h.useTextareaForIME?(u.value="",f="",I=C=0):(u.msGetInputContext&&(h.context=u.msGetInputContext()),u.getInputContext&&(h.context=u.getInputContext()))}}),T.addListener(u,"compositionupdate",x),T.addListener(u,"keyup",function(e){27==e.keyCode&&u.value.lengththis.$focusTimeout)&&this.startSelect(this.mousedownEvent.getDocumentPosition())},this.onDoubleClick=function(e){var t=e.getDocumentPosition(),i=this.editor,n=i.session.getBracketRange(t);n?(n.isEmpty()&&(n.start.column--,n.end.column++),this.setState("select")):(n=i.selection.getWordRange(t.row,t.column),this.setState("selectByWords")),this.$clickSelection=n,this.select()},this.onTripleClick=function(e){var t=e.getDocumentPosition(),i=this.editor;this.setState("selectByLines");var n=i.getSelectionRange();n.isMultiLine()&&n.contains(t.row,t.column)?(this.$clickSelection=i.selection.getLineRange(n.start.row),this.$clickSelection.end=i.selection.getLineRange(n.end.row).end):this.$clickSelection=i.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()){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 i=this.$lastScroll,n=e.domEvent.timeStamp,r=n-i.t,s=r?e.wheelX/r:i.vx,o=r?e.wheelY/r:i.vy;r<550&&(s=(s+i.vx)/2,o=(o+i.vy)/2);var a=Math.abs(s/o),l=!1;if(1<=a&&t.renderer.isScrollableBy(e.wheelX*e.speed,0)&&(l=!0),a<=1&&t.renderer.isScrollableBy(0,e.wheelY*e.speed)&&(l=!0),l)i.allowed=n;else if(n-i.allowed<550){Math.abs(s)<=1.5*Math.abs(i.vx)&&Math.abs(o)<=1.5*Math.abs(i.vy)?(l=!0,i.allowed=n):i.allowed=0}return i.t=n,i.vx=s,i.vy=o,l?(t.renderer.scrollBy(e.wheelX*e.speed,e.wheelY*e.speed),e.stop()):void 0}}}).call(n.prototype),t.DefaultHandlers=n}),ace.define("ace/tooltip",["require","exports","module","ace/lib/oop","ace/lib/dom"],function(e,t,i){"use strict";e("./lib/oop");var n=e("./lib/dom");function r(e){this.isOpen=!1,this.$element=null,this.$parentNode=e}(function(){this.$init=function(){return this.$element=n.createElement("div"),this.$element.className="ace_tooltip",this.$element.style.display="none",this.$parentNode.appendChild(this.$element),this.$element},this.getElement=function(){return this.$element||this.$init()},this.setText=function(e){this.getElement().textContent=e},this.setHtml=function(e){this.getElement().innerHTML=e},this.setPosition=function(e,t){this.getElement().style.left=e+"px",this.getElement().style.top=t+"px"},this.setClassName=function(e){n.addCssClass(this.getElement(),e)},this.show=function(e,t,i){null!=e&&this.setText(e),null!=t&&null!=i&&this.setPosition(t,i),this.isOpen||(this.getElement().style.display="block",this.isOpen=!0)},this.hide=function(){this.isOpen&&(this.getElement().style.display="none",this.isOpen=!1)},this.getHeight=function(){return this.getElement().offsetHeight},this.getWidth=function(){return this.getElement().offsetWidth},this.destroy=function(){this.isOpen=!1,this.$element&&this.$element.parentNode&&this.$element.parentNode.removeChild(this.$element)}}).call(r.prototype),t.Tooltip=r}),ace.define("ace/mouse/default_gutter_handler",["require","exports","module","ace/lib/dom","ace/lib/oop","ace/lib/event","ace/tooltip"],function(e,t,i){"use strict";var n=e("../lib/dom"),r=e("../lib/oop"),s=e("../lib/event"),o=e("../tooltip").Tooltip;function p(e){o.call(this,e)}r.inherits(p,o),function(){this.setPosition=function(e,t){var i=window.innerWidth||document.documentElement.clientWidth,n=window.innerHeight||document.documentElement.clientHeight,r=this.getWidth(),s=this.getHeight();i<(e+=15)+r&&(e-=e+r-i),n<(t+=15)+s&&(t-=20+s),o.prototype.setPosition.call(this,e,t)}}.call(p.prototype),t.GutterHandler=function(o){var i,a,l,c=o.editor,h=c.renderer.$gutterLayer,d=new p(c.container);function u(){i=i&&clearTimeout(i),l&&(d.hide(),l=null,c._signal("hideGutterTooltip",d),c.removeEventListener("mousewheel",u))}function g(e){d.setPosition(e.x,e.y)}o.editor.setDefaultHandler("guttermousedown",function(e){if(c.isFocused()&&0==e.getButton()&&"foldWidgets"!=h.getRegion(e)){var t=e.getDocumentPosition().row,i=c.session.selection;if(e.getShiftKey())i.selectTo(t,0);else{if(2==e.domEvent.detail)return c.selectAll(),e.preventDefault();o.$clickSelection=c.selection.getLineRange(t)}return o.setState("selectByLines"),o.captureMouse(e),e.preventDefault()}}),o.editor.setDefaultHandler("guttermousemove",function(e){var t=e.domEvent.target||e.domEvent.srcElement;if(n.hasCssClass(t,"ace_fold-widget"))return u();l&&o.$tooltipFollowsMouse&&g(e),a=e,i=i||setTimeout(function(){i=null,a&&!o.isMousePressed?function(){var e=a.getDocumentPosition().row,t=h.$annotations[e];if(!t)return u();if(e==c.session.getLength()){var i=c.renderer.pixelToScreenCoordinates(0,a.y).row,n=a.$pos;if(i>c.session.documentToScreenRow(n.row,n.column))return u()}if(l!=t)if(l=t.text.join("
    "),d.setHtml(l),d.show(),c._signal("showGutterTooltip",d),c.on("mousewheel",u),o.$tooltipFollowsMouse)g(a);else{var r=a.domEvent.target.getBoundingClientRect(),s=d.getElement().style;s.left=r.right+"px",s.top=r.bottom+"px"}}():u()},50)}),s.addListener(c.renderer.$gutter,"mouseout",function(e){a=null,l&&!i&&(i=setTimeout(function(){i=null,u()},50))}),c.on("changeSession",u)}}),ace.define("ace/mouse/mouse_event",["require","exports","module","ace/lib/event","ace/lib/useragent"],function(e,t,i){"use strict";var n=e("../lib/event"),r=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(){n.stopPropagation(this.domEvent),this.propagationStopped=!0},this.preventDefault=function(){n.preventDefault(this.domEvent),this.defaultPrevented=!0},this.stop=function(){this.stopPropagation(),this.preventDefault()},this.getDocumentPosition=function(){return this.$pos||(this.$pos=this.editor.renderer.screenToTextCoordinates(this.clientX,this.clientY)),this.$pos},this.inSelection=function(){if(null!==this.$inSelection)return this.$inSelection;var e=this.editor.getSelectionRange();if(e.isEmpty())this.$inSelection=!1;else{var t=this.getDocumentPosition();this.$inSelection=e.contains(t.row,t.column)}return this.$inSelection},this.getButton=function(){return n.getButton(this.domEvent)},this.getShiftKey=function(){return this.domEvent.shiftKey},this.getAccelKey=r.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,i){"use strict";var w=e("../lib/dom"),S=e("../lib/event"),x=e("../lib/useragent");function n(t){var g=t.editor,n=w.createElement("img");n.src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==",x.isOpera&&(n.style.cssText="width:1px;height:1px;position:fixed;top:0;left:0;z-index:2147483647;opacity:0;");["dragWait","dragWaitEnd","startDrag","dragReadyEnd","onMouseDrag"].forEach(function(e){t[e]=this[e]},this),g.addEventListener("mousedown",this.onMouseDown.bind(t));var i,p,m,e,r,o,s,a,f,l,c,h=g.container,d=0;function u(){var e,t,i,n,r,s=o;o=g.renderer.screenToTextCoordinates(p,m),e=o,t=s,i=Date.now(),n=!t||e.row!=t.row,r=!t||e.column!=t.column,!l||n||r?(g.moveCursorToPosition(e),l=i,c={x:p,y:m}):5this.editor.getDragDelay()&&this.startDrag()},this.dragWaitEnd=function(){this.editor.container.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;e.container.draggable=!0,e.renderer.$cursorLayer.setBlinking(!1),e.setStyle("ace_dragging");var t=x.isWin?"default":"move";e.renderer.setCursorStyle(t),this.setState("dragReady")},this.onMouseDrag=function(e){var t=this.editor.container;x.isIE&&"dragReady"==this.state&&3i&&(v=-1),I=e.clientX=o,A=e.clientY=a,E=B=0;var l=new Z(e,C);if(w=l.getDocumentPosition(),r-v<500&&1==t.length&&!k)R++,e.preventDefault(),e.button=0,function(){y=null,clearTimeout(y),C.selection.moveToPosition(w);var e=2<=R?C.selection.getLineRange(w.row):C.session.getBracketRange(w);e&&!e.isEmpty()?C.selection.setRange(e):C.selection.selectWord(),x="wait"}();else{R=0;var c=C.selection.cursor,h=C.selection.isEmpty()?c:C.selection.anchor,d=C.renderer.$cursorLayer.getPixelPosition(c,!0),u=C.renderer.$cursorLayer.getPixelPosition(h,!0),g=C.renderer.scroller.getBoundingClientRect(),p=function(e,t){return(e/=n)*e+(t=t/i-.75)*t};if(e.clientX=e){for(n=l+1;n=e;)n++;for(r=l,s=n-1;r=t.length||(r=i[n-1])!=g&&r!=p||(s=t[n+1])!=g&&s!=p?y:(C&&(s=p),s==r?s:y);case E:return(r=0>8;return 0==i?191x&&t[a]t.row||e.row==t.row&&e.column>t.column},this.getRange=function(){var e=this.anchor,t=this.lead;return this.$isEmpty?a.fromPoints(t,t):this.isBackwards()?a.fromPoints(t,e):a.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 i=t?e.end:e.start,n=t?e.start:e.end;this.$setSelection(i.row,i.column,n.row,n.column)},this.$setSelection=function(e,t,i,n){var r=this.$isEmpty,s=this.inMultiSelectMode;this.$silent=!0,this.$cursorChanged=this.$anchorChanged=!1,this.anchor.setPosition(e,t),this.cursor.setPosition(i,n),this.$isEmpty=!a.comparePoints(this.anchor,this.cursor),this.$silent=!1,this.$cursorChanged&&this._emit("changeCursor"),(this.$cursorChanged||this.$anchorChanged||r!=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(void 0===t){var i=e||this.lead;e=i.row,t=i.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 i,n="number"==typeof e?e:this.lead.row,r=this.session.getFoldLine(n);return i=r?(n=r.start.row,r.end.row):n,!0===t?new a(n,0,i,this.session.getLine(i).length):new a(n,0,i+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,i){var n=e.column,r=e.column+t;return i<0&&(n=e.column-t,r=e.column),this.session.isTabStop(e)&&this.doc.getLine(e.row).slice(n,r).split(" ").length-1==t},this.moveCursorLeft=function(){var e,t=this.lead.getPosition();if(e=this.session.getFoldAt(t.row,t.column,-1))this.moveCursorTo(e.start.row,e.start.column);else if(0===t.column)0=i.length)return this.moveCursorTo(e,i.length),this.moveCursorRight(),void(ec){var f=e.substring(c,m-p.length);d.type==u?d.value+=f:(d.type&&l.push(d),d={type:u,value:f})}for(var C=0;CI){for(h>2*e.length&&this.reportError("infinite loop with in ace tokenizer",{startState:t,line:e});c=this.$rowTokens.length;){if(this.$row+=1,e=e||this.$session.getLength(),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,i=e[t].start;if(void 0!==i)return i;for(i=0;0e.length&&(v=e.length)}),l==1/0&&(l=v,a=o=!1),t&&l%c!=0&&(l=Math.floor(l/c)*c),A(a?C:f)},this.toggleBlockComment=function(e,t,i,n){var r=this.blockComment;if(r){!r.start&&r[0]&&(r=r[0]);var s,o,a=(g=new m(t,n.row,n.column)).getCurrentToken(),l=(t.selection,t.selection.toOrientedRange());if(a&&/comment/.test(a.type)){for(var c,h;a&&/comment/.test(a.type);){if(-1!=(p=a.value.indexOf(r.start))){var d=g.getCurrentTokenRow(),u=g.getCurrentTokenColumn()+p;c=new f(d,u,d,u+r.start.length);break}a=g.stepBackward()}var g;for(a=(g=new m(t,n.row,n.column)).getCurrentToken();a&&/comment/.test(a.type);){var p;if(-1!=(p=a.value.indexOf(r.end))){d=g.getCurrentTokenRow(),u=g.getCurrentTokenColumn()+p;h=new f(d,u,d,u+r.end.length);break}a=g.stepForward()}h&&t.remove(h),c&&(t.remove(c),s=c.start.row,o=-r.start.length)}else o=r.start.length,s=i.start.row,t.insert(i.end,r.end),t.insert(i.start,r.start);l.start.row==s&&(l.start.column+=o),l.end.row==s&&(l.end.column+=o),t.selection.fromOrientedRange(l)}},this.getNextLineIndent=function(e,t,i){return this.$getIndent(t)},this.checkOutdent=function(e,t,i){return!1},this.autoOutdent=function(e,t,i){},this.$getIndent=function(e){return e.match(/^\s*/)[0]},this.createWorker=function(e){return null},this.createModeDelegates=function(e){for(var n in this.$embeds=[],this.$modes={},e)if(e[n]){var t=e[n],i=t.prototype.$id,r=o.$modes[i];r||(o.$modes[i]=r=new t),o.$modes[n]||(o.$modes[n]=r),this.$embeds.push(n),this.$modes[n]=r}var s=["toggleBlockComment","toggleCommentLines","getNextLineIndent","checkOutdent","autoOutdent","transformAction","getCompletions"];for(n=0;nthis.row)){var t=function(e,t,i){var n="insert"==e.action,r=(n?1:-1)*(e.end.row-e.start.row),s=(n?1:-1)*(e.end.column-e.start.column),o=e.start,a=n?o:e.end;if(l(t,o,i))return{row:t.row,column:t.column};if(l(a,t,!i))return{row:t.row+r,column:t.column+(t.row==a.row?s:0)};return{row:o.row,column:o.column}}(e,{row:this.row,column:this.column},this.$insertRight);this.setPosition(t.row,t.column,!0)}},this.setPosition=function(e,t,i){var n;if(n=i?{row:e,column:t}:this.$clipPositionToDocument(e,t),this.row!=n.row||this.column!=n.column){var r={row:this.row,column:this.column};this.row=n.row,this.column=n.column,this._signal("change",{old:r,value:n})}},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 i={};return e>=this.document.getLength()?(i.row=Math.max(0,this.document.getLength()-1),i.column=this.document.getLine(i.row).length):e<0?(i.row=0,i.column=0):(i.row=e,i.column=Math.min(this.document.getLine(i.row).length,Math.max(0,t))),t<0&&(i.column=0),i}}).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,i){"use strict";function n(e){this.$lines=[""],0===e.length?this.$lines=[""]:Array.isArray(e)?this.insertMergedLines({row:0,column:0},e):this.insert({row:0,column:0},e)}var r=e("./lib/oop"),s=e("./apply_delta").applyDelta,o=e("./lib/event_emitter").EventEmitter,h=e("./range").Range,a=e("./anchor").Anchor;(function(){r.implement(this,o),this.setValue=function(e){var t=this.getLength()-1;this.remove(new h(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 a(this,e,t)},0==="aaa".split(/a/).length?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){this.$newLineMode!==e&&(this.$newLineMode=e,this._signal("changeNewLineMode"))},this.getNewLineMode=function(){return this.$newLineMode},this.isNewLine=function(e){return"\r\n"==e||"\r"==e||"\n"==e},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))[0]=(t[0]||"").substring(e.start.column);var i=t.length-1;e.end.row-e.start.row==i&&(t[i]=t[i].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 i=this.clippedPos(e.row,e.column),n=this.pos(e.row,e.column+t.length);return this.applyDelta({start:i,end:n,action:"insert",lines:[t]},!0),this.clonePos(n)},this.clippedPos=function(e,t){var i=this.getLength();void 0===e?e=i:e<0?e=0:i<=e&&(e=i-1,t=void 0);var n=this.getLine(e);return null==t&&(t=n.length),{row:e,column:t=Math.min(Math.max(t,0),n.length)}},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){var i=0;i=(e=Math.min(Math.max(e,0),this.getLength()))e+1&&(this.currentLine=e+1)):this.currentLine==e&&(this.currentLine=e+1),this.lines[e]=n.tokens}}).call(n.prototype),t.BackgroundTokenizer=n}),ace.define("ace/search_highlight",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"],function(e,t,i){"use strict";function n(e,t,i){this.setRegexp(e),this.clazz=t,this.type=i||"text"}var c=e("./lib/lang"),h=(e("./lib/oop"),e("./range").Range);(function(){this.MAX_RANGES=500,this.setRegexp=function(e){this.regExp+""!=e+""&&(this.regExp=e,this.cache=[])},this.update=function(e,t,i,n){if(this.regExp)for(var r=n.firstRow,s=n.lastRow,o=r;o<=s;o++){var a=this.cache[o];null==a&&((a=c.getMatchOffsets(i.getLine(o),this.regExp)).length>this.MAX_RANGES&&(a=a.slice(0,this.MAX_RANGES)),a=a.map(function(e){return new h(o,e.offset,o,e.offset+e.length)}),this.cache[o]=a.length?a:"");for(var l=a.length;l--;)t.drawSingleLineMarker(e,a[l].toScreenRange(i),this.clazz,n)}}}).call(n.prototype),t.SearchHighlight=n}),ace.define("ace/edit_session/fold_line",["require","exports","module","ace/range"],function(e,t,i){"use strict";var n=e("../range").Range;function c(e,t){this.foldData=e,Array.isArray(t)?this.folds=t:t=this.folds=[t];var i=t[t.length-1];this.range=new n(t[0].start.row,t[0].start.column,i.end.row,i.end.column),this.start=this.range.start,this.end=this.range.end,this.folds.forEach(function(e){e.setFoldLine(this)},this)}(function(){this.shiftRow=function(t){this.start.row+=t,this.end.row+=t,this.folds.forEach(function(e){e.start.row+=t,e.end.row+=t})},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)}),0=this.start.row&&e<=this.end.row},this.walk=function(e,t,i){var n,r,s=0,o=this.folds,a=!0;null==t&&(t=this.end.row,i=this.end.column);for(var l=0;lt||i[i.length-1].start.row=n)break}if("insert"==e.action)for(var l=r-n,c=-t.column+i.column;on)break;if(h.start.row==n&&h.start.column>=t.column&&(h.start.column==t.column&&this.$bias<=0||(h.start.column+=c,h.start.row+=l)),h.end.row==n&&h.end.column>=t.column){if(h.end.column==t.column&&this.$bias<0)continue;h.end.column==t.column&&0h.start.column&&h.end.column==s[o+1].start.column&&(h.end.column-=c),h.end.column+=c,h.end.row+=l}}else for(l=n-r,c=t.column-i.column;or)break;h.end.rowt.column)&&(h.end.column=t.column,h.end.row=t.row):(h.end.column+=c,h.end.row+=l):h.end.row>r&&(h.end.row+=l),h.start.rowt.column)&&(h.start.column=t.column,h.start.row=t.row):(h.start.column+=c,h.start.row+=l):h.start.row>r&&(h.start.row+=l)}if(0!=l&&o=e)return r;if(r.end.row>e)return null}return null},this.getNextFoldLine=function(e,t){var i=this.$foldData,n=0;for(t&&(n=i.indexOf(t)),-1==n&&(n=0);n=e)return r}return null},this.getFoldedRowCount=function(e,t){for(var i=this.$foldData,n=t-e+1,r=0;rl)break}while(r&&o.test(r.type));r=n.stepBackward()}else r=n.getCurrentToken();return a.end.row=n.getCurrentTokenRow(),a.end.column=n.getCurrentTokenColumn()+r.value.length-2,a}},this.foldAll=function(e,t,i){null==i&&(i=1e5);var n=this.foldWidgets;if(n){t=t||this.getLength();for(var r=e=e||0;r=e){r=s.end.row;try{var o=this.addFold("...",s);o&&(o.collapseChildren=i)}catch(e){}}}}},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){"manual"==(this.$foldStyle=e)&&this.unfold();var t=this.$foldMode;this.$setFolding(null),this.$setFolding(t)}},this.$setFolding=function(e){this.$foldMode!=e&&(this.$foldMode=e, +this.off("change",this.$updateFoldWidgets),this.off("tokenizerUpdate",this.$tokenizerUpdateFoldWidgets),this._signal("changeAnnotation"),e&&"manual"!=this.$foldStyle?(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.foldWidgets=null)},this.getParentFoldRangeData=function(e,t){var i=this.foldWidgets;if(!i||t&&i[e])return{};for(var n,r=e-1;0<=r;){var s=i[r];if(null==s&&(s=i[r]=this.getFoldWidget(r)),"start"==s){var o=this.getFoldWidgetRange(r);if(n=n||o,o&&o.end.row>=e)break}r--}return{range:-1!==r&&o,firstRange:n}},this.onFoldWidgetClick=function(e,t){var i={children:(t=t.domEvent).shiftKey,all:t.ctrlKey||t.metaKey,siblings:t.altKey};if(!this.$toggleFoldWidget(e,i)){var n=t.target||t.srcElement;n&&/ace_fold-widget/.test(n.className)&&(n.className+=" ace_invalid")}},this.$toggleFoldWidget=function(e,t){if(this.getFoldWidget){var i=this.getFoldWidget(e),n=this.getLine(e),r="end"===i?-1:1,s=this.getFoldAt(e,-1==r?0:n.length,r);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))&&o.isEqual(s.range))return this.removeFold(s),s;if(t.siblings){var a=this.getParentFoldRangeData(e);if(a.range)var l=a.range.start.row+1,c=a.range.end.row;this.foldAll(l,c,t.all?1e4:0)}else t.children?(c=o?o.end.row:this.getLength(),this.foldAll(e+1,c,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 i=this.$toggleFoldWidget(t,{});if(!i){var n=this.getParentFoldRangeData(t,!0);if(i=n.range||n.firstRange){t=i.start.row;var r=this.getFoldAt(t,this.getLine(t).length,1);r?this.removeFold(r):this.addFold("...",i)}}},this.updateFoldWidgets=function(e){var t=e.start.row,i=e.end.row-t;if(0==i)this.foldWidgets[t]=null;else if("remove"==e.action)this.foldWidgets.splice(t,1+i,null);else{var n=Array(1+i);n.unshift(t,1),this.foldWidgets.splice.apply(this.foldWidgets,n)}},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)}}}),ace.define("ace/edit_session/bracket_match",["require","exports","module","ace/token_iterator","ace/range"],function(e,t,i){"use strict";var d=e("../token_iterator").TokenIterator,a=e("../range").Range;t.BracketMatch=function(){this.findMatchingBracket=function(e,t){if(0==e.column)return null;var i=t||this.getLine(e.row).charAt(e.column-1);if(""==i)return null;var n=i.match(/([\(\[\{])|([\)\]\}])/);return n?n[1]?this.$findClosingBracket(n[1],e):this.$findOpeningBracket(n[2],e):null},this.getBracketRange=function(e){var t,i=this.getLine(e.row),n=!0,r=i.charAt(e.column-1),s=r&&r.match(/([\(\[\{])|([\)\]\}])/);if(s||(r=i.charAt(e.column),e={row:e.row,column:e.column+1},s=r&&r.match(/([\(\[\{])|([\)\]\}])/),n=!1),!s)return null;if(s[1]){if(!(o=this.$findClosingBracket(s[1],e)))return null;t=a.fromPoints(e,o),n||(t.end.column++,t.start.column--),t.cursor=t.end}else{var o;if(!(o=this.$findOpeningBracket(s[2],e)))return null;t=a.fromPoints(o,e),n||(t.start.column++,t.end.column--),t.cursor=t.start}return t},this.$brackets={")":"(","(":")","]":"[","[":"]","{":"}","}":"{","<":">",">":"<"},this.$findOpeningBracket=function(e,t,i){var n=this.$brackets[e],r=1,s=new d(this,t.row,t.column),o=s.getCurrentToken();if(o=o||s.stepForward()){i=i||new RegExp("(\\.?"+o.type.replace(".","\\.").replace("rparen",".paren").replace(/\b(?:end)\b/,"(?:start|begin|end)")+")+");for(var a=t.column-s.getCurrentTokenColumn()-2,l=o.value;;){for(;0<=a;){var c=l.charAt(a);if(c==n){if(0==--r)return{row:s.getCurrentTokenRow(),column:a+s.getCurrentTokenColumn()}}else c==e&&(r+=1);--a}for(;(o=s.stepBackward())&&!i.test(o.type););if(null==o)break;a=(l=o.value).length-1}return null}},this.$findClosingBracket=function(e,t,i){var n=this.$brackets[e],r=1,s=new d(this,t.row,t.column),o=s.getCurrentToken();if(o=o||s.stepForward()){i=i||new RegExp("(\\.?"+o.type.replace(".","\\.").replace("lparen",".paren").replace(/\b(?:start|begin)\b/,"(?:start|begin|end)")+")+");for(var a=t.column-s.getCurrentTokenColumn();;){for(var l=o.value,c=l.length;a>1,s=e[r];if(st&&(t=e.screenWidth)}),this.lineWidgetWidth=t},this.$computeWidth=function(e){if(this.$modified||e){if(this.$modified=!1,this.$useWrapMode)return this.screenWidth=this.$wrapLimit;for(var t=this.doc.getAllLines(),i=this.$rowLengthCache,n=0,r=0,s=this.$foldData[r],o=s?s.start.row:1/0,a=t.length,l=0;ln&&(n=i[l])}this.screenWidth=n}},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){this.$fromUndo=!0;for(var i=e.length-1;-1!=i;i--){var n=e[i];"insert"==n.action||"remove"==n.action?this.doc.revertDelta(n):n.folds&&this.addFolds(n.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){this.$fromUndo=!0;for(var i=0;ie.end.column&&(s.start.column+=c),s.end.row==e.end.row&&s.end.column>e.end.column&&(s.end.column+=c)),o&&s.start.row>=e.end.row&&(s.start.row+=o,s.end.row+=o)}if(s.end=this.insert(s.start,n),r.length){var a=e.start,l=s.start,c=(o=l.row-a.row,l.column-a.column);this.addFolds(r.map(function(e){return(e=e.clone()).start.row==a.row&&(e.start.column+=c),e.end.row==a.row&&(e.end.column+=c),e.start.row+=o,e.end.row+=o,e}))}return s},this.indentRows=function(e,t,i){i=i.replace(/\t/g,this.getTabString());for(var n=e;n<=t;n++)this.doc.insertInLine({row:n,column:0},i)},this.outdentRows=function(e){for(var t=e.collapseRows(),i=new h(0,0,0,0),n=this.getTabSize(),r=t.start.row;r<=t.end.row;++r){var s=this.getLine(r);i.start.row=r,i.end.row=r;for(var o=0;othis.doc.getLength()-1)return 0;n=r-t}else{e=this.$clipRowToDocument(e);n=(t=this.$clipRowToDocument(t))-e+1}var s=new h(e,0,t,Number.MAX_VALUE),o=this.getFoldsInRange(s).map(function(e){return(e=e.clone()).start.row+=n,e.end.row+=n,e}),a=0==i?this.doc.getLines(e,t):this.doc.removeFullLines(e,t);return this.doc.insertFullLines(e+n,a),o.length&&this.addFolds(o),n},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){if(t=Math.max(0,t),e<0)t=e=0;else{var i=this.doc.getLength();t=i<=e?(e=i-1,this.doc.getLine(i-1).length):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){if(this.$useWrapMode=e,this.$modified=!0,this.$resetRowCache(0),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){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 i=this.$wrapLimitRange;i.max<0&&(i={min:t,max:t});var n=this.$constrainWrapLimit(e,i.min,i.max);return n!=this.$wrapLimit&&1=r.row&&m.shiftRow(-a)}o=s}else{var u=Array(a);u.unshift(s,0);var g=t?this.$wrapData:this.$rowLengthCache;g.splice.apply(g,u);c=this.$foldData,h=0;if(m=this.getFoldLine(s)){var p=m.range.compareInside(n.row,n.column);0==p?(m=m.split(n.row,n.column))&&(m.shiftRow(a),m.addRemoveChars(o,0,r.column-n.column)):-1==p&&(m.addRemoveChars(s,0,r.column-n.column),m.shiftRow(a)),h=c.indexOf(m)+1}for(;h=s&&m.shiftRow(a)}}else a=Math.abs(e.start.column-e.end.column),"remove"===i&&(l=this.getFoldsInRange(e),this.removeFolds(l),a=-a),(m=this.getFoldLine(s))&&m.addRemoveChars(s,n.column,a);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),l},this.$updateRowLengthCache=function(e,t,i){this.$rowLengthCache[e]=null,this.$rowLengthCache[t]=null},this.$updateWrapData=function(e,t){var o,i,a=this.doc.getAllLines(),n=this.getTabSize(),r=this.$wrapData,s=this.$wrapLimit,l=e;for(t=Math.min(t,a.length-1);l<=t;)(i=this.getFoldLine(l,i))?(o=[],i.walk(function(e,t,i,n){var r;if(null!=e){(r=this.$getDisplayTokens(e,o.length))[0]=p;for(var s=1;s>2)),a-1);gc[d-1]}else u=!d;for(var g=this.getLength()-1,p=this.getNextFoldLine(s),m=p?p.start.row:1/0;a<=e&&!(el[h-1]}else d=!h;for(var u=this.getNextFoldLine(a),g=u?u.start.row:1/0;a=f[C];)r++,C++;p=p.substring(f[C-1]||0,p.length),m=0g||(r.push(o=new b(c,g,c+a-1,p)),2A&&r[h].end.row==i.end.row;)h--;for(r=r.slice(m,h+1),m=0,h=r.length;m=r.length)break;d.lastIndex=o+=1}if(n.index+a>t)break;s.push(n.index,a)}for(var l=s.length-1;0<=l;l-=2){var c=s[l-1];if(i(e,c,e,c+(a=s[l])))return!0}};else a=function(e,t,i){var n,r,s=h.getLine(e);for(d.lastIndex=t;r=d.exec(s);){var o=r[0].length;if(i(e,n=r.index,e,n+o))return!0;if(!o&&(d.lastIndex=n+=1,n>=s.length))return!1}};return{forEach:o}}}).call(n.prototype),t.Search=n}),ace.define("ace/keyboard/hash_handler",["require","exports","module","ace/lib/keys","ace/lib/useragent"],function(e,t,i){"use strict";var a=e("../lib/keys"),n=e("../lib/useragent"),l=a.KEY_MODS;function r(e,t){this.platform=t||(n.isMac?"mac":"win"),this.commands={},this.commandKeyBinding={},this.addCommands(e),this.$singleCommand=!0}function s(e,t){r.call(this,e,t),this.$singleCommand=!1}s.prototype=r.prototype,function(){function o(e){return"object"==typeof e&&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).bindKey&&this._buildKeyHash(e)},this.removeCommand=function(e,t){var i=e&&("string"==typeof e?e:e.name);e=this.commands[i],t||delete this.commands[i];var n=this.commandKeyBinding;for(var r in n){var s=n[r];if(s==e)delete n[r];else if(Array.isArray(s)){var o=s.indexOf(e);-1!=o&&(s.splice(o,1),1==s.length&&(n[r]=s[0]))}}},this.bindKey=function(e,s,o){if("object"==typeof e&&e&&(null==o&&(o=e.position),e=e[this.platform]),e)return"function"==typeof s?this.addCommand({exec:s,bindKey:e,name:s.name||e}):void e.split("|").forEach(function(e){var n="";if(-1!=e.indexOf(" ")){var t=e.split(/\s+/);e=t.pop(),t.forEach(function(e){var t=this.parseKeys(e),i=l[t.hashId]+t.key;n+=(n?" ":"")+i,this._addCommandToBinding(n,"chainKeys")},this),n+=" "}var i=this.parseKeys(e),r=l[i.hashId]+i.key;this._addCommandToBinding(n+r,s,o)},this)},this._addCommandToBinding=function(e,t,i){var n,r=this.commandKeyBinding;if(t)if(!r[e]||this.$singleCommand)r[e]=t;else{Array.isArray(r[e])?-1!=(n=r[e].indexOf(t))&&r[e].splice(n,1):r[e]=[r[e]],"number"!=typeof i&&(i=o(t));var s=r[e];for(n=0;nr?r+1:r,e.selection.moveCursorTo(i.row,r))},multiSelectAction:"forEach",readOnly:!0},{name:"invertSelection",description:"Invert selection",bindKey:r(null,null),exec:function(e){var t=e.session.doc.getLength()-1,i=e.session.doc.getLine(t).length,n=e.selection.rangeList.ranges,r=[];n.length<1&&(n=[e.selection.getRange()]);for(var s=0;s=r.lastRow||n.end.row<=r.firstRow)&&this.renderer.scrollSelectionIntoView(this.selection.anchor,this.selection.lead)}"animate"==i&&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){var t=this.prevOp,i=this.$mergeableCommands,n=t.command&&e.command.name==t.command.name;if("insertstring"==e.command.name){var r=e.args;void 0===this.mergeNextCommand&&(this.mergeNextCommand=!0),n=n&&this.mergeNextCommand&&(!/\s/.test(r)||/\s/.test(t.args)),this.mergeNextCommand=!0}else n=n&&-1!==i.indexOf(e.command.name);"always"!=this.$mergeUndoDeltas&&2e3a.search(/\S|$/)){var l=a.substr(r.column).search(/\S|$/);i.doc.removeInLine(r.row,r.column,r.column+l)}}this.clearSelection();var c=r.column,h=i.getState(r.row),d=(a=i.getLine(r.row),n.checkOutdent(h,a,e));if(i.insert(r,e),s&&s.selection&&(2==s.selection.length?this.selection.setSelectionRange(new m(r.row,c+s.selection[0],r.row,c+s.selection[1])):this.selection.setSelectionRange(new m(r.row+s.selection[0],s.selection[1],r.row+s.selection[2],s.selection[3]))),i.getDocument().isNewLine(e)){var u=n.getNextLineIndent(h,a.slice(0,r.column),i.getTabString());i.insert({row:r.row+1,column:0},u)}d&&n.autoOutdent(h,i,r.row)},this.onTextInput=function(e,t){if(!t)return this.keyBinding.onTextInput(e);this.startOperation({command:{name:"insertstring"}});var i=this.applyComposition.bind(this,e,t);this.selection.rangeCount?this.forEachSelection(i):i(),this.endOperation()},this.applyComposition=function(e,t){var i;(t.extendLeft||t.extendRight)&&((i=this.selection.getRange()).start.column-=t.extendLeft,i.end.column+=t.extendRight,this.selection.setRange(i),e||i.isEmpty()||this.remove());!e&&this.selection.isEmpty()||this.insert(e,!0),(t.restoreStart||t.restoreEnd)&&((i=this.selection.getRange()).start.column-=t.restoreStart,i.end.column-=t.restoreEnd,this.selection.setRange(i))},this.onCommandKey=function(e,t,i){return this.keyBinding.onCommandKey(e,t,i)},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()&&("left"==e?this.selection.selectLeft():this.selection.selectRight());var t=this.getSelectionRange();if(this.getBehavioursEnabled()){var i=this.session,n=i.getState(t.start.row),r=i.getMode().transformAction(n,"deletion",this,i,t);if(0===t.end.column){var s=i.getTextRange(t);if("\n"==s[s.length-1]){var o=i.getLine(t.end.row);/^\s+$/.test(o)&&(t.end.column=o.length)}}r&&(t=r)}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()){var e=this.getCursorPosition(),t=e.column;if(0!==t){var i,n,r=this.session.getLine(e.row);n=tt.toLowerCase()?1:0});var r=new m(0,0,0,0);for(n=e.first;n<=e.last;n++){var s=t.getLine(n);r.start.row=n,r.end.row=n,r.end.column=s.length,t.replace(r,i[n-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),i=this.getSelectionRange();this.session.getMode().toggleBlockComment(t,this.session,i,e)},this.getNumberAt=function(e,t){var i=/[\-]?[0-9]+(?:\.[0-9]+)?/g;i.lastIndex=0;for(var n=this.session.getLine(e);i.lastIndex=t)return{value:r[0],start:r.index,end:r.index+r[0].length}}return null},this.modifyNumber=function(e){var t=this.selection.getCursor().row,i=this.selection.getCursor().column,n=new m(t,i-1,t,i),r=this.session.getTextRange(n);if(!isNaN(parseFloat(r))&&isFinite(r)){var s=this.getNumberAt(t,i);if(s){var o=0<=s.value.indexOf(".")?s.start+s.value.indexOf(".")+1:s.end,a=s.start+s.value.length-o,l=parseFloat(s.value);l*=Math.pow(10,a),o!==s.end&&ig+1)break;g=p.last}for(h--,a=this.session.$moveLines(u,g,t?0:e),t&&-1==e&&(d=h+1);d<=h;)o[d].moveBy(a,0),d++;t||(a=0),l+=a}r.fromOrientedRange(r.ranges[0]),r.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 i=this.renderer,n=this.renderer.layerConfig,r=e*Math.floor(n.height/n.lineHeight);!0===t?this.selection.$moveSelection(function(){this.moveCursorBy(r,0)}):!1===t&&(this.selection.moveCursorBy(r,0),this.selection.clearSelection());var s=i.scrollTop;i.scrollBy(0,r*n.lineHeight),null!=t&&i.scrollCursorIntoView(null,.5),i.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,i,n){this.renderer.scrollToLine(e,t,i,n)},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 i=this.getCursorPosition(),n=new I(this.session,i.row,i.column),r=n.getCurrentToken(),s=r||n.stepForward();if(s){var o,a,l=!1,c={},h=i.column-s.start,d={")":"(","(":"(","]":"[","[":"[","{":"{","}":"{"};do{if(s.value.match(/[{}()\[\]]/g)){for(;hwindow.innerHeight)&&null)&&(a.style.top=n+"px",a.style.left=t.left+"px",a.style.height=i.lineHeight+"px",a.scrollIntoView(o)),o=r=null}});this.setAutoScrollEditorIntoView=function(e){e||(delete this.setAutoScrollEditorIntoView,this.off("changeSelection",t),this.renderer.off("afterRender",n),this.renderer.off("beforeRender",i))}}},this.$resetCursorStyle=function(){var e=this.$cursorStyle||"ace",t=this.renderer.$cursorLayer;t&&(t.setSmoothBlinking(/smooth/.test(e)),t.isBlinking=!this.$readOnly&&"wide"!=e,n.setCssClass(t.element,"ace_slim-cursors",/slim/.test(e)))},this.prompt=function(t,i,n){var r=this;C.loadModule("./ext/prompt",function(e){e.prompt(r,t,i,n)})}}.call(v.prototype),C.defineOptions(v.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?b.attach(this):b.detach(this)},initialValue:!0},relativeLineNumbers:{set:function(e){this.$showLineNumbers&&e?b.attach(this):b.detach(this)}},placeholder:{set:function(e){this.$updatePlaceholder||(this.$updatePlaceholder=function(){var e=this.renderer.$composition||this.getValue();if(e&&this.renderer.placeholderNode)this.renderer.off("afterRender",this.$updatePlaceholder),n.removeCssClass(this.container,"ace_hasPlaceholder"),this.renderer.placeholderNode.remove(),this.renderer.placeholderNode=null;else if(!e&&!this.renderer.placeholderNode){this.renderer.on("afterRender",this.$updatePlaceholder),n.addCssClass(this.container,"ace_hasPlaceholder");var t=n.createElement("div");t.className="ace_placeholder",t.textContent=this.$placeholder||"",this.renderer.placeholderNode=t,this.renderer.content.appendChild(this.renderer.placeholderNode)}}.bind(this),this.on("input",this.$updatePlaceholder)),this.$updatePlaceholder()}},hScrollBarAlwaysVisible:"renderer",vScrollBarAlwaysVisible:"renderer",highlightGutterLine:"renderer",animatedScroll:"renderer",showInvisibles:"renderer",showPrintMargin:"renderer",printMarginColumn:"renderer",printMargin:"renderer",fadeFoldWidgets:"renderer",showFoldWidgets:"renderer",displayIndentGuides:"renderer",showGutter:"renderer",fontSize:"renderer",fontFamily:"renderer",maxLines:"renderer",minLines:"renderer",scrollPastEnd:"renderer",fixedWidthGutter:"renderer",theme:"renderer",hasCssTransforms:"renderer",maxPixelHeight:"renderer",useTextareaForIME:"renderer",scrollSpeed:"$mouseHandler",dragDelay:"$mouseHandler",dragEnabled:"$mouseHandler",focusTimeout:"$mouseHandler",tooltipFollowsMouse:"$mouseHandler",firstLineNumber:"session",overwrite:"session",newLineMode:"session",useWorker:"session",useSoftTabs:"session",navigateWithinSoftTabs:"session",tabSize:"session",wrap:"session",indentedSoftWrap:"session",foldStyle:"session",mode:"session"});var b={getText:function(e,t){return(Math.abs(e.selection.lead.row-t)||t+1+(t<9?"·":""))+""},getWidth:function(e,t,i){return Math.max(t.toString().length,(i.lastRow+1).toString().length,2)*i.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=v}),ace.define("ace/undomanager",["require","exports","module","ace/range"],function(e,t,i){"use strict";function n(){this.$maxRev=0,this.$fromUndo=!1,this.reset()}(function(){this.addSession=function(e){this.$session=e},this.add=function(e,t,i){this.$fromUndo||e!=this.$lastDelta&&(!1!==t&&this.lastDeltas||(this.lastDeltas=[],this.$undoStack.push(this.lastDeltas),e.id=this.$rev=++this.$maxRev),"remove"!=e.action&&"insert"!=e.action||(this.$lastDelta=e),this.lastDeltas.push(e))},this.addSelection=function(e,t){this.selections.push({value:e,rev:t||this.$rev})},this.startNewGroup=function(){return this.lastDeltas=null,this.$rev},this.markIgnored=function(e,t){null==t&&(t=this.$rev+1);for(var i=this.$undoStack,n=i.length;n--;){var r=i[n][0];if(r.id<=e)break;r.id"+e.end.row+":"+e.end.column}function c(e,t){var i="insert"==e.action,n="insert"==t.action;if(i&&n)if(0<=a(t.start,e.end))u(t,e,-1);else{if(!(a(t.start,e.start)<=0))return null;u(e,t,1)}else if(i&&!n)if(0<=a(t.start,e.end))u(t,e,-1);else{if(!(a(t.end,e.start)<=0))return null;u(e,t,-1)}else if(!i&&n)if(0<=a(t.start,e.start))u(t,e,1);else{if(!(a(t.start,e.start)<=0))return null;u(e,t,1)}else if(!i&&!n)if(0<=a(t.start,e.start))u(t,e,1);else{if(!(a(t.end,e.start)<=0))return null;u(e,t,-1)}return[t,e]}function h(e,t){for(var i=e.length;i--;)for(var n=0;na+1;)this.$lines.pop();break}(o=this.$lines.get(++a))?o.row=l:(o=this.$lines.createCell(l,e,this.session,c),this.$lines.push(o)),this.$renderCell(o,e,r,l),l++}this._signal("afterRender"),this.$updateGutterWidth(e)},this.$updateGutterWidth=function(e){var t=this.session,i=t.gutterRenderer||this.$renderer,n=t.$firstLineNumber,r=this.$lines.last()?this.$lines.last().text:"";(this.$fixedWidth||t.$useWrapMode)&&(r=t.getLength()+n-1);var s=i?i.getWidth(t,r,e):r.toString().length*e.characterWidth,o=this.$padding||this.$computePadding();(s+=o.left+o.right)===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){var e=this.session.selection.getCursor();this.$cursorRow!==e.row&&(this.$cursorRow=e.row)}},this.updateLineHighlight=function(){if(this.$highlightGutterLine){var e=this.session.selection.cursor.row;if(this.$cursorRow=e,!this.$cursorCell||this.$cursorCell.row!=e){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 i=0;i=this.$cursorRow){if(n.row>this.$cursorRow){var r=this.session.getFoldLine(this.$cursorRow);if(!(0i.right-t.right?"foldWidgets":void 0}}).call(n.prototype),t.Gutter=n}),ace.define("ace/layer/marker",["require","exports","module","ace/range","ace/lib/dom"],function(e,t,i){"use strict";function n(e){this.element=r.createElement("div"),this.element.className="ace_layer ace_marker-layer",e.appendChild(this.element)}var g=e("../range").Range,r=e("../lib/dom");(function(){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 i=-1!=this.i&&this.element.childNodes[this.i];i?this.i++:(i=document.createElement("div"),this.element.appendChild(i),this.i=-1),i.style.cssText=t,i.className=e},this.update=function(e){if(e){var t;for(var i in this.config=e,this.i=0,this.markers){var n=this.markers[i];if(n.range){var r=n.range.clipRows(e.firstRow,e.lastRow);if(!r.isEmpty())if(r=r.toScreenRange(this.session),n.renderer){var s=this.$getTop(r.start.row,e),o=this.$padding+r.start.column*e.characterWidth;n.renderer(t,r,o,s,e)}else"fullLine"==n.type?this.drawFullLineMarker(t,r,n.clazz,e):"screenLine"==n.type?this.drawScreenLineMarker(t,r,n.clazz,e):r.isMultiLine()?"text"==n.type?this.drawTextMarker(t,r,n.clazz,e):this.drawMultiLineMarker(t,r,n.clazz,e):this.drawSingleLineMarker(t,r,n.clazz+" ace_start ace_br15",e)}else n.update(t,this,this.session,e)}if(-1!=this.i)for(;this.ie.lastRow)for(r=this.session.getFoldedRowCount(e.lastRow+1,t.lastRow);0t.lastRow&&this.$lines.push(this.$renderLinesFragment(e,t.lastRow+1,e.lastRow))},this.$renderLinesFragment=function(e,t,i){for(var n=[],r=t,s=this.session.getNextFoldLine(r),o=s?s.start.row:1/0;o=s;)o=this.$renderToken(a,o,c,h.substring(0,s-n)),h=h.substring(s-n),n=s,a=this.$createLineElement(),e.appendChild(a),a.appendChild(this.dom.createTextNode(I.stringRepeat(" ",i.indent),this.element)),o=0,s=i[++r]||Number.MAX_VALUE;0!=h.length&&(n+=h.length,o=this.$renderToken(a,o,c,h))}}i[i.length-1]>this.MAX_LINE_LENGTH&&this.$renderOverflowMessage(a,o,null,"",!0)},this.$renderSimpleLine=function(e,t){var i=0,n=t[0],r=n.value;this.displayIndentGuides&&(r=this.renderIndentGuide(e,r)),r&&(i=this.$renderToken(e,i,n,r));for(var s=1;sthis.MAX_LINE_LENGTH)return this.$renderOverflowMessage(e,i,n,r);i=this.$renderToken(e,i,n,r)}},this.$renderOverflowMessage=function(e,t,i,n,r){i&&this.$renderToken(e,t,i,n.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=r?"":"",e.appendChild(s)},this.$renderLine=function(e,t,i){if(i||0==i||(i=this.session.getFoldLine(t)),i)var n=this.$getFoldLineTokens(t,i);else n=this.session.getTokens(t);var r=e;if(n.length){var s=this.session.getRowSplitData(t);if(s&&s.length){this.$renderWrappedLine(e,n,s);r=e.lastChild}else{r=e;this.$useLineGroups()&&(r=this.$createLineElement(), +e.appendChild(r)),this.$renderSimpleLine(r,n)}}else this.$useLineGroups()&&(r=this.$createLineElement(),e.appendChild(r));if(this.showInvisibles&&r){i&&(t=i.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,r.appendChild(o)}},this.$getFoldLineTokens=function(e,t){var s=this.session,o=[];var a=s.getTokens(e);return t.walk(function(e,t,i,n,r){null!=e?o.push({type:"fold",value:e}):(r&&(a=s.getTokens(t)),a.length&&function(e,t,i){for(var n=0,r=0;r+e[n].value.lengthi-t&&(s=s.substring(0,i-t)),o.push({type:e[n].type,value:s}),r=t+s.length,n+=1);ri?o.push({type:e[n].type,value:s.substring(0,i-r)}):o.push(e[n]),r+=s.length,n+=1}}(a,n,i))},t.end.row,this.session.getLine(t.end.row).length),o},this.$useLineGroups=function(){return this.session.getUseWrapMode()},this.destroy=function(){}}).call(n.prototype),t.Text=n}),ace.define("ace/layer/cursor",["require","exports","module","ace/lib/dom"],function(e,t,i){"use strict";function n(e){this.element=c.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(),c.addCssClass(this.element,"ace_hidden-cursors"),this.$updateCursors=this.$updateOpacity.bind(this)}var c=e("../lib/dom");(function(){this.$updateOpacity=function(e){for(var t=this.cursors,i=t.length;i--;)c.setStyle(t[i].style,"opacity",e?"":"0")},this.$startCssAnimation=function(){for(var e=this.cursors,t=e.length;t--;)e[t].style.animationDuration=this.blinkInterval+"ms";setTimeout(function(){c.addCssClass(this.element,"ace_animate-blinking")}.bind(this))},this.$stopCssAnimation=function(){c.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,c.setCssClass(this.element,"ace_smooth-blinking",e),this.$updateCursors(!0),this.restartTimer())},this.addCursor=function(){var e=c.createElement("div");return e.className="ace_cursor",this.element.appendChild(e),this.cursors.push(e),e},this.removeCursor=function(){if(1e.height+e.offset||s.top<0)&&1n;)this.removeCursor();var l=this.session.getOverwrite();this.$setOverwrite(l),this.$pixelPos=s,this.restartTimer()},this.drawCursor=null,this.$setOverwrite=function(e){e!=this.overwrite&&((this.overwrite=e)?c.addCssClass(this.element,"ace_overwrite-cursors"):c.removeCssClass(this.element,"ace_overwrite-cursors"))},this.destroy=function(){clearInterval(this.intervalId),clearTimeout(this.timeoutId)}}).call(n.prototype),t.Cursor=n}),ace.define("ace/scrollbar",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/event","ace/lib/event_emitter"],function(e,t,i){"use strict";function n(e){this.element=s.createElement("div"),this.element.className="ace_scrollbar ace_scrollbar"+this.classSuffix,this.inner=s.createElement("div"),this.inner.className="ace_scrollbar-inner",this.inner.textContent=" ",this.element.appendChild(this.inner),e.appendChild(this.element),this.setVisible(!1),this.skipEvent=!1,o.addListener(this.element,"scroll",this.onScroll.bind(this)),o.addListener(this.element,"mousedown",o.preventDefault)}var r=e("./lib/oop"),s=e("./lib/dom"),o=e("./lib/event"),a=e("./lib/event_emitter").EventEmitter;(function(){r.implement(this,a),this.setVisible=function(e){this.element.style.display=e?"":"none",this.isVisible=e,this.coeff=1}}).call(n.prototype);function l(e,t){n.call(this,e),this.scrollTop=0,this.scrollHeight=0,t.$scrollbarWidth=this.width=s.scrollbarWidth(e.ownerDocument),this.inner.style.width=this.element.style.width=(this.width||15)+5+"px",this.$minWidth=0}r.inherits(l,n),function(){this.classSuffix="-v",this.onScroll=function(){if(!this.skipEvent){if(this.scrollTop=this.element.scrollTop,1!=this.coeff){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){32768<(this.scrollHeight=e)?(this.coeff=32768/e,e=32768):1!=this.coeff&&(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(l.prototype);function c(e,t){n.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(c,n),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(c.prototype),t.ScrollBar=l,t.ScrollBarV=l,t.ScrollBarH=c,t.VScrollBar=l,t.HScrollBar=c}),ace.define("ace/renderloop",["require","exports","module","ace/lib/event"],function(e,t,i){"use strict";function n(e,t){this.onRender=e,this.pending=!1,this.changes=0,this.$recursionLimit=2,this.window=t||window;var i=this;this._flush=function(e){i.pending=!1;var t=i.changes;if(t&&(r.blockIdle(100),i.changes=0,i.onRender(t)),i.changes){if(i.$recursionLimit--<0)return;i.schedule()}else i.$recursionLimit=2}}var r=e("./lib/event");(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(n.prototype),t.RenderLoop=n}),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,i){var n=e("../lib/oop"),r=e("../lib/dom"),s=e("../lib/lang"),o=e("../lib/event"),a=e("../lib/useragent"),l=e("../lib/event_emitter").EventEmitter,c="function"==typeof ResizeObserver,h=t.FontMetrics=function(e){this.el=r.createElement("div"),this.$setMeasureNodeStyles(this.el.style,!0),this.$main=r.createElement("div"),this.$setMeasureNodeStyles(this.$main.style),this.$measureNode=r.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",256),this.$characterSize={width:0,height:0},c?this.$addObserver():this.checkForSizeChanges()};(function(){n.implement(this,l),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",a.isIE<8?e["font-family"]="inherit":e.font="inherit",e.overflow=t?"hidden":"visible"},this.checkForSizeChanges=function(e){if(void 0===e&&(e=this.$measureSizes()),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 i=this;this.$observer=new window.ResizeObserver(function(e){var t=e[0].contentRect;i.checkForSizeChanges({height:t.height,width:t.width/256})}),this.$observer.observe(this.$measureNode)},this.$pollSizeChanges=function(){if(this.$pollSizeChangesTimer||this.$observer)return this.$pollSizeChangesTimer;var t=this;return this.$pollSizeChangesTimer=o.onIdle(function e(){t.checkForSizeChanges(),o.onIdle(e,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/256};return 0===t.width||0===t.height?null:t},this.$measureCharWidth=function(e){return this.$main.innerHTML=s.stringRepeat(e,256),this.$main.getBoundingClientRect().width/256},this.getCharacterWidth=function(e){var t=this.charSizes[e];return void 0===t&&(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(){function e(e,t){return["div",{style:"position: absolute;top:"+e+"px;left:"+t+"px;"}]}this.els=r.buildDom([e(0,0),e(200,0),e(0,200),e(200,200)],this.el)},this.transformCoordinates=function(e,t){e=e&&s(1/this.$getZoom(this.el),e);function i(e,t,i){var n=e[1]*t[0]-e[0]*t[1];return[(-t[1]*i[0]+t[0]*i[1])/n,(e[1]*i[0]-e[0]*i[1])/n]}function n(e,t){return[e[0]-t[0],e[1]-t[1]]}function r(e,t){return[e[0]+t[0],e[1]+t[1]]}function s(e,t){return[e*t[0],e*t[1]]}function o(e){var t=e.getBoundingClientRect();return[t.left,t.top]}this.els||this.$initTransformMeasureNodes();var a=o(this.els[0]),l=o(this.els[1]),c=o(this.els[2]),h=o(this.els[3]),d=i(n(h,l),n(h,c),n(r(l,c),r(h,a))),u=s(1+d[0],n(l,a)),g=s(1+d[1],n(c,a));if(t){var p=t,m=d[0]*p[0]/200+d[1]*p[1]/200+1,f=r(s(p[0],u),s(p[1],g));return r(s(1/m/200,f),a)}var C=n(e,a),I=i(n(u,s(d[0],C)),n(g,s(d[1],C)),C);return s(200,I)}}).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,i){"use strict";var n=e("./lib/oop"),h=e("./lib/dom"),s=e("./config"),r=e("./layer/gutter").Gutter,o=e("./layer/marker").Marker,a=e("./layer/text").Text,l=e("./layer/cursor").Cursor,c=e("./scrollbar").HScrollBar,d=e("./scrollbar").VScrollBar,u=e("./renderloop").RenderLoop,g=e("./layer/font_metrics").FontMetrics,p=e("./lib/event_emitter").EventEmitter,m='.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_hasPlaceholder .ace_hidden-cursors .ace_cursor {opacity: 0;}.ace_smooth-blinking .ace_cursor {transition: opacity 0.18s;}.ace_animate-blinking .ace_cursor {animation-duration: 1000ms;animation-timing-function: step-end;animation-name: blink-ace-animate;animation-iteration-count: infinite;}.ace_animate-blinking.ace_smooth-blinking .ace_cursor {animation-duration: 1000ms;animation-timing-function: ease-in-out;animation-name: blink-ace-animate-smooth;}@keyframes blink-ace-animate {from, to { opacity: 1; }60% { opacity: 0; }}@keyframes blink-ace-animate-smooth {from, to { opacity: 1; }45% { opacity: 1; }60% { opacity: 0; }85% { opacity: 0; }}.ace_marker-layer .ace_step, .ace_marker-layer .ace_stack {position: absolute;z-index: 3;}.ace_marker-layer .ace_selection {position: absolute;z-index: 5;}.ace_marker-layer .ace_bracket {position: absolute;z-index: 6;}.ace_marker-layer .ace_active-line {position: absolute;z-index: 2;}.ace_marker-layer .ace_selected-word {position: absolute;z-index: 4;box-sizing: border-box;}.ace_line .ace_fold {box-sizing: border-box;display: inline-block;height: 11px;margin-top: -2px;vertical-align: middle;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="),url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACJJREFUeNpi+P//fxgTAwPDBxDxD078RSX+YeEyDFMCIMAAI3INmXiwf2YAAAAASUVORK5CYII=");background-repeat: no-repeat, repeat-x;background-position: center center, top left;color: transparent;border: 1px solid black;border-radius: 2px;cursor: pointer;pointer-events: auto;}.ace_dark .ace_fold {}.ace_fold:hover{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="),url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACBJREFUeNpi+P//fz4TAwPDZxDxD5X4i5fLMEwJgAADAEPVDbjNw87ZAAAAAElFTkSuQmCC");}.ace_tooltip {background-color: #FFF;background-image: linear-gradient(to bottom, transparent, rgba(0, 0, 0, 0.1));border: 1px solid gray;border-radius: 1px;box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);color: black;max-width: 100%;padding: 3px 4px;position: fixed;z-index: 999999;box-sizing: border-box;cursor: default;white-space: pre;word-wrap: break-word;line-height: normal;font-style: normal;font-weight: normal;letter-spacing: normal;pointer-events: none;}.ace_folding-enabled > .ace_gutter-cell {padding-right: 13px;}.ace_fold-widget {box-sizing: border-box;margin: 0 -12px 0 1px;display: none;width: 11px;vertical-align: top;background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42mWKsQ0AMAzC8ixLlrzQjzmBiEjp0A6WwBCSPgKAXoLkqSot7nN3yMwR7pZ32NzpKkVoDBUxKAAAAABJRU5ErkJggg==");background-repeat: no-repeat;background-position: center;border-radius: 3px;border: 1px solid transparent;cursor: pointer;}.ace_folding-enabled .ace_fold-widget {display: inline-block; }.ace_fold-widget.ace_end {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42m3HwQkAMAhD0YzsRchFKI7sAikeWkrxwScEB0nh5e7KTPWimZki4tYfVbX+MNl4pyZXejUO1QAAAABJRU5ErkJggg==");}.ace_fold-widget.ace_closed {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAGCAYAAAAG5SQMAAAAOUlEQVR42jXKwQkAMAgDwKwqKD4EwQ26sSOkVWjgIIHAzPiCgaqiqnJHZnKICBERHN194O5b9vbLuAVRL+l0YWnZAAAAAElFTkSuQmCCXA==");}.ace_fold-widget:hover {border: 1px solid rgba(0, 0, 0, 0.3);background-color: rgba(255, 255, 255, 0.2);box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);}.ace_fold-widget:active {border: 1px solid rgba(0, 0, 0, 0.4);background-color: rgba(0, 0, 0, 0.05);box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);}.ace_dark .ace_fold-widget {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHklEQVQIW2P4//8/AzoGEQ7oGCaLLAhWiSwB146BAQCSTPYocqT0AAAAAElFTkSuQmCC");}.ace_dark .ace_fold-widget.ace_end {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAH0lEQVQIW2P4//8/AxQ7wNjIAjDMgC4AxjCVKBirIAAF0kz2rlhxpAAAAABJRU5ErkJggg==");}.ace_dark .ace_fold-widget.ace_closed {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAHElEQVQIW2P4//+/AxAzgDADlOOAznHAKgPWAwARji8UIDTfQQAAAABJRU5ErkJggg==");}.ace_dark .ace_fold-widget:hover {box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);background-color: rgba(255, 255, 255, 0.1);}.ace_dark .ace_fold-widget:active {box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);}.ace_inline_button {border: 1px solid lightgray;display: inline-block;margin: -1px 8px;padding: 0 5px;pointer-events: auto;cursor: pointer;}.ace_inline_button:hover {border-color: gray;background: rgba(200,200,200,0.2);display: inline-block;pointer-events: auto;}.ace_fold-widget.ace_invalid {background-color: #FFB4B4;border-color: #DE5555;}.ace_fade-fold-widgets .ace_fold-widget {transition: opacity 0.4s ease 0.05s;opacity: 0;}.ace_fade-fold-widgets:hover .ace_fold-widget {transition: opacity 0.05s ease 0.05s;opacity:1;}.ace_underline {text-decoration: underline;}.ace_bold {font-weight: bold;}.ace_nobold .ace_bold {font-weight: normal;}.ace_italic {font-style: italic;}.ace_error-marker {background-color: rgba(255, 0, 0,0.2);position: absolute;z-index: 9;}.ace_highlight-marker {background-color: rgba(255, 255, 0,0.2);position: absolute;z-index: 8;}.ace_mobile-menu {position: absolute;line-height: 1.5;border-radius: 4px;-ms-user-select: none;-moz-user-select: none;-webkit-user-select: none;user-select: none;background: white;box-shadow: 1px 3px 2px grey;border: 1px solid #dcdcdc;color: black;}.ace_dark > .ace_mobile-menu {background: #333;color: #ccc;box-shadow: 1px 3px 2px grey;border: 1px solid #444;}.ace_mobile-button {padding: 2px;cursor: pointer;overflow: hidden;}.ace_mobile-button:hover {background-color: #eee;opacity:1;}.ace_mobile-button:active {background-color: #ddd;}.ace_placeholder {font-family: arial;transform: scale(0.9);opacity: 0.7;transform-origin: left;text-indent: 10px;}',f=e("./lib/useragent"),C=f.isIE;h.importCssString(m,"ace_editor.css");function I(e,t){var i=this;this.container=e||h.createElement("div"),h.addCssClass(this.container,"ace_editor"),h.HI_DPI&&h.addCssClass(this.container,"ace_hidpi"),this.setTheme(t),this.$gutter=h.createElement("div"),this.$gutter.className="ace_gutter",this.container.appendChild(this.$gutter),this.$gutter.setAttribute("aria-hidden",!0),this.scroller=h.createElement("div"),this.scroller.className="ace_scroller",this.container.appendChild(this.scroller),this.content=h.createElement("div"),this.content.className="ace_content",this.scroller.appendChild(this.content),this.$gutterLayer=new r(this.$gutter),this.$gutterLayer.on("changeGutterWidth",this.onGutterResize.bind(this)),this.$markerBack=new o(this.content);var n=this.$textLayer=new a(this.content);this.canvas=n.element,this.$markerFront=new o(this.content),this.$cursorLayer=new l(this.content),this.$horizScroll=!1,this.$vScroll=!1,this.scrollBar=this.scrollBarV=new d(this.container,this),this.scrollBarH=new c(this.container,this),this.scrollBarV.addEventListener("scroll",function(e){i.$scrollAnimation||i.session.setScrollTop(e.data-i.scrollMargin.top)}),this.scrollBarH.addEventListener("scroll",function(e){i.$scrollAnimation||i.session.setScrollLeft(e.data-i.scrollMargin.left)}),this.scrollTop=0,this.scrollLeft=0,this.cursorPos={row:0,column:0},this.$fontMetrics=new g(this.container),this.$textLayer.$setFontMetrics(this.$fontMetrics),this.$textLayer.addEventListener("changeCharacterSize",function(e){i.updateCharacterSize(),i.onResize(!0,i.gutterWidth,i.$size.width,i.$size.height),i._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=!f.isIOS,this.$loop=new u(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,n.implement(this,p),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(),h.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)&&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),e&&(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,i){if(void 0===t&&(t=1/0),this.$changedLines?(this.$changedLines.firstRow>e&&(this.$changedLines.firstRow=e),this.$changedLines.lastRowthis.layerConfig.lastRow||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,i,n){if(!(2n.height-o)h.translate(this.textarea,0,0);else{var a=1,l=this.$size.height-o;if(t)if(t.useTextareaForIME){var c=this.textarea.value;a=this.characterWidth*this.session.$getStringScreenWidth(c)[0]}else r+=this.lineHeight+2;else r+=this.lineHeight;(s-=this.scrollLeft)>this.$size.scrollerWidth-a&&(s=this.$size.scrollerWidth-a),s+=this.gutterWidth+this.margin.left,h.setStyle(e,"height",o+"px"),h.setStyle(e,"width",a+"px"),h.translate(this.textarea,Math.min(s,this.$size.scrollerWidth-a),Math.min(r,l))}}}else h.translate(this.textarea,-100,0)}},this.getFirstVisibleRow=function(){return this.layerConfig.firstRow},this.getFirstFullyVisibleRow=function(){return this.layerConfig.firstRow+(0===this.layerConfig.offset?0:1)},this.getLastFullyVisibleRow=function(){var e=this.layerConfig,t=e.lastRow;return this.session.documentToScreenRow(t,0)*e.lineHeight-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,i,n){var r=this.scrollMargin;r.top=0|e,r.bottom=0|t,r.right=0|n,r.left=0|i,r.v=r.top+r.bottom,r.h=r.left+r.right,r.top&&this.scrollTop<=0&&this.session&&this.session.setScrollTop(-r.top),this.updateFull()},this.setMargin=function(e,t,i,n){var r=this.margin;r.top=0|e,r.bottom=0|t,r.right=0|n,r.left=0|i,r.v=r.top+r.bottom,r.h=r.left+r.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){if(this.$changes&&(e|=this.$changes,this.$changes=0),this.session&&this.container.offsetWidth&&!this.$frozen&&(e||t)){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 i=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){if(e|=this.$computeLayerConfig()|this.$loop.clear(),i.firstRow!=this.layerConfig.firstRow&&i.firstRowScreen==this.layerConfig.firstRowScreen){var n=this.scrollTop+(i.firstRow-this.layerConfig.firstRow)*this.lineHeight;0this.$maxPixelHeight&&(i=this.$maxPixelHeight);var n=!(i<=2*this.lineHeight)&&th.top),u=a!==d;u&&(this.$vScroll=d,this.scrollBarV.setVisible(d));var g,p,m=this.scrollTop%this.lineHeight,f=Math.ceil(l/this.lineHeight)-1,C=Math.max(0,Math.round((this.scrollTop-m)/this.lineHeight)),I=C+f,A=this.lineHeight;C=e.screenToDocumentRow(C,0);var v=e.getFoldLine(C);v&&(C=v.start.row),g=e.documentToScreenRow(C,0),p=e.getRowLength(C)*A,I=Math.min(e.screenToDocumentRow(I,0),e.getLength()-1),l=t.scrollerHeight+e.getRowLength(I)*A+p,m=this.scrollTop-g*A;var b=0;return this.layerConfig.width==r&&!o||(b=this.CHANGE_H_SCROLL),(o||u)&&(b|=this.$updateCachedSize(!0,this.gutterWidth,t.width,t.height),this._signal("scrollbarVisibilityChanged"),u&&(r=this.$getLongestLine())),this.layerConfig={width:r,padding:this.$padding,firstRow:C,firstRowScreen:g,lastRow:I,lineHeight:A,characterWidth:this.characterWidth,minHeight:l,maxHeight:n,offset:m,gutterOffset:A?Math.max(0,Math.ceil((m+t.height-t.scrollerHeight)/A)):0,height:this.$size.scrollerHeight},this.session.$bidiHandler&&this.session.$bidiHandler.setContentWidth(r-this.$padding),b},this.$updateLines=function(){if(this.$changedLines){var e=this.$changedLines.firstRow,t=this.$changedLines.lastRow;this.$changedLines=null;var i=this.layerConfig;if(!(e>i.lastRow+1||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,i){this.scrollCursorIntoView(e,i),this.scrollCursorIntoView(t,i)},this.scrollCursorIntoView=function(e,t,i){if(0!==this.$size.scrollerHeight){var n=this.$cursorLayer.getPixelPosition(e),r=n.left,s=n.top,o=i&&i.top||0,a=i&&i.bottom||0,l=this.$scrollAnimation?this.session.getScrollTop():this.scrollTop;ss+this.lineHeight&&(s-=t*this.$size.scrollerHeight),0===s&&(s=-this.scrollMargin.top),this.session.setScrollTop(s)):l+this.$size.scrollerHeight-a=1-this.scrollMargin.top||(0=1-this.scrollMargin.left||(0this.$doc.getLength()>>1?this.call("setValue",[this.$doc.getValue()]):this.emit("change",{data:e}))}}).call(d.prototype);e.UIWorkerClient=function(e,t,i){var n=null,r=!1,s=Object.create(c),o=[],a=new d({messageBuffer:o,terminate:function(){},postMessage:function(e){o.push(e),n&&(r?setTimeout(l):l())}});a.setEmitSync=function(e){r=e};var l=function(){var e=o.shift();e.command?n[e.command].apply(n,e.args):e.event&&s._signal(e.event,e.data)};return s.postMessage=function(e){a.onMessage({data:e})},s.callback=function(e,t){this.postMessage({type:"call",id:t,data:e})},s.emit=function(e,t){this.postMessage({type:"event",name:e,data:t})},h.loadModule(["worker",t],function(e){for(n=new e[i](s);o.length;)l()}),a},e.WorkerClient=d,e.createWorker=l}),ace.define("ace/placeholder",["require","exports","module","ace/range","ace/lib/event_emitter","ace/lib/oop"],function(e,t,i){"use strict";function n(e,t,i,n,r,s){var o=this;this.length=t,this.session=e,this.doc=e.getDocument(),this.mainClass=r,this.othersClass=s,this.$onUpdate=this.onUpdate.bind(this),this.doc.on("change",this.$onUpdate),this.$others=n,this.$onCursorChange=function(){setTimeout(function(){o.onCursorChange()})},this.$pos=i;var a=e.getUndoManager().$undoStack||e.getUndoManager().$undostack||{length:-1};this.$undoStackDepth=a.length,this.setup(),e.selection.on("changeCursor",this.$onCursorChange)}var l=e("./range").Range,r=e("./lib/event_emitter").EventEmitter,s=e("./lib/oop");(function(){s.implement(this,r),this.setup=function(){var i=this,n=this.doc,e=this.session;this.selectionBefore=e.selection.toJSON(),e.selection.inMultiSelectMode&&e.selection.toSingleRange(),this.pos=n.createAnchor(this.$pos.row,this.$pos.column);var t=this.pos;t.$insertRight=!0,t.detach(),t.markerId=e.addMarker(new l(t.row,t.column,t.row,t.column+this.length),this.mainClass,null,!1),this.others=[],this.$others.forEach(function(e){var t=n.createAnchor(e.row,e.column);t.$insertRight=!0,t.detach(),i.others.push(t)}),e.setUndoSelect(!1)},this.showOtherMarkers=function(){if(!this.othersActive){var t=this.session,i=this;this.othersActive=!0,this.others.forEach(function(e){e.markerId=t.addMarker(new l(e.row,e.column,e.row,e.column+i.length),i.othersClass,null,!1)})}},this.hideOtherMarkers=function(){if(this.othersActive){this.othersActive=!1;for(var e=0;e=this.pos.column&&t.start.column<=this.pos.column+this.length+1,r=t.start.column-this.pos.column;if(this.updateAnchors(e),n&&(this.length+=i),n&&!this.session.$fromUndo)if("insert"===e.action)for(var s=this.others.length-1;0<=s;s--){var o={row:(a=this.others[s]).row,column:a.column+r};this.doc.insertMergedLines(o,e.lines)}else if("remove"===e.action)for(s=this.others.length-1;0<=s;s--){var a;o={row:(a=this.others[s]).row,column:a.column+r};this.doc.remove(new l(o.row,o.column,o.row,o.column-i))}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){var i=this,n=this.session,e=function(e,t){n.removeMarker(e.markerId),e.markerId=n.addMarker(new l(e.row,e.column,e.row,e.column+i.length),t,null,!1)};e(this.pos,this.mainClass);for(var t=this.others.length;t--;)e(this.others[t],this.othersClass)}},this.onCursorChange=function(e){if(!this.$updating&&this.session){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(-1!==this.$undoStackDepth){for(var e=this.session.getUndoManager(),t=(e.$undoStack||e.$undostack).length-this.$undoStackDepth,i=0;io&&(o=t.column),it[1].length&&(i=t[1].length),nt[3].length&&(r=t[3].length)),t):[e]}).map(t?l:s?o?function(e){return e[2]?a(i+n-e[2].length)+e[2]+a(r)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}:l:function(e){return e[2]?a(i)+e[2]+a(r)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]});function a(e){return f.stringRepeat(" ",e)}function l(e){return e[2]?a(i)+e[2]+a(n-e[2].length+r)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}}}).call(l.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 i=e.oldSession;i&&(i.multiSelect.off("addRange",this.$onAddRange),i.multiSelect.off("removeRange",this.$onRemoveRange),i.multiSelect.off("multiSelect",this.$onMultiSelect),i.multiSelect.off("singleSelect",this.$onSingleSelect),i.multiSelect.lead.off("change",this.$checkMultiselectChange),i.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=c,e("./config").defineOptions(l.prototype,"editor",{enableMultiselect:{set:function(e){c(this),e?(this.on("changeSession",this.$multiselectOnSessionChange),this.on("mousedown",r)):(this.off("changeSession",this.$multiselectOnSessionChange),this.off("mousedown",r))},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,i){"use strict";var g=e("../../range").Range,n=t.FoldMode=function(){};(function(){this.foldingStartMarker=null,this.foldingStopMarker=null,this.getFoldWidget=function(e,t,i){var n=e.getLine(i);return this.foldingStartMarker.test(n)?"start":"markbeginend"==t&&this.foldingStopMarker&&this.foldingStopMarker.test(n)?"end":""},this.getFoldWidgetRange=function(e,t,i){return null},this.indentationBlock=function(e,t,i){var n=/\S/,r=e.getLine(t),s=r.search(n);if(-1!=s){for(var o=i||r.length,a=e.getLength(),l=t,c=t;++ts.row&&(o.row--,o.column=e.getLine(o.row).length),g.fromPoints(s,o)}},this.closingBracketBlock=function(e,t,i,n,r){var s={row:i,column:n},o=e.$findOpeningBracket(t,s);if(o)return o.column++,s.column--,g.fromPoints(o,s)}}).call(n.prototype)}),ace.define("ace/theme/textmate",["require","exports","module","ace/lib/dom"],function(e,t,i){"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",e("../lib/dom").importCssString(t.cssText,t.cssClass)}),ace.define("ace/line_widgets",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/range"],function(e,t,i){"use strict";e("./lib/oop");var s=e("./lib/dom");e("./range").Range;function n(e){this.session=e,(this.session.widgetManager=this).session.getRowLength=this.getRowLength,this.session.$getWidgetScreenLength=this.$getWidgetScreenLength,this.updateOnChange=this.updateOnChange.bind(this),this.renderWidgets=this.renderWidgets.bind(this),this.measureWidgets=this.measureWidgets.bind(this),this.session._changedWidgets=[],this.$onChangeEditor=this.$onChangeEditor.bind(this),this.session.on("change",this.updateOnChange),this.session.on("changeFold",this.updateOnFold),this.session.on("changeEditor",this.$onChangeEditor)}(function(){this.getRowLength=function(e){var t;return t=this.lineWidgets&&this.lineWidgets[e]&&this.lineWidgets[e].rowCount||0,this.$useWrapMode&&this.$wrapData[e]?this.$wrapData[e].length+1+t:1+t},this.$getWidgetScreenLength=function(){var t=0;return this.lineWidgets.forEach(function(e){e&&e.rowCount&&!e.hidden&&(t+=e.rowCount)}),t},this.$onChangeEditor=function(e){this.attach(e.editor)},this.attach=function(e){e&&e.widgetManager&&e.widgetManager!=this&&e.widgetManager.detach(),this.editor!=e&&(this.detach(),(this.editor=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){this.editor=null,t.widgetManager=null,t.renderer.off("beforeRender",this.measureWidgets),t.renderer.off("afterRender",this.renderWidgets);var i=this.session.lineWidgets;i&&i.forEach(function(e){e&&e.el&&e.el.parentNode&&(e._inDocument=!1,e.el.parentNode.removeChild(e.el))})}},this.updateOnFold=function(e,t){var i=t.lineWidgets;if(i&&e.action){for(var n=e.data,r=n.start.row,s=n.end.row,o="add"==e.action,a=r+1;a>1,o=i(t,e[s]);if(0=n.length?r=0"),h.appendChild(m.createElement("div"));function g(e,t,i){if(0===t&&("esc"===i||"return"===i))return c.destroy(),{command:"null"}}c.destroy=function(){e.$mouseHandler.isMousePressed||(e.keyBinding.removeKeyboardHandler(g),i.widgetManager.removeLineWidget(c),e.off("changeSelection",c.destroy),e.off("changeSession",c.destroy),e.off("mouseup",c.destroy),e.off("change",c.destroy))},e.keyBinding.addKeyboardHandler(g),e.on("changeSelection",c.destroy),e.on("changeSession",c.destroy),e.on("mouseup",c.destroy),e.on("change",c.destroy),e.session.widgetManager.addLineWidget(c),c.el.onmousedown=e.focus.bind(e),e.renderer.scrollCursorIntoView(null,.5,{bottom:c.el.offsetHeight})},m.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,l,t){"use strict";e("./lib/fixoldbrowsers");var c=e("./lib/dom"),h=e("./lib/event"),i=e("./range").Range,d=e("./editor").Editor,n=e("./edit_session").EditSession,r=e("./undomanager").UndoManager,u=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"),l.config=e("./config"),l.require=e,l.define=A(23),l.edit=function(e,t){if("string"==typeof e){var i=e;if(!(e=document.getElementById(i)))throw new Error("ace.edit can't find div #"+i)}if(e&&e.env&&e.env.editor instanceof d)return e.env.editor;var n="";if(e&&/input|textarea/i.test(e.tagName)){var r=e;n=r.value,e=c.createElement("pre"),r.parentNode.replaceChild(e,r)}else e&&(n=e.textContent,e.innerHTML="");var s=l.createEditSession(n),o=new d(new u(e),s,t),a={document:s,editor:o,onResize:o.resize.bind(o,null)};return r&&(a.textarea=r),h.addListener(window,"resize",a.onResize),o.on("destroy",function(){h.removeListener(window,"resize",a.onResize),a.editor.container.env=null}),o.container.env=o.env=a,o},l.createEditSession=function(e,t){var i=new n(e,t);return i.setUndoManager(new r),i},l.Range=i,l.Editor=d,l.EditSession=n,l.UndoManager=r,l.VirtualRenderer=u,l.version=l.config.version}),ace.require(["ace/ace"],function(e){for(var t in e&&(e.config.init(!0),e.define=ace.define),window.ace||(window.ace=e),e)e.hasOwnProperty(t)&&(window.ace[t]=e[t]);window.ace.default=window.ace,i&&(i.exports=window.ace)})}).call(this,A(16)(e))},function(e,t,i){(function(t){ace.define("ace/mode/json_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,i){"use strict";function n(){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"}]}}var r=e("../lib/oop"),s=e("./text_highlight_rules").TextHighlightRules;r.inherits(n,s),t.JsonHighlightRules=n}),ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,i){"use strict";function n(){}var o=e("../range").Range;(function(){this.checkOutdent=function(e,t){return!!/^\s+$/.test(e)&&/^\s*\}/.test(t)},this.autoOutdent=function(e,t){var i=e.getLine(t).match(/^(\s*\})/);if(!i)return 0;var n=i[1].length,r=e.findMatchingBracket({row:t,column:n});if(!r||r.row==t)return 0;var s=this.$getIndent(e.getLine(r.row));e.replace(new o(t,0,t,n-1),s)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(n.prototype),t.MatchingBraceOutdent=n}),ace.define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,i){"use strict";var n=e("../../lib/oop"),h=e("../../range").Range,r=e("./fold_mode").FoldMode,s=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};n.inherits(s,r),function(){this.foldingStartMarker=/([\{\[\(])[^\}\]\)]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{\(]*([\}\]\)])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,i){var n=e.getLine(i);if(this.singleLineBlockCommentRe.test(n)&&!this.startRegionRe.test(n)&&!this.tripleStarBlockCommentRe.test(n))return"";var r=this._getFoldWidgetBase(e,t,i);return!r&&this.startRegionRe.test(n)?"start":r},this.getFoldWidgetRange=function(e,t,i,n){var r,s=e.getLine(i);if(this.startRegionRe.test(s))return this.getCommentRegionBlock(e,s,i);if(r=s.match(this.foldingStartMarker)){var o=r.index;if(r[1])return this.openingBracketBlock(e,r[1],i,o);var a=e.getCommentFoldRange(i,o+r[0].length,1);return a&&!a.isMultiLine()&&(n?a=this.getSectionRange(e,i):"all"!=t&&(a=null)),a}if("markbegin"!==t&&(r=s.match(this.foldingStopMarker))){o=r.index+r[0].length;return r[1]?this.closingBracketBlock(e,r[1],i,o):e.getCommentFoldRange(i,o,-1)}},this.getSectionRange=function(e,t){for(var i=e.getLine(t),n=i.search(/\S/),r=t,s=i.length,o=t+=1,a=e.getLength();++t=r.length))););}this.searchCounter.textContent=n+" of "+(9991&&arguments[1]!==undefined?arguments[1]:1;var i=t>0?e.toFixed(t).replace(/0+$/,"").replace(/\.$/,""):e.toString();return i||"0"}var s=function(){function p(e,t,i,n){o(this,p);var g=this;function r(e){if(e.startsWith("hsl")){var t=e.match(/([\-\d\.e]+)/g).map(Number),i=m(t,4),n=i[0],r=i[1],s=i[2],o=i[3];if(o===undefined){o=1}n/=360;r/=100;s/=100;g.hsla=[n,r,s,o]}else if(e.startsWith("rgb")){var a=e.match(/([\-\d\.e]+)/g).map(Number),l=m(a,4),c=l[0],h=l[1],d=l[2],u=l[3];if(u===undefined){u=1}g.rgba=[c,h,d,u]}else{if(e.startsWith("#")){g.rgba=p.hexToRgb(e)}else{g.rgba=p.nameToRgb(e)||p.hexToRgb(e)}}}if(e===undefined);else if(Array.isArray(e)){this.rgba=e}else if(i===undefined){var s=e&&""+e;if(s){r(s.toLowerCase())}}else{this.rgba=[e,t,i,n===undefined?1:n]}}e(p,[{key:"printRGB",value:function e(t){var i=t?this.rgba:this.rgba.slice(0,3),n=i.map(function(e,t){return a(e,t===3?3:0)});return t?"rgba("+n+")":"rgb("+n+")"}},{key:"printHSL",value:function e(t){var i=[360,100,100,1],n=["","%","%",""];var r=t?this.hsla:this.hsla.slice(0,3),s=r.map(function(e,t){return a(e*i[t],t===3?3:1)+n[t]});return t?"hsla("+s+")":"hsl("+s+")"}},{key:"printHex",value:function e(t){var i=this.hex;return t?i:i.substring(0,7)}},{key:"rgba",get:function e(){if(this._rgba){return this._rgba}if(!this._hsla){throw new Error("No color is set")}return this._rgba=p.hslToRgb(this._hsla)},set:function e(t){if(t.length===3){t[3]=1}this._rgba=t;this._hsla=null}},{key:"rgbString",get:function e(){return this.printRGB()}},{key:"rgbaString",get:function e(){return this.printRGB(true)}},{key:"hsla",get:function e(){if(this._hsla){return this._hsla}if(!this._rgba){throw new Error("No color is set")}return this._hsla=p.rgbToHsl(this._rgba)},set:function e(t){if(t.length===3){t[3]=1}this._hsla=t;this._rgba=null}},{key:"hslString",get:function e(){return this.printHSL()}},{key:"hslaString",get:function e(){return this.printHSL(true)}},{key:"hex",get:function e(){var t=this.rgba,i=t.map(function(e,t){return t<3?e.toString(16):Math.round(e*255).toString(16)});return"#"+i.map(function(e){return e.padStart(2,"0")}).join("")},set:function e(t){this.rgba=p.hexToRgb(t)}}],[{key:"hexToRgb",value:function e(t){var i=(t.startsWith("#")?t.slice(1):t).replace(/^(\w{3})$/,"$1F").replace(/^(\w)(\w)(\w)(\w)$/,"$1$1$2$2$3$3$4$4").replace(/^(\w{6})$/,"$1FF");if(!i.match(/^([0-9a-fA-F]{8})$/)){throw new Error("Unknown hex color; "+t)}var n=i.match(/^(\w\w)(\w\w)(\w\w)(\w\w)$/).slice(1).map(function(e){return parseInt(e,16)});n[3]=n[3]/255;return n}},{key:"nameToRgb",value:function e(t){var i=t.toLowerCase().replace("at","T").replace(/[aeiouyldf]/g,"").replace("ght","L").replace("rk","D").slice(-5,4),n=r[i];return n===undefined?n:p.hexToRgb(n.replace(/\-/g,"00").padStart(6,"f"))}},{key:"rgbToHsl",value:function e(t){var i=m(t,4),n=i[0],r=i[1],s=i[2],o=i[3];n/=255;r/=255;s/=255;var a=Math.max(n,r,s),l=Math.min(n,r,s);var c=void 0,h=void 0,d=(a+l)/2;if(a===l){c=h=0}else{var u=a-l;h=d>.5?u/(2-a-l):u/(a+l);switch(a){case n:c=(r-s)/u+(r1)n-=1;if(n<1/6)return t+(i-t)*6*n;if(n<1/2)return i;if(n<2/3)return t+(i-t)*(2/3-n)*6;return t};var d=s<.5?s*(1+r):s+r-s*r,u=2*s-d;a=h(u,d,n+1/3);l=h(u,d,n);c=h(u,d,n-1/3)}var g=[a*255,l*255,c*255].map(Math.round);g[3]=o;return g}}]);return p}(),i=function(){function s(){o(this,s);this._events=[]}e(s,[{key:"add",value:function e(t,i,n){t.addEventListener(i,n,false);this._events.push({target:t,type:i,handler:n})}},{key:"remove",value:function e(i,n,r){this._events=this._events.filter(function(e){var t=true;if(i&&i!==e.target){t=false}if(n&&n!==e.type){t=false}if(r&&r!==e.handler){t=false}if(t){s._doRemove(e.target,e.type,e.handler)}return!t})}},{key:"destroy",value:function e(){this._events.forEach(function(e){return s._doRemove(e.target,e.type,e.handler)});this._events=[]}}],[{key:"_doRemove",value:function e(t,i,n){t.removeEventListener(i,n,false)}}]);return s}();function t(e){var i=document.createElement("div");return t.innerHTML=e,t.firstElementChild}function l(e,h,d){var u=false;function g(e,t,i){return Math.max(t,Math.min(e,i))}function n(e,t,i){if(i){u=true}if(!u){return}e.preventDefault();var n=h.getBoundingClientRect(),r=n.width,s=n.height,o=t.clientX,a=t.clientY;var l=g(o-n.left,0,r),c=g(a-n.top,0,s);d(l/r,c/s)}function t(e,t){var i=e.buttons===undefined?e.which:e.buttons;if(i===1){n(e,e,t)}else{u=false}}function i(e,t){if(e.touches.length===1){n(e,e.touches[0],t)}else{u=false}}e.add(h,"mousedown",function(e){t(e,true)});e.add(h,"touchstart",function(e){i(e,true)});e.add(window,"mousemove",t);e.add(h,"touchmove",i);e.add(window,"mouseup",function(e){u=false});e.add(h,"touchend",function(e){u=false});e.add(h,"touchcancel",function(e){u=false})}var n="url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2' height='2'%3E%3Cpath d='M1,0H0V1H2V2H1' fill='lightgrey'/%3E%3C/svg%3E\")",c=360,h="keydown",d="mousedown",u="focusin";function v(e,t){return(t||document).querySelector(e)}function g(e){e.preventDefault();e.stopPropagation()}function p(e,t,i,n,r){e.add(t,h,function(e){if(i.indexOf(e.key)>=0){if(r){g(e)}n(e)}})}var f=document.createElement("style"),C;return f.textContent=".picker_wrapper.no_alpha .picker_alpha{display:none}.picker_wrapper.no_editor .picker_editor{position:absolute;z-index:-1;opacity:0}.picker_wrapper.no_cancel .picker_cancel{display:none}.layout_default.picker_wrapper{display:-webkit-box;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;flex-flow:row wrap;-webkit-box-pack:justify;justify-content:space-between;-webkit-box-align:stretch;align-items:stretch;font-size:10px;width:25em;padding:.5em}.layout_default.picker_wrapper input,.layout_default.picker_wrapper button{font-size:1rem}.layout_default.picker_wrapper>*{margin:.5em}.layout_default.picker_wrapper::before{content:'';display:block;width:100%;height:0;-webkit-box-ordinal-group:2;order:1}.layout_default .picker_slider,.layout_default .picker_selector{padding:1em}.layout_default .picker_hue{width:100%}.layout_default .picker_sl{-webkit-box-flex:1;flex:1 1 auto}.layout_default .picker_sl::before{content:'';display:block;padding-bottom:100%}.layout_default .picker_editor{-webkit-box-ordinal-group:2;order:1;width:6.5rem}.layout_default .picker_editor input{width:100%;height:100%}.layout_default .picker_sample{-webkit-box-ordinal-group:2;order:1;-webkit-box-flex:1;flex:1 1 auto}.layout_default .picker_done,.layout_default .picker_cancel{-webkit-box-ordinal-group:2;order:1}.picker_wrapper{box-sizing:border-box;background:#f2f2f2;box-shadow:0 0 0 1px silver;cursor:default;font-family:sans-serif;color:#444;pointer-events:auto}.picker_wrapper:focus{outline:none}.picker_wrapper button,.picker_wrapper input{box-sizing:border-box;border:none;box-shadow:0 0 0 1px silver;outline:none}.picker_wrapper button:focus,.picker_wrapper button:active,.picker_wrapper input:focus,.picker_wrapper input:active{box-shadow:0 0 2px 1px dodgerblue}.picker_wrapper button{padding:.4em .6em;cursor:pointer;background-color:whitesmoke;background-image:-webkit-gradient(linear, left bottom, left top, from(gainsboro), to(transparent));background-image:-webkit-linear-gradient(bottom, gainsboro, transparent);background-image:linear-gradient(0deg, gainsboro, transparent)}.picker_wrapper button:active{background-image:-webkit-gradient(linear, left bottom, left top, from(transparent), to(gainsboro));background-image:-webkit-linear-gradient(bottom, transparent, gainsboro);background-image:linear-gradient(0deg, transparent, gainsboro)}.picker_wrapper button:hover{background-color:white}.picker_selector{position:absolute;z-index:1;display:block;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);border:2px solid white;border-radius:100%;box-shadow:0 0 3px 1px #67b9ff;background:currentColor;cursor:pointer}.picker_slider .picker_selector{border-radius:2px}.picker_hue{position:relative;background-image:-webkit-gradient(linear, left top, right top, from(red), color-stop(yellow), color-stop(lime), color-stop(cyan), color-stop(blue), color-stop(magenta), to(red));background-image:-webkit-linear-gradient(left, red, yellow, lime, cyan, blue, magenta, red);background-image:linear-gradient(90deg, red, yellow, lime, cyan, blue, magenta, red);box-shadow:0 0 0 1px silver}.picker_sl{position:relative;box-shadow:0 0 0 1px silver;background-image:-webkit-gradient(linear, left top, left bottom, from(white), color-stop(50%, rgba(255,255,255,0))),-webkit-gradient(linear, left bottom, left top, from(black), color-stop(50%, rgba(0,0,0,0))),-webkit-gradient(linear, left top, right top, from(gray), to(rgba(128,128,128,0)));background-image:-webkit-linear-gradient(top, white, rgba(255,255,255,0) 50%),-webkit-linear-gradient(bottom, black, rgba(0,0,0,0) 50%),-webkit-linear-gradient(left, gray, rgba(128,128,128,0));background-image:linear-gradient(180deg, white, rgba(255,255,255,0) 50%),linear-gradient(0deg, black, rgba(0,0,0,0) 50%),linear-gradient(90deg, gray, rgba(128,128,128,0))}.picker_alpha,.picker_sample{position:relative;background:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2' height='2'%3E%3Cpath d='M1,0H0V1H2V2H1' fill='lightgrey'/%3E%3C/svg%3E\") left top/contain white;box-shadow:0 0 0 1px silver}.picker_alpha .picker_selector,.picker_sample .picker_selector{background:none}.picker_editor input{font-family:monospace;padding:.2em .4em}.picker_sample::before{content:'';position:absolute;display:block;width:100%;height:100%;background:currentColor}.picker_arrow{position:absolute;z-index:-1}.picker_wrapper.popup{position:absolute;z-index:2;margin:1.5em}.picker_wrapper.popup,.picker_wrapper.popup .picker_arrow::before,.picker_wrapper.popup .picker_arrow::after{background:#f2f2f2;box-shadow:0 0 10px 1px rgba(0,0,0,0.4)}.picker_wrapper.popup .picker_arrow{width:3em;height:3em;margin:0}.picker_wrapper.popup .picker_arrow::before,.picker_wrapper.popup .picker_arrow::after{content:\"\";display:block;position:absolute;top:0;left:0;z-index:-99}.picker_wrapper.popup .picker_arrow::before{width:100%;height:100%;-webkit-transform:skew(45deg);transform:skew(45deg);-webkit-transform-origin:0 100%;transform-origin:0 100%}.picker_wrapper.popup .picker_arrow::after{width:150%;height:150%;box-shadow:none}.popup.popup_top{bottom:100%;left:0}.popup.popup_top .picker_arrow{bottom:0;left:0;-webkit-transform:rotate(-90deg);transform:rotate(-90deg)}.popup.popup_bottom{top:100%;left:0}.popup.popup_bottom .picker_arrow{top:0;left:0;-webkit-transform:rotate(90deg) scale(1, -1);transform:rotate(90deg) scale(1, -1)}.popup.popup_left{top:0;right:100%}.popup.popup_left .picker_arrow{top:0;right:0;-webkit-transform:scale(-1, 1);transform:scale(-1, 1)}.popup.popup_right{top:0;left:100%}.popup.popup_right .picker_arrow{top:0;left:0}",document.documentElement.firstElementChild.appendChild(f),function(){function t(e){o(this,t),this.settings={popup:"right",layout:"default",alpha:!0,editor:!0,editorFormat:"hex",cancelButton:!1},this._events=new i,this.onChange=null,this.onDone=null,this.onOpen=null,this.onClose=null,this.setOptions(e)}return e(t,[{key:"setOptions",value:function(e){var t=this;if(e){var i=this.settings;if(e instanceof HTMLElement)i.parent=e;else{i.parent&&e.parent&&i.parent!==e.parent&&(this._events.remove(i.parent),this._popupInited=!1),function(e,t,i){for(var n in e)i&&0<=i.indexOf(n)||(t[n]=e[n])}(e,i),e.onChange&&(this.onChange=e.onChange),e.onDone&&(this.onDone=e.onDone),e.onOpen&&(this.onOpen=e.onOpen),e.onClose&&(this.onClose=e.onClose);var n=e.color||e.colour;n&&this._setColor(n)}var r=i.parent;if(r&&i.popup&&!this._popupInited){function s(e){return t.openHandler(e)}this._events.add(r,"click",s),p(this._events,r,[" ","Spacebar","Enter"],s),this._popupInited=!0}else e.parent&&!i.popup&&this.show()}}},{key:"openHandler",value:function(e){if(this.show()){e&&e.preventDefault(),this.settings.parent.style.pointerEvents="none";var t=e&&"keydown"===e.type?this._domEdit:this.domElement;setTimeout(function(){return t.focus()},100),this.onOpen&&this.onOpen(this.colour)}}},{key:"closeHandler",value:function(e){var t=e&&e.type,i=!1;if(e)if(t===d||t===u){var n=(this.__containedEvent||0)+100;e.timeStamp>n&&(i=!0)}else g(e),i=!0;else i=!0;i&&this.hide()&&(this.settings.parent.style.pointerEvents="",t!==d&&this.settings.parent.focus(),this.onClose&&this.onClose(this.colour))}},{key:"movePopup",value:function(e,t){this.closeHandler(),this.setOptions(e),t&&this.openHandler()}},{key:"setColor",value:function(e,t){this._setColor(e,{silent:t})}},{key:"_setColor",value:function(e,t){if("string"==typeof e&&(e=e.trim()),e){t=t||{};var i=void 0;try{i=new s(e)}catch(e){if(t.failSilently)return;throw e}if(!this.settings.alpha){var n=i.hsla;n[3]=1,i.hsla=n}this.colour=this.color=i,this._setHSLA(null,null,null,null,t)}}},{key:"setColour",value:function(e,t){this.setColor(e,t)}},{key:"show",value:function(){if(!this.settings.parent)return!1;if(this.domElement){var e=this._toggleDOM(!0);return this._setPosition(),e}var t,i,n=this.settings.template||'
    ',r=(t=n,(i=document.createElement("div")).innerHTML=t,i.firstElementChild);return this.domElement=r,this._domH=v(".picker_hue",r),this._domSL=v(".picker_sl",r),this._domA=v(".picker_alpha",r),this._domEdit=v(".picker_editor input",r),this._domSample=v(".picker_sample",r),this._domOkay=v(".picker_done button",r),this._domCancel=v(".picker_cancel button",r),r.classList.add("layout_"+this.settings.layout),this.settings.alpha||r.classList.add("no_alpha"),this.settings.editor||r.classList.add("no_editor"),this.settings.cancelButton||r.classList.add("no_cancel"),this._ifPopup(function(){return r.classList.add("popup")}),this._setPosition(),this.colour?this._updateUI():this._setColor("#0cf"),this._bindEvents(),!0}},{key:"hide",value:function(){return this._toggleDOM(!1)}},{key:"destroy",value:function(){this._events.destroy(),this.domElement&&this.settings.parent.removeChild(this.domElement)}},{key:"_bindEvents",value:function(){var i=this,n=this,r=this.domElement,s=this._events;function o(e,t,i){s.add(e,t,i)}o(r,"click",function(e){return e.preventDefault()}),l(s,this._domH,function(e,t){return n._setHSLA(e)}),l(s,this._domSL,function(e,t){return n._setHSLA(null,e,1-t)}),this.settings.alpha&&l(s,this._domA,function(e,t){return n._setHSLA(null,null,null,1-t)});var e=this._domEdit;o(e,"input",function(e){n._setColor(this.value,{fromEditor:!0,failSilently:!0})}),o(e,"focus",function(e){this.selectionStart===this.selectionEnd&&this.select()}),this._ifPopup(function(){function e(e){return i.closeHandler(e)}o(window,d,e),o(window,u,e),p(s,r,["Esc","Escape"],e);function t(e){i.__containedEvent=e.timeStamp}o(r,d,t),o(r,u,t),o(i._domCancel,"click",e)});function t(e){i._ifPopup(function(){return i.closeHandler(e)}),i.onDone&&i.onDone(i.colour)}o(this._domOkay,"click",t),p(s,r,["Enter"],t)}},{key:"_setPosition",value:function(){var i=this.settings.parent,n=this.domElement;i!==n.parentNode&&i.appendChild(n),this._ifPopup(function(e){"static"===getComputedStyle(i).position&&(i.style.position="relative");var t=!0===e?"popup_right":"popup_"+e;["popup_top","popup_bottom","popup_left","popup_right"].forEach(function(e){e===t?n.classList.add(e):n.classList.remove(e)}),n.classList.add(t)})}},{key:"_setHSLA",value:function(e,t,i,n,r){r=r||{};var s=this.colour,o=s.hsla;[e,t,i,n].forEach(function(e,t){!e&&0!==e||(o[t]=e)}),s.hsla=o,this._updateUI(r),this.onChange&&!r.silent&&this.onChange(s)}},{key:"_updateUI",value:function(e){if(this.domElement){e=e||{};var t=this.colour,i=t.hsla,n="hsl("+360*i[0]+", 100%, 50%)",r=t.hslString,s=t.hslaString,o=this._domH,a=this._domSL,l=this._domA,c=v(".picker_selector",o),h=v(".picker_selector",a),d=v(".picker_selector",l);I(0,c,i[0]),this._domSL.style.backgroundColor=this._domH.style.color=n,I(0,h,i[1]),A(0,h,1-i[2]),a.style.color=r,A(0,d,1-i[3]);var u=r,g=u.replace("hsl","hsla").replace(")",", 0)"),p="linear-gradient("+[u,g]+")";if(this._domA.style.backgroundImage=p+", url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2' height='2'%3E%3Cpath d='M1,0H0V1H2V2H1' fill='lightgrey'/%3E%3C/svg%3E\")",!e.fromEditor){var m=this.settings.editorFormat,f=this.settings.alpha,C=void 0;switch(m){case"rgb":C=t.printRGB(f);break;case"hsl":C=t.printHSL(f);break;default:C=t.printHex(f)}this._domEdit.value=C}this._domSample.style.color=s}function I(e,t,i){t.style.left=100*i+"%"}function A(e,t,i){t.style.top=100*i+"%"}}},{key:"_ifPopup",value:function(e,t){this.settings.parent&&this.settings.popup?e&&e(this.settings.popup):t&&t()}},{key:"_toggleDOM",value:function(e){var t=this.domElement;if(!t)return!1;var i=e?"":"none",n=t.style.display!==i;return n&&(t.style.display=i),n}}],[{key:"StyleElement",get:function(){return f}}]),t}()}()},function(e,t){window.ace.define("ace/theme/jsoneditor",["require","exports","module","ace/lib/dom"],function(e,t,i){t.isDark=!1,t.cssClass="ace-jsoneditor",t.cssText='.ace-jsoneditor .ace_gutter {\nbackground: #ebebeb;\ncolor: #333\n}\n\n.ace-jsoneditor.ace_editor {\nfont-family: "dejavu sans mono", "droid sans mono", consolas, monaco, "lucida console", "courier new", courier, monospace, sans-serif;\nline-height: 1.3;\nbackground-color: #fff;\n}\n.ace-jsoneditor .ace_print-margin {\nwidth: 1px;\nbackground: #e8e8e8\n}\n.ace-jsoneditor .ace_scroller {\nbackground-color: #FFFFFF\n}\n.ace-jsoneditor .ace_text-layer {\ncolor: gray\n}\n.ace-jsoneditor .ace_variable {\ncolor: #1a1a1a\n}\n.ace-jsoneditor .ace_cursor {\nborder-left: 2px solid #000000\n}\n.ace-jsoneditor .ace_overwrite-cursors .ace_cursor {\nborder-left: 0px;\nborder-bottom: 1px solid #000000\n}\n.ace-jsoneditor .ace_marker-layer .ace_selection {\nbackground: lightgray\n}\n.ace-jsoneditor.ace_multiselect .ace_selection.ace_start {\nbox-shadow: 0 0 3px 0px #FFFFFF;\nborder-radius: 2px\n}\n.ace-jsoneditor .ace_marker-layer .ace_step {\nbackground: rgb(255, 255, 0)\n}\n.ace-jsoneditor .ace_marker-layer .ace_bracket {\nmargin: -1px 0 0 -1px;\nborder: 1px solid #BFBFBF\n}\n.ace-jsoneditor .ace_marker-layer .ace_active-line {\nbackground: #FFFBD1\n}\n.ace-jsoneditor .ace_gutter-active-line {\nbackground-color : #dcdcdc\n}\n.ace-jsoneditor .ace_marker-layer .ace_selected-word {\nborder: 1px solid lightgray\n}\n.ace-jsoneditor .ace_invisible {\ncolor: #BFBFBF\n}\n.ace-jsoneditor .ace_keyword,\n.ace-jsoneditor .ace_meta,\n.ace-jsoneditor .ace_support.ace_constant.ace_property-value {\ncolor: #AF956F\n}\n.ace-jsoneditor .ace_keyword.ace_operator {\ncolor: #484848\n}\n.ace-jsoneditor .ace_keyword.ace_other.ace_unit {\ncolor: #96DC5F\n}\n.ace-jsoneditor .ace_constant.ace_language {\ncolor: darkorange\n}\n.ace-jsoneditor .ace_constant.ace_numeric {\ncolor: red\n}\n.ace-jsoneditor .ace_constant.ace_character.ace_entity {\ncolor: #BF78CC\n}\n.ace-jsoneditor .ace_invalid {\ncolor: #FFFFFF;\nbackground-color: #FF002A;\n}\n.ace-jsoneditor .ace_fold {\nbackground-color: #AF956F;\nborder-color: #000000\n}\n.ace-jsoneditor .ace_storage,\n.ace-jsoneditor .ace_support.ace_class,\n.ace-jsoneditor .ace_support.ace_function,\n.ace-jsoneditor .ace_support.ace_other,\n.ace-jsoneditor .ace_support.ace_type {\ncolor: #C52727\n}\n.ace-jsoneditor .ace_string {\ncolor: green\n}\n.ace-jsoneditor .ace_comment {\ncolor: #BCC8BA\n}\n.ace-jsoneditor .ace_entity.ace_name.ace_tag,\n.ace-jsoneditor .ace_entity.ace_other.ace_attribute-name {\ncolor: #606060\n}\n.ace-jsoneditor .ace_markup.ace_underline {\ntext-decoration: underline\n}\n.ace-jsoneditor .ace_indent-guide {\nbackground: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y\n}',e("../lib/dom").importCssString(t.cssText,t.cssClass)})},function(e,t,i){t.tryRequireAjv=function(){try{return i(43)}catch(e){}}},function(e,t,n){"use strict";var a=n(44),u=n(17),i=n(48),g=n(25),r=n(26),s=n(49),o=n(50),l=n(71),c=n(5);(e.exports=C).prototype.validate=function(e,t){var i;if("string"==typeof e){if(!(i=this.getSchema(e)))throw new Error('no schema with key or ref "'+e+'"')}else{var n=this._addSchema(e);i=n.validate||this._compile(n)}var r=i(t);!0!==i.$async&&(this.errors=i.errors);return r},C.prototype.compile=function(e,t){var i=this._addSchema(e,void 0,t);return i.validate||this._compile(i)},C.prototype.addSchema=function(e,t,i,n){if(Array.isArray(e)){for(var r=0;r1){t[0]=t[0].slice(0,-1);var n=t.length-1;for(var r=1;r= 0x80 (not a basic code point)","invalid-input":"Invalid input"},g=H-L,F=Math.floor,P=String.fromCharCode;function K(e){throw new RangeError(u[e])}function f(e,t){var i=[];var n=e.length;while(n--){i[n]=t(e[n])}return i}function C(e,t){var i=e.split("@");var n="";if(i.length>1){n=i[0]+"@";e=i[1]}e=e.replace(d,".");var r=e.split(".");var s=f(r,t).join(".");return n+s}function M(e){var t=[];var i=0;var n=e.length;while(i=55296&&r<=56319&&i>1;t+=F(t/i);for(;t>g*$>>1;r+=H){t=F(t/g)}return F(r+(g+1)*t/(t+s))},v=function e(t){var i=[];var n=t.length;var r=0;var s=N;var o=V;var a=t.lastIndexOf(O);if(a<0){a=0}for(var l=0;l=128){K("not-basic")}i.push(t.charCodeAt(l))}for(var c=a>0?a+1:0;c=n){K("invalid-input")}var g=A(t.charCodeAt(c++));if(g>=H||g>F((_-r)/d)){K("overflow")}r+=g*d;var p=u<=o?L:u>=o+$?$:u-o;if(gF(_/m)){K("overflow")}d*=m}var f=i.length+1;o=j(r-h,f,h==0);if(F(r/f)>_-s){K("overflow")}s+=F(r/f);r%=f;i.splice(r++,0,s)}return String.fromCodePoint.apply(String,i)},b=function e(t){var i=[];t=M(t);var n=t.length;var r=N;var s=0;var o=V;var a=true;var l=false;var c=undefined;try{for(var h=t[Symbol.iterator](),d;!(a=(d=h.next()).done);a=true){var u=d.value;if(u<128){i.push(P(u))}}}catch(e){l=true;c=e}finally{try{if(!a&&h.return){h.return()}}finally{if(l){throw c}}}var g=i.length;var p=g;if(g){i.push(O)}while(p=r&&bF((_-s)/y)){K("overflow")}s+=(m-r)*y;r=m;var w=true;var S=false;var x=undefined;try{for(var k=t[Symbol.iterator](),R;!(w=(R=k.next()).done);w=true){var E=R.value;if(E_){K("overflow")}if(E==r){var B=s;for(var G=H;;G+=H){var Z=G<=o?L:G>=o+$?$:G-o;if(B>6|192).toString(16).toUpperCase()+"%"+(t&63|128).toString(16).toUpperCase();else i="%"+(t>>12|224).toString(16).toUpperCase()+"%"+(t>>6&63|128).toString(16).toUpperCase()+"%"+(t&63|128).toString(16).toUpperCase();return i}function E(e){var t="";var i=0;var n=e.length;while(i=194&&r<224){if(n-i>=6){var s=parseInt(e.substr(i+4,2),16);t+=String.fromCharCode((r&31)<<6|s&63)}else{t+=e.substr(i,6)}i+=6}else if(r>=224){if(n-i>=9){var o=parseInt(e.substr(i+4,2),16);var a=parseInt(e.substr(i+7,2),16);t+=String.fromCharCode((r&15)<<12|(o&63)<<6|a&63)}else{t+=e.substr(i,9)}i+=9}else{t+=e.substr(i,3);i+=3}}return t}function B(e,i){function t(e){var t=E(e);return!t.match(i.UNRESERVED)?e:t}if(e.scheme)e.scheme=String(e.scheme).replace(i.PCT_ENCODED,t).toLowerCase().replace(i.NOT_SCHEME,"");if(e.userinfo!==undefined)e.userinfo=String(e.userinfo).replace(i.PCT_ENCODED,t).replace(i.NOT_USERINFO,R).replace(i.PCT_ENCODED,p);if(e.host!==undefined)e.host=String(e.host).replace(i.PCT_ENCODED,t).toLowerCase().replace(i.NOT_HOST,R).replace(i.PCT_ENCODED,p);if(e.path!==undefined)e.path=String(e.path).replace(i.PCT_ENCODED,t).replace(e.scheme?i.NOT_PATH:i.NOT_PATH_NOSCHEME,R).replace(i.PCT_ENCODED,p);if(e.query!==undefined)e.query=String(e.query).replace(i.PCT_ENCODED,t).replace(i.NOT_QUERY,R).replace(i.PCT_ENCODED,p);if(e.fragment!==undefined)e.fragment=String(e.fragment).replace(i.PCT_ENCODED,t).replace(i.NOT_FRAGMENT,R).replace(i.PCT_ENCODED,p);return e}function G(e){return e.replace(/^0*(.*)/,"$1")||"0"}function Z(e,t){var i=e.match(t.IPV4ADDRESS)||[];var n=y(i,2),r=n[1];if(r){return r.split(".").map(G).join(".")}else{return e}}function T(e,t){var i=e.match(t.IPV6ADDRESS)||[];var n=y(i,3),r=n[1],s=n[2];if(r){var o=r.toLowerCase().split("::").reverse(),a=y(o,2),l=a[0],c=a[1];var h=c?c.split(":").map(G):[];var d=l.split(":").map(G);var u=t.IPV4ADDRESS.test(d[d.length-1]);var g=u?7:8;var p=d.length-g;var m=Array(g);for(var f=0;f1){var v=m.slice(0,I.index);var b=m.slice(I.index+I.length);A=v.join(":")+"::"+b.join(":")}else{A=m.join(":")}if(s){A+="%"+s}return A}else{return e}}var W=/^(?:([^:\/?#]+):)?(?:\/\/((?:([^\/?#@]*)@)?(\[[^\/?#\]]+\]|[^\/?#:]*)(?:\:(\d*))?))?([^?#]*)(?:\?([^#]*))?(?:#((?:.|\n|\r)*))?/i,X="".match(/(){0}/)[1]===undefined;function Y(e){var t=arguments.length>1&&arguments[1]!==undefined?arguments[1]:{};var i={};var n=t.iri!==false?l:a;if(t.reference==="suffix")e=(t.scheme?t.scheme+":":"")+"//"+e;var r=e.match(W);if(r){if(X){i.scheme=r[1];i.userinfo=r[3];i.host=r[4];i.port=parseInt(r[5],10);i.path=r[6]||"";i.query=r[7];i.fragment=r[8];if(isNaN(i.port)){i.port=r[5]}}else{i.scheme=r[1]||undefined;i.userinfo=e.indexOf("@")!==-1?r[3]:undefined;i.host=e.indexOf("//")!==-1?r[4]:undefined;i.port=parseInt(r[5],10);i.path=r[6]||"";i.query=e.indexOf("?")!==-1?r[7]:undefined;i.fragment=e.indexOf("#")!==-1?r[8]:undefined;if(isNaN(i.port)){i.port=e.match(/\/\/(?:.|\n)*\:(?:\/|\?|\#|$)/)?r[4]:undefined}}if(i.host){i.host=T(Z(i.host,n),n)}if(i.scheme===undefined&&i.userinfo===undefined&&i.host===undefined&&i.port===undefined&&!i.path&&i.query===undefined){i.reference="same-document"}else if(i.scheme===undefined){i.reference="relative"}else if(i.fragment===undefined){i.reference="absolute"}else{i.reference="uri"}if(t.reference&&t.reference!=="suffix"&&t.reference!==i.reference){i.error=i.error||"URI is not a "+t.reference+" reference."}var s=k[(t.scheme||i.scheme||"").toLowerCase()];if(!t.unicodeSupport&&(!s||!s.unicodeSupport)){if(i.host&&(t.domainHost||s&&s.domainHost)){try{i.host=x.toASCII(i.host.replace(n.PCT_ENCODED,E).toLowerCase())}catch(e){i.error=i.error||"Host's domain name can not be converted to ASCII via punycode: "+e}}B(i,a)}else{B(i,n)}if(s&&s.parse){s.parse(i,t)}}else{i.error=i.error||"URI can not be parsed."}return i}function J(e,t){var i=t.iri!==false?l:a;var n=[];if(e.userinfo!==undefined){n.push(e.userinfo);n.push("@")}if(e.host!==undefined){n.push(T(Z(String(e.host),i),i).replace(i.IPV6ADDRESS,function(e,t,i){return"["+t+(i?"%25"+i:"")+"]"}))}if(typeof e.port==="number"){n.push(":");n.push(e.port.toString(10))}return n.length?n.join(""):undefined}var z=/^\.\.?\//,U=/^\/\.(\/|$)/,Q=/^\/\.\.(\/|$)/,q=/^\/?(?:.|\n)*?(?=\/|$)/;function ee(e){var t=[];while(e.length){if(e.match(z)){e=e.replace(z,"")}else if(e.match(U)){e=e.replace(U,"/")}else if(e.match(Q)){e=e.replace(Q,"/");t.pop()}else if(e==="."||e===".."){e=""}else{var i=e.match(q);if(i){var n=i[0];e=e.slice(n.length);t.push(n)}else{throw new Error("Unexpected dot segment condition")}}}return t.join("")}function te(t){var i=arguments.length>1&&arguments[1]!==undefined?arguments[1]:{};var e=i.iri?l:a;var n=[];var r=k[(i.scheme||t.scheme||"").toLowerCase()];if(r&&r.serialize)r.serialize(t,i);if(t.host){if(e.IPV6ADDRESS.test(t.host)){}else if(i.domainHost||r&&r.domainHost){try{t.host=!i.iri?x.toASCII(t.host.replace(e.PCT_ENCODED,E).toLowerCase()):x.toUnicode(t.host)}catch(e){t.error=t.error||"Host's domain name can not be converted to "+(!i.iri?"ASCII":"Unicode")+" via punycode: "+e}}}B(t,e);if(i.reference!=="suffix"&&t.scheme){n.push(t.scheme);n.push(":")}var s=J(t,i);if(s!==undefined){if(i.reference!=="suffix"){n.push("//")}n.push(s);if(t.path&&t.path.charAt(0)!=="/"){n.push("/")}}if(t.path!==undefined){var o=t.path;if(!i.absolutePath&&(!r||!r.absolutePath)){o=ee(o)}if(s===undefined){o=o.replace(/^\/\//,"/%2F")}n.push(o)}if(t.query!==undefined){n.push("?");n.push(t.query)}if(t.fragment!==undefined){n.push("#");n.push(t.fragment)}return n.join("")}function ie(e,t){var i=arguments.length>2&&arguments[2]!==undefined?arguments[2]:{};var n=arguments[3];var r={};if(!n){e=Y(te(e,i),i);t=Y(te(t,i),i)}i=i||{};if(!i.tolerant&&t.scheme){r.scheme=t.scheme;r.userinfo=t.userinfo;r.host=t.host;r.port=t.port;r.path=ee(t.path||"");r.query=t.query}else{if(t.userinfo!==undefined||t.host!==undefined||t.port!==undefined){r.userinfo=t.userinfo;r.host=t.host;r.port=t.port;r.path=ee(t.path||"");r.query=t.query}else{if(!t.path){r.path=e.path;if(t.query!==undefined){r.query=t.query}else{r.query=e.query}}else{if(t.path.charAt(0)==="/"){r.path=ee(t.path)}else{if((e.userinfo!==undefined||e.host!==undefined||e.port!==undefined)&&!e.path){r.path="/"+t.path}else if(!e.path){r.path=t.path}else{r.path=e.path.slice(0,e.path.lastIndexOf("/")+1)+t.path}r.path=ee(r.path)}r.query=t.query}r.userinfo=e.userinfo;r.host=e.host;r.port=e.port}r.scheme=e.scheme}r.fragment=t.fragment;return r}function ne(e,t,i){var n=r({scheme:"null"},i);return te(ie(Y(e,n),Y(t,n),n,true),n)}function re(e,t){if(typeof e==="string"){e=te(Y(e,t),t)}else if(n(e)==="object"){e=Y(te(e,t),t)}return e}function se(e,t,i){if(typeof e==="string"){e=te(Y(e,i),i)}else if(n(e)==="object"){e=te(e,i)}if(typeof t==="string"){t=te(Y(t,i),i)}else if(n(t)==="object"){t=te(t,i)}return e===t}function oe(e,t){return e&&e.toString().replace(!t||!t.iri?a.ESCAPE:l.ESCAPE,R)}function ae(e,t){return e&&e.toString().replace(!t||!t.iri?a.PCT_ENCODED:l.PCT_ENCODED,E)}var le={scheme:"http",domainHost:true,parse:function e(t,i){if(!t.host){t.error=t.error||"HTTP URIs must have a host."}return t},serialize:function e(t,i){if(t.port===(String(t.scheme).toLowerCase()!=="https"?80:443)||t.port===""){t.port=undefined}if(!t.path){t.path="/"}return t}},ce={scheme:"https",domainHost:le.domainHost,parse:le.parse,serialize:le.serialize},ue={},ge,pe="[A-Za-z0-9\\-\\.\\_\\~"+(true?"\\xA0-\\u200D\\u2010-\\u2029\\u202F-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF":"")+"]",me="[0-9A-Fa-f]",fe=de(de("%[EFef]"+me+"%"+me+me+"%"+me+me)+"|"+de("%[89A-Fa-f]"+me+"%"+me+me)+"|"+de("%"+me+me)),Ce="[A-Za-z0-9\\!\\$\\%\\'\\*\\+\\-\\^\\_\\`\\{\\|\\}\\~]",Ie,Ae=he("[\\!\\$\\%\\'\\(\\)\\*\\+\\,\\-\\.0-9\\<\\>A-Z\\x5E-\\x7E]",'[\\"\\\\]'),ve="[\\!\\$\\'\\(\\)\\*\\+\\,\\;\\:\\@]",be=new RegExp(pe,"g"),ye=new RegExp(fe,"g"),we=new RegExp(he("[^]",Ce,"[\\.]",'[\\"]',Ae),"g"),Se=new RegExp(he("[^]",pe,ve),"g"),xe=Se;function ke(e){var t=E(e);return!t.match(be)?e:t}var Re={scheme:"mailto",parse:function e(t,i){var n=t;var r=n.to=n.path?n.path.split(","):[];n.path=undefined;if(n.query){var s=false;var o={};var a=n.query.split("&");for(var l=0,c=a.length;l%\\^`{|}]|%[0-9a-f]{2})|\{[+#./;?&=,!@|]?(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?(?:,(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?)*\})*$/i,h=/^(?:(?:http[s\u017F]?|ftp):\/\/)(?:(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+(?::(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*)?@)?(?:(?!10(?:\.[0-9]{1,3}){3})(?!127(?:\.[0-9]{1,3}){3})(?!169\.254(?:\.[0-9]{1,3}){2})(?!192\.168(?:\.[0-9]{1,3}){2})(?!172\.(?:1[6-9]|2[0-9]|3[01])(?:\.[0-9]{1,3}){2})(?:[1-9][0-9]?|1[0-9][0-9]|2[01][0-9]|22[0-3])(?:\.(?:1?[0-9]{1,2}|2[0-4][0-9]|25[0-5])){2}(?:\.(?:[1-9][0-9]?|1[0-9][0-9]|2[0-4][0-9]|25[0-4]))|(?:(?:(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+-?)*(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+)(?:\.(?:(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+-?)*(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+)*(?:\.(?:(?:[KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]){2,})))(?::[0-9]{2,5})?(?:\/(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*)?$/i,d=/^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i,u=/^(?:\/(?:[^~/]|~0|~1)*)*$/,g=/^#(?:\/(?:[a-z0-9_\-.!$&'()*+,;:=@]|%[0-9a-f]{2}|~0|~1)*)*$/i,p=/^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)$/;function m(e){return e="full"==e?"full":"fast",n.copy(m[e])}function f(e){var t=e.match(o);if(!t)return!1;var i,n=+t[1],r=+t[2],s=+t[3];return 1<=r&&r<=12&&1<=s&&s<=(2!=r||((i=n)%4!=0||i%100==0&&i%400!=0)?a[r]:29)}function C(e,t){var i=e.match(l);if(!i)return!1;var n=i[1],r=i[2],s=i[3],o=i[5];return(n<=23&&r<=59&&s<=59||23==n&&59==r&&60==s)&&(!t||o)}(e.exports=m).fast={date:/^\d\d\d\d-[0-1]\d-[0-3]\d$/,time:/^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d:\d\d)?$/i,"date-time":/^\d\d\d\d-[0-1]\d-[0-3]\d[t\s](?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d:\d\d)$/i,uri:/^(?:[a-z][a-z0-9+-.]*:)(?:\/?\/)?[^\s]*$/i,"uri-reference":/^(?:(?:[a-z][a-z0-9+-.]*:)?\/?\/)?(?:[^\\\s#][^\s#]*)?(?:#[^\\\s]*)?$/i,"uri-template":c,url:h,email:/^[a-z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/i,hostname:r,ipv4:/^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/,ipv6:/^\s*(?:(?:(?:[0-9a-f]{1,4}:){7}(?:[0-9a-f]{1,4}|:))|(?:(?:[0-9a-f]{1,4}:){6}(?::[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){5}(?:(?:(?::[0-9a-f]{1,4}){1,2})|:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){4}(?:(?:(?::[0-9a-f]{1,4}){1,3})|(?:(?::[0-9a-f]{1,4})?:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){3}(?:(?:(?::[0-9a-f]{1,4}){1,4})|(?:(?::[0-9a-f]{1,4}){0,2}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){2}(?:(?:(?::[0-9a-f]{1,4}){1,5})|(?:(?::[0-9a-f]{1,4}){0,3}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){1}(?:(?:(?::[0-9a-f]{1,4}){1,6})|(?:(?::[0-9a-f]{1,4}){0,4}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?::(?:(?:(?::[0-9a-f]{1,4}){1,7})|(?:(?::[0-9a-f]{1,4}){0,5}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(?:%.+)?\s*$/i,regex:b,uuid:d,"json-pointer":u,"json-pointer-uri-fragment":g,"relative-json-pointer":p},m.full={date:f,time:C,"date-time":function(e){var t=e.split(I);return 2==t.length&&f(t[0])&&C(t[1],!0)},uri:function(e){return A.test(e)&&s.test(e)},"uri-reference":/^(?:[a-z][a-z0-9+\-.]*:)?(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'"()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?(?:\?(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i,"uri-template":c,url:h,email:/^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i,hostname:function(e){return e.length<=255&&r.test(e)},ipv4:/^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/,ipv6:/^\s*(?:(?:(?:[0-9a-f]{1,4}:){7}(?:[0-9a-f]{1,4}|:))|(?:(?:[0-9a-f]{1,4}:){6}(?::[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){5}(?:(?:(?::[0-9a-f]{1,4}){1,2})|:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){4}(?:(?:(?::[0-9a-f]{1,4}){1,3})|(?:(?::[0-9a-f]{1,4})?:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){3}(?:(?:(?::[0-9a-f]{1,4}){1,4})|(?:(?::[0-9a-f]{1,4}){0,2}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){2}(?:(?:(?::[0-9a-f]{1,4}){1,5})|(?:(?::[0-9a-f]{1,4}){0,3}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){1}(?:(?:(?::[0-9a-f]{1,4}){1,6})|(?:(?::[0-9a-f]{1,4}){0,4}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?::(?:(?:(?::[0-9a-f]{1,4}){1,7})|(?:(?::[0-9a-f]{1,4}){0,5}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(?:%.+)?\s*$/i,regex:b,uuid:d,"json-pointer":u,"json-pointer-uri-fragment":g,"relative-json-pointer":p};var I=/t|\s/i;var A=/\/|:/;var v=/[^\\]\\Z/;function b(e){if(v.test(e))return!1;try{return new RegExp(e),!0}catch(e){return!1}}},function(e,t,i){"use strict";var s=i(51),o=i(5).toHash;e.exports=function(){var n=[{type:"number",rules:[{maximum:["exclusiveMaximum"]},{minimum:["exclusiveMinimum"]},"multipleOf","format"]},{type:"string",rules:["maxLength","minLength","pattern","format"]},{type:"array",rules:["maxItems","minItems","items","contains","uniqueItems"]},{type:"object",rules:["maxProperties","minProperties","required","dependencies","propertyNames",{properties:["additionalProperties","patternProperties"]}]},{rules:["$ref","const","enum","not","anyOf","oneOf","allOf","if"]}],r=["type","$comment"];return n.all=o(r),n.types=o(["number","integer","string","array","object","boolean","null"]),n.forEach(function(e){e.rules=e.rules.map(function(e){var t;if("object"==typeof e){var i=Object.keys(e)[0];t=e[i],e=i,t.forEach(function(e){r.push(e),n.all[e]=!0})}return r.push(e),n.all[e]={keyword:e,code:s[e],implements:t}}),n.all.$comment={keyword:"$comment",code:s.$comment},e.type&&(n.types[e.type]=e)}),n.keywords=o(r.concat(["$schema","$id","id","$data","$async","title","description","default","definitions","examples","readOnly","writeOnly","contentMediaType","contentEncoding","additionalItems","then","else"])),n.custom={},n}},function(e,t,i){"use strict";e.exports={$ref:i(52),allOf:i(53),anyOf:i(54),$comment:i(55),const:i(56),contains:i(57),dependencies:i(58),enum:i(59),format:i(60),if:i(61),items:i(62),maximum:i(28),minimum:i(28),maxItems:i(29),minItems:i(29),maxLength:i(30),minLength:i(30),maxProperties:i(31),minProperties:i(31),multipleOf:i(63),not:i(64),oneOf:i(65),pattern:i(66),properties:i(67),propertyNames:i(68),required:i(69),uniqueItems:i(70),validate:i(27)}},function(e,t,i){"use strict";e.exports=function(e,t){var i,n,r=" ",s=e.level,o=e.dataLevel,a=e.schema[t],l=e.errSchemaPath+"/"+t,c=!e.opts.allErrors,h="data"+(o||""),d="valid"+s;if("#"==a||"#/"==a)n=e.isRoot?(i=e.async,"validate"):(i=!0===e.root.schema.$async,"root.refVal[0]");else{var u=e.resolveRef(e.baseId,a,e.isRoot);if(void 0===u){var g=e.MissingRefError.message(e.baseId,a);if("fail"==e.opts.missingRefs){e.logger.error(g),(C=C||[]).push(r),r="",!1!==e.createErrors?(r+=" { keyword: '$ref' , dataPath: (dataPath || '') + "+e.errorPath+" , schemaPath: "+e.util.toQuotedString(l)+" , params: { ref: '"+e.util.escapeQuotes(a)+"' } ",!1!==e.opts.messages&&(r+=" , message: 'can\\'t resolve reference "+e.util.escapeQuotes(a)+"' "),e.opts.verbose&&(r+=" , schema: "+e.util.toQuotedString(a)+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),r+=" } "):r+=" {} ";var p=r;r=C.pop(),!e.compositeRule&&c?e.async?r+=" throw new ValidationError(["+p+"]); ":r+=" validate.errors = ["+p+"]; return false; ":r+=" var err = "+p+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",c&&(r+=" if (false) { ")}else{if("ignore"!=e.opts.missingRefs)throw new e.MissingRefError(e.baseId,a,g);e.logger.warn(g),c&&(r+=" if (true) { ")}}else if(u.inline){var m=e.util.copy(e);m.level++;var f="valid"+m.level;m.schema=u.schema,m.schemaPath="",m.errSchemaPath=a,r+=" "+e.validate(m).replace(/validate\.schema/g,u.code)+" ",c&&(r+=" if ("+f+") { ")}else i=!0===u.$async||e.async&&!1!==u.$async,n=u.code}if(n){var C;(C=C||[]).push(r),r="",e.opts.passContext?r+=" "+n+".call(this, ":r+=" "+n+"( ",r+=" "+h+", (dataPath || '')",'""'!=e.errorPath&&(r+=" + "+e.errorPath);var I=r+=" , "+(o?"data"+(o-1||""):"parentData")+" , "+(o?e.dataPathArr[o]:"parentDataProperty")+", rootData) ";if(r=C.pop(),i){if(!e.async)throw new Error("async schema referenced by sync schema");c&&(r+=" var "+d+"; "),r+=" try { await "+I+"; ",c&&(r+=" "+d+" = true; "),r+=" } catch (e) { if (!(e instanceof ValidationError)) throw e; if (vErrors === null) vErrors = e.errors; else vErrors = vErrors.concat(e.errors); errors = vErrors.length; ",c&&(r+=" "+d+" = false; "),r+=" } ",c&&(r+=" if ("+d+") { ")}else r+=" if (!"+I+") { if (vErrors === null) vErrors = "+n+".errors; else vErrors = vErrors.concat("+n+".errors); errors = vErrors.length; } ",c&&(r+=" else { ")}return r}},function(e,t,i){"use strict";e.exports=function(e,t){var i=" ",n=e.schema[t],r=e.schemaPath+e.util.getProperty(t),s=e.errSchemaPath+"/"+t,o=!e.opts.allErrors,a=e.util.copy(e),l="";a.level++;var c="valid"+a.level,h=a.baseId,d=!0,u=n;if(u)for(var g,p=-1,m=u.length-1;p "+x+") { ";var R=c+"["+x+"]";u.schema=S,u.schemaPath=o+"["+x+"]",u.errSchemaPath=a+"/"+x,u.errorPath=e.util.getPathExpr(e.errorPath,x,e.opts.jsonPointers,!0),u.dataPathArr[f]=x;var E=e.validate(u);u.baseId=I,e.util.varOccurences(E,C)<2?i+=" "+e.util.varReplace(E,C,R)+" ":i+=" var "+C+" = "+R+"; "+E+" ",i+=" } ",l&&(i+=" if ("+p+") { ",g+="}")}if("object"==typeof A&&(e.opts.strictKeywords?"object"==typeof A&&0 "+s.length+") { for (var "+m+" = "+s.length+"; "+m+" < "+c+".length; "+m+"++) { ",u.errorPath=e.util.getPathExpr(e.errorPath,m,e.opts.jsonPointers,!0);R=c+"["+m+"]";u.dataPathArr[f]=m;E=e.validate(u);u.baseId=I,e.util.varOccurences(E,C)<2?i+=" "+e.util.varReplace(E,C,R)+" ":i+=" var "+C+" = "+R+"; "+E+" ",l&&(i+=" if (!"+p+") break; "),i+=" } } ",l&&(i+=" if ("+p+") { ",g+="}")}}else if(e.opts.strictKeywords?"object"==typeof s&&0 1e-"+e.opts.multipleOfPrecision+" ":n+=" division"+r+" !== parseInt(division"+r+") ",n+=" ) ",d&&(n+=" ) "),n+=" ) { ";var u=u||[];u.push(n),n="",!1!==e.createErrors?(n+=" { keyword: 'multipleOf' , dataPath: (dataPath || '') + "+e.errorPath+" , schemaPath: "+e.util.toQuotedString(l)+" , params: { multipleOf: "+i+" } ",!1!==e.opts.messages&&(n+=" , message: 'should be multiple of ",n+=d?"' + "+i:i+"'"),e.opts.verbose&&(n+=" , schema: ",n+=d?"validate.schema"+a:""+o,n+=" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ";var g=n;return n=u.pop(),!e.compositeRule&&c?e.async?n+=" throw new ValidationError(["+g+"]); ":n+=" validate.errors = ["+g+"]; return false; ":n+=" var err = "+g+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",n+="} ",c&&(n+=" else { "),n}},function(e,t,i){"use strict";e.exports=function(e,t){var i=" ",n=e.level,r=e.dataLevel,s=e.schema[t],o=e.schemaPath+e.util.getProperty(t),a=e.errSchemaPath+"/"+t,l=!e.opts.allErrors,c="data"+(r||""),h="errs__"+n,d=e.util.copy(e);d.level++;var u="valid"+d.level;if(e.opts.strictKeywords?"object"==typeof s&&0=e.opts.loopRequired,b=e.opts.ownProperties;if(l)if(i+=" var missing"+n+"; ",v){d||(i+=" var "+u+" = validate.schema"+o+"; ");var y="' + "+(E="schema"+n+"["+(x="i"+n)+"]")+" + '";e.opts._errorDataPathProperty&&(e.errorPath=e.util.getPathExpr(A,E,e.opts.jsonPointers)),i+=" var "+h+" = true; ",d&&(i+=" if (schema"+n+" === undefined) "+h+" = true; else if (!Array.isArray(schema"+n+")) "+h+" = false; else {"),i+=" for (var "+x+" = 0; "+x+" < "+u+".length; "+x+"++) { "+h+" = "+c+"["+u+"["+x+"]] !== undefined ",b&&(i+=" && Object.prototype.hasOwnProperty.call("+c+", "+u+"["+x+"]) "),i+="; if (!"+h+") break; } ",d&&(i+=" } "),(R=R||[]).push(i+=" if (!"+h+") { "),i="",!1!==e.createErrors?(i+=" { keyword: 'required' , dataPath: (dataPath || '') + "+e.errorPath+" , schemaPath: "+e.util.toQuotedString(a)+" , params: { missingProperty: '"+y+"' } ",!1!==e.opts.messages&&(i+=" , message: '",e.opts._errorDataPathProperty?i+="is a required property":i+="should have required property \\'"+y+"\\'",i+="' "),e.opts.verbose&&(i+=" , schema: validate.schema"+o+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+c+" "),i+=" } "):i+=" {} ";var w=i;i=R.pop(),!e.compositeRule&&l?e.async?i+=" throw new ValidationError(["+w+"]); ":i+=" validate.errors = ["+w+"]; return false; ":i+=" var err = "+w+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",i+=" } else { "}else{i+=" if ( ";var S=g;if(S)for(var x=-1,k=S.length-1;x 1) { ";var g=e.schema.items&&e.schema.items.type,p=Array.isArray(g);if(!g||"object"==g||"array"==g||p&&(0<=g.indexOf("object")||0<=g.indexOf("array")))n+=" outer: for (;i--;) { for (j = i; j--;) { if (equal("+h+"[i], "+h+"[j])) { "+d+" = false; break outer; } } } ";else{n+=" var itemIndices = {}, item; for (;i--;) { var item = "+h+"[i]; ";var m="checkDataType"+(p?"s":"");n+=" if ("+e.util[m](g,"item",!0)+") continue; ",p&&(n+=" if (typeof item == 'string') item = '\"' + item; "),n+=" if (typeof itemIndices[item] == 'number') { "+d+" = false; j = itemIndices[item]; break; } itemIndices[item] = i; } "}n+=" } ",u&&(n+=" } "),n+=" if (!"+d+") { ";var f=f||[];f.push(n),n="",!1!==e.createErrors?(n+=" { keyword: 'uniqueItems' , dataPath: (dataPath || '') + "+e.errorPath+" , schemaPath: "+e.util.toQuotedString(l)+" , params: { i: i, j: j } ",!1!==e.opts.messages&&(n+=" , message: 'should NOT have duplicate items (items ## ' + j + ' and ' + i + ' are identical)' "),e.opts.verbose&&(n+=" , schema: ",n+=u?"validate.schema"+a:""+o,n+=" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ";var C=n;n=f.pop(),!e.compositeRule&&c?e.async?n+=" throw new ValidationError(["+C+"]); ":n+=" validate.errors = ["+C+"]; return false; ":n+=" var err = "+C+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",n+=" } ",c&&(n+=" else { ")}else c&&(n+=" if (true) { ");return n}},function(e,t,i){"use strict";var l=["multipleOf","maximum","exclusiveMaximum","minimum","exclusiveMinimum","maxLength","minLength","pattern","additionalItems","maxItems","minItems","uniqueItems","maxProperties","minProperties","required","additionalProperties","enum","format","const"];e.exports=function(e,t){for(var i=0;ithis.results.length-1&&(t=0),this._setActiveResult(t,e)}}},{key:"previous",value:function(e){if(this.results){var t=this.results.length-1,i=null!==this.resultIndex?this.resultIndex-1:t;i<0&&(i=t),this._setActiveResult(i,e)}}},{key:"_setActiveResult",value:function(e,t){if(this.activeResult){var i=this.activeResult.node;"field"===this.activeResult.elem?delete i.searchFieldActive:delete i.searchValueActive,i.updateDom()}if(!this.results||!this.results[e])return this.resultIndex=void 0,void(this.activeResult=void 0);this.resultIndex=e;var n=this.results[this.resultIndex].node,r=this.results[this.resultIndex].elem;"field"===r?n.searchFieldActive=!0:n.searchValueActive=!0,this.activeResult=this.results[this.resultIndex],n.updateDom(),n.scrollTo(function(){t&&n.focus(r)})}},{key:"_clearDelay",value:function(){void 0!==this.timeout&&(clearTimeout(this.timeout),delete this.timeout)}},{key:"_onDelayedSearch",value:function(){this._clearDelay();var t=this;this.timeout=setTimeout(function(e){t._onSearch()},this.delay)}},{key:"_onSearch",value:function(e){this._clearDelay();var t=this.dom.search.value,i=0=e.length;o--)this.removeChild(this.childs[o],!1)}else if("object"===this.type){for(this.childs||(this.childs=[]),o=this.childs.length-1;0<=o;o--)E(e,this.childs[o].field)||this.removeChild(this.childs[o],!1);for(var l in s=0,e)E(e,l)&&(void 0===(i=e[l])||i instanceof Function||((n=this.findChildByProperty(l))?(n.setField(l,!0),n.setValue(i)):(n=new _(this.editor,{field:l,value:i}),r=s=e.childs.length;s--)this.removeChild(this.childs[s],!1)}else if("object"===e.type){for(this.childs||(this.childs=[]),r=0;r=e.childs.length;s--)this.removeChild(this.childs[s],!1)}else this.hideChilds(),delete this.append,delete this.showMore,delete this.expanded,delete this.childs,this.value=e.value;Array.isArray(o)!==Array.isArray(this.childs)&&this.recreateDom(),this.updateDom({updateIndexes:!0}),this.previousValue=this.value}},{key:"recreateDom",value:function(){if(this.dom&&this.dom.tr&&this.dom.tr.parentNode){var e=this._detachFromDom();this.clearDom(),this._attachToDom(e)}else this.clearDom()}},{key:"getValue",value:function(){if("array"===this.type){var t=[];return this.childs.forEach(function(e){t.push(e.getValue())}),t}if("object"!==this.type)return void 0===this.value&&this._getDomValue(),this.value;var i={};return this.childs.forEach(function(e){i[e.getField()]=e.getValue()}),i}},{key:"getInternalValue",value:function(){return"array"===this.type?{type:this.type,childs:this.childs.map(function(e){return e.getInternalValue()})}:"object"===this.type?{type:this.type,childs:this.childs.map(function(e){return{field:e.getField(),value:e.getInternalValue()}})}:(void 0===this.value&&this._getDomValue(),{type:this.type,value:this.value})}},{key:"getLevel",value:function(){return this.parent?this.parent.getLevel()+1:0}},{key:"getNodePath",value:function(){var e=this.parent?this.parent.getNodePath():[];return e.push(this),e}},{key:"clone",value:function(){var i=new _(this.editor);if(i.type=this.type,i.field=this.field,i.fieldInnerText=this.fieldInnerText,i.fieldEditable=this.fieldEditable,i.previousField=this.previousField,i.value=this.value,i.valueInnerText=this.valueInnerText,i.previousValue=this.previousValue,i.expanded=this.expanded,i.visibleChilds=this.visibleChilds,this.childs){var n=[];this.childs.forEach(function(e){var t=e.clone();t.setParent(i),n.push(t)}),i.childs=n}else i.childs=void 0;return i}},{key:"expand",value:function(t){this.childs&&(this.expanded=!0,this.dom.expand&&(this.dom.expand.className="jsoneditor-button jsoneditor-expanded"),this.showChilds(),!1!==t&&this.childs.forEach(function(e){e.expand(t)}))}},{key:"collapse",value:function(t){this.childs&&(this.hideChilds(),!1!==t&&this.childs.forEach(function(e){e.collapse(t)}),this.dom.expand&&(this.dom.expand.className="jsoneditor-button jsoneditor-collapsed"),this.expanded=!1)}},{key:"showChilds",value:function(){if(this.childs&&this.expanded){var e,t=this.dom.tr,i=t?t.parentNode:void 0;if(i){var n=this.getAppendDom();n.parentNode||((e=t.nextSibling)?i.insertBefore(n,e):i.appendChild(n));var r=Math.min(this.childs.length,this.visibleChilds);e=this._getNextTr();for(var s=0;sthis.visibleChilds){var r=this.childs[this.visibleChilds-1];this.insertBefore(e,r)}else this.appendChild(e);else this.insertBefore(e,t);i&&i.removeChild(n)}}},{key:"insertBefore",value:function(e,t){if(this._hasChilds()){if(this.visibleChilds++,"object"===this.type&&void 0===e.field&&e.setField(""),t===this.append)e.setParent(this),e.fieldEditable="object"===this.type,this.childs.push(e);else{var i=this.childs.indexOf(t);if(-1===i)throw new Error("Node not found");e.setParent(this),e.fieldEditable="object"===this.type,this.childs.splice(i,0,e)}if(this.expanded){var n=e.getDom(),r=t.getDom(),s=r?r.parentNode:void 0;r&&s&&s.insertBefore(n,r),e.showChilds(),this.showChilds()}this.updateDom({updateIndexes:!0}),e.updateDom({recurse:!0})}}},{key:"insertAfter",value:function(e,t){if(this._hasChilds()){var i=this.childs.indexOf(t),n=this.childs[i+1];n?this.insertBefore(e,n):this.appendChild(e)}}},{key:"search",value:function(t,i){Array.isArray(i)||(i=[]);var e=t?t.toLowerCase():void 0;delete this.searchField,delete this.searchValue,void 0!==this.field&&i.length<=this.MAX_SEARCH_RESULTS&&(-1!==String(this.field).toLowerCase().indexOf(e)&&(this.searchField=!0,i.push({node:this,elem:"field"})),this._updateDomField());this._hasChilds()?this.childs&&this.childs.forEach(function(e){e.search(t,i)}):void 0!==this.value&&i.length<=this.MAX_SEARCH_RESULTS&&(-1!==String(this.value).toLowerCase().indexOf(e)&&(this.searchValue=!0,i.push({node:this,elem:"value"})),this._updateDomValue());return i}},{key:"scrollTo",value:function(e){this.expandPathToNode(),this.dom.tr&&this.dom.tr.parentNode&&this.editor.scrollTo(this.dom.tr.offsetTop,e)}},{key:"expandPathToNode",value:function(){for(var e=this;e&&e.parent;){for(var t="array"===e.parent.type?e.index:e.parent.childs.indexOf(e);e.parent.visibleChilds/g,">").replace(/ {2}/g,"  ").replace(/^ /," ").replace(/ $/," "),i=JSON.stringify(t),n=i.substring(1,i.length-1);return!0===this.editor.options.escapeUnicode&&(n=Object(H.escapeUnicodeChars)(n)),n}},{key:"_unescapeHTML",value:function(e){var t='"'+this._escapeJSON(e)+'"';return Object(H.parse)(t).replace(/</g,"<").replace(/>/g,">").replace(/ |\u00A0/g," ").replace(/&/g,"&")}},{key:"_escapeJSON",value:function(e){for(var t="",i=0;ithis.parent.visibleChilds},T.prototype.onEvent=function(e){"keydown"===e.type&&this.onKeyDown(e)},T);function T(e,t){this.editor=e,this.parent=t,this.dom={}}var W=i(7),_=i(8),V={start:function(e,t){return 0===t.indexOf(e)},contain:function(e,t){return-1/g,">"),t.getBoundingClientRect().right}h.className="autocomplete dropdown",h.style.position="absolute",h.style.visibility="hidden";var u,g,p,i,m,f={onArrowDown:function(){},onArrowUp:function(){},onEnter:function(){},onTab:function(){},startFrom:0,options:[],element:null,elementHint:null,elementStyle:null,wrapper:o,show:function(e,t,i){var n=this;this.startFrom=t,this.wrapper.remove(),this.elementHint&&(this.elementHint.remove(),this.elementHint=null),""===r&&(r=window.getComputedStyle(e).getPropertyValue("font-size")),""===s&&(s=window.getComputedStyle(e).getPropertyValue("font-family")),h.style.marginLeft="0",h.style.marginTop=e.getBoundingClientRect().height+"px",this.options=i.map(String),this.element!==e&&(this.element=e,this.elementStyle={zIndex:this.element.style.zIndex,position:this.element.style.position,backgroundColor:this.element.style.backgroundColor,borderColor:this.element.style.borderColor}),this.element.style.zIndex=3,this.element.style.position="relative",this.element.style.backgroundColor="transparent",this.element.style.borderColor="transparent",this.elementHint=e.cloneNode(),this.elementHint.className="autocomplete hint",this.elementHint.style.zIndex=2,this.elementHint.style.position="absolute",this.elementHint.onfocus=function(){n.element.focus()},this.element.addEventListener&&(this.element.removeEventListener("keydown",b),this.element.addEventListener("keydown",b,!1),this.element.removeEventListener("blur",y),this.element.addEventListener("blur",y,!1)),o.appendChild(this.elementHint),o.appendChild(h),e.parentElement.appendChild(o),this.repaint(e)},setText:function(e){this.element.innerText=e},getText:function(){return this.element.innerText},hideDropDown:function(){this.wrapper.remove(),this.elementHint&&(this.elementHint.remove(),this.elementHint=null,C.hide(),this.element.style.zIndex=this.elementStyle.zIndex,this.element.style.position=this.elementStyle.position,this.element.style.backgroundColor=this.elementStyle.backgroundColor,this.element.style.borderColor=this.elementStyle.borderColor)},repaint:function(e){var t=e.innerText;t=t.replace("\n","");var i=this.options.length,n=t.substring(this.startFrom);c=t.substring(0,this.startFrom);for(var r=0;r"+e.substring(i.length)+"",u.appendChild(t),t})).length&&(1===g.length&&(i.toLowerCase()===g[0].__hint.toLowerCase()&&!l.caseSensitive||i===g[0].__hint&&l.caseSensitive)||g.length<2||(m.highlight(0),3*sthis.limit&&1m.d){var i=this;Object(C.addClassName)(i.frame,"busy"),i.dom.busyContent.innerText=t,setTimeout(function(){e(),Object(C.removeClassName)(i.frame,"busy"),i.dom.busyContent.innerText=""},100)}else e()},h.validate=c.validate,h._renderErrors=c._renderErrors;var d=[{mode:"preview",mixin:h,data:"json"}]}],r.c=n,r.d=function(e,t,i){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var i=Object.create(null);if(r.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var n in t)r.d(i,n,function(e){return t[e]}.bind(null,n));return i},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=35);function r(e){if(n[e])return n[e].exports;var t=n[e]={i:e,l:!1,exports:{}};return i[e].call(t.exports,t,t.exports,r),t.l=!0,t.exports}var i,n}); +//# sourceMappingURL=jsoneditor.map \ No newline at end of file diff --git a/ui/site/site.js b/ui/site/site.js new file mode 100644 index 0000000..8c17066 --- /dev/null +++ b/ui/site/site.js @@ -0,0 +1,16 @@ +function bunker_logout() +{ + localStorage.removeItem("xtoken"); + localStorage.removeItem("xtoken"); + localStorage.removeItem("login"); + document.location = "/"; +} + +function dateFormat(value, row, index) { + //return moment(value).format('DD/MM/YYYY'); + var d = new Date(parseInt(value) * 1000); + let f_date = d.getFullYear() + "-" + (d.getMonth() + 1) + "-" + d.getDate() + + " " + d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds() + //return d.toUTCString(); + return f_date; +} \ No newline at end of file diff --git a/ui/site/style.css b/ui/site/style.css new file mode 100644 index 0000000..2c24a47 --- /dev/null +++ b/ui/site/style.css @@ -0,0 +1,113 @@ +html { + height: 100%; + -webkit-font-smoothing: antialiased; +} +body, h1, h2, h3, h4, h5, h6 { + font-family: Roboto,-apple-system,system-ui,BlinkMacSystemFont,Segoe UI,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,Arial,sans-serif; + line-height: 1.5; +} +body { + font-size: 14px; + color: #72777a; + letter-spacing: 0.2px; + font-weight: 400; + padding:0; + margin:0; + height: 100%; + background-color: #f9fafb !important; +} +.container { + height: 100%; + width: 100%; + display: block; +} +.bigblock { + background-color: #fff !important; + border-radius: 3px !important; + border: 1px solid rgba(0, 0, 0, 0.0625) !important; + padding: 20px !important; + width: 100%; +} +div.bigblock table { + color: #72777a; +} +h4 { + color: #313435!important; + margin-bottom: 20px!important; + letter-spacing: .5px; + font-size: 1.3125rem; +} +.peer { + display: block; + height: auto; + -webkit-box-flex: 0; + -ms-flex: 0 0 auto; + flex: 0 0 auto; +} +.peers { + -webkit-box-sizing: border-box; + box-sizing: border-box; + display: -webkit-box!important; + display: -ms-flexbox!important; + display: flex!important; + -webkit-box-align: start; + -ms-flex-align: start; + align-items: flex-start; + -webkit-box-pack: start; + -ms-flex-pack: start; + justify-content: flex-start; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-flow: row wrap; + flex-flow: row wrap; + height: auto; + max-width: 100%; + margin: 0; + padding: 0; +} +.jc-sb { + -webkit-box-pack: justify; + -ms-flex-pack: justify; + justify-content: space-between; +} +.ai-c { + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} +.fxw-nw { + -ms-flex-wrap: nowrap; + flex-wrap: nowrap; +} +.btn-primary { + color: #fff; + background-color: #2196f3; + border-color: #2196f3; +} +.btn { + display: inline-block; + font-weight: 400; + text-align: center; + white-space: nowrap; + vertical-align: middle; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + border: 1px solid transparent; + padding: 6px 12px; + padding: .375rem .75rem; + font-size: 14px; + font-size: .875rem; + line-height: 1.5; + border-radius: .25rem; + -webkit-transition: background-color .15s ease-in-out,border-color .15s ease-in-out,-webkit-box-shadow .15s ease-in-out; + transition: background-color .15s ease-in-out,border-color .15s ease-in-out,-webkit-box-shadow .15s ease-in-out; + -o-transition: background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out; + transition: background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out; + transition: background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-box-shadow .15s ease-in-out; +} +.pY-120 { + padding-top: 120px!important; + padding-bottom: 120px!important; +} \ No newline at end of file diff --git a/ui/site/user-apps.html b/ui/site/user-apps.html new file mode 100644 index 0000000..eaac40c --- /dev/null +++ b/ui/site/user-apps.html @@ -0,0 +1,110 @@ + + + + + + + Data Bunker - list of apps + + + + + + + + + + + + + + + + +
    +
    +
    + +
    +
    +

    User apps data

    +

    text

    +
    
    +            
    +
    +
    + + \ No newline at end of file diff --git a/ui/site/user-audit.html b/ui/site/user-audit.html new file mode 100644 index 0000000..ca728ac --- /dev/null +++ b/ui/site/user-audit.html @@ -0,0 +1,102 @@ + + + + + + + Data Bunker - audit events + + + + + + + + + + + + + + + + + +
    +
    +
    + +
    +
    +

    Audit table

    +

    text here, text, text

    + + + + + + + + + + + +
    whenWhoAppTitleStatusMsg
    +
    +
    +
    + + + \ No newline at end of file diff --git a/ui/site/user-data-processing.html b/ui/site/user-data-processing.html new file mode 100644 index 0000000..6e0e9c8 --- /dev/null +++ b/ui/site/user-data-processing.html @@ -0,0 +1,166 @@ + + + + + + + Data Bunker - List of data processing requests + + + + + + + + + + + + +
    +
    +
    + +
    +
    +

    Data processing requests

    +

    List of all user consents. You can allow and cancel your consent request here.

    +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/ui/site/user-profile.html b/ui/site/user-profile.html new file mode 100644 index 0000000..ca9a733 --- /dev/null +++ b/ui/site/user-profile.html @@ -0,0 +1,146 @@ + + + + + + + Data Bunker - user profile + + + + + + + + + + + + + + + + +
    +
    +
    + +
    +
    +

    User record profile

    +

    text

    +
    +
    
    +
    +                
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    + + + \ No newline at end of file